From 6950c262c1cdf671a25109a0cd51e6ab85160236 Mon Sep 17 00:00:00 2001 From: Joe Taylor Date: Mon, 15 Oct 2012 17:43:49 +0000 Subject: [PATCH] Working toward implementing JT9 decoder in WSJT-X. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@2663 ab8295b8-cf94-4d9e-aec4-7959e3be5d79 --- jt9.txt | 44 ++++++++++++++++++++++---------------------- lib/jt9.f90 | 10 +++++----- lib/redsync.f90 | 44 ++++++++++++++++++++++++++++++++++++++++++++ lib/symspec.f90 | 29 +++++++++++++++-------------- lib/sync9.f90 | 1 - mainwindow.cpp | 20 +++++++------------- mainwindow.h | 3 +-- mainwindow.ui | 6 +++--- plotter.cpp | 40 ++++++++++++---------------------------- plotter.h | 4 +++- widegraph.cpp | 32 ++++++++++++++++++++++++-------- widegraph.h | 11 +++++------ widegraph.ui | 7 +++++++ 13 files changed, 148 insertions(+), 103 deletions(-) create mode 100644 lib/redsync.f90 diff --git a/jt9.txt b/jt9.txt index 8d29b1151..a2bea4ab1 100644 --- a/jt9.txt +++ b/jt9.txt @@ -10,25 +10,25 @@ used for synchronization, so a transmission requires a total of 207/3 Exact symbol lengths are chosen so that nsps, the number of samples per symbol (at 12000 samples per second) is a number with no prime factor greater than 7. This choice makes for efficient FFTs. Tone -spacing of the 9-FSK modulation is df=1/tsym=12000/nsps, the same as +spacing of the 9-FSK modulation is df=1/tsym=12000/nsps, equal to the keying rate. The total occupied bandwidth is 9*df. Parameters of five JT9 sub-modes are summarized in the following table, along with S/N thresholds measured by simulation on an AWGN channel. -------------------------------------------------------------------------- -Mode nsps nsps2 df tsym BW S/N* Tdec Tfree Factors - 12000 1500 (Hz) (s) (Hz) (dB) (s) (s) of nsps -------------------------------------------------------------------------- -JT9-1 6912 864 1.736 0.58 15.6 -26.9 52.5 7.5 2^8 3^3 -JT9-2 15360 1920 0.781 1.28 7.0 -30.2 112.3 7.7 2^10 3 5 -JT9-5 40960 5120 0.293 3.41 2.6 -34.4 293.6 6.4 2^13 5 -JT9-10 82944 10368 0.145 6.91 1.3 -37.5 591.0 9.0 2^10 3^4 -JT9-30 252000 31500 0.048 21.00 0.4 -42.3 1788.5 11.5 2^5 3^2 5^3 7 -------------------------------------------------------------------------- +-------------------------------------------------------------------------- +Mode nsps nsps2 df tsym BW S/N* Tdec Tfree Factors + 12000 1500 (Hz) (s) (Hz) (dB) (s) (s) of nsps nfft3 +-------------------------------------------------------------------------- +JT9-1 6912 864 1.736 0.58 15.6 -26.9 52.5 7.5 2^8 3^3 1024 +JT9-2 15360 1920 0.781 1.28 7.0 -30.2 112.3 7.7 2^10 3 5 2048 +JT9-5 40960 5120 0.293 3.41 2.6 -34.4 293.6 6.4 2^13 5 6144 +JT9-10 82944 10368 0.145 6.91 1.3 -37.5 591.0 9.0 2^10 3^4 12288 +JT9-30 252000 31500 0.048 21.00 0.4 -42.3 1788.5 11.5 2^5 3^2 5^3 7 32768 +-------------------------------------------------------------------------- * Noise power measured in a 2500 Hz bandwidth. - +NB: nfft3 might be doubled and used with a sin^2 window. Transmitting ------------ @@ -37,27 +37,27 @@ Transmitting 3. Interleave to scramble the bit order 4. Assemble 3-bit groups to make (206+1)/3 = 69 symbols 5. Gray-code the symbol values -6. Insert 16 sync symbols ==> 69+15=81 channel symbols, values 0-8 +6. Insert 16 sync symbols ==> 69+16=85 channel symbols, values 0-8 Receiving --------- 1. Apply noise blanking with the timf2 method 2. Filter to 1000 Hz bandwidth and downsample (1/8) to 1500 Hz, saving - complex data to array c0(1350000). (use FIR? NFFT2/NFFT2A?) -3. Compute symbol-length spectra at half-symbol steps. Use for - waterfall display s(22000) and save in ss(184,22000) and + complex data to array c0(2,700,000). +3. Compute spectra at half-symbol steps. Use for waterfall display + s(22000) and save in ss(184,22000) and savg(22000), for detecting sync vectors. -4. At time Tdec, find sync vectors in ss(); get estimates of DF, DT +4. At time Tdec, find sync vectors in ss(); get approx DF or list of DFs 5. Do full-length FFT, NFFT1=96*nsps2, zero-padded as required. -6. For each candidate signal, do inverse FFT of length 1536. This - yields 16 complex samples per symbol, and sync tone should be +6. For each candidate signal, do inverse FFT of length 1536 (or 3072?). + This yields 16 complex samples per symbol, and sync tone should be close to zero frequency. 7. Use afc65b method to get improved values of DF, DT. 8. Tweak freq and time offset to 0. -9. Compute 8-bin spectra of 69 data symbols: s2(8,69). Re-order bins - by removing Gray code. -10. Compute soft symbols for 206 bits. +9. Compute 8-bin spectra of 69 data symbols: ssym(0:7,69). Re-order the + bins to remove Gray code. +10. Compute soft symbols for 206 bits (bit 207 is always 0). 11. Remove interleaving 12. Pack bits into bytes, send to Fano decoder 13. If Fano succeeds, remove source encoding and display user message. diff --git a/lib/jt9.f90 b/lib/jt9.f90 index 2811dfa31..acda16884 100644 --- a/lib/jt9.f90 +++ b/lib/jt9.f90 @@ -11,7 +11,7 @@ program jt9 parameter (NSMAX=22000) !Max length of saved spectra integer*4 ihdr(11) real*4 s(NSMAX) - real*4 ccfred(NSMAX) + real*4 red(NSMAX) logical*1 lstrong(0:1023) integer*1 i1SoftSymbols(207) character*22 msg @@ -83,7 +83,7 @@ program jt9 if(nhsym.ge.1 .and. nhsym.ne.nhsym0) then ! Emit signal readyForFFT call symspec(k,ntrperiod,nsps,ndiskdat,nb,nbslider,pxdb, & - s,f0a,df3,ihsym,nzap,slimit,lstrong) + s,red,f0a,df3,ihsym,nzap,slimit,lstrong) nhsym0=nhsym if(ihsym.ge.184) go to 10 endif @@ -99,13 +99,13 @@ program jt9 nfqso=1500 ! Get sync, approx freq - call sync9(ss,tstep,f0a,df3,ntol,nfqso,sync,fpk,ccfred) + call sync9(ss,tstep,f0a,df3,ntol,nfqso,sync,fpk,red) fpk0=fpk iz=1000.0/df3 do i=1,iz freq=1000.0 + (i-1)*df3 - write(72,3001) freq,ccfred(i) -3001 format(2f10.3) + write(72,3001) freq,red(i),db(red(i)) +3001 format(3f10.3) enddo flush(72) diff --git a/lib/redsync.f90 b/lib/redsync.f90 new file mode 100644 index 000000000..cdfb286a2 --- /dev/null +++ b/lib/redsync.f90 @@ -0,0 +1,44 @@ +subroutine redsync(ss,ntrperiod,ihsym,iz,red) + + Parameter (NSMAX=22000) + real*4 ss(184,NSMAX) + real*4 red(NSMAX) + integer ii(16) !Locations of sync half-symbols + data ii/1,11,21,31,41,51,61,77,89,101,113,125,137,149,161,169/ + + lagmax=9 + if(ntrperiod.eq.2) lagmax=5 + if(ntrperiod.eq.5) lagmax=2 + if(ntrperiod.eq.10) lagmax=1 + if(ntrperiod.eq.30) lagmax=1 + + do i=1,iz + smax=0. + do lag=-lagmax,lagmax + sig=0. + ns=0 + ref=0. + nr=0 + do j=1,16 + k=ii(j)+lag + if(k.ge.1 .and. k.le.ihsym) then + sig=sig + ss(k,i) + ns=ns+1 + endif + do n=k+2,k+8,2 + if(n.ge.1 .and. n.le.ihsym) then + ref=ref + ss(n,i) + nr=nr+1 + endif + enddo + enddo + s=0. + if(ref.gt.0.0) s=(sig/ns)/(ref/nr) + if(s.gt.smax) smax=s + enddo + red(i)=db(smax) + enddo + + return +end subroutine redsync + diff --git a/lib/symspec.f90 b/lib/symspec.f90 index 24855141a..3c6de69dc 100644 --- a/lib/symspec.f90 +++ b/lib/symspec.f90 @@ -1,5 +1,5 @@ -subroutine symspec(k,ntrperiod,nsps,ndiskdat,nb,nbslider,pxdb,s,f0a,df3, & - ihsym,nzap,slimit,lstrong) +subroutine symspec(k,ntrperiod,nsps,ndiskdat,nb,nbslider,pxdb,s,red, & + f0a,df3,ihsym,nzap,slimit,lstrong) ! Input: ! k pointer to the most recent new data @@ -23,11 +23,11 @@ subroutine symspec(k,ntrperiod,nsps,ndiskdat,nb,nbslider,pxdb,s,f0a,df3, & parameter (NFFT1=1024) parameter (NFFT2=1024,NFFT2A=NFFT2/8) parameter (MAXFFT3=32768) - real*4 s(NSMAX),w(NFFT1),w3(MAXFFT3) + real*4 s(NSMAX),w3(MAXFFT3) real*4 x0(NFFT1),x1(NFFT1) real*4 x2(NFFT1+105) - real*4 xx(NMAX) real*4 ssum(NSMAX) + real*4 red(NSMAX) complex cx(0:MAXFFT3-1) logical*1 lstrong(0:1023) !Should be (0:512) integer*2 id2 @@ -73,7 +73,6 @@ subroutine symspec(k,ntrperiod,nsps,ndiskdat,nb,nbslider,pxdb,s,f0a,df3, & peaklimit=sigmas*max(10.0,rms) faclim=3.0 px=0. - df2=12000.0/NFFT2 nwindow=2 ! nwindow=0 !### No windowing ### @@ -137,20 +136,22 @@ subroutine symspec(k,ntrperiod,nsps,ndiskdat,nb,nbslider,pxdb,s,f0a,df3, & 999 continue - call pctile(s,iz,50,xmed0) s(1:iz)=s(1:iz)/xmed0 call pctile(ssum,iz,50,xmed1) savg(1:iz)=ssum(1:iz)/xmed1 -! if(ihsym.eq.160) then -! rewind 71 -! do i=1,iz -! write(71,3003) 1000+i*df3,savg(i) -!3003 format(2f12.3) -! enddo -! flush(71) -! endif + call redsync(ss,ntrperiod,ihsym,iz,red) + + if(ihsym.eq.160) then + rewind 71 + do i=1,iz + write(71,3003) 1000+i*df3,savg(i),red(i) +3003 format(3f12.3) + enddo + flush(71) + endif + return end subroutine symspec diff --git a/lib/sync9.f90 b/lib/sync9.f90 index 7315b97fa..b5424522b 100644 --- a/lib/sync9.f90 +++ b/lib/sync9.f90 @@ -4,7 +4,6 @@ subroutine sync9(ss,tstep,f0a,df3,ntol,nfqso,sync,fpk,ccfred) real ss(184,NSMAX) real ccfred(NSMAX) - integer ii0(16) integer ii(16) !Locations of sync half-symbols data ii/1,11,21,31,41,51,61,77,89,101,113,125,137,149,161,169/ integer isync(85) !Sync vector for half-symbols diff --git a/mainwindow.cpp b/mainwindow.cpp index 02dc35acc..43ac2d8e7 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -97,7 +97,7 @@ MainWindow::MainWindow(QWidget *parent) : m_myGrid="FN20qi"; m_appDir = QApplication::applicationDirPath(); m_saveDir="/users/joe/wsjtx/install/save"; - m_txFreq=125; + m_txFreq=1500; m_setftx=0; m_loopall=false; m_startAnother=false; @@ -153,7 +153,7 @@ MainWindow::MainWindow(QWidget *parent) : m_monitoring=true; // Start with Monitoring ON soundInThread.setMonitoring(m_monitoring); m_diskData=false; - m_tol=500; + m_tol=50; g_pWideGraph->setTol(m_tol); // Create "m_worked", a dictionary of all calls in wsjt.log @@ -301,7 +301,7 @@ void MainWindow::readSettings() //-------------------------------------------------------------- dataSink() void MainWindow::dataSink(int k) { - static float s[NSMAX],splot[NSMAX]; + static float s[NSMAX],red[NSMAX],splot[NSMAX]; static int n=0; static int ihsym=0; static int nzap=0; @@ -328,7 +328,7 @@ void MainWindow::dataSink(int k) nb=0; if(m_NB) nb=1; trmin=m_TRperiod/60; - symspec_(&k, &trmin, &m_nsps, &ndiskdat, &nb, &m_NBslider, &px, s, + symspec_(&k, &trmin, &m_nsps, &ndiskdat, &nb, &m_NBslider, &px, s, red, &f0a, &df3, &ihsym, &nzap, &slimit, lstrong); if(ihsym <=0) return; QString t; @@ -337,7 +337,7 @@ void MainWindow::dataSink(int k) lab4->setText(t); ui->xThermo->setValue((double)px); //Update the thermometer if(m_monitoring || m_diskData) { - g_pWideGraph->dataSink2(s,df3,ihsym,m_diskData,lstrong); + g_pWideGraph->dataSink2(s,red,df3,ihsym,m_diskData,lstrong); } //Average over specified number of spectra @@ -594,7 +594,7 @@ void MainWindow::createStatusBar() //createStatusBar void MainWindow::on_tolSpinBox_valueChanged(int i) //tolSpinBox { - static int ntol[] = {10,20,50,100,200,500,1000}; + static int ntol[] = {1,2,5,10,20,50,100,200,500,1000}; m_tol=ntol[i]; g_pWideGraph->setTol(m_tol); ui->labTol1->setText(QString::number(ntol[i])); @@ -753,12 +753,6 @@ void MainWindow::on_actionDelete_all_wav_files_in_SaveDir_triggered() } } -void MainWindow::on_actionFind_Delta_Phi_triggered() //Find dPhi -{ - m_RxLog |= 8; - on_DecodeButton_clicked(); -} - void MainWindow::on_actionF4_sets_Tx6_triggered() //F4 sets Tx6 { m_kb8rq = !m_kb8rq; @@ -810,7 +804,7 @@ void MainWindow::on_actionAvailable_suffixes_and_add_on_prefixes_triggered() void MainWindow::on_DecodeButton_clicked() //Decode request { - + qDebug() << "A" << g_pWideGraph->QSOfreq() << m_tol; } void MainWindow::freezeDecode(int n) //freezeDecode() diff --git a/mainwindow.h b/mainwindow.h index fb5fd39fd..655d6d355 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -65,7 +65,6 @@ private slots: void on_actionOpen_next_in_directory_triggered(); void on_actionDecode_remaining_files_in_directory_triggered(); void on_actionDelete_all_wav_files_in_SaveDir_triggered(); - void on_actionFind_Delta_Phi_triggered(); void on_actionF4_sets_Tx6_triggered(); void on_actionNo_shorthands_if_Tx1_triggered(); void on_actionNo_Deep_Search_triggered(); @@ -213,7 +212,7 @@ extern void getDev(int* numDevices,char hostAPI_DeviceName[][50], extern "C" { //----------------------------------------------------- C and Fortran routines void symspec_(int* k, int* ntrperiod, int* nsps, int* ndiskdat, - int* nb, int* m_NBslider, float* px, float s[], + int* nb, int* m_NBslider, float* px, float s[], float red[], float* f0a, float* df3, int* nhsym, int* nzap, float* slimit, uchar lstrong[]); void genjt9_(char* msg, int* minutes, char* msgsent, int itone[], diff --git a/mainwindow.ui b/mainwindow.ui index 22e906e9b..eeee7578f 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -35,7 +35,7 @@ - + 11 @@ -627,7 +627,7 @@ p, li { white-space: pre-wrap; } - 500 + 50 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter @@ -661,7 +661,7 @@ p, li { white-space: pre-wrap; } 0 - 6 + 9 5 diff --git a/plotter.cpp b/plotter.cpp index 4471bbe6d..003529114 100644 --- a/plotter.cpp +++ b/plotter.cpp @@ -27,7 +27,7 @@ CPlotter::CPlotter(QWidget *parent) : //CPlotter Constructor m_ScalePixmap = QPixmap(0,0); m_OverlayPixmap = QPixmap(0,0); m_Size = QSize(0,0); - m_fQSO = 125; + m_fQSO = 1050; m_line = 0; m_fSample = 12000; m_nsps=6912; @@ -84,9 +84,9 @@ void CPlotter::paintEvent(QPaintEvent *) // paintEvent() m_paintEventBusy=false; } -void CPlotter::draw(float swide[], int i0) //draw() +void CPlotter::draw(float swide[], float red[], int i0) //draw() { - int j; + int j,y2; float y; m_i0=i0; @@ -103,6 +103,8 @@ void CPlotter::draw(float swide[], int i0) //draw() } painter2D.setPen(Qt::green); + if(m_bJT9Sync) painter2D.setPen(Qt::red); + QPoint LineBuf[MAX_SCREENSIZE]; j=0; bool strong0=false; @@ -124,8 +126,9 @@ void CPlotter::draw(float swide[], int i0) //draw() m_hist1[y1]++; painter1.setPen(m_ColorTbl[y1]); painter1.drawPoint(i,0); - int y2 = gain*y + 30; - if(!m_bCurrent) y2=gain*10.0*log10(jt9com_.savg[i]); + if(m_bCurrent) y2 = gain*y + 30; + if(m_bCumulative) y2=gain*10.0*log10(jt9com_.savg[i]); + if(m_bJT9Sync) y2=3*gain*red[i]; if(strong != strong0 or i==m_w-1) { painter2D.drawPolyline(LineBuf,j); j=0; @@ -297,19 +300,10 @@ void CPlotter::DrawOverlay() //DrawOverlay() painter0.setPen(pen0); x = m_xClick; painter0.drawLine(x,15,x,30); - int x0=(16384-m_i0)/m_binsPerPixel; - m_fGreen=(x-x0)*df; - x0 += (x0-x); - QPen pen3(Qt::red, 3); - painter0.setPen(pen3); - if(x0>0 and x0fDialLineEdit->setText(QString::number(m_dialFreq)); ui->widePlot->m_bCurrent=settings.value("Current",true).toBool(); + ui->widePlot->m_bCumulative=settings.value("Cumulative",false).toBool(); + ui->widePlot->m_bJT9Sync=settings.value("JT9Sync",false).toBool(); ui->rbCurrent->setChecked(ui->widePlot->m_bCurrent); - ui->rbCumulative->setChecked(!ui->widePlot->m_bCurrent); + ui->rbCumulative->setChecked(ui->widePlot->m_bCumulative); + ui->rbJT9Sync->setChecked(ui->widePlot->m_bJT9Sync); int nbpp=settings.value("BinsPerPixel",1).toInt(); ui->widePlot->setBinsPerPixel(nbpp); settings.endGroup(); @@ -70,12 +73,14 @@ void WideGraph::saveSettings() settings.setValue("WaterfallAvg",ui->waterfallAvgSpinBox->value()); settings.setValue("DialFreqMHz",m_dialFreq); settings.setValue("Current",ui->widePlot->m_bCurrent); + settings.setValue("Cumulative",ui->widePlot->m_bCumulative); + settings.setValue("JT9Sync",ui->widePlot->m_bJT9Sync); settings.setValue("BinsPerPixel",ui->widePlot->binsPerPixel()); settings.endGroup(); } -void WideGraph::dataSink2(float s[], float df3, int ihsym, int ndiskdata, - uchar lstrong[]) +void WideGraph::dataSink2(float s[], float red[], float df3, int ihsym, + int ndiskdata, uchar lstrong[]) { static float splot[NSMAX]; static float swide[2048]; @@ -139,7 +144,7 @@ void WideGraph::dataSink2(float s[], float df3, int ihsym, int ndiskdata, } } ntr0=ntr; - ui->widePlot->draw(swide,i0); + ui->widePlot->draw(swide,red,i0); } } @@ -266,12 +271,23 @@ void WideGraph::setPeriod(int ntrperiod, int nsps) ui->widePlot->setNsps(nsps); } -void WideGraph::on_rbCurrent_toggled(bool checked) +void WideGraph::on_rbCurrent_clicked() { - ui->widePlot->m_bCurrent=checked; + ui->widePlot->m_bCurrent=true; + ui->widePlot->m_bCumulative=false; + ui->widePlot->m_bJT9Sync=false; } -void WideGraph::on_rbCumulative_toggled(bool checked) +void WideGraph::on_rbCumulative_clicked() { - ui->widePlot->m_bCurrent=!checked; + ui->widePlot->m_bCurrent=false; + ui->widePlot->m_bCumulative=true; + ui->widePlot->m_bJT9Sync=false; +} + +void WideGraph::on_rbJT9Sync_clicked() +{ + ui->widePlot->m_bCurrent=false; + ui->widePlot->m_bCumulative=false; + ui->widePlot->m_bJT9Sync=true; } diff --git a/widegraph.h b/widegraph.h index e3a5c2b15..967381f2b 100644 --- a/widegraph.h +++ b/widegraph.h @@ -16,8 +16,8 @@ public: double m_dialFreq; - void dataSink2(float s[], float df3, int ihsym, int ndiskdata, - uchar lstrong[]); + void dataSink2(float s[], float red[], float df3, int ihsym, + int ndiskdata, uchar lstrong[]); int QSOfreq(); int nSpan(); int nStartFreq(); @@ -53,10 +53,9 @@ private slots: void on_gainSpinBox_valueChanged(int arg1); void on_autoZeroPushButton_clicked(); void on_fDialLineEdit_editingFinished(); - - void on_rbCurrent_toggled(bool checked); - - void on_rbCumulative_toggled(bool checked); + void on_rbCurrent_clicked(); + void on_rbCumulative_clicked(); + void on_rbJT9Sync_clicked(); private: qint32 m_waterfallAvg; diff --git a/widegraph.ui b/widegraph.ui index bf1970c07..5768adf94 100644 --- a/widegraph.ui +++ b/widegraph.ui @@ -297,6 +297,13 @@ + + + + JT9 Sync + + +