Starting to implement experimental mode "WSPR-LF". Beware possible undesired side effects!

git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@7658 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
This commit is contained in:
Joe Taylor 2017-04-28 16:49:09 +00:00
parent 13842167ef
commit b256185ed1
13 changed files with 199 additions and 57 deletions

View File

@ -505,6 +505,7 @@ set (wsjt_FSRCS
lib/wqencode.f90
lib/fsk4hf/wspr5d.f90
lib/fsk4hf/wspr5sim.f90
lib/fsk4hf/wspr5_wav.f90
lib/fsk4hf/wsprlfsim.f90
lib/wspr_downsample.f90
lib/zplot9.f90

View File

@ -2,7 +2,7 @@
#define COMMONS_H
#define NSMAX 6827
#define NTMAX 120
#define NTMAX 300
#define RX_SAMPLE_RATE 12000
#ifdef __cplusplus

View File

@ -1,4 +1,4 @@
integer, parameter :: NTMAX=120
integer, parameter :: NTMAX=300
integer, parameter :: NMAX=NTMAX*12000 !Total sample intervals (one minute)
integer, parameter :: NDMAX=NTMAX*1500 !Sample intervals at 1500 Hz rate
integer, parameter :: NSMAX=6827 !Max length of saved spectra

View File

@ -15,7 +15,7 @@ subroutine genwspr5(msg,ichk,msgsent,itone,itype)
integer jd(NS+ND)
integer isync(48) !Long sync vector
integer ib13(13) !Barker 13 code
integer itone(NZ)
integer itone(NN)
integer*8 n8
data ib13/1,1,1,1,1,-1,-1,1,1,-1,1,-1,1/
data first/.true./

View File

@ -25,7 +25,7 @@ subroutine getfc1w(c,fs,fc1,xsnr)
smax=0.
ipk=0
fc1=0.
ia=nint(100.0/df1)
ia=nint(190.0/df1)
do i=-ia,ia
f=i*df1
if(s(i).gt.smax) then

48
lib/fsk4hf/wspr5_wav.f90 Normal file
View File

@ -0,0 +1,48 @@
subroutine wspr5_wav(baud,xdt,f0,itone,snrdb,iwave)
include 'wsprlf_params.f90'
parameter (NMAX=300*12000)
integer itone(NN)
integer*2 iwave(NMAX)
real*8 twopi,dt,dphi0,dphi1,dphi,phi
real dat(NMAX)
twopi=8.d0*atan(1.d0)
dt=1.d0/12000.d0
dat=0.
if(snrdb.lt.90) then
do i=1,NMAX
dat(i)=gran() !Generate gaussian noise
enddo
bandwidth_ratio=2500.0/6000.0
sig=sqrt(2*bandwidth_ratio)*10.0**(0.05*snrdb)
else
sig=1.0
endif
dphi0=twopi*(f0-0.25d0*baud)*dt
dphi1=twopi*(f0+0.25d0*baud)*dt
phi=0.d0
k=nint(xdt/dt)
do j=1,NN
dphi=dphi0
if(itone(j).eq.1) dphi=dphi1
if(k.eq.0) phi=-dphi
do i=1,NSPS0
k=k+1
phi=phi+dphi
if(phi.gt.twopi) phi=phi-twopi
xphi=phi
if(k.gt.0 .and. k.le.NMAX) dat(k)=dat(k) + sig*sin(xphi)
enddo
enddo
print*,NN,NSPS0,k
fac=32767.0
rms=100.0
if(snrdb.ge.90.0) iwave=nint(fac*dat)
if(snrdb.lt.90.0) iwave=nint(rms*dat)
return
end subroutine wspr5_wav

View File

@ -3,6 +3,8 @@ program wspr5d
! Simulate characteristics of a potential "WSPR-LF" mode using LDPC (300,60)
! code, OQPSK modulation, and 5 minute T/R sequences.
! Q: Would it be better for central Sync array to use both I and Q channels?
! Reception and Demodulation algorithm:
! 1. Compute coarse spectrum; find fc1 = approx carrier freq
! 2. Mix from fc1 to 0; LPF at +/- 0.75*R
@ -13,15 +15,14 @@ program wspr5d
! 7. Get soft bits from equalized data
include 'wsprlf_params.f90'
! Q: Would it be better for central Sync array to use both I and Q channels?
character arg*8,message*22,cbits*50
parameter (NMAX=300*12000)
character arg*8,message*22,cbits*50,infile*80,fname*16
complex csync(0:NZ-1) !Sync symbols only, from cbb
complex c(0:NZ-1) !Complex waveform
complex c1(0:NZ-1) !Complex waveform
complex zz(NS+ND) !Complex symbol values (intermediate)
complex z
real*8 fMHz
real rxdata(ND),llr(ND) !Soft symbols
real pp(2*NSPS) !Shaped pulse for OQPSK
real a(5) !For twkfreq1
@ -31,13 +32,14 @@ program wspr5d
integer isync(48) !Long sync vector
integer ib13(13) !Barker 13 code
integer*8 n8
integer*2 iwave(NMAX) !Generated full-length waveform
integer*1 idat(7)
integer*1 decoded(KK),apmask(ND),cw(ND)
data ib13/1,1,1,1,1,-1,-1,1,1,-1,1,-1,1/
nargs=iargc()
if(nargs.ne.1) then
print*,'Usage: wspr5d maxn'
if(nargs.lt.2) then
print*,'Usage: wspr5d maxn file1 [file2 ...]'
go to 999
endif
call getarg(1,arg)
@ -82,8 +84,17 @@ program wspr5d
endif
enddo
do ifile=1,9999
read(10,end=999) c
do ifile=1,nargs-1
call getarg(ifile+1,infile)
open(10,file=infile,status='old',access='stream')
if(index(infile,'.c5').gt.0) then
read(10,end=999) fname,ntrmin,fMHz,c
close(10)
read(fname(8:11),*) nutc
else
print*,'Wrong file format?'
go to 999
endif
! do i=0,NZ-1
! write(40,4001) i,c(i),csync(i)
!4001 format(i8,4f12.6)
@ -102,7 +113,8 @@ program wspr5d
jpk=0
iaa=0
ibb=NZ-1
do j=-20*NSPS,20*NSPS,NSPS/8
jmax=1260
do j=-jmax,jmax,NSPS/8
ia=j
ib=NZ-1+j
if(ia.lt.0) then
@ -171,8 +183,8 @@ program wspr5d
call wqdecode(idat,message,itype)
endif
nsnr=nint(xsnr)
write(*,1110) ifile,nsnr,xdt,fc1+fc2,message
1110 format(2i5,f7.2,f7.2,2x,a22)
enddo
write(*,1110) nutc,nsnr,xdt,fc1+fc2,message
1110 format(i4.4,i5,f7.2,f7.2,2x,a22)
enddo ! ifile loop
999 end program wspr5d

View File

@ -1,38 +1,40 @@
program wspr5sim
include 'wsprlf_params.f90'
character*12 arg
character*22 msg,msgsent
! Generate simulated data for the 5-minute "WSPR-LF" mode.
use wavhdr
include 'wsprlf_params.f90' !Set various constants
parameter (NMAX=300*12000)
type(hdr) h !Header for .wav file
character arg*12,fname*16
character msg*22,msgsent*22
complex c0(0:NZ-1)
complex c(0:NZ-1)
integer itone(NZ)
real*8 fMHz
integer itone(NN)
integer*2 iwave(NMAX) !Generated full-length waveform
! Get command-line argument(s)
nargs=iargc()
if(nargs.ne.5) then
print*,'Usage: wspr5sim "message" f0 DT nfiles snr'
print*,'Example: wspr5sim "K1ABC FN42 30" 1500 0.0 10 -33'
if(nargs.ne.6) then
print*,'Usage: wspr5sim "message" f0 DT nwav nfiles snr'
print*,'Example: wspr5sim "K1ABC FN42 30" 50 0.0 0 10 -33'
go to 999
endif
call getarg(1,msg) !Get message from command line
call getarg(1,msg) !Message to be transmitted
call getarg(2,arg)
read(arg,*) f0
read(arg,*) f0 !Freq relative to WSPR-band center (Hz)
call getarg(3,arg)
read(arg,*) xdt
read(arg,*) xdt !Time offset from nominal (s)
call getarg(4,arg)
read(arg,*) nfiles
read(arg,*) nwav !1 for *.wav file, 0 for *.c5 file
call getarg(5,arg)
read(arg,*) snrdb
call genwspr5(msg,ichk,msgsent,itone,itype)
txt=NN*NSPS0/12000.0
write(*,1000) f0,xdt,txt,snrdb,nfiles,msgsent
1000 format('f0:',f9.3,' DT:',f6.2,' txt:',f6.1,' SNR:',f6.1, &
' nfiles:',i3,2x,a22)
read(arg,*) nfiles !Number of files
call getarg(6,arg)
read(arg,*) snrdb !SNR_2500
twopi=8.0*atan(1.0)
fs=NSPS*12000.0/NSPS0 !Sample rate
fs=12000.0/NDOWN !Sample rate
dt=1.0/fs !Sample interval (s)
tt=NSPS*dt !Duration of "itone" symbols (s)
ts=2*NSPS*dt !Duration of OQPSK symbols (s)
@ -41,10 +43,16 @@ program wspr5sim
bandwidth_ratio=2500.0/(fs/2.0)
sig=sqrt(bandwidth_ratio) * 10.0**(0.05*snrdb)
if(snrdb.gt.90.0) sig=1.0
dphi0=twopi*(f0-0.25d0*baud)*dt
dphi1=twopi*(f0+0.25d0*baud)*dt
phi=0.d0
txt=NN*NSPS0/12000.0
call genwspr5(msg,ichk,msgsent,itone,itype) !Encode the message, get itone()
write(*,1000) f0,xdt,txt,snrdb,nfiles,msgsent
1000 format('f0:',f9.3,' DT:',f6.2,' txt:',f6.1,' SNR:',f6.1, &
' nfiles:',i3,2x,a22)
dphi0=twopi*(f0-0.25*baud)*dt
dphi1=twopi*(f0+0.25*baud)*dt
phi=0.0
c0=0.
k=-1 + nint(xdt/dt)
do j=1,NN
@ -62,14 +70,30 @@ program wspr5sim
c0=sig*c0 !Scale to requested sig level
do ifile=1,nfiles
if(snrdb.lt.90) then
do i=0,NZ-1 !Add gaussian noise at specified SNR
xnoise=gran()
ynoise=gran()
c(i)=c0(i) + cmplx(xnoise,ynoise)
enddo
if(nwav.eq.0) then
if(snrdb.lt.90) then
do i=0,NZ-1 !Add gaussian noise at specified SNR
xnoise=gran()
ynoise=gran()
c(i)=c0(i) + cmplx(xnoise,ynoise)
enddo
endif
write(fname,1100) ifile
1100 format('000000_',i4.4,'.c5')
open(10,file=fname,status='unknown',access='stream')
fMHz=10.1387d0
nmin=5
write(10) fname,nmin,fMHz,c
close(10)
else
call wspr5_wav(baud,xdt,f0,itone,snrdb,iwave)
h=default_header(12000,NMAX)
write(fname,1102) ifile
1102 format('000000_',i4.4,'.wav')
open(10,file=fname,status='unknown',access='stream')
write(10) h,iwave !Save the .wav file
close(10)
endif
write(10) c
enddo
999 end program wspr5sim

View File

@ -489,6 +489,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
ui->actionJT9_JT65->setActionGroup(modeGroup);
ui->actionJT4->setActionGroup(modeGroup);
ui->actionWSPR->setActionGroup(modeGroup);
ui->actionWSPR_LF->setActionGroup(modeGroup);
ui->actionEcho->setActionGroup(modeGroup);
ui->actionISCAT->setActionGroup(modeGroup);
ui->actionMSK144->setActionGroup(modeGroup);
@ -811,6 +812,7 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
if(m_mode=="JT65") on_actionJT65_triggered();
if(m_mode=="JT9+JT65") on_actionJT9_JT65_triggered();
if(m_mode=="WSPR") on_actionWSPR_triggered();
if(m_mode=="WSPR-LF") on_actionWSPR_LF_triggered();
if(m_mode=="ISCAT") on_actionISCAT_triggered();
if(m_mode=="MSK144") on_actionMSK144_triggered();
if(m_mode=="QRA64") on_actionQRA64_triggered();
@ -879,6 +881,9 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
splashTimer.setSingleShot (true);
splashTimer.start (20 * 1000);
if(m_config.my_callsign()=="K1JT" or m_config.my_callsign()=="K9AN" or
m_config.my_callsign()=="G4WJS") ui->actionWSPR_LF->setEnabled(true);
// this must be the last statement of constructor
if (!m_valid) throw std::runtime_error {"Fatal initialization exception"};
}
@ -1101,8 +1106,8 @@ void MainWindow::fixStop()
m_hsymStop=179;
if(m_mode=="WSPR") {
m_hsymStop=396;
} else if(m_mode=="WSPR-15") {
m_hsymStop=3090;
} else if(m_mode=="WSPR-LF") {
m_hsymStop=1030;
} else if(m_mode=="Echo") {
m_hsymStop=10;
} else if (m_mode=="JT4"){
@ -1536,6 +1541,7 @@ void MainWindow::on_actionSettings_triggered() //Setup Dialog
if(m_config.contestMode() != bcontest0) genStdMsgs(m_rpt);
}
if(m_mode=="WSPR") on_actionWSPR_triggered();
if(m_mode=="WSPR-LF") on_actionWSPR_LF_triggered();
if(m_mode=="Echo") on_actionEcho_triggered();
}
@ -2175,8 +2181,9 @@ void MainWindow::read_wav_file (QString const& fname)
auto n = file.read (reinterpret_cast<char *> (dec_data.d2),
std::min (max_bytes, file.size ()));
int frames_read = n / bytes_per_frame;
// qDebug() << "a" << max_bytes << n << frames_read << frames_read/12000.0 << m_TRperiod;
// zero unfilled remaining sample space
std::memset (&dec_data.d2[0] + n, 0, max_bytes - n);
std::memset(&dec_data.d2[frames_read],0,max_bytes - n);
if (11025 == file.format ().sampleRate ()) {
short sample_size = file.format ().sampleSize ();
wav12_ (dec_data.d2, dec_data.d2, &frames_read, &sample_size);
@ -2793,7 +2800,8 @@ void MainWindow::guiUpdate()
if(m_modeTx=="JT9") txDuration=1.0 + 85.0*m_nsps/12000.0; // JT9
if(m_modeTx=="JT65") txDuration=1.0 + 126*4096/11025.0; // JT65
if(m_mode=="QRA64") txDuration=1.0 + 84*6912/12000.0; // QRA64
if(m_mode=="WSPR") txDuration=2.0 + 162*8192/12000.0; // WSPR
if(m_mode=="WSPR") txDuration=2.0 + 162*8192/12000.0; // WSPR
if(m_mode=="WSPR-LF") txDuration=2.0 + 412*8640/12000.0; // WSPR-LF
if(m_mode=="ISCAT" or m_mode=="MSK144" or m_bFast9) {
txDuration=m_TRperiod-0.25; // ISCAT, JT9-fast, MSK144
}
@ -2977,7 +2985,7 @@ void MainWindow::guiUpdate()
&m_currentMessageType, len1, len1);
if(m_mode=="QRA64") genqra64_(message, &ichk, msgsent, const_cast<int *> (itone),
&m_currentMessageType, len1, len1);
if(m_mode.startsWith ("WSPR")) genwspr_(message, msgsent, const_cast<int *> (itone),
if(m_mode=="WSPR") genwspr_(message, msgsent, const_cast<int *> (itone),
len1, len1);
if(m_modeTx=="MSK144") {
bool bcontest=m_config.contestMode();
@ -4558,6 +4566,23 @@ void MainWindow::on_actionWSPR_triggered()
statusChanged();
}
void MainWindow::on_actionWSPR_LF_triggered()
{
on_actionWSPR_triggered();
m_mode="WSPR-LF";
switch_mode (Modes::WSPR);
m_modeTx="WSPR-LF";
m_TRperiod=300;
m_modulator->setPeriod(m_TRperiod); // TODO - not thread safe
m_detector->setPeriod(m_TRperiod); // TODO - not thread safe
m_hsymStop=1030;
m_toneSpacing=0.5*12000.0/8640.0;
setup_status_bar (false);
ui->actionWSPR_LF->setChecked(true);
m_wideGraph->setPeriod(m_TRperiod,m_nsps);
statusChanged();
}
void MainWindow::on_actionEcho_triggered()
{
on_actionJT4_triggered();

View File

@ -234,6 +234,7 @@ private slots:
void networkError (QString const&);
void on_ClrAvgButton_clicked();
void on_actionWSPR_triggered();
void on_actionWSPR_LF_triggered();
void on_syncSpinBox_valueChanged(int n);
void on_TxPowerComboBox_currentIndexChanged(const QString &arg1);
void on_sbTxPercent_valueChanged(int n);

View File

@ -2,6 +2,14 @@
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>867</width>
<height>583</height>
</rect>
</property>
<property name="windowTitle">
<string>WSJT-X by K1JT</string>
</property>
@ -2358,6 +2366,14 @@ QPushButton[state=&quot;ok&quot;] {
</layout>
</widget>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>867</width>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
@ -2436,6 +2452,7 @@ QPushButton[state=&quot;ok&quot;] {
<addaction name="actionMSK144"/>
<addaction name="separator"/>
<addaction name="actionWSPR"/>
<addaction name="actionWSPR_LF"/>
<addaction name="separator"/>
<addaction name="actionEcho"/>
<addaction name="separator"/>
@ -2980,6 +2997,20 @@ QPushButton[state=&quot;ok&quot;] {
<string>View phase response ...</string>
</property>
</action>
<action name="actionWSPR_LF">
<property name="checkable">
<bool>true</bool>
</property>
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>WSPR-LF</string>
</property>
<property name="toolTip">
<string>Experimental LF/MF mode</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>

View File

@ -50,7 +50,7 @@ void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, Audi
m_sink = sink;
QAudioFormat format (device.preferredFormat());
qDebug () << "Preferred audio input format:" << format;
// qDebug () << "Preferred audio input format:" << format;
format.setChannelCount (AudioDevice::Mono == channel ? 1 : 2);
format.setCodec ("audio/pcm");
format.setSampleRate (12000 * downSampleFactor);
@ -65,11 +65,11 @@ void SoundInput::start(QAudioDeviceInfo const& device, int framesPerBuffer, Audi
if (!device.isFormatSupported (format))
{
qDebug () << "Nearest supported audio format:" << device.nearestFormat (format);
// qDebug () << "Nearest supported audio format:" << device.nearestFormat (format);
Q_EMIT error (tr ("Requested input audio format is not supported on device."));
return;
}
qDebug () << "Selected audio input format:" << format;
// qDebug () << "Selected audio input format:" << format;
m_stream.reset (new QAudioInput {device, format});
if (audioError ())

View File

@ -54,7 +54,7 @@ void SoundOutput::setFormat (QAudioDeviceInfo const& device, unsigned channels,
m_msBuffered = msBuffered;
QAudioFormat format (device.preferredFormat ());
qDebug () << "Preferred audio output format:" << format;
// qDebug () << "Preferred audio output format:" << format;
format.setChannelCount (channels);
format.setCodec ("audio/pcm");
format.setSampleRate (48000);
@ -69,7 +69,7 @@ void SoundOutput::setFormat (QAudioDeviceInfo const& device, unsigned channels,
{
Q_EMIT error (tr ("Requested output audio format is not supported on device."));
}
qDebug () << "Selected audio output format:" << format;
// qDebug () << "Selected audio output format:" << format;
m_stream.reset (new QAudioOutput (device, format));
audioError ();