Merged master 8748
This commit is contained in:
@@ -1,187 +0,0 @@
|
||||
program chkfft
|
||||
|
||||
! Tests and times one-dimensional FFTs computed by FFTW3
|
||||
use FFTW3
|
||||
parameter (NMAX=8*1024*1024) !Maximum FFT length
|
||||
complex a(NMAX),b(NMAX),c(NMAX)
|
||||
real ar(NMAX),br(NMAX),cr(NMAX)
|
||||
real mflops
|
||||
integer*8 plan1,plan2 !Pointers to stored plans
|
||||
character infile*12,arg*8
|
||||
logical list
|
||||
common/patience/npatience
|
||||
equivalence (a,ar),(b,br),(c,cr)
|
||||
! include 'fftw3.f90' !FFTW definitions
|
||||
|
||||
nargs=iargc()
|
||||
if(nargs.ne.6) then
|
||||
print*,'Usage: chkfft <nfft | infile> nr nw nc np inplace'
|
||||
print*,' nfft: length of FFT'
|
||||
print*,' nfft=0: do lengths 2^n, n=2^4 to 2^23'
|
||||
print*,' infile: name of file with nfft values, one per line'
|
||||
print*,' nr: 0/1 to not read (or read) wisdom'
|
||||
print*,' nw: 0/1 to not write (or write) wisdom'
|
||||
print*,' nc: 0/1 for real or complex data'
|
||||
print*,' np: 0-4 patience for finding best algorithm'
|
||||
print*,' inplace: 1 for inplace, 0 otherwise'
|
||||
go to 999
|
||||
endif
|
||||
|
||||
list=.false.
|
||||
nfft=-1
|
||||
call getarg(1,infile)
|
||||
open(10,file=infile,status='old',err=1)
|
||||
list=.true. !A valid file name was provided
|
||||
go to 2
|
||||
1 read(infile,*) nfft !Take first argument to be nfft
|
||||
2 call getarg(2,arg)
|
||||
read(arg,*) nr
|
||||
call getarg(3,arg)
|
||||
read(arg,*) nw
|
||||
call getarg(4,arg)
|
||||
read(arg,*) ncomplex
|
||||
call getarg(5,arg)
|
||||
read(arg,*) npatience
|
||||
call getarg(6,arg)
|
||||
read(arg,*) inplace
|
||||
|
||||
if(list) write(*,1000) infile,nr,nw,ncomplex,npatience
|
||||
1000 format(/'infile: ',a12,' nr:',i2,' nw',i2,' nc:',i2,' np:',i2/)
|
||||
if(.not.list) write(*,1002) nfft,nr,nw,ncomplex,npatience
|
||||
1002 format(/'nfft: ',i10,' nr:',i2,' nw',i2,' nc:',i2,' np:',i2/)
|
||||
|
||||
nflags=FFTW_ESTIMATE
|
||||
if(npatience.eq.1) nflags=FFTW_ESTIMATE_PATIENT
|
||||
if(npatience.eq.2) nflags=FFTW_MEASURE
|
||||
if(npatience.eq.3) nflags=FFTW_PATIENT
|
||||
if(npatience.eq.4) nflags=FFTW_EXHAUSTIVE
|
||||
|
||||
open(12,file='chkfft.out',status='unknown')
|
||||
open(13,file='fftwf_wisdom.dat',status='unknown')
|
||||
|
||||
if(nr.ne.0) then
|
||||
call import_wisdom_from_file(isuccess,13)
|
||||
if(isuccess.eq.0) then
|
||||
write(*,1010)
|
||||
1010 format('Failed to import FFTW wisdom.')
|
||||
go to 999
|
||||
endif
|
||||
endif
|
||||
|
||||
idum=-1 !Set random seed
|
||||
ndim=1 !One-dimensional transforms
|
||||
do i=1,NMAX !Set random data
|
||||
x=gran()
|
||||
y=gran()
|
||||
b(i)=cmplx(x,y) !Generate random data
|
||||
enddo
|
||||
|
||||
iters=1000000
|
||||
if(list .or. (nfft.gt.0)) then
|
||||
n1=1
|
||||
n2=1
|
||||
if(nfft.eq.-1) n2=999999
|
||||
write(*,1020)
|
||||
1020 format(' NFFT Time rms MHz MFlops iters', &
|
||||
' tplan'/61('-'))
|
||||
else
|
||||
n1=4
|
||||
n2=23
|
||||
write(*,1030)
|
||||
1030 format(' n N=2^n Time rms MHz MFlops iters', &
|
||||
' tplan'/63('-'))
|
||||
endif
|
||||
|
||||
do ii=n1,n2 !Test one or more FFT lengths
|
||||
if(list) then
|
||||
read(10,*,end=900) nfft !Read nfft from file
|
||||
else if(n2.gt.n1) then
|
||||
nfft=2**ii !Do powers of 2
|
||||
endif
|
||||
|
||||
iformf=1
|
||||
iformb=1
|
||||
if(ncomplex.eq.0) then
|
||||
iformf=0 !Real-to-complex transform
|
||||
iformb=-1 !Complex-to-real (inverse) transform
|
||||
endif
|
||||
|
||||
if(nfft.gt.NMAX) go to 900
|
||||
a(1:nfft)=b(1:nfft) !Copy test data into a()
|
||||
t0=second()
|
||||
if(inplace.ne.0) then
|
||||
if(ncomplex.ne.0) then
|
||||
call sfftw_plan_dft_1d(plan1,nfft,a,a,FFTW_FORWARD,nflags)
|
||||
call sfftw_plan_dft_1d(plan2,nfft,a,a,FFTW_BACKWARD,nflags)
|
||||
else
|
||||
call sfftw_plan_dft_r2c_1d(plan1,nfft,a,a,nflags)
|
||||
call sfftw_plan_dft_c2r_1d(plan2,nfft,a,a,nflags)
|
||||
endif
|
||||
else
|
||||
if(ncomplex.ne.0) then
|
||||
call sfftw_plan_dft_1d(plan1,nfft,a,c,FFTW_FORWARD,nflags)
|
||||
call sfftw_plan_dft_1d(plan2,nfft,c,a,FFTW_BACKWARD,nflags)
|
||||
else
|
||||
call sfftw_plan_dft_r2c_1d(plan1,nfft,a,c,nflags)
|
||||
call sfftw_plan_dft_c2r_1d(plan2,nfft,c,a,nflags)
|
||||
endif
|
||||
endif
|
||||
|
||||
t2=second()
|
||||
tplan=t2-t0 !Total planning time for this length
|
||||
|
||||
total=0.
|
||||
do iter=1,iters !Now do many iterations
|
||||
a(1:nfft)=b(1:nfft) !Copy test data into a()
|
||||
t0=second()
|
||||
call sfftw_execute(plan1)
|
||||
call sfftw_execute(plan2)
|
||||
t1=second()
|
||||
total=total+t1-t0
|
||||
if(total.ge.1.0) go to 40 !Cut iterations short if t>1 s
|
||||
enddo
|
||||
iter=iters
|
||||
|
||||
40 time=0.5*total/iter !Time for one FFT of current length
|
||||
tplan=0.5*tplan-time !Planning time for one FFT
|
||||
if(tplan.lt.0) tplan=0.
|
||||
a(1:nfft)=a(1:nfft)/nfft
|
||||
|
||||
! Compute RMS difference between original array and back-transformed array.
|
||||
sq=0.
|
||||
if(ncomplex.eq.1) then
|
||||
do i=1,nfft
|
||||
sq=sq + real(a(i)-b(i))**2 + imag(a(i)-b(i))**2
|
||||
enddo
|
||||
else
|
||||
do i=1,nfft
|
||||
sq=sq + (ar(i)-br(i))**2
|
||||
enddo
|
||||
endif
|
||||
rms=sqrt(sq/nfft)
|
||||
|
||||
freq=1.e-6*nfft/time
|
||||
mflops=5.0/(1.e6*time/(nfft*log(float(nfft))/log(2.0)))
|
||||
if(n2.eq.1 .or. n2.eq.999999) then
|
||||
write(*,1050) nfft,time,rms,freq,mflops,iter,tplan
|
||||
write(12,1050) nfft,time,rms,freq,mflops,iter,tplan
|
||||
1050 format(i8,f11.7,f12.8,f7.2,f8.1,i8,f6.1)
|
||||
else
|
||||
write(*,1060) ii,nfft,time,rms,freq,mflops,iter,tplan
|
||||
write(12,1060) ii,nfft,time,rms,freq,mflops,iter,tplan
|
||||
1060 format(i2,i8,f11.7,f12.8,f7.2,f8.1,i8,f6.1)
|
||||
endif
|
||||
enddo
|
||||
|
||||
900 continue
|
||||
if(nw.eq.1) then
|
||||
rewind 13
|
||||
call export_wisdom_to_file(13)
|
||||
! write(*,1070)
|
||||
!1070 format(/'Exported FFTW wisdom')
|
||||
endif
|
||||
|
||||
call sfftw_destroy_plan(plan1)
|
||||
call sfftw_destroy_plan(plan2)
|
||||
|
||||
999 end program chkfft
|
||||
@@ -0,0 +1,23 @@
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,73 +0,0 @@
|
||||
subroutine sync8(iwave,xdt,f1,s)
|
||||
|
||||
include 'ft8_params.f90'
|
||||
parameter (IZ=10,JZ=20)
|
||||
complex cx(0:NH1)
|
||||
real s(NH1,NHSYM)
|
||||
real savg(NH1)
|
||||
real x(NFFT1)
|
||||
real sync2d(-IZ:IZ,-JZ:JZ)
|
||||
integer*2 iwave(NMAX)
|
||||
integer icos7(0:6)
|
||||
data icos7/2,5,6,0,4,1,3/ !Costas 7x7 tone pattern
|
||||
equivalence (x,cx)
|
||||
|
||||
! Compute symbol spectra at half-symbol steps.
|
||||
savg=0.
|
||||
istep=NSPS/2
|
||||
tstep=istep/12000.0
|
||||
df=12000.0/NFFT1
|
||||
|
||||
fac=1.0/300.0
|
||||
do j=1,NHSYM
|
||||
ia=(j-1)*istep + 1
|
||||
ib=ia+NSPS-1
|
||||
x(1:NSPS)=fac*iwave(ia:ib)
|
||||
x(NSPS+1:)=0.
|
||||
call four2a(x,NFFT1,1,-1,0) !r2c FFT
|
||||
do i=1,NH1
|
||||
s(i,j)=real(cx(i))**2 + aimag(cx(i))**2
|
||||
enddo
|
||||
savg=savg + s(1:NH1,j)
|
||||
enddo
|
||||
|
||||
ia=nint(30.0/df)
|
||||
ib=nint(3000.0/df)
|
||||
savg=savg/NHSYM
|
||||
pmax=0.
|
||||
i0=0
|
||||
do i=ia,ib
|
||||
p=sum(savg(i-8:i+8))/17.0
|
||||
if(p.gt.pmax) then
|
||||
pmax=p
|
||||
i0=i-7
|
||||
endif
|
||||
enddo
|
||||
|
||||
tmax=0.
|
||||
ipk=0
|
||||
jpk=0
|
||||
j0=1
|
||||
do i=-IZ,IZ
|
||||
do j=-JZ,JZ
|
||||
t=0.
|
||||
do n=0,6
|
||||
k=j0+j+2*n
|
||||
if(k.ge.1) t=t + s(i0+i+2*icos7(n),k)
|
||||
t=t + s(i0+i+2*icos7(n),k+72)
|
||||
if(k+144.le.NHSYM) t=t + s(i0+i+2*icos7(n),k+144)
|
||||
enddo
|
||||
sync2d(i,j)=t
|
||||
if(t.gt.tmax) then
|
||||
tmax=t
|
||||
jpk=j
|
||||
ipk=i
|
||||
endif
|
||||
enddo
|
||||
enddo
|
||||
f0=i0*df
|
||||
f1=(i0+ipk)*df
|
||||
xdt=jpk*tstep
|
||||
|
||||
return
|
||||
end subroutine sync8
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Example of a (10000,5000) LDPC code with varying numbers of check per bit,
|
||||
# tested on Additive White Gaussian Noise channels with noise standard
|
||||
# deviations varying from 0.80 to 0.95. The code has 20% columns with two
|
||||
# check bits, 70% columns with three check bits, and 10% columns with seven
|
||||
# check bits.
|
||||
#
|
||||
# Testing is done by transmitting random messages, with pipes used so that
|
||||
# intermediate files are avoided. Decoding is done using a maximum of 250
|
||||
# iterations of probability propagation.
|
||||
|
||||
set -e # Stop if an error occurs
|
||||
set -v # Echo commands as they are read
|
||||
|
||||
make-ldpc ex-ldpcvar-5000a.pchk 5000 10000 2 evenboth 2x2/7x3/1x7 no4cycle
|
||||
make-gen ex-ldpcvar-5000a.pchk ex-ldpcvar-5000a.gen dense
|
||||
rand-src ex-ldpcvar-5000a.src 1 5000x100
|
||||
|
||||
# NOISE STANDARD DEVIATION 0.80, Eb/N0 = 1.94 dB
|
||||
|
||||
encode ex-ldpcvar-5000a.pchk ex-ldpcvar-5000a.gen ex-ldpcvar-5000a.src - \
|
||||
| transmit - - 1 awgn 0.80 \
|
||||
| decode ex-ldpcvar-5000a.pchk - - awgn 0.80 prprp 250 \
|
||||
| verify ex-ldpcvar-5000a.pchk - ex-ldpcvar-5000a.gen ex-ldpcvar-5000a.src
|
||||
|
||||
# NOISE STANDARD DEVIATION 0.85, Eb/N0 = 1.41 dB
|
||||
|
||||
encode ex-ldpcvar-5000a.pchk ex-ldpcvar-5000a.gen ex-ldpcvar-5000a.src - \
|
||||
| transmit - - 1 awgn 0.85 \
|
||||
| decode ex-ldpcvar-5000a.pchk - - awgn 0.85 prprp 250 \
|
||||
| verify ex-ldpcvar-5000a.pchk - ex-ldpcvar-5000a.gen ex-ldpcvar-5000a.src
|
||||
|
||||
# NOISE STANDARD DEVIATION 0.90, Eb/N0 = 0.92 dB
|
||||
|
||||
encode ex-ldpcvar-5000a.pchk ex-ldpcvar-5000a.gen ex-ldpcvar-5000a.src - \
|
||||
| transmit - - 1 awgn 0.90 \
|
||||
| decode ex-ldpcvar-5000a.pchk - - awgn 0.90 prprp 250 \
|
||||
| verify ex-ldpcvar-5000a.pchk - ex-ldpcvar-5000a.gen ex-ldpcvar-5000a.src
|
||||
|
||||
# NOISE STANDARD DEVIATION 0.95, Eb/N0 = 0.45 dB
|
||||
|
||||
encode ex-ldpcvar-5000a.pchk ex-ldpcvar-5000a.gen ex-ldpcvar-5000a.src - \
|
||||
| transmit - - 1 awgn 0.95 \
|
||||
| decode ex-ldpcvar-5000a.pchk - - awgn 0.95 prprp 250 \
|
||||
| verify ex-ldpcvar-5000a.pchk - ex-ldpcvar-5000a.gen ex-ldpcvar-5000a.src
|
||||
@@ -1,123 +0,0 @@
|
||||
#include "fastgraph.h"
|
||||
|
||||
#include "commons.h"
|
||||
#include <QSettings>
|
||||
#include <QApplication>
|
||||
#include "fastplot.h"
|
||||
#include "SettingsGroup.hpp"
|
||||
|
||||
#include "ui_fastgraph.h"
|
||||
#include "moc_fastgraph.cpp"
|
||||
|
||||
#define NSMAX2 1366
|
||||
|
||||
FastGraph::FastGraph(QSettings * settings, QWidget *parent) :
|
||||
QDialog {parent, Qt::Window | Qt::WindowTitleHint |
|
||||
Qt::WindowCloseButtonHint |
|
||||
Qt::WindowMinimizeButtonHint},
|
||||
m_settings {settings},
|
||||
m_ave {40},
|
||||
ui {new Ui::FastGraph}
|
||||
{
|
||||
ui->setupUi(this);
|
||||
setWindowTitle (QApplication::applicationName () + " - " + tr ("Fast Graph"));
|
||||
installEventFilter(parent); //Installing the filter
|
||||
ui->fastPlot->setCursor(Qt::CrossCursor);
|
||||
|
||||
//Restore user's settings
|
||||
SettingsGroup g {m_settings, "FastGraph"};
|
||||
restoreGeometry (m_settings->value ("geometry", saveGeometry ()).toByteArray ());
|
||||
ui->fastPlot->setPlotZero(m_settings->value("PlotZero", 0).toInt());
|
||||
ui->fastPlot->setPlotGain(m_settings->value("PlotGain", 0).toInt());
|
||||
ui->zeroSlider->setValue(ui->fastPlot->m_plotZero);
|
||||
ui->gainSlider->setValue(ui->fastPlot->m_plotGain);
|
||||
ui->fastPlot->setGreenZero(m_settings->value("GreenZero", 0).toInt());
|
||||
ui->greenZeroSlider->setValue(ui->fastPlot->m_greenZero);
|
||||
ui->controls_widget->setVisible (!m_settings->value("HideControls", false).toBool ());
|
||||
|
||||
connect (ui->fastPlot, &FPlotter::fastPick, this, &FastGraph::fastPick);
|
||||
}
|
||||
|
||||
void FastGraph::closeEvent (QCloseEvent * e)
|
||||
{
|
||||
saveSettings ();
|
||||
QDialog::closeEvent (e);
|
||||
}
|
||||
|
||||
FastGraph::~FastGraph ()
|
||||
{
|
||||
}
|
||||
|
||||
void FastGraph::saveSettings()
|
||||
{
|
||||
//Save user's settings
|
||||
SettingsGroup g {m_settings, "FastGraph"};
|
||||
m_settings->setValue ("geometry", saveGeometry ());
|
||||
m_settings->setValue("PlotZero",ui->fastPlot->m_plotZero);
|
||||
m_settings->setValue("PlotGain",ui->fastPlot->m_plotGain);
|
||||
m_settings->setValue("GreenZero",ui->fastPlot->m_greenZero);
|
||||
m_settings->setValue("GreenGain",ui->fastPlot->m_greenGain);
|
||||
m_settings->setValue ("HideControls", ui->controls_widget->isHidden ());
|
||||
}
|
||||
|
||||
void FastGraph::plotSpec(bool diskData, int UTCdisk)
|
||||
{
|
||||
ui->fastPlot->m_diskData=diskData;
|
||||
ui->fastPlot->m_UTCdisk=UTCdisk;
|
||||
ui->fastPlot->draw();
|
||||
}
|
||||
|
||||
void FastGraph::on_gainSlider_valueChanged(int value)
|
||||
{
|
||||
ui->fastPlot->setPlotGain(value);
|
||||
ui->fastPlot->draw();
|
||||
}
|
||||
|
||||
void FastGraph::on_zeroSlider_valueChanged(int value)
|
||||
{
|
||||
ui->fastPlot->setPlotZero(value);
|
||||
ui->fastPlot->draw();
|
||||
}
|
||||
|
||||
void FastGraph::on_greenZeroSlider_valueChanged(int value)
|
||||
{
|
||||
ui->fastPlot->setGreenZero(value);
|
||||
ui->fastPlot->draw();
|
||||
}
|
||||
|
||||
void FastGraph::setTRperiod(int n)
|
||||
{
|
||||
m_TRperiod=n;
|
||||
ui->fastPlot->setTRperiod(m_TRperiod);
|
||||
}
|
||||
|
||||
void FastGraph::on_pbAutoLevel_clicked()
|
||||
{
|
||||
float sum=0.0;
|
||||
for(int i=0; i<=fast_jh; i++) {
|
||||
sum += fast_green[i];
|
||||
}
|
||||
m_ave=sum/fast_jh;
|
||||
ui->gainSlider->setValue(127-int(2.2*m_ave));
|
||||
ui->zeroSlider->setValue(int(m_ave)+20);
|
||||
ui->greenZeroSlider->setValue(160-int(3.3*m_ave));
|
||||
}
|
||||
|
||||
void FastGraph::setMode(QString mode) //setMode
|
||||
{
|
||||
ui->fastPlot->setMode(mode);
|
||||
}
|
||||
|
||||
void FastGraph::keyPressEvent(QKeyEvent *e)
|
||||
{
|
||||
switch(e->key())
|
||||
{
|
||||
case Qt::Key_M:
|
||||
if(e->modifiers() & Qt::ControlModifier) {
|
||||
ui->controls_widget->setVisible (!ui->controls_widget->isVisible ());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
QDialog::keyPressEvent (e);
|
||||
}
|
||||
}
|
||||
@@ -1,328 +0,0 @@
|
||||
#include "Modulator.hpp"
|
||||
#include <limits>
|
||||
#include <qmath.h>
|
||||
#include <QDateTime>
|
||||
#include <QDebug>
|
||||
#include "mainwindow.h"
|
||||
#include "soundout.h"
|
||||
|
||||
#include "moc_Modulator.cpp"
|
||||
|
||||
extern float gran(); // Noise generator (for tests only)
|
||||
|
||||
#define RAMP_INCREMENT 64 // MUST be an integral factor of 2^16
|
||||
|
||||
#if defined (WSJT_SOFT_KEYING)
|
||||
# define SOFT_KEYING WSJT_SOFT_KEYING
|
||||
#else
|
||||
# define SOFT_KEYING 1
|
||||
#endif
|
||||
|
||||
double constexpr Modulator::m_twoPi;
|
||||
|
||||
// float wpm=20.0;
|
||||
// unsigned m_nspd=1.2*48000.0/wpm;
|
||||
// m_nspd=3072; //18.75 WPM
|
||||
|
||||
Modulator::Modulator (unsigned frameRate, unsigned periodLengthInSeconds,
|
||||
QObject * parent)
|
||||
: AudioDevice {parent}
|
||||
, m_quickClose {false}
|
||||
, m_phi {0.0}
|
||||
, m_toneSpacing {0.0}
|
||||
, m_fSpread {0.0}
|
||||
, m_frameRate {frameRate}
|
||||
, m_period {periodLengthInSeconds}
|
||||
, m_state {Idle}
|
||||
, m_tuning {false}
|
||||
, m_cwLevel {false}
|
||||
, m_j0 {-1}
|
||||
, m_toneFrequency0 {1500.0}
|
||||
{
|
||||
}
|
||||
|
||||
void Modulator::start (unsigned symbolsLength, double framesPerSymbol,
|
||||
double frequency, double toneSpacing,
|
||||
SoundOutput * stream, Channel channel,
|
||||
bool synchronize, bool fastMode, double dBSNR, int TRperiod)
|
||||
{
|
||||
Q_ASSERT (stream);
|
||||
// Time according to this computer which becomes our base time
|
||||
qint64 ms0 = QDateTime::currentMSecsSinceEpoch() % 86400000;
|
||||
|
||||
if (m_state != Idle)
|
||||
{
|
||||
stop ();
|
||||
}
|
||||
|
||||
m_quickClose = false;
|
||||
|
||||
m_symbolsLength = symbolsLength;
|
||||
m_isym0 = std::numeric_limits<unsigned>::max (); // big number
|
||||
m_frequency0 = 0.;
|
||||
m_phi = 0.;
|
||||
m_addNoise = dBSNR < 0.;
|
||||
m_nsps = framesPerSymbol;
|
||||
m_frequency = frequency;
|
||||
m_amp = std::numeric_limits<qint16>::max ();
|
||||
m_toneSpacing = toneSpacing;
|
||||
m_bFastMode=fastMode;
|
||||
m_TRperiod=TRperiod;
|
||||
|
||||
// noise generator parameters
|
||||
if (m_addNoise) {
|
||||
m_snr = qPow (10.0, 0.05 * (dBSNR - 6.0));
|
||||
m_fac = 3000.0;
|
||||
if (m_snr > 1.0) m_fac = 3000.0 / m_snr;
|
||||
}
|
||||
|
||||
unsigned mstr = ms0 % (1000 * m_period); // ms in period
|
||||
m_ic = (mstr / 1000) * m_frameRate; // we start exactly N seconds
|
||||
// into period where N is the next whole second
|
||||
if(m_bFastMode) m_ic=0;
|
||||
|
||||
m_silentFrames = 0;
|
||||
// calculate number of silent frames to send
|
||||
if (synchronize && !m_tuning && !m_bFastMode) {
|
||||
m_silentFrames = m_ic + m_frameRate - (mstr * m_frameRate / 1000);
|
||||
}
|
||||
if(m_ic==0 and (m_silentFrames/48000.0 > 0.6) and m_nsps==1920 and m_period==15) {
|
||||
// FT8 mode: Start audio at t=0.5 s rather than t=1.0 s.
|
||||
m_silentFrames=m_silentFrames-24000;
|
||||
}
|
||||
|
||||
initialize (QIODevice::ReadOnly, channel);
|
||||
Q_EMIT stateChanged ((m_state = (synchronize && m_silentFrames) ?
|
||||
Synchronizing : Active));
|
||||
m_stream = stream;
|
||||
if (m_stream) m_stream->restart (this);
|
||||
}
|
||||
|
||||
void Modulator::tune (bool newState)
|
||||
{
|
||||
m_tuning = newState;
|
||||
if (!m_tuning) stop (true);
|
||||
}
|
||||
|
||||
void Modulator::stop (bool quick)
|
||||
{
|
||||
m_quickClose = quick;
|
||||
close ();
|
||||
}
|
||||
|
||||
void Modulator::close ()
|
||||
{
|
||||
if (m_stream)
|
||||
{
|
||||
if (m_quickClose)
|
||||
{
|
||||
m_stream->reset ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_stream->stop ();
|
||||
}
|
||||
}
|
||||
if (m_state != Idle)
|
||||
{
|
||||
Q_EMIT stateChanged ((m_state = Idle));
|
||||
}
|
||||
AudioDevice::close ();
|
||||
}
|
||||
|
||||
qint64 Modulator::readData (char * data, qint64 maxSize)
|
||||
{
|
||||
double toneFrequency=1500.0;
|
||||
if(m_nsps==6) {
|
||||
toneFrequency=1000.0;
|
||||
m_frequency=1000.0;
|
||||
m_frequency0=1000.0;
|
||||
}
|
||||
if(maxSize==0) return 0;
|
||||
Q_ASSERT (!(maxSize % qint64 (bytesPerFrame ()))); // no torn frames
|
||||
Q_ASSERT (isOpen ());
|
||||
|
||||
qint64 numFrames (maxSize / bytesPerFrame ());
|
||||
qint16 * samples (reinterpret_cast<qint16 *> (data));
|
||||
qint16 * end (samples + numFrames * (bytesPerFrame () / sizeof (qint16)));
|
||||
qint64 framesGenerated (0);
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case Synchronizing:
|
||||
{
|
||||
if (m_silentFrames) { // send silence up to first second
|
||||
framesGenerated = qMin (m_silentFrames, numFrames);
|
||||
for ( ; samples != end; samples = load (0, samples)) { // silence
|
||||
}
|
||||
m_silentFrames -= framesGenerated;
|
||||
return framesGenerated * bytesPerFrame ();
|
||||
}
|
||||
|
||||
Q_EMIT stateChanged ((m_state = Active));
|
||||
m_cwLevel = false;
|
||||
m_ramp = 0; // prepare for CW wave shaping
|
||||
}
|
||||
// fall through
|
||||
|
||||
case Active:
|
||||
{
|
||||
unsigned int isym=0;
|
||||
if(!m_tuning) isym=m_ic/(4.0*m_nsps); // Actual fsample=48000
|
||||
bool slowCwId=((isym >= m_symbolsLength) && (icw[0] > 0)) && (!m_bFastMode);
|
||||
if(m_TRperiod==3) slowCwId=false;
|
||||
bool fastCwId=false;
|
||||
static bool bCwId=false;
|
||||
qint64 ms = QDateTime::currentMSecsSinceEpoch();
|
||||
float tsec=0.001*(ms % (1000*m_TRperiod));
|
||||
if(m_bFastMode and (icw[0]>0) and (tsec>(m_TRperiod-5.0))) fastCwId=true;
|
||||
if(!m_bFastMode) m_nspd=2560; // 22.5 WPM
|
||||
|
||||
if(slowCwId or fastCwId) { // Transmit CW ID?
|
||||
m_dphi = m_twoPi*m_frequency/m_frameRate;
|
||||
if(m_bFastMode and !bCwId) {
|
||||
m_frequency=1500; // Set params for CW ID
|
||||
m_dphi = m_twoPi*m_frequency/m_frameRate;
|
||||
m_symbolsLength=126;
|
||||
m_nsps=4096.0*12000.0/11025.0;
|
||||
m_ic=2246949;
|
||||
m_nspd=2560; // 22.5 WPM
|
||||
if(icw[0]*m_nspd/48000.0 > 4.0) m_nspd=4.0*48000.0/icw[0]; //Faster CW for long calls
|
||||
}
|
||||
bCwId=true;
|
||||
unsigned ic0 = m_symbolsLength * 4 * m_nsps;
|
||||
unsigned j(0);
|
||||
|
||||
while (samples != end) {
|
||||
j = (m_ic - ic0)/m_nspd + 1; // symbol of this sample
|
||||
bool level {bool (icw[j])};
|
||||
m_phi += m_dphi;
|
||||
if (m_phi > m_twoPi) m_phi -= m_twoPi;
|
||||
qint16 sample=0;
|
||||
float amp=32767.0;
|
||||
float x=0;
|
||||
if(m_ramp!=0) {
|
||||
x=qSin(float(m_phi));
|
||||
if(SOFT_KEYING) {
|
||||
amp=qAbs(qint32(m_ramp));
|
||||
if(amp>32767.0) amp=32767.0;
|
||||
}
|
||||
sample=round(amp*x);
|
||||
}
|
||||
if(m_bFastMode) {
|
||||
sample=0;
|
||||
if(level) sample=32767.0*x;
|
||||
}
|
||||
if (int (j) <= icw[0] && j < NUM_CW_SYMBOLS) { // stop condition
|
||||
samples = load (postProcessSample (sample), samples);
|
||||
++framesGenerated;
|
||||
++m_ic;
|
||||
} else {
|
||||
Q_EMIT stateChanged ((m_state = Idle));
|
||||
return framesGenerated * bytesPerFrame ();
|
||||
}
|
||||
|
||||
// adjust ramp
|
||||
if ((m_ramp != 0 && m_ramp != std::numeric_limits<qint16>::min ()) || level != m_cwLevel) {
|
||||
// either ramp has terminated at max/min or direction has changed
|
||||
m_ramp += RAMP_INCREMENT; // ramp
|
||||
}
|
||||
m_cwLevel = level;
|
||||
}
|
||||
return framesGenerated * bytesPerFrame ();
|
||||
} else {
|
||||
bCwId=false;
|
||||
} //End of code for CW ID
|
||||
|
||||
double const baud (12000.0 / m_nsps);
|
||||
// fade out parameters (no fade out for tuning)
|
||||
unsigned int i0,i1;
|
||||
if(m_tuning) {
|
||||
i1 = i0 = (m_bFastMode ? 999999 : 9999) * m_nsps;
|
||||
} else {
|
||||
i0=(m_symbolsLength - 0.017) * 4.0 * m_nsps;
|
||||
i1= m_symbolsLength * 4.0 * m_nsps;
|
||||
}
|
||||
if(m_bFastMode and !m_tuning) {
|
||||
i1=m_TRperiod*48000 - 24000;
|
||||
i0=i1-816;
|
||||
}
|
||||
|
||||
|
||||
for (unsigned i = 0; i < numFrames && m_ic <= i1; ++i) {
|
||||
isym=0;
|
||||
if(!m_tuning and m_TRperiod!=3) isym=m_ic / (4.0 * m_nsps); //Actual fsample=48000
|
||||
if(m_bFastMode) isym=isym%m_symbolsLength;
|
||||
if (isym != m_isym0 || m_frequency != m_frequency0) {
|
||||
if(itone[0]>=100) {
|
||||
m_toneFrequency0=itone[0];
|
||||
} else {
|
||||
if(m_toneSpacing==0.0) {
|
||||
m_toneFrequency0=m_frequency + itone[isym]*baud;
|
||||
} else {
|
||||
m_toneFrequency0=m_frequency + itone[isym]*m_toneSpacing;
|
||||
}
|
||||
}
|
||||
// qDebug() << "B" << m_bFastMode << m_ic << numFrames << isym << itone[isym]
|
||||
// << m_toneFrequency0 << m_nsps;
|
||||
m_dphi = m_twoPi * m_toneFrequency0 / m_frameRate;
|
||||
m_isym0 = isym;
|
||||
m_frequency0 = m_frequency; //???
|
||||
}
|
||||
|
||||
int j=m_ic/480;
|
||||
if(m_fSpread>0.0 and j!=m_j0) {
|
||||
float x1=(float)qrand()/RAND_MAX;
|
||||
float x2=(float)qrand()/RAND_MAX;
|
||||
toneFrequency = m_toneFrequency0 + 0.5*m_fSpread*(x1+x2-1.0);
|
||||
m_dphi = m_twoPi * toneFrequency / m_frameRate;
|
||||
m_j0=j;
|
||||
}
|
||||
|
||||
m_phi += m_dphi;
|
||||
if (m_phi > m_twoPi) m_phi -= m_twoPi;
|
||||
if (m_ic > i0) m_amp = 0.98 * m_amp;
|
||||
if (m_ic > i1) m_amp = 0.0;
|
||||
|
||||
samples = load (postProcessSample (m_amp * qSin (m_phi)), samples);
|
||||
++framesGenerated;
|
||||
++m_ic;
|
||||
}
|
||||
|
||||
if (m_amp == 0.0) { // TODO G4WJS: compare double with zero might not be wise
|
||||
if (icw[0] == 0) {
|
||||
// no CW ID to send
|
||||
Q_EMIT stateChanged ((m_state = Idle));
|
||||
return framesGenerated * bytesPerFrame ();
|
||||
}
|
||||
m_phi = 0.0;
|
||||
}
|
||||
|
||||
m_frequency0 = m_frequency;
|
||||
// done for this chunk - continue on next call
|
||||
return framesGenerated * bytesPerFrame ();
|
||||
}
|
||||
// fall through
|
||||
|
||||
case Idle:
|
||||
break;
|
||||
}
|
||||
|
||||
Q_ASSERT (Idle == m_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint16 Modulator::postProcessSample (qint16 sample) const
|
||||
{
|
||||
if (m_addNoise) { // Test frame, we'll add noise
|
||||
qint32 s = m_fac * (gran () + sample * m_snr / 32768.0);
|
||||
if (s > std::numeric_limits<qint16>::max ()) {
|
||||
s = std::numeric_limits<qint16>::max ();
|
||||
}
|
||||
if (s < std::numeric_limits<qint16>::min ()) {
|
||||
s = std::numeric_limits<qint16>::min ();
|
||||
}
|
||||
sample = s;
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 9.7 KiB |
Reference in New Issue
Block a user