From fc40bf74a3b46ff5a513ca1f2a584b8907bee624 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Mon, 4 May 2020 20:59:17 -0400 Subject: [PATCH 01/32] Initial experiment of no timing requirement --- commons.h | 1 + lib/decoder.f90 | 17 ++++++++++++++--- lib/jt9.f90 | 8 ++++++-- lib/jt9com.f90 | 1 + mainwindow.cpp | 33 ++++++++++++++++++++++++++++++++- 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/commons.h b/commons.h index 687a1ef..0cc67f7 100644 --- a/commons.h +++ b/commons.h @@ -97,6 +97,7 @@ extern struct dec_data { int nfSplit; // JT65 | JT9 split frequency int nfb; // High decode limit (Hz) (filter max) int ntol; // +/- decoding range around fQSO (Hz) + bool synconly; // only compute sync candidates int kin; // number of frames written to d2 int kposA; // starting position of decode for submode A int kposB; // starting position of decode for submode B diff --git a/lib/decoder.f90 b/lib/decoder.f90 index a41c14e..737f7c2 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -177,10 +177,21 @@ subroutine multimode_decoder(ss,id2,params,nfsample) write(*,*) ' mode A decode started' ! copy the relevant frames for decoding - pos = max(0,params%kposA) - sz = max(0,params%kszA) + pos = int(max(0,params%kposA)) + sz = int(max(0,params%kszA)) id0=0 - id0(1:sz+1)=id2(pos+1:pos+sz+1) + imax=int(NTMAX*12000) + + if((imax-pos).lt.sz) then + ! this means that the first part of the id0 is at the end of the buffer + ! and the second half is at the beginning of the buffer + firstsize=int(imax-pos)-1 + secondsize=int(sz-firstsize)+1 + id0(1:firstsize+1)=id2(pos+1:pos+firstsize+1) + id0(firstsize+1:firstsize+secondsize+1)=id2(1:secondsize+1) + else + id0(1:sz+1)=id2(pos+1:pos+sz+1) + endif call my_js8a%decode(js8a_decoded,id0,params%nQSOProgress,params%nfqso, & params%nftx,newdat,params%nutc,params%nfa,params%nfb, & diff --git a/lib/jt9.f90 b/lib/jt9.f90 index 279c126..30173ad 100644 --- a/lib/jt9.f90 +++ b/lib/jt9.f90 @@ -21,8 +21,8 @@ program jt9 !### ndepth was defined as 60001. Why??? integer :: arglen,stat,offset,remain,mode=0,flow=200,fsplit=2700, & fhigh=4000,nrxfreq=1500,ntrperiod=1,ndepth=1,nexp_decode=0 - logical :: read_files = .true., tx9 = .false., display_help = .false. - type (option) :: long_options(21) = [ & + logical :: read_files = .true., tx9 = .false., display_help = .false., synconly = .false. + type (option) :: long_options(22) = [ & option ('help', .false., 'h', 'Display this help message', ''), & option ('shmem',.true.,'s','Use shared memory for sample data','KEY'), & option ('tr-period', .true., 'p', 'Tx/Rx period, default MINUTES=1', & @@ -50,6 +50,7 @@ program jt9 !option ('jt65', .false., '6', 'JT65 mode', ''), & !option ('jt9', .false., '9', 'JT9 mode', ''), & option ('js8', .false., '8', 'JS8 mode', ''), & + option ('synconly', .false., 'y', 'Sync only', ''), & !option ('jt4', .false., '4', 'JT4 mode', ''), & !option ('qra64', .false., 'q', 'QRA64 mode', ''), & option ('sub-mode', .true., 'b', 'Sub mode, default SUBMODE=A', 'A'), & @@ -118,6 +119,8 @@ program jt9 ! if (mode.lt.9.or.mode.eq.65) mode = mode + 9 case ('8') mode = 8 + case ('y') + synconly = .true. case ('T') tx9 = .true. case ('w') @@ -254,6 +257,7 @@ program jt9 shared_data%params%ljt65apon=.true. shared_data%params%napwid=75 shared_data%params%dttol=3. + shared_data%params%synconly=synconly ! shared_data%params%minsync=0 !### TEST ONLY ! shared_data%params%nfqso=1500 !### TEST ONLY diff --git a/lib/jt9com.f90 b/lib/jt9com.f90 index bb30d57..3999653 100644 --- a/lib/jt9com.f90 +++ b/lib/jt9com.f90 @@ -18,6 +18,7 @@ integer(c_int) :: nfsplit integer(c_int) :: nfb integer(c_int) :: ntol + logical(c_bool) :: synconly integer(c_int) :: kin integer(c_int) :: kposA integer(c_int) :: kposB diff --git a/mainwindow.cpp b/mainwindow.cpp index 4310c4c..9942e08 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4266,6 +4266,27 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ // enqueue those decodes that are "ready" // on an interval, issue a decode + static qint32 lastDecodeStartA = -1; + qint32 startA = -1; + qint32 szA = -1; + qint32 cycleA = -1; + bool couldDecodeA = false; + qint32 oneSecondFrames = computeFramesPerCycleForDecode(Varicode::JS8CallNormal)/computePeriodForSubmode(Varicode::JS8CallNormal); + if(lastDecodeStartA == -1 || k < k0 || k - lastDecodeStartA > oneSecondFrames){ + startA = k-computeFramesNeededForDecode(Varicode::JS8CallNormal); + + if(startA < 0){ + // TODO: decoder doesn't copy wrap around ranges + startA += m_detector->period() * RX_SAMPLE_RATE; + } + + szA = computeFramesNeededForDecode(Varicode::JS8CallNormal); + lastDecodeStartA = k; + couldDecodeA = true; + qDebug() << "? NORMAL " << startA << k << k0; + } + +#if 0 static qint32 currentDecodeStartA = -1; static qint32 nextDecodeStartA = -1; qint32 startA = -1; @@ -4278,6 +4299,7 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ szA = NTMAX*RX_SAMPLE_RATE-1; couldDecodeA = true; } +#endif static qint32 currentDecodeStartB = -1; static qint32 nextDecodeStartB = -1; @@ -4335,6 +4357,9 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ } #endif + + couldDecodeB = couldDecodeC = couldDecodeE = false; + int decodes = 0; if(couldDecodeA){ @@ -4699,7 +4724,13 @@ void MainWindow::decodeDone () m_nclearave=0; m_RxLog=0; m_blankLine=true; - m_messageDupeCache.clear(); + + static int dupeClearI = 0; + if(dupeClearI > 2*m_TRperiod){ + m_messageDupeCache.clear(); + dupeClearI = 0; + } + dupeClearI++; decodeBusy(false); } From 4b36884abbd82349d0e9e1159a03bcf8908779ee Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Wed, 6 May 2020 11:15:41 -0400 Subject: [PATCH 02/32] Experimental drawing of sync state --- lib/decoder.f90 | 2 +- lib/js8/js8a_params.f90 | 4 ++-- lib/js8/js8dec.f90 | 16 +++++++++++-- lib/js8a_decode.f90 | 39 +++++++++++++++++++------------- lib/js8b_decode.f90 | 2 +- lib/js8c_decode.f90 | 2 +- lib/js8e_decode.f90 | 2 +- lib/js8i_decode.f90 | 2 +- mainwindow.cpp | 50 ++++++++++++++++++++++++++++++++++++++++- plotter.cpp | 42 +++++++++------------------------- plotter.h | 2 +- widegraph.cpp | 4 ++-- widegraph.h | 2 +- 13 files changed, 108 insertions(+), 61 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 737f7c2..ab20fc6 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -197,7 +197,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) params%nftx,newdat,params%nutc,params%nfa,params%nfb, & params%nexp_decode,params%ndepth,logical(params%nagain), & logical(params%lft8apon),logical(params%lapcqonly),params%napwid, & - mycall,mygrid,hiscall,hisgrid) + mycall,mygrid,hiscall,hisgrid,logical(params%synconly)) write(*,*) ' mode A decode finished' diff --git a/lib/js8/js8a_params.f90 b/lib/js8/js8a_params.f90 index 806754e..44db61b 100644 --- a/lib/js8/js8a_params.f90 +++ b/lib/js8/js8a_params.f90 @@ -2,11 +2,11 @@ parameter (NCOSTAS=1) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas) -parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=62) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s +parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=32) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s parameter (AZ=12000.0/(1.0*NSPS)*0.64d0) !Dedupe overlap in Hz parameter (ASTART=0.5) !Start delay in seconds -parameter (ASYNCMIN=1.5) !Minimum Sync +parameter (ASYNCMIN=2) !Minimum Sync parameter (KK=87) !Information bits (75 + CRC12) parameter (ND=58) !Data symbols diff --git a/lib/js8/js8dec.f90 b/lib/js8/js8dec.f90 index d98bd70..af73b12 100644 --- a/lib/js8/js8dec.f90 +++ b/lib/js8/js8dec.f90 @@ -1,4 +1,4 @@ -subroutine js8dec(dd0,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, & +subroutine js8dec(dd0,icos,newdat,synconly,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, & napwid,lsubtract,nagain,iaptype,mycall12,mygrid6,hiscall12,bcontest, & sync0,f1,xdt,xbase,apsym,nharderrors,dmin,nbadcrc,ipass,iera,msg37,xsnr) @@ -14,7 +14,7 @@ subroutine js8dec(dd0,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly character*12 mycall12,hiscall12 character*6 mycall6,mygrid6,hiscall6,c1,c2 character*87 cbits - logical bcontest + logical bcontest,synconly real a(5) real s1(0:7,ND),s2(0:7,NN),s1sort(8*ND) real ps(0:7),psl(0:7) @@ -224,6 +224,11 @@ subroutine js8dec(dd0,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly return endif + if(synconly) then + write(*,*) ' candidate X ', 'f', f1, 'sync', nsync, 'xdt', xdt + flush(6) + endif + j=0 do k=1,NN if(k.le.7) cycle @@ -417,6 +422,13 @@ subroutine js8dec(dd0,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly i3bit=4*decoded(73) + 2*decoded(74) + decoded(75) if(nbadcrc.eq.0) then + + if(synconly) then + write(*,*) ' decode X ', 'f1', f1, 'sync', sync, 'xdt', xdt2 + flush(6) + return + endif + decoded0=decoded call extractmessage174(decoded,origmsg,ncrcflag) decoded=decoded0 diff --git a/lib/js8a_decode.f90 b/lib/js8a_decode.f90 index 5c67bcb..ce47087 100644 --- a/lib/js8a_decode.f90 +++ b/lib/js8a_decode.f90 @@ -25,7 +25,7 @@ contains subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, & nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, & - mycall12,mygrid6,hiscall12,hisgrid6) + mycall12,mygrid6,hiscall12,hisgrid6,synconly) ! use wavhdr use timer_module, only: timer ! type(hdr) h @@ -38,7 +38,7 @@ contains real candidate(3,NMAXCAND) real dd(NMAX) logical, intent(in) :: lft8apon,lapcqonly,nagain - logical newdat,lsubtract,ldupe,bcontest + logical newdat,lsubtract,ldupe,bcontest,synconly character*12 mycall12, hiscall12 character*6 mygrid6,hisgrid6 integer*2 iwave(NMAX) @@ -93,24 +93,31 @@ contains lsubtract=.false. endif + if(synconly) then + if(NWRITELOG.eq.0) then + write(*,*) ' synconly' + flush(6) + endif + endif + call timer('syncjs8 ',0) call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase) call timer('syncjs8 ',1) - if(NWRITELOG.eq.1) then - write(*,*) '', ncand, "candidates" - flush(6) + !if(NWRITELOG.eq.0) then + ! write(*,*) '', ncand, "candidates" + ! flush(6) - do icand=1,ncand - sync=candidate(3,icand) - f1=candidate(1,icand) - xdt=candidate(2,icand) - xbase=10.0**(0.1*(sbase(nint(f1/(12000.0/NFFT1)))-40.0)) ! 3.125Hz + ! do icand=1,ncand + ! sync=candidate(3,icand) + ! f1=candidate(1,icand) + ! xdt=candidate(2,icand) + ! xbase=10.0**(0.1*(sbase(nint(f1/(12000.0/NFFT1)))-40.0)) ! 3.125Hz - write(*,*) ' candidate', icand, 'f1', f1, 'sync', sync, 'xdt', xdt, 'xbase', xbase - flush(6) - enddo - endif + ! write(*,*) ' candidate', icand, 'f1', f1, 'sync', sync, 'xdt', xdt, 'xbase', xbase + ! flush(6) + ! enddo + !endif do icand=1,ncand sync=candidate(3,icand) @@ -124,7 +131,7 @@ contains endif call timer('js8dec ',0) - call js8dec(dd,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & + call js8dec(dd,icos,newdat,synconly,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, & hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, & nbadcrc,iappass,iera,msg37,xsnr) @@ -137,7 +144,7 @@ contains write(*,*) ' candidate', icand, 'hard', hd, 'nbadcrc', nbadcrc flush(6) endif - + call timer('js8dec ',1) if(nbadcrc.eq.0) then ldupe=.false. diff --git a/lib/js8b_decode.f90 b/lib/js8b_decode.f90 index c3ea4ce..99f1392 100644 --- a/lib/js8b_decode.f90 +++ b/lib/js8b_decode.f90 @@ -114,7 +114,7 @@ contains endif call timer('js8dec ',0) - call js8dec(dd,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & + call js8dec(dd,icos,newdat,.false.,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, & hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, & nbadcrc,iappass,iera,msg37,xsnr) diff --git a/lib/js8c_decode.f90 b/lib/js8c_decode.f90 index af97a74..69377c6 100644 --- a/lib/js8c_decode.f90 +++ b/lib/js8c_decode.f90 @@ -114,7 +114,7 @@ contains endif call timer('js8dec ',0) - call js8dec(dd,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & + call js8dec(dd,icos,newdat,.false.,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, & hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, & nbadcrc,iappass,iera,msg37,xsnr) diff --git a/lib/js8e_decode.f90 b/lib/js8e_decode.f90 index 6a6674d..aa73861 100644 --- a/lib/js8e_decode.f90 +++ b/lib/js8e_decode.f90 @@ -114,7 +114,7 @@ contains endif call timer('js8dec ',0) - call js8dec(dd,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & + call js8dec(dd,icos,newdat,.false.,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, & hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, & nbadcrc,iappass,iera,msg37,xsnr) diff --git a/lib/js8i_decode.f90 b/lib/js8i_decode.f90 index ee161b7..d5d0788 100644 --- a/lib/js8i_decode.f90 +++ b/lib/js8i_decode.f90 @@ -114,7 +114,7 @@ contains endif call timer('js8dec ',0) - call js8dec(dd,icos,newdat,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & + call js8dec(dd,icos,newdat,.false.,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, & hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, & nbadcrc,iappass,iera,msg37,xsnr) diff --git a/mainwindow.cpp b/mainwindow.cpp index 9942e08..963e6f7 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4461,6 +4461,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){ if(JS8_DEBUG_DECODE) qDebug() << "--> decoder skipping at least 1 decode cycle" << "count" << count << "max" << maxDecodes; } + dec_data.params.synconly = true; dec_data.params.nsubmodes = 0; while(!m_decoderQueue.isEmpty()){ @@ -4872,6 +4873,54 @@ void MainWindow::processDecodedLine(QByteArray t){ bool bAvgMsg=false; int navg=0; if(t.indexOf("") >= 0) { + if(t.indexOf("f1") >= 0){ + auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts); + if(!segs.isEmpty()){ + auto f1 = QString(segs.at(4)); + auto f = int(f1.toFloat()); + + auto s1 = QString(segs.at(6)); + auto s = int(s1.toFloat()); + + auto xdt1 = QString(segs.at(8)); + auto xdt = int(xdt1.toFloat()); + + if(abs(xdt) <= 1.28){ + //if(s > 7 && s < 10){ + // m_wideGraph->drawLine(QColor(Qt::white), f, f + computeBandwidthForSubmode(m_nSubMode)); + //} else if (s < 15){ + // m_wideGraph->drawLine(QColor(Qt::darkMagenta), f, f + computeBandwidthForSubmode(m_nSubMode)); + //} else { + /* + int secs = DriftingDateTime::currentDateTimeUtc().secsTo(nextTransmitCycle()); + int txtime = computeFramesNeededForDecode(m_nSubMode)/RX_SAMPLE_RATE; + qDebug() << "seconds til transmit" << secs << "time offset" << xdt << "potential drift" << (secs-xdt)*1000; + setDrift((secs+xdt-txtime)*1000); + */ + auto now = QDateTime::currentDateTimeUtc(); + + int n = 0; + int nPos = m_TRperiod - (now.time().second() % m_TRperiod); + int nNeg = (now.time().second() % m_TRperiod) - m_TRperiod; + + if(abs(nNeg) < nPos){ + n = nNeg; + } else { + n = nPos; + } + + + int xdtmin = qMin(n*1000, (int)DriftingDateTime::drift()); + int xdtmax = qMax(n*1000, (int)DriftingDateTime::drift()); + setDrift(xdtmin + (xdtmax-xdtmin)/2); + + m_wideGraph->drawLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m_nSubMode)); + //} + } + } + } + + if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists()); return; } @@ -4882,7 +4931,6 @@ void MainWindow::processDecodedLine(QByteArray t){ } if(t.indexOf("") >= 0) { - if(m_mode=="QRA64") m_wideGraph->drawRed(0,0); m_bDecoded = t.mid(16).trimmed().toInt() > 0; int mswait=3*1000*m_TRperiod/4; if(!m_diskData) killFileTimer.start(mswait); //Kill in 3/4 period diff --git a/plotter.cpp b/plotter.cpp index 231b450..b2e44a7 100644 --- a/plotter.cpp +++ b/plotter.cpp @@ -277,42 +277,22 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) painter2D.drawText(x1-4,y,"73"); } - if(bRed) { - std::ifstream f; - f.open(m_redFile.toLatin1()); - if(f) { - int x,y; - float freq,sync; - float slimit=6.0; - QPen pen0(Qt::red,1); - painter1.setPen(pen0); - for(int i=0; i<99999; i++) { - f >> freq >> sync; - if(f.eof()) break; - x=XfromFreq(freq); - y=(sync-slimit)*3.0; - if(y>0) { - if(y>15.0) y=15.0; - if(x>=0 and x<=m_w) { - painter1.setPen(pen0); - painter1.drawLine(x,0,x,y); - } - } - } - f.close(); - } -// m_bDecodeFinished=false; - } - update(); //trigger a new paintEvent m_bScaleOK=true; } -void CPlotter::drawRed(int ia, int ib, float swide[]) +void CPlotter::drawLine(const QColor &color, int ia, int ib) { - m_ia=ia; - m_ib=ib; - draw(swide,false,true); + int x1=XfromFreq(ia); + int x2=XfromFreq(ib); + + QPen pen0(color, 1); + + QPainter painter1(&m_WaterfallPixmap); + painter1.setPen(pen0); + painter1.drawLine(qMin(x1, x2),4,qMax(x1, x2),4); + painter1.drawLine(qMin(x1, x2),0,qMin(x1, x2),9); + painter1.drawLine(qMax(x1, x2),0,qMax(x1, x2),9); } void CPlotter::replot() diff --git a/plotter.h b/plotter.h index 54e6d74..ced70fb 100644 --- a/plotter.h +++ b/plotter.h @@ -90,7 +90,7 @@ public: void setReference(bool b) {m_bReference = b;} bool Reference() const {return m_bReference;} #endif - void drawRed(int ia, int ib, float swide[]); + void drawLine(const QColor &color, int ia, int ib); void setVHF(bool bVHF); void setRedFile(QString fRed); bool scaleOK () const {return m_bScaleOK;} diff --git a/widegraph.cpp b/widegraph.cpp index a897027..d913874 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -244,9 +244,9 @@ void WideGraph::saveSettings() //saveS m_settings->setValue ("WaterfallFPS", ui->fpsSpinBox->value()); } -void WideGraph::drawRed(int ia, int ib) +void WideGraph::drawLine(const QColor &color, int ia, int ib) { - ui->widePlot->drawRed(ia,ib,swide); + ui->widePlot->drawLine(color, ia, ib); } void WideGraph::dataSink2(float s[], float df3, int ihsym, int ndiskdata) //dataSink2 diff --git a/widegraph.h b/widegraph.h index 1f2eed9..29768c1 100644 --- a/widegraph.h +++ b/widegraph.h @@ -63,7 +63,7 @@ public: int smoothYellow(); void setRxBand (QString const& band); void setWSPRtransmitted(); - void drawRed(int ia, int ib); + void drawLine(const QColor &color, int ia, int ib); void setVHF(bool bVHF); void setRedFile(QString fRed); void setTurbo(bool turbo); From e87704f1198929472b036d9f3d7b6eb98551fdb7 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Wed, 6 May 2020 20:08:30 -0400 Subject: [PATCH 03/32] Experiments in computing and setting drift automatically --- lib/js8/js8a_params.f90 | 2 +- lib/js8/js8dec.f90 | 5 ++-- mainwindow.cpp | 59 ++++++++++++++++++++++++----------------- 3 files changed, 38 insertions(+), 28 deletions(-) diff --git a/lib/js8/js8a_params.f90 b/lib/js8/js8a_params.f90 index 44db61b..e98959f 100644 --- a/lib/js8/js8a_params.f90 +++ b/lib/js8/js8a_params.f90 @@ -2,7 +2,7 @@ parameter (NCOSTAS=1) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas) -parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=32) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s +parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=36) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s parameter (AZ=12000.0/(1.0*NSPS)*0.64d0) !Dedupe overlap in Hz parameter (ASTART=0.5) !Start delay in seconds diff --git a/lib/js8/js8dec.f90 b/lib/js8/js8dec.f90 index af73b12..d3e123c 100644 --- a/lib/js8/js8dec.f90 +++ b/lib/js8/js8dec.f90 @@ -225,7 +225,7 @@ subroutine js8dec(dd0,icos,newdat,synconly,nQSOProgress,nfqso,nftx,ndepth,lapon, endif if(synconly) then - write(*,*) ' candidate X ', 'f', f1, 'sync', nsync, 'xdt', xdt + write(*,*) ' candidate X ', 'f1', f1, 'sync', nsync, 'xdt', xdt flush(6) endif @@ -424,9 +424,8 @@ subroutine js8dec(dd0,icos,newdat,synconly,nQSOProgress,nfqso,nftx,ndepth,lapon, if(nbadcrc.eq.0) then if(synconly) then - write(*,*) ' decode X ', 'f1', f1, 'sync', sync, 'xdt', xdt2 + write(*,*) ' decode X ', 'f1', f1, 'sync', (sync*10), 'xdt', xdt2 flush(6) - return endif decoded0=decoded diff --git a/mainwindow.cpp b/mainwindow.cpp index 963e6f7..4d70593 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4872,6 +4872,10 @@ void MainWindow::processDecodedLine(QByteArray t){ bool bAvgMsg=false; int navg=0; + + static bool hasNewDrift = false; + static int newDrift = 0; + if(t.indexOf("") >= 0) { if(t.indexOf("f1") >= 0){ auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts); @@ -4885,37 +4889,36 @@ void MainWindow::processDecodedLine(QByteArray t){ auto xdt1 = QString(segs.at(8)); auto xdt = int(xdt1.toFloat()); - if(abs(xdt) <= 1.28){ - //if(s > 7 && s < 10){ - // m_wideGraph->drawLine(QColor(Qt::white), f, f + computeBandwidthForSubmode(m_nSubMode)); - //} else if (s < 15){ - // m_wideGraph->drawLine(QColor(Qt::darkMagenta), f, f + computeBandwidthForSubmode(m_nSubMode)); - //} else { - /* - int secs = DriftingDateTime::currentDateTimeUtc().secsTo(nextTransmitCycle()); - int txtime = computeFramesNeededForDecode(m_nSubMode)/RX_SAMPLE_RATE; - qDebug() << "seconds til transmit" << secs << "time offset" << xdt << "potential drift" << (secs-xdt)*1000; - setDrift((secs+xdt-txtime)*1000); - */ + if(abs(xdt) <= 2){ + if(s < 10){ + m_wideGraph->drawLine(QColor(Qt::darkCyan), f, f + computeBandwidthForSubmode(m_nSubMode)); + } else if (s <= 21){ + m_wideGraph->drawLine(QColor(Qt::white), f, f + computeBandwidthForSubmode(m_nSubMode)); + } + } + + if(t.contains("decode X")){ auto now = QDateTime::currentDateTimeUtc(); - int n = 0; - int nPos = m_TRperiod - (now.time().second() % m_TRperiod); - int nNeg = (now.time().second() % m_TRperiod) - m_TRperiod; + float n = 0; + float nPos = m_TRperiod - (now.time().second() % m_TRperiod); + float nNeg = (now.time().second() % m_TRperiod) - m_TRperiod; + float offset = m_TRperiod - computeFramesNeededForDecode(m_nSubMode)/RX_SAMPLE_RATE + xdt; - if(abs(nNeg) < nPos){ - n = nNeg; + if(qAbs(nNeg) < nPos){ + n = nNeg + offset; } else { - n = nPos; + n = nPos - offset; } + int xdtmin = qMin(n*1000, (float)DriftingDateTime::drift()); + int xdtmax = qMax(n*1000, (float)DriftingDateTime::drift()); - int xdtmin = qMin(n*1000, (int)DriftingDateTime::drift()); - int xdtmax = qMax(n*1000, (int)DriftingDateTime::drift()); - setDrift(xdtmin + (xdtmax-xdtmin)/2); - - m_wideGraph->drawLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m_nSubMode)); - //} + m_wideGraph->drawLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m_nSubMode)); + int oldNewDrift = newDrift; + newDrift = (xdtmin + (xdtmax-xdtmin)/2); + newDrift = qMin(oldNewDrift, newDrift) + (qMax(oldNewDrift, newDrift)-qMin(oldNewDrift, newDrift))/2; + hasNewDrift = true; } } } @@ -4931,6 +4934,14 @@ void MainWindow::processDecodedLine(QByteArray t){ } if(t.indexOf("") >= 0) { + + if(hasNewDrift){ + int oldDrift = DriftingDateTime::drift(); + newDrift = qMin(oldDrift, newDrift) + (qMax(oldDrift, newDrift)-qMin(oldDrift, newDrift))/2; + setDrift(newDrift); + hasNewDrift = false; + } + m_bDecoded = t.mid(16).trimmed().toInt() > 0; int mswait=3*1000*m_TRperiod/4; if(!m_diskData) killFileTimer.start(mswait); //Kill in 3/4 period From b21f9374818cc9544a0eee57cc6720de081888b7 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Wed, 6 May 2020 20:16:29 -0400 Subject: [PATCH 04/32] Write notice text to UI when drifting automatically --- mainwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/mainwindow.cpp b/mainwindow.cpp index 4d70593..e313e71 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4939,6 +4939,7 @@ void MainWindow::processDecodedLine(QByteArray t){ int oldDrift = DriftingDateTime::drift(); newDrift = qMin(oldDrift, newDrift) + (qMax(oldDrift, newDrift)-qMin(oldDrift, newDrift))/2; setDrift(newDrift); + writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Drift: %1").arg(newDrift)); hasNewDrift = false; } From 6bf4b413112dab1b959d3b915621c8e7b9b82d86 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Thu, 7 May 2020 15:32:28 -0400 Subject: [PATCH 05/32] Working automatic time sync based on syncStats --- commons.h | 2 +- lib/decoder.f90 | 12 +++--- lib/js8/js8a_params.f90 | 1 + lib/js8/js8b_params.f90 | 1 + lib/js8/js8c_params.f90 | 1 + lib/js8/js8dec.f90 | 13 +++--- lib/js8/js8e_params.f90 | 1 + lib/js8/js8i_params.f90 | 1 + lib/js8a_decode.f90 | 28 ++----------- lib/js8b_decode.f90 | 6 +-- lib/js8c_decode.f90 | 6 +-- lib/js8e_decode.f90 | 6 +-- lib/js8i_decode.f90 | 6 +-- lib/jt9.f90 | 8 ++-- lib/jt9com.f90 | 2 +- mainwindow.cpp | 87 ++++++++++++++++++++++------------------- widegraph.cpp | 2 + 17 files changed, 86 insertions(+), 97 deletions(-) diff --git a/commons.h b/commons.h index 0cc67f7..d7ec425 100644 --- a/commons.h +++ b/commons.h @@ -97,7 +97,7 @@ extern struct dec_data { int nfSplit; // JT65 | JT9 split frequency int nfb; // High decode limit (Hz) (filter max) int ntol; // +/- decoding range around fQSO (Hz) - bool synconly; // only compute sync candidates + bool syncStats; // only compute sync candidates int kin; // number of frames written to d2 int kposA; // starting position of decode for submode A int kposB; // starting position of decode for submode B diff --git a/lib/decoder.f90 b/lib/decoder.f90 index ab20fc6..593086b 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -94,7 +94,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) params%nftx,newdat,params%nutc,params%nfa,params%nfb, & params%nexp_decode,params%ndepth,logical(params%nagain), & logical(params%lft8apon),logical(params%lapcqonly),params%napwid, & - mycall,mygrid,hiscall,hisgrid) + mycall,mygrid,hiscall,hisgrid,logical(params%syncStats)) write(*,*) ' mode I decode finished' @@ -117,7 +117,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) params%nftx,newdat,params%nutc,params%nfa,params%nfb, & params%nexp_decode,params%ndepth,logical(params%nagain), & logical(params%lft8apon),logical(params%lapcqonly),params%napwid, & - mycall,mygrid,hiscall,hisgrid) + mycall,mygrid,hiscall,hisgrid,logical(params%syncStats)) write(*,*) ' mode E decode finished' @@ -140,7 +140,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) params%nftx,newdat,params%nutc,params%nfa,params%nfb, & params%nexp_decode,params%ndepth,logical(params%nagain), & logical(params%lft8apon),logical(params%lapcqonly),params%napwid, & - mycall,mygrid,hiscall,hisgrid) + mycall,mygrid,hiscall,hisgrid,logical(params%syncStats)) write(*,*) ' mode C decode finished' @@ -163,7 +163,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) params%nftx,newdat,params%nutc,params%nfa,params%nfb, & params%nexp_decode,params%ndepth,logical(params%nagain), & logical(params%lft8apon),logical(params%lapcqonly),params%napwid, & - mycall,mygrid,hiscall,hisgrid) + mycall,mygrid,hiscall,hisgrid,logical(params%syncStats)) write(*,*) ' mode B decode finished' @@ -197,7 +197,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) params%nftx,newdat,params%nutc,params%nfa,params%nfb, & params%nexp_decode,params%ndepth,logical(params%nagain), & logical(params%lft8apon),logical(params%lapcqonly),params%napwid, & - mycall,mygrid,hiscall,hisgrid,logical(params%synconly)) + mycall,mygrid,hiscall,hisgrid,logical(params%syncStats)) write(*,*) ' mode A decode finished' @@ -207,7 +207,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) write(*,*) ' finished' call flush(6) - ndecoded = my_js8a%decoded + my_js8b%decoded + my_js8c%decoded + my_js8e%decoded + ndecoded = my_js8a%decoded + my_js8b%decoded + my_js8c%decoded + my_js8e%decoded + my_js8i%decoded !call sleep_msec(3000) write(*,1010) ndecoded 1010 format('',i4) diff --git a/lib/js8/js8a_params.f90 b/lib/js8/js8a_params.f90 index e98959f..0092ae7 100644 --- a/lib/js8/js8a_params.f90 +++ b/lib/js8/js8a_params.f90 @@ -1,5 +1,6 @@ ! When modifying this file, please ensure the modifications are made in ft8_params.f90 too. +parameter (NSUBMODE=0) parameter (NCOSTAS=1) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas) parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=36) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s diff --git a/lib/js8/js8b_params.f90 b/lib/js8/js8b_params.f90 index 65aba11..8f98a3e 100644 --- a/lib/js8/js8b_params.f90 +++ b/lib/js8/js8b_params.f90 @@ -1,3 +1,4 @@ +parameter (NSUBMODE=1) parameter (NCOSTAS=2) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas) parameter (NSPS=1200, NTXDUR=10, NDOWNSPS=20, NDD=100, JZ=144) ! 80 Hz 10 baud 24 wpm -23.0dB (1.0Eb/N0) 7.90s diff --git a/lib/js8/js8c_params.f90 b/lib/js8/js8c_params.f90 index 7be1230..2cbb088 100644 --- a/lib/js8/js8c_params.f90 +++ b/lib/js8/js8c_params.f90 @@ -1,3 +1,4 @@ +parameter (NSUBMODE=2) parameter (NCOSTAS=2) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas) parameter (NSPS=600, NTXDUR=6, NDOWNSPS=12, NDD=120, JZ=172) ! 160 Hz 20 baud 40 wpm -20.0dB (1.0Eb/N0) 3.95s diff --git a/lib/js8/js8dec.f90 b/lib/js8/js8dec.f90 index d3e123c..df3dc2d 100644 --- a/lib/js8/js8dec.f90 +++ b/lib/js8/js8dec.f90 @@ -1,4 +1,4 @@ -subroutine js8dec(dd0,icos,newdat,synconly,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, & +subroutine js8dec(dd0,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lapon,lapcqonly, & napwid,lsubtract,nagain,iaptype,mycall12,mygrid6,hiscall12,bcontest, & sync0,f1,xdt,xbase,apsym,nharderrors,dmin,nbadcrc,ipass,iera,msg37,xsnr) @@ -14,7 +14,7 @@ subroutine js8dec(dd0,icos,newdat,synconly,nQSOProgress,nfqso,nftx,ndepth,lapon, character*12 mycall12,hiscall12 character*6 mycall6,mygrid6,hiscall6,c1,c2 character*87 cbits - logical bcontest,synconly + logical bcontest,syncStats real a(5) real s1(0:7,ND),s2(0:7,NN),s1sort(8*ND) real ps(0:7),psl(0:7) @@ -224,8 +224,8 @@ subroutine js8dec(dd0,icos,newdat,synconly,nQSOProgress,nfqso,nftx,ndepth,lapon, return endif - if(synconly) then - write(*,*) ' candidate X ', 'f1', f1, 'sync', nsync, 'xdt', xdt + if(syncStats) then + write(*,*) ' candidate ', NSUBMODE, 'f1', f1, 'sync', nsync, 'xdt', xdt flush(6) endif @@ -422,9 +422,8 @@ subroutine js8dec(dd0,icos,newdat,synconly,nQSOProgress,nfqso,nftx,ndepth,lapon, i3bit=4*decoded(73) + 2*decoded(74) + decoded(75) if(nbadcrc.eq.0) then - - if(synconly) then - write(*,*) ' decode X ', 'f1', f1, 'sync', (sync*10), 'xdt', xdt2 + if(syncStats) then + write(*,*) ' decode ', NSUBMODE, 'f1', f1, 'sync', (sync*10), 'xdt', xdt2 flush(6) endif diff --git a/lib/js8/js8e_params.f90 b/lib/js8/js8e_params.f90 index 9c9a6bd..9f444ff 100644 --- a/lib/js8/js8e_params.f90 +++ b/lib/js8/js8e_params.f90 @@ -1,3 +1,4 @@ +parameter (NSUBMODE=4) parameter (NCOSTAS=2) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas) parameter (NSPS=3840, NTXDUR=28, NDOWNSPS=32, NDD=90, JZ=32) ! 25 Hz 3.125 baud 8 wpm -28.0dB (1.0Eb/N0) 25.28s diff --git a/lib/js8/js8i_params.f90 b/lib/js8/js8i_params.f90 index 688a991..4aeaaf1 100644 --- a/lib/js8/js8i_params.f90 +++ b/lib/js8/js8i_params.f90 @@ -1,3 +1,4 @@ +parameter (NSUBMODE=8) parameter (NCOSTAS=2) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas) parameter (NSPS=384, NTXDUR=4, NDOWNSPS=12, NDD=125, JZ=250) ! 250 Hz 31.25 baud 60 wpm -18.0dB (1.0Eb/N0) 2.52s diff --git a/lib/js8a_decode.f90 b/lib/js8a_decode.f90 index ce47087..2ddd004 100644 --- a/lib/js8a_decode.f90 +++ b/lib/js8a_decode.f90 @@ -25,7 +25,7 @@ contains subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, & nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, & - mycall12,mygrid6,hiscall12,hisgrid6,synconly) + mycall12,mygrid6,hiscall12,hisgrid6,syncStats) ! use wavhdr use timer_module, only: timer ! type(hdr) h @@ -38,7 +38,7 @@ contains real candidate(3,NMAXCAND) real dd(NMAX) logical, intent(in) :: lft8apon,lapcqonly,nagain - logical newdat,lsubtract,ldupe,bcontest,synconly + logical newdat,lsubtract,ldupe,bcontest,syncStats character*12 mycall12, hiscall12 character*6 mygrid6,hisgrid6 integer*2 iwave(NMAX) @@ -93,32 +93,10 @@ contains lsubtract=.false. endif - if(synconly) then - if(NWRITELOG.eq.0) then - write(*,*) ' synconly' - flush(6) - endif - endif - call timer('syncjs8 ',0) call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase) call timer('syncjs8 ',1) - !if(NWRITELOG.eq.0) then - ! write(*,*) '', ncand, "candidates" - ! flush(6) - - ! do icand=1,ncand - ! sync=candidate(3,icand) - ! f1=candidate(1,icand) - ! xdt=candidate(2,icand) - ! xbase=10.0**(0.1*(sbase(nint(f1/(12000.0/NFFT1)))-40.0)) ! 3.125Hz - - ! write(*,*) ' candidate', icand, 'f1', f1, 'sync', sync, 'xdt', xdt, 'xbase', xbase - ! flush(6) - ! enddo - !endif - do icand=1,ncand sync=candidate(3,icand) f1=candidate(1,icand) @@ -131,7 +109,7 @@ contains endif call timer('js8dec ',0) - call js8dec(dd,icos,newdat,synconly,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & + call js8dec(dd,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, & hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, & nbadcrc,iappass,iera,msg37,xsnr) diff --git a/lib/js8b_decode.f90 b/lib/js8b_decode.f90 index 99f1392..eab3524 100644 --- a/lib/js8b_decode.f90 +++ b/lib/js8b_decode.f90 @@ -25,7 +25,7 @@ contains subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, & nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, & - mycall12,mygrid6,hiscall12,hisgrid6) + mycall12,mygrid6,hiscall12,hisgrid6,syncStats) ! use wavhdr use timer_module, only: timer ! type(hdr) h @@ -38,7 +38,7 @@ contains real candidate(3,NMAXCAND) real dd(NMAX) logical, intent(in) :: lft8apon,lapcqonly,nagain - logical newdat,lsubtract,ldupe,bcontest + logical newdat,lsubtract,ldupe,bcontest,syncStats character*12 mycall12, hiscall12 character*6 mygrid6,hisgrid6 integer*2 iwave(NMAX) @@ -114,7 +114,7 @@ contains endif call timer('js8dec ',0) - call js8dec(dd,icos,newdat,.false.,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & + call js8dec(dd,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, & hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, & nbadcrc,iappass,iera,msg37,xsnr) diff --git a/lib/js8c_decode.f90 b/lib/js8c_decode.f90 index 69377c6..df75c5a 100644 --- a/lib/js8c_decode.f90 +++ b/lib/js8c_decode.f90 @@ -25,7 +25,7 @@ contains subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, & nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, & - mycall12,mygrid6,hiscall12,hisgrid6) + mycall12,mygrid6,hiscall12,hisgrid6,syncStats) ! use wavhdr use timer_module, only: timer ! type(hdr) h @@ -38,7 +38,7 @@ contains real candidate(3,NMAXCAND) real dd(NMAX) logical, intent(in) :: lft8apon,lapcqonly,nagain - logical newdat,lsubtract,ldupe,bcontest + logical newdat,lsubtract,ldupe,bcontest,syncStats character*12 mycall12, hiscall12 character*6 mygrid6,hisgrid6 integer*2 iwave(NMAX) @@ -114,7 +114,7 @@ contains endif call timer('js8dec ',0) - call js8dec(dd,icos,newdat,.false.,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & + call js8dec(dd,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, & hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, & nbadcrc,iappass,iera,msg37,xsnr) diff --git a/lib/js8e_decode.f90 b/lib/js8e_decode.f90 index aa73861..1461163 100644 --- a/lib/js8e_decode.f90 +++ b/lib/js8e_decode.f90 @@ -25,7 +25,7 @@ contains subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, & nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, & - mycall12,mygrid6,hiscall12,hisgrid6) + mycall12,mygrid6,hiscall12,hisgrid6,syncStats) ! use wavhdr use timer_module, only: timer ! type(hdr) h @@ -38,7 +38,7 @@ contains real candidate(3,NMAXCAND) real dd(NMAX) logical, intent(in) :: lft8apon,lapcqonly,nagain - logical newdat,lsubtract,ldupe,bcontest + logical newdat,lsubtract,ldupe,bcontest,syncStats character*12 mycall12, hiscall12 character*6 mygrid6,hisgrid6 integer*2 iwave(NMAX) @@ -114,7 +114,7 @@ contains endif call timer('js8dec ',0) - call js8dec(dd,icos,newdat,.false.,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & + call js8dec(dd,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, & hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, & nbadcrc,iappass,iera,msg37,xsnr) diff --git a/lib/js8i_decode.f90 b/lib/js8i_decode.f90 index d5d0788..044522f 100644 --- a/lib/js8i_decode.f90 +++ b/lib/js8i_decode.f90 @@ -25,7 +25,7 @@ contains subroutine decode(this,callback,iwave,nQSOProgress,nfqso,nftx,newdat, & nutc,nfa,nfb,nexp_decode,ndepth,nagain,lft8apon,lapcqonly,napwid, & - mycall12,mygrid6,hiscall12,hisgrid6) + mycall12,mygrid6,hiscall12,hisgrid6,syncStats) ! use wavhdr use timer_module, only: timer ! type(hdr) h @@ -38,7 +38,7 @@ contains real candidate(3,NMAXCAND) real dd(NMAX) logical, intent(in) :: lft8apon,lapcqonly,nagain - logical newdat,lsubtract,ldupe,bcontest + logical newdat,lsubtract,ldupe,bcontest,syncStats character*12 mycall12, hiscall12 character*6 mygrid6,hisgrid6 integer*2 iwave(NMAX) @@ -114,7 +114,7 @@ contains endif call timer('js8dec ',0) - call js8dec(dd,icos,newdat,.false.,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & + call js8dec(dd,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lft8apon, & lapcqonly,napwid,lsubtract,nagain,iaptype,mycall12,mygrid6, & hiscall12,bcontest,sync,f1,xdt,xbase,apsym,nharderrors,dmin, & nbadcrc,iappass,iera,msg37,xsnr) diff --git a/lib/jt9.f90 b/lib/jt9.f90 index 30173ad..79aea64 100644 --- a/lib/jt9.f90 +++ b/lib/jt9.f90 @@ -21,7 +21,7 @@ program jt9 !### ndepth was defined as 60001. Why??? integer :: arglen,stat,offset,remain,mode=0,flow=200,fsplit=2700, & fhigh=4000,nrxfreq=1500,ntrperiod=1,ndepth=1,nexp_decode=0 - logical :: read_files = .true., tx9 = .false., display_help = .false., synconly = .false. + logical :: read_files = .true., tx9 = .false., display_help = .false., syncStats = .false. type (option) :: long_options(22) = [ & option ('help', .false., 'h', 'Display this help message', ''), & option ('shmem',.true.,'s','Use shared memory for sample data','KEY'), & @@ -50,7 +50,7 @@ program jt9 !option ('jt65', .false., '6', 'JT65 mode', ''), & !option ('jt9', .false., '9', 'JT9 mode', ''), & option ('js8', .false., '8', 'JS8 mode', ''), & - option ('synconly', .false., 'y', 'Sync only', ''), & + option ('syncStats', .false., 'y', 'Sync only', ''), & !option ('jt4', .false., '4', 'JT4 mode', ''), & !option ('qra64', .false., 'q', 'QRA64 mode', ''), & option ('sub-mode', .true., 'b', 'Sub mode, default SUBMODE=A', 'A'), & @@ -120,7 +120,7 @@ program jt9 case ('8') mode = 8 case ('y') - synconly = .true. + syncStats = .true. case ('T') tx9 = .true. case ('w') @@ -257,7 +257,7 @@ program jt9 shared_data%params%ljt65apon=.true. shared_data%params%napwid=75 shared_data%params%dttol=3. - shared_data%params%synconly=synconly + shared_data%params%syncStats=syncStats ! shared_data%params%minsync=0 !### TEST ONLY ! shared_data%params%nfqso=1500 !### TEST ONLY diff --git a/lib/jt9com.f90 b/lib/jt9com.f90 index 3999653..0a69ef2 100644 --- a/lib/jt9com.f90 +++ b/lib/jt9com.f90 @@ -18,7 +18,7 @@ integer(c_int) :: nfsplit integer(c_int) :: nfb integer(c_int) :: ntol - logical(c_bool) :: synconly + logical(c_bool) :: syncStats integer(c_int) :: kin integer(c_int) :: kposA integer(c_int) :: kposB diff --git a/mainwindow.cpp b/mainwindow.cpp index e313e71..ffc9743 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4267,39 +4267,21 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ // on an interval, issue a decode static qint32 lastDecodeStartA = -1; + static qint32 currentDecodeStartA = -1; + static qint32 nextDecodeStartA = -1; + qint32 startA = -1; qint32 szA = -1; qint32 cycleA = -1; bool couldDecodeA = false; - qint32 oneSecondFrames = computeFramesPerCycleForDecode(Varicode::JS8CallNormal)/computePeriodForSubmode(Varicode::JS8CallNormal); - if(lastDecodeStartA == -1 || k < k0 || k - lastDecodeStartA > oneSecondFrames){ - startA = k-computeFramesNeededForDecode(Varicode::JS8CallNormal); - if(startA < 0){ - // TODO: decoder doesn't copy wrap around ranges - startA += m_detector->period() * RX_SAMPLE_RATE; - } - - szA = computeFramesNeededForDecode(Varicode::JS8CallNormal); - lastDecodeStartA = k; - couldDecodeA = true; - qDebug() << "? NORMAL " << startA << k << k0; - } - -#if 0 - static qint32 currentDecodeStartA = -1; - static qint32 nextDecodeStartA = -1; - qint32 startA = -1; - qint32 szA = -1; - qint32 cycleA = -1; if(JS8_DEBUG_DECODE) qDebug() << "? NORMAL " << currentDecodeStartA << nextDecodeStartA; - bool couldDecodeA = isDecodeReady(Varicode::JS8CallNormal, k, k0, ¤tDecodeStartA, &nextDecodeStartA, &startA, &szA, &cycleA); + couldDecodeA = isDecodeReady(Varicode::JS8CallNormal, k, k0, ¤tDecodeStartA, &nextDecodeStartA, &startA, &szA, &cycleA); if(m_diskData){ startA = 0; szA = NTMAX*RX_SAMPLE_RATE-1; couldDecodeA = true; } -#endif static qint32 currentDecodeStartB = -1; static qint32 nextDecodeStartB = -1; @@ -4357,11 +4339,28 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ } #endif - - couldDecodeB = couldDecodeC = couldDecodeE = false; - int decodes = 0; + // default to including sync stats + dec_data.params.syncStats = true; + + // when no other mode is being decoded, do a sync stats decode for normal mode + if(!couldDecodeA && !couldDecodeB && !couldDecodeC && !couldDecodeE){ + qint32 oneSecondFrames = computeFramesPerCycleForDecode(Varicode::JS8CallNormal)/computePeriodForSubmode(Varicode::JS8CallNormal); + if(lastDecodeStartA == -1 || k < k0 || k - lastDecodeStartA > oneSecondFrames){ + startA = k-computeFramesNeededForDecode(Varicode::JS8CallNormal); + + if(startA < 0){ + // decoder wraps around ranges + startA += m_detector->period() * RX_SAMPLE_RATE; + } + + szA = computeFramesNeededForDecode(Varicode::JS8CallNormal); + lastDecodeStartA = k; + couldDecodeA = true; + } + } + if(couldDecodeA){ DecodeParams d; d.submode = Varicode::JS8CallNormal; @@ -4461,7 +4460,6 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){ if(JS8_DEBUG_DECODE) qDebug() << "--> decoder skipping at least 1 decode cycle" << "count" << count << "max" << maxDecodes; } - dec_data.params.synconly = true; dec_data.params.nsubmodes = 0; while(!m_decoderQueue.isEmpty()){ @@ -4880,6 +4878,10 @@ void MainWindow::processDecodedLine(QByteArray t){ if(t.indexOf("f1") >= 0){ auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts); if(!segs.isEmpty()){ + auto m1 = QString(segs.at(2)); + auto m = int(m1.toInt()); + auto period = computePeriodForSubmode(m); + auto f1 = QString(segs.at(4)); auto f = int(f1.toFloat()); @@ -4891,40 +4893,43 @@ void MainWindow::processDecodedLine(QByteArray t){ if(abs(xdt) <= 2){ if(s < 10){ - m_wideGraph->drawLine(QColor(Qt::darkCyan), f, f + computeBandwidthForSubmode(m_nSubMode)); + m_wideGraph->drawLine(QColor(Qt::darkCyan), f, f + computeBandwidthForSubmode(m)); } else if (s <= 21){ - m_wideGraph->drawLine(QColor(Qt::white), f, f + computeBandwidthForSubmode(m_nSubMode)); + m_wideGraph->drawLine(QColor(Qt::white), f, f + computeBandwidthForSubmode(m)); } } - if(t.contains("decode X")){ + if(t.contains("decode")){ auto now = QDateTime::currentDateTimeUtc(); float n = 0; - float nPos = m_TRperiod - (now.time().second() % m_TRperiod); - float nNeg = (now.time().second() % m_TRperiod) - m_TRperiod; - float offset = m_TRperiod - computeFramesNeededForDecode(m_nSubMode)/RX_SAMPLE_RATE + xdt; + float nPos = period - (now.time().second() % period); + float nNeg = (now.time().second() % period) - period; if(qAbs(nNeg) < nPos){ - n = nNeg + offset; + n = nNeg; } else { - n = nPos - offset; + n = nPos; } + n -= (float)period; + n += computeFramesNeededForDecode(m)/RX_SAMPLE_RATE; + n -= xdt; + int xdtmin = qMin(n*1000, (float)DriftingDateTime::drift()); int xdtmax = qMax(n*1000, (float)DriftingDateTime::drift()); - m_wideGraph->drawLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m_nSubMode)); + m_wideGraph->drawLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m)); + int oldNewDrift = newDrift; newDrift = (xdtmin + (xdtmax-xdtmin)/2); newDrift = qMin(oldNewDrift, newDrift) + (qMax(oldNewDrift, newDrift)-qMin(oldNewDrift, newDrift))/2; hasNewDrift = true; } } - } + } - - if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists()); + if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists()); return; } @@ -4934,11 +4939,11 @@ void MainWindow::processDecodedLine(QByteArray t){ } if(t.indexOf("") >= 0) { - if(hasNewDrift){ - int oldDrift = DriftingDateTime::drift(); - newDrift = qMin(oldDrift, newDrift) + (qMax(oldDrift, newDrift)-qMin(oldDrift, newDrift))/2; + static int driftN = 1; + newDrift = ((driftN-1)*DriftingDateTime::drift() + newDrift)/driftN; setDrift(newDrift); + if(driftN < 60) driftN++; // cap it to 60 observations writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Drift: %1").arg(newDrift)); hasNewDrift = false; } diff --git a/widegraph.cpp b/widegraph.cpp index d913874..2607fdf 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -356,6 +356,8 @@ void WideGraph::drawSwide(){ swideLocal[i] = flagValue; } ui->widePlot->draw(swideLocal,true,false); + } else if(lastSecondInPeriod != secondInPeriod) { + drawLine(Qt::gray, ui->widePlot->startFreq(), 10); } lastSecondInPeriod=secondInPeriod; From bf8fb4aa8589327737a706745734976a0b3705a0 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Thu, 7 May 2020 15:47:50 -0400 Subject: [PATCH 06/32] Smaller increment adjustment for drift. 1 second interval lines. --- mainwindow.cpp | 2 +- plotter.cpp | 11 ++++++++++- plotter.h | 3 ++- widegraph.cpp | 4 ++-- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index ffc9743..86d3298 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4914,7 +4914,7 @@ void MainWindow::processDecodedLine(QByteArray t){ n -= (float)period; n += computeFramesNeededForDecode(m)/RX_SAMPLE_RATE; - n -= xdt; + n -= xdt/2; int xdtmin = qMin(n*1000, (float)DriftingDateTime::drift()); int xdtmax = qMax(n*1000, (float)DriftingDateTime::drift()); diff --git a/plotter.cpp b/plotter.cpp index b2e44a7..f580585 100644 --- a/plotter.cpp +++ b/plotter.cpp @@ -281,7 +281,7 @@ void CPlotter::draw(float swide[], bool bScroll, bool bRed) m_bScaleOK=true; } -void CPlotter::drawLine(const QColor &color, int ia, int ib) +void CPlotter::drawDecodeLine(const QColor &color, int ia, int ib) { int x1=XfromFreq(ia); int x2=XfromFreq(ib); @@ -295,6 +295,15 @@ void CPlotter::drawLine(const QColor &color, int ia, int ib) painter1.drawLine(qMax(x1, x2),0,qMax(x1, x2),9); } +void CPlotter::drawHorizontalLine(const QColor &color, int x, int width) +{ + QPen pen0(color, 1); + + QPainter painter1(&m_WaterfallPixmap); + painter1.setPen(pen0); + painter1.drawLine(x,0,width <= 0 ? m_w : x+width,0); +} + void CPlotter::replot() { float swide[m_w]; diff --git a/plotter.h b/plotter.h index ced70fb..a3df2a6 100644 --- a/plotter.h +++ b/plotter.h @@ -90,7 +90,8 @@ public: void setReference(bool b) {m_bReference = b;} bool Reference() const {return m_bReference;} #endif - void drawLine(const QColor &color, int ia, int ib); + void drawDecodeLine(const QColor &color, int ia, int ib); + void drawHorizontalLine(const QColor &color, int x, int width); void setVHF(bool bVHF); void setRedFile(QString fRed); bool scaleOK () const {return m_bScaleOK;} diff --git a/widegraph.cpp b/widegraph.cpp index 2607fdf..71b69d4 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -246,7 +246,7 @@ void WideGraph::saveSettings() //saveS void WideGraph::drawLine(const QColor &color, int ia, int ib) { - ui->widePlot->drawLine(color, ia, ib); + ui->widePlot->drawDecodeLine(color, ia, ib); } void WideGraph::dataSink2(float s[], float df3, int ihsym, int ndiskdata) //dataSink2 @@ -357,7 +357,7 @@ void WideGraph::drawSwide(){ } ui->widePlot->draw(swideLocal,true,false); } else if(lastSecondInPeriod != secondInPeriod) { - drawLine(Qt::gray, ui->widePlot->startFreq(), 10); + ui->widePlot->drawHorizontalLine(Qt::white, 0, 5); } lastSecondInPeriod=secondInPeriod; From 25d644955f013f0af99e5f6cb4b705119ffb3199 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Thu, 7 May 2020 16:15:38 -0400 Subject: [PATCH 07/32] SyncStats should be output differently than DecoderDebug --- lib/js8/js8dec.f90 | 4 +- lib/js8/subtractjs8.f90 | 10 ++-- mainwindow.cpp | 110 ++++++++++++++++++++++------------------ 3 files changed, 67 insertions(+), 57 deletions(-) diff --git a/lib/js8/js8dec.f90 b/lib/js8/js8dec.f90 index df3dc2d..7db0d2d 100644 --- a/lib/js8/js8dec.f90 +++ b/lib/js8/js8dec.f90 @@ -225,7 +225,7 @@ subroutine js8dec(dd0,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lapon endif if(syncStats) then - write(*,*) ' candidate ', NSUBMODE, 'f1', f1, 'sync', nsync, 'xdt', xdt + write(*,*) ' candidate ', NSUBMODE, 'f1', f1, 'sync', nsync, 'xdt', xdt flush(6) endif @@ -423,7 +423,7 @@ subroutine js8dec(dd0,icos,newdat,syncStats,nQSOProgress,nfqso,nftx,ndepth,lapon if(nbadcrc.eq.0) then if(syncStats) then - write(*,*) ' decode ', NSUBMODE, 'f1', f1, 'sync', (sync*10), 'xdt', xdt2 + write(*,*) ' decode ', NSUBMODE, 'f1', f1, 'sync', (sync*10), 'xdt', xdt2 flush(6) endif diff --git a/lib/js8/subtractjs8.f90 b/lib/js8/subtractjs8.f90 index 18bcd95..93daaf5 100644 --- a/lib/js8/subtractjs8.f90 +++ b/lib/js8/subtractjs8.f90 @@ -20,7 +20,7 @@ subroutine subtractjs8(dd,itone,f0,dt) nstart=dt*12000+1 - if(NWRITELOG.eq.0) then + if(NWRITELOG.eq.1) then write(*,*) ' generating reference signal', nstart flush(6) endif @@ -33,14 +33,14 @@ subroutine subtractjs8(dd,itone,f0,dt) if(id.ge.1.and.id.le.NMAX) camp(i)=dd(id)*conjg(cref(i)) enddo - if(NWRITELOG.eq.0) then + if(NWRITELOG.eq.1) then write(*,*) ' filtering', NFFT flush(6) endif if(first) then ! Create and normalize the filter - if(NWRITELOG.eq.0) then + if(NWRITELOG.eq.1) then write(*,*) ' creating and normalizing filter' flush(6) endif @@ -94,7 +94,7 @@ subroutine subtractjs8(dd,itone,f0,dt) endif endif - if(NWRITELOG.eq.0) then + if(NWRITELOG.eq.1) then write(*,*) ' generating complex amplitude' flush(6) endif @@ -105,7 +105,7 @@ subroutine subtractjs8(dd,itone,f0,dt) cfilt(1:NFFT)=cfilt(1:NFFT)*cw(1:NFFT) call four2a(cfilt,NFFT,1,1,1) - if(NWRITELOG.eq.0) then + if(NWRITELOG.eq.1) then write(*,*) ' subtracting filtered reference', NFFT flush(6) endif diff --git a/mainwindow.cpp b/mainwindow.cpp index 86d3298..e8d3fcb 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4874,71 +4874,81 @@ void MainWindow::processDecodedLine(QByteArray t){ static bool hasNewDrift = false; static int newDrift = 0; - if(t.indexOf("") >= 0) { - if(t.indexOf("f1") >= 0){ + if(t.indexOf("") >= 0) { auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts); - if(!segs.isEmpty()){ - auto m1 = QString(segs.at(2)); - auto m = int(m1.toInt()); - auto period = computePeriodForSubmode(m); + if(segs.isEmpty()){ + return; + } - auto f1 = QString(segs.at(4)); - auto f = int(f1.toFloat()); + auto m1 = QString(segs.at(2)); + auto m = int(m1.toInt()); + auto period = computePeriodForSubmode(m); - auto s1 = QString(segs.at(6)); - auto s = int(s1.toFloat()); + auto f1 = QString(segs.at(4)); + auto f = int(f1.toFloat()); - auto xdt1 = QString(segs.at(8)); - auto xdt = int(xdt1.toFloat()); + auto s1 = QString(segs.at(6)); + auto s = int(s1.toFloat()); - if(abs(xdt) <= 2){ - if(s < 10){ - m_wideGraph->drawLine(QColor(Qt::darkCyan), f, f + computeBandwidthForSubmode(m)); - } else if (s <= 21){ - m_wideGraph->drawLine(QColor(Qt::white), f, f + computeBandwidthForSubmode(m)); - } - } + auto xdt1 = QString(segs.at(8)); + auto xdt = int(xdt1.toFloat()); - if(t.contains("decode")){ - auto now = QDateTime::currentDateTimeUtc(); - - float n = 0; - float nPos = period - (now.time().second() % period); - float nNeg = (now.time().second() % period) - period; - - if(qAbs(nNeg) < nPos){ - n = nNeg; - } else { - n = nPos; - } - - n -= (float)period; - n += computeFramesNeededForDecode(m)/RX_SAMPLE_RATE; - n -= xdt/2; - - int xdtmin = qMin(n*1000, (float)DriftingDateTime::drift()); - int xdtmax = qMax(n*1000, (float)DriftingDateTime::drift()); - - m_wideGraph->drawLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m)); - - int oldNewDrift = newDrift; - newDrift = (xdtmin + (xdtmax-xdtmin)/2); - newDrift = qMin(oldNewDrift, newDrift) + (qMax(oldNewDrift, newDrift)-qMin(oldNewDrift, newDrift))/2; - hasNewDrift = true; + // only draw candidates + if(abs(xdt) <= 2){ + if(s < 10){ + m_wideGraph->drawLine(QColor(Qt::darkCyan), f, f + computeBandwidthForSubmode(m)); + } else if (s <= 21){ + m_wideGraph->drawLine(QColor(Qt::white), f, f + computeBandwidthForSubmode(m)); } } - } - if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists()); - return; + if(!t.contains("decode")){ + return; + } + + // but use decodes for drift + auto now = QDateTime::currentDateTimeUtc(); + + float n = 0; + float nPos = period - (now.time().second() % period); + float nNeg = (now.time().second() % period) - period; + + if(qAbs(nNeg) < nPos){ + n = nNeg; + } else { + n = nPos; + } + + n -= (float)period; + n += computeFramesNeededForDecode(m)/RX_SAMPLE_RATE; + n -= xdt; + + int xdtmin = qMin(n*1000, (float)DriftingDateTime::drift()); + int xdtmax = qMax(n*1000, (float)DriftingDateTime::drift()); + + m_wideGraph->drawLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m)); + + int oldNewDrift = newDrift; + newDrift = (xdtmin + (xdtmax-xdtmin)/2); + newDrift = qMin(oldNewDrift, newDrift) + (qMax(oldNewDrift, newDrift)-qMin(oldNewDrift, newDrift))/2; + hasNewDrift = true; + + if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists()); + + return; } if(t.indexOf("") >= 0) { - if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists()); - return; + if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists()); + return; + } + + if(t.indexOf("") >= 0) { + return; } if(t.indexOf("") >= 0) { + // TODO: decide if we should adjust here... if(hasNewDrift){ static int driftN = 1; newDrift = ((driftN-1)*DriftingDateTime::drift() + newDrift)/driftN; From 4dbb91047f4c7983aee1ef2fe391ce92a1c7118b Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Thu, 7 May 2020 22:24:48 -0400 Subject: [PATCH 08/32] Wraparound buffer for BCDEI decoders --- lib/decoder.f90 | 52 +++++++++++++++++++++++++++++++++++++---- lib/js8/js8a_params.f90 | 4 ++-- 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 593086b..81f7ee1 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -88,7 +88,18 @@ subroutine multimode_decoder(ss,id2,params,nfsample) pos = max(0,params%kposI) sz = max(0,params%kszI) id0=0 - id0(1:sz+1)=id2(pos+1:pos+sz+1) + imax=int(NTMAX*12000) + + if((imax-pos).lt.sz) then + ! this means that the first part of the id0 is at the end of the buffer + ! and the second half is at the beginning of the buffer + firstsize=int(imax-pos)-1 + secondsize=int(sz-firstsize)+1 + id0(1:firstsize+1)=id2(pos+1:pos+firstsize+1) + id0(firstsize+1:firstsize+secondsize+1)=id2(1:secondsize+1) + else + id0(1:sz+1)=id2(pos+1:pos+sz+1) + endif call my_js8i%decode(js8i_decoded,id0,params%nQSOProgress,params%nfqso, & params%nftx,newdat,params%nutc,params%nfa,params%nfb, & @@ -111,7 +122,18 @@ subroutine multimode_decoder(ss,id2,params,nfsample) pos = max(0,params%kposE) sz = max(0,params%kszE) id0=0 - id0(1:sz+1)=id2(pos+1:pos+sz+1) + imax=int(NTMAX*12000) + + if((imax-pos).lt.sz) then + ! this means that the first part of the id0 is at the end of the buffer + ! and the second half is at the beginning of the buffer + firstsize=int(imax-pos)-1 + secondsize=int(sz-firstsize)+1 + id0(1:firstsize+1)=id2(pos+1:pos+firstsize+1) + id0(firstsize+1:firstsize+secondsize+1)=id2(1:secondsize+1) + else + id0(1:sz+1)=id2(pos+1:pos+sz+1) + endif call my_js8e%decode(js8e_decoded,id0,params%nQSOProgress,params%nfqso, & params%nftx,newdat,params%nutc,params%nfa,params%nfb, & @@ -134,7 +156,18 @@ subroutine multimode_decoder(ss,id2,params,nfsample) pos = max(0,params%kposC) sz = max(0,params%kszC) id0=0 - id0(1:sz+1)=id2(pos+1:pos+sz+1) + imax=int(NTMAX*12000) + + if((imax-pos).lt.sz) then + ! this means that the first part of the id0 is at the end of the buffer + ! and the second half is at the beginning of the buffer + firstsize=int(imax-pos)-1 + secondsize=int(sz-firstsize)+1 + id0(1:firstsize+1)=id2(pos+1:pos+firstsize+1) + id0(firstsize+1:firstsize+secondsize+1)=id2(1:secondsize+1) + else + id0(1:sz+1)=id2(pos+1:pos+sz+1) + endif call my_js8c%decode(js8c_decoded,id0,params%nQSOProgress,params%nfqso, & params%nftx,newdat,params%nutc,params%nfa,params%nfb, & @@ -157,7 +190,18 @@ subroutine multimode_decoder(ss,id2,params,nfsample) pos = max(0,params%kposB) sz = max(0,params%kszB) id0=0 - id0(1:sz+1)=id2(pos+1:pos+sz+1) + imax=int(NTMAX*12000) + + if((imax-pos).lt.sz) then + ! this means that the first part of the id0 is at the end of the buffer + ! and the second half is at the beginning of the buffer + firstsize=int(imax-pos)-1 + secondsize=int(sz-firstsize)+1 + id0(1:firstsize+1)=id2(pos+1:pos+firstsize+1) + id0(firstsize+1:firstsize+secondsize+1)=id2(1:secondsize+1) + else + id0(1:sz+1)=id2(pos+1:pos+sz+1) + endif call my_js8b%decode(js8b_decoded,id0,params%nQSOProgress,params%nfqso, & params%nftx,newdat,params%nutc,params%nfa,params%nfb, & diff --git a/lib/js8/js8a_params.f90 b/lib/js8/js8a_params.f90 index 0092ae7..788c6c2 100644 --- a/lib/js8/js8a_params.f90 +++ b/lib/js8/js8a_params.f90 @@ -3,11 +3,11 @@ parameter (NSUBMODE=0) parameter (NCOSTAS=1) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas) -parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=36) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s +parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=62) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s parameter (AZ=12000.0/(1.0*NSPS)*0.64d0) !Dedupe overlap in Hz parameter (ASTART=0.5) !Start delay in seconds -parameter (ASYNCMIN=2) !Minimum Sync +parameter (ASYNCMIN=1.5) !Minimum Sync parameter (KK=87) !Information bits (75 + CRC12) parameter (ND=58) !Data symbols From 45c1d141128f4ad4bc32e13c82dbbec913acbf96 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Thu, 7 May 2020 22:41:10 -0400 Subject: [PATCH 09/32] Turning off auto drift experiment, only drawing sync stats --- mainwindow.cpp | 148 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 116 insertions(+), 32 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index e8d3fcb..db30111 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4266,10 +4266,8 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ // enqueue those decodes that are "ready" // on an interval, issue a decode - static qint32 lastDecodeStartA = -1; static qint32 currentDecodeStartA = -1; static qint32 nextDecodeStartA = -1; - qint32 startA = -1; qint32 szA = -1; qint32 cycleA = -1; @@ -4341,13 +4339,11 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ int decodes = 0; - // default to including sync stats - dec_data.params.syncStats = true; - // when no other mode is being decoded, do a sync stats decode for normal mode if(!couldDecodeA && !couldDecodeB && !couldDecodeC && !couldDecodeE){ - qint32 oneSecondFrames = computeFramesPerCycleForDecode(Varicode::JS8CallNormal)/computePeriodForSubmode(Varicode::JS8CallNormal); - if(lastDecodeStartA == -1 || k < k0 || k - lastDecodeStartA > oneSecondFrames){ + static qint32 lastDecodeStartA = -1; + qint32 oneSecondFramesA = computeFramesPerCycleForDecode(Varicode::JS8CallNormal)/computePeriodForSubmode(Varicode::JS8CallNormal); + if(lastDecodeStartA == -1 || k < k0 || k - lastDecodeStartA > oneSecondFramesA){ startA = k-computeFramesNeededForDecode(Varicode::JS8CallNormal); if(startA < 0){ @@ -4359,6 +4355,72 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ lastDecodeStartA = k; couldDecodeA = true; } + +#if 0 + static qint32 lastDecodeStartB = -1; + qint32 oneSecondFramesB = computeFramesPerCycleForDecode(Varicode::JS8CallFast)/computePeriodForSubmode(Varicode::JS8CallFast); + if(lastDecodeStartB == -1 || k < k0 || k - lastDecodeStartB > oneSecondFramesB){ + startB = k-computeFramesNeededForDecode(Varicode::JS8CallFast); + + if(startB < 0){ + // decoder wraps around ranges + startB += m_detector->period() * RX_SAMPLE_RATE; + } + + szB = computeFramesNeededForDecode(Varicode::JS8CallFast); + lastDecodeStartB = k; + couldDecodeB = true; + } + + static qint32 lastDecodeStartC = -1; + qint32 oneSecondFramesC = computeFramesPerCycleForDecode(Varicode::JS8CallTurbo)/computePeriodForSubmode(Varicode::JS8CallTurbo); + if(lastDecodeStartC == -1 || k < k0 || k - lastDecodeStartC > oneSecondFramesC){ + startC = k-computeFramesNeededForDecode(Varicode::JS8CallTurbo); + + if(startC < 0){ + // decoder wraps around ranges + startC += m_detector->period() * RX_SAMPLE_RATE; + } + + szC = computeFramesNeededForDecode(Varicode::JS8CallTurbo); + lastDecodeStartC = k; + couldDecodeC = true; + } + +#if JS8_ENABLE_JS8E + static qint32 lastDecodeStartE = -1; + qint32 oneSecondFramesE = computeFramesPerCycleForDecode(Varicode::JS8CallSlow)/computePeriodForSubmode(Varicode::JS8CallSlow); + if(lastDecodeStartE == -1 || k < k0 || k - lastDecodeStartE > oneSecondFramesE){ + startE = k-computeFramesNeededForDecode(Varicode::JS8CallSlow); + + if(startE < 0){ + // decoder wraps around ranges + startE += m_detector->period() * RX_SAMPLE_RATE; + } + + szE = computeFramesNeededForDecode(Varicode::JS8CallSlow); + lastDecodeStartE = k; + couldDecodeE = true; + } +#endif + +#if JS8_ENABLE_JS8I + static qint32 lastDecodeStartI = -1; + qint32 oneSecondFramesI = computeFramesPerCycleForDecode(Varicode::JS8CallUltra)/computePeriodForSubmode(Varicode::JS8CallUltra); + if(lastDecodeStartI == -1 || k < k0 || k - lastDecodeStartI > oneSecondFramesI){ + startI = k-computeFramesNeededForDecode(Varicode::JS8CallUltra); + + if(startI < 0){ + // decoder wraps around ranges + startI += m_detector->period() * RX_SAMPLE_RATE; + } + + szI = computeFramesNeededForDecode(Varicode::JS8CallUltra); + lastDecodeStartI = k; + couldDecodeI = true; + } +#endif +#endif } if(couldDecodeA){ @@ -4460,6 +4522,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){ if(JS8_DEBUG_DECODE) qDebug() << "--> decoder skipping at least 1 decode cycle" << "count" << count << "max" << maxDecodes; } + // default to no submodes being decoded, then bitwise OR the modes together to decode them all at once dec_data.params.nsubmodes = 0; while(!m_decoderQueue.isEmpty()){ @@ -4518,6 +4581,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){ int period = computePeriodForSubmode(submode); + dec_data.params.syncStats = true; dec_data.params.npts8=(m_ihsym*m_nsps)/16; dec_data.params.newdat=1; dec_data.params.nagain=0; @@ -4893,10 +4957,12 @@ void MainWindow::processDecodedLine(QByteArray t){ auto xdt1 = QString(segs.at(8)); auto xdt = int(xdt1.toFloat()); - // only draw candidates + // draw candidates if(abs(xdt) <= 2){ if(s < 10){ m_wideGraph->drawLine(QColor(Qt::darkCyan), f, f + computeBandwidthForSubmode(m)); + } else if (s <= 15){ + m_wideGraph->drawLine(QColor(Qt::cyan), f, f + computeBandwidthForSubmode(m)); } else if (s <= 21){ m_wideGraph->drawLine(QColor(Qt::white), f, f + computeBandwidthForSubmode(m)); } @@ -4906,32 +4972,50 @@ void MainWindow::processDecodedLine(QByteArray t){ return; } - // but use decodes for drift - auto now = QDateTime::currentDateTimeUtc(); - - float n = 0; - float nPos = period - (now.time().second() % period); - float nNeg = (now.time().second() % period) - period; - - if(qAbs(nNeg) < nPos){ - n = nNeg; - } else { - n = nPos; - } - - n -= (float)period; - n += computeFramesNeededForDecode(m)/RX_SAMPLE_RATE; - n -= xdt; - - int xdtmin = qMin(n*1000, (float)DriftingDateTime::drift()); - int xdtmax = qMax(n*1000, (float)DriftingDateTime::drift()); - + // draw decodes m_wideGraph->drawLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m)); - int oldNewDrift = newDrift; - newDrift = (xdtmin + (xdtmax-xdtmin)/2); - newDrift = qMin(oldNewDrift, newDrift) + (qMax(oldNewDrift, newDrift)-qMin(oldNewDrift, newDrift))/2; - hasNewDrift = true; +#if 0 + // but use decodes for drift + if(m == Varicode::JS8CallNormal){ + auto now = QDateTime::currentDateTimeUtc(); + float n = 0; + float nPos = period - ((now.time().second()) % period); + float nNeg = ((now.time().second()) % period) - period; + + if(qAbs(nNeg) < nPos){ + n = nNeg; + } else { + n = nPos; + } + + int offset = period - computeFramesNeededForDecode(m)/RX_SAMPLE_RATE; + n -= offset; + n -= xdt-1.0; + + //n += period; + // n -= computeFramesNeededForDecode(m)/RX_SAMPLE_RATE; + // n += xdt; + + //// // auto now = QDateTime::currentDateTimeUtc(); + //// // float n = 0; + //// // float s = now.time().second() + now.time().msec()/1000.0; + //// // + //// // if(s < 30){ + //// // n = -s; + //// // } else { + //// // n = 60 - s; + //// // } + + int xdtmin = qMin(n*1000, (float)DriftingDateTime::drift()); + int xdtmax = qMax(n*1000, (float)DriftingDateTime::drift()); + + int oldNewDrift = newDrift; + newDrift = (xdtmin + (xdtmax-xdtmin)/2); + newDrift = qMin(oldNewDrift, newDrift) + (qMax(oldNewDrift, newDrift)-qMin(oldNewDrift, newDrift))/2; + hasNewDrift = true; + } +#endif if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists()); From 0887f12e8c14806d7445570f8052c3ff3fa763e3 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Sat, 9 May 2020 11:36:09 -0400 Subject: [PATCH 10/32] Working sync via 1 second timing for A mode only --- lib/js8/js8a_params.f90 | 2 +- mainwindow.cpp | 52 ++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/js8/js8a_params.f90 b/lib/js8/js8a_params.f90 index 788c6c2..0adbb37 100644 --- a/lib/js8/js8a_params.f90 +++ b/lib/js8/js8a_params.f90 @@ -3,7 +3,7 @@ parameter (NSUBMODE=0) parameter (NCOSTAS=1) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas) -parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=62) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s +parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=26) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s parameter (AZ=12000.0/(1.0*NSPS)*0.64d0) !Dedupe overlap in Hz parameter (ASTART=0.5) !Start delay in seconds diff --git a/mainwindow.cpp b/mainwindow.cpp index db30111..5be94de 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4340,7 +4340,9 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ int decodes = 0; // when no other mode is being decoded, do a sync stats decode for normal mode - if(!couldDecodeA && !couldDecodeB && !couldDecodeC && !couldDecodeE){ + bool experiment = true; + + if(experiment){ static qint32 lastDecodeStartA = -1; qint32 oneSecondFramesA = computeFramesPerCycleForDecode(Varicode::JS8CallNormal)/computePeriodForSubmode(Varicode::JS8CallNormal); if(lastDecodeStartA == -1 || k < k0 || k - lastDecodeStartA > oneSecondFramesA){ @@ -4356,6 +4358,8 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ couldDecodeA = true; } + couldDecodeB = couldDecodeC = couldDecodeE = false; + #if 0 static qint32 lastDecodeStartB = -1; qint32 oneSecondFramesB = computeFramesPerCycleForDecode(Varicode::JS8CallFast)/computePeriodForSubmode(Varicode::JS8CallFast); @@ -4492,7 +4496,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){ QMutexLocker mutex(m_detector->getMutex()); if(m_decoderBusy){ - int seconds = m_decoderBusyStartTime.secsTo(DriftingDateTime::currentDateTimeUtc()); + int seconds = m_decoderBusyStartTime.secsTo(QDateTime::currentDateTimeUtc()); if(seconds > 60){ if(JS8_DEBUG_DECODE) qDebug() << "--> decoder should be killed!" << QString("(%1 seconds)").arg(seconds); } else if(seconds > 30){ @@ -4761,7 +4765,7 @@ void MainWindow::decodeBusy(bool b) //decodeBusy() m_decoderBusy=b; if(m_decoderBusy){ tx_status_label.setText("Decoding"); - m_decoderBusyStartTime = DriftingDateTime::currentDateTimeUtc(); + m_decoderBusyStartTime = QDateTime::currentDateTimeUtc(); //DriftingDateTime::currentDateTimeUtc(); m_decoderBusyFreq = dialFrequency(); m_decoderBusyBand = m_config.bands()->find (m_decoderBusyFreq); } @@ -4844,7 +4848,7 @@ void MainWindow::decodeCheckHangingDecoder(){ return; } - if(!m_decoderBusyStartTime.isValid() || m_decoderBusyStartTime.secsTo(DriftingDateTime::currentDateTimeUtc()) < 60){ + if(!m_decoderBusyStartTime.isValid() || m_decoderBusyStartTime.secsTo(QDateTime::currentDateTimeUtc()) < 60){ return; } @@ -4975,37 +4979,27 @@ void MainWindow::processDecodedLine(QByteArray t){ // draw decodes m_wideGraph->drawLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m)); -#if 0 - // but use decodes for drift - if(m == Varicode::JS8CallNormal){ +#if 1 + // use normal decodes for auto drift if we haven't already defined a new drift for this period + if(/*!hasNewDrift && */ (m == Varicode::JS8CallSlow || m == Varicode::JS8CallNormal)){ auto now = QDateTime::currentDateTimeUtc(); float n = 0; - float nPos = period - ((now.time().second()) % period); float nNeg = ((now.time().second()) % period) - period; - + float nPos = period - ((now.time().second()) % period); if(qAbs(nNeg) < nPos){ - n = nNeg; + n = nNeg + 1; } else { - n = nPos; + n = nPos - 1; } - int offset = period - computeFramesNeededForDecode(m)/RX_SAMPLE_RATE; - n -= offset; - n -= xdt-1.0; + // subtract to the previous period + n -= (float)period; - //n += period; - // n -= computeFramesNeededForDecode(m)/RX_SAMPLE_RATE; - // n += xdt; + // subtract the actual tx duration + n += computeFramesNeededForDecode(m)/RX_SAMPLE_RATE; - //// // auto now = QDateTime::currentDateTimeUtc(); - //// // float n = 0; - //// // float s = now.time().second() + now.time().msec()/1000.0; - //// // - //// // if(s < 30){ - //// // n = -s; - //// // } else { - //// // n = 60 - s; - //// // } + // subtract the time delta + n -= xdt; int xdtmin = qMin(n*1000, (float)DriftingDateTime::drift()); int xdtmax = qMax(n*1000, (float)DriftingDateTime::drift()); @@ -5032,6 +5026,12 @@ void MainWindow::processDecodedLine(QByteArray t){ } if(t.indexOf("") >= 0) { + int msec = m_decoderBusyStartTime.msecsTo(QDateTime::currentDateTimeUtc()); + qDebug() << "Decoder Duration:" << msec; + if(msec >= 1000){ + writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Decoder Duration: %1").arg(msec)); + } + // TODO: decide if we should adjust here... if(hasNewDrift){ static int driftN = 1; From 6d9e2b846185147c95fabe5212c6178b5d24927b Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Sun, 10 May 2020 09:02:50 -0400 Subject: [PATCH 11/32] Keeping track of last decode position --- mainwindow.cpp | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 5be94de..9e82b13 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4343,21 +4343,52 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ bool experiment = true; if(experiment){ - static qint32 lastDecodeStartA = -1; - qint32 oneSecondFramesA = computeFramesPerCycleForDecode(Varicode::JS8CallNormal)/computePeriodForSubmode(Varicode::JS8CallNormal); - if(lastDecodeStartA == -1 || k < k0 || k - lastDecodeStartA > oneSecondFramesA){ - startA = k-computeFramesNeededForDecode(Varicode::JS8CallNormal); + static qint32 lastDecodeStartA = 0; + qint32 maxSamples = m_detector->period()*RX_SAMPLE_RATE; + qint32 oneSecondSamples = RX_SAMPLE_RATE; + + // when we've incremented at least one second into the future + qint32 incrementedBy = k - lastDecodeStartA; + if(k < lastDecodeStartA){ + incrementedBy = maxSamples - lastDecodeStartA + k; + } + + if(incrementedBy >= oneSecondSamples){ + //startA = lastDecodeStartA + oneSecondSamples; + //szA = computeFramesNeededForDecode(Varicode::JS8CallNormal) + oneSecondSamples; + //lastDecodeStartA += + + startA = k - incrementedBy - computeFramesNeededForDecode(Varicode::JS8CallNormal); if(startA < 0){ - // decoder wraps around ranges - startA += m_detector->period() * RX_SAMPLE_RATE; + startA += maxSamples; } - szA = computeFramesNeededForDecode(Varicode::JS8CallNormal); + szA = incrementedBy + computeFramesNeededForDecode(Varicode::JS8CallNormal); + + qDebug() << "A: start:" << startA << "sz:" << szA << "stop:" << startA + szA; + lastDecodeStartA = k; couldDecodeA = true; } + + + + //qint32 oneSecondFramesA = computeFramesPerCycleForDecode(Varicode::JS8CallNormal)/computePeriodForSubmode(Varicode::JS8CallNormal); + //if(lastDecodeStartA == -1 || k < k0 || k - lastDecodeStartA > oneSecondFramesA){ + // startA = k-computeFramesNeededForDecode(Varicode::JS8CallNormal); + // + // if(startA < 0){ + // // decoder wraps around ranges + // startA += m_detector->period() * RX_SAMPLE_RATE; + // } + // + // szA = computeFramesNeededForDecode(Varicode::JS8CallNormal); + // lastDecodeStartA = k; + // couldDecodeA = true; + //} + couldDecodeB = couldDecodeC = couldDecodeE = false; #if 0 @@ -4979,7 +5010,7 @@ void MainWindow::processDecodedLine(QByteArray t){ // draw decodes m_wideGraph->drawLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m)); -#if 1 +#if 0 // use normal decodes for auto drift if we haven't already defined a new drift for this period if(/*!hasNewDrift && */ (m == Varicode::JS8CallSlow || m == Varicode::JS8CallNormal)){ auto now = QDateTime::currentDateTimeUtc(); From 52ecf192ace5bc1791b4582fd7364ca0a1158ca4 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Sun, 10 May 2020 14:05:24 -0400 Subject: [PATCH 12/32] Back to 2.48s JZ for normal --- lib/js8/js8a_params.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/js8/js8a_params.f90 b/lib/js8/js8a_params.f90 index 0adbb37..788c6c2 100644 --- a/lib/js8/js8a_params.f90 +++ b/lib/js8/js8a_params.f90 @@ -3,7 +3,7 @@ parameter (NSUBMODE=0) parameter (NCOSTAS=1) !Which JS8 Costas Arrays to use (1=original, 2=three symmetrical costas) -parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=26) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s +parameter (NSPS=1920, NTXDUR=15, NDOWNSPS=32, NDD=100, JZ=62) ! 50 Hz 6.250 baud 16 wpm -25.0dB (1.0Eb/N0) 12.64s parameter (AZ=12000.0/(1.0*NSPS)*0.64d0) !Dedupe overlap in Hz parameter (ASTART=0.5) !Start delay in seconds From 856aa853dbb8f451629872b6bf2954290522914a Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Sun, 10 May 2020 20:13:20 -0400 Subject: [PATCH 13/32] Restructuring decoder for every second --- Detector.cpp | 1 - mainwindow.cpp | 149 ++++++++++++++++++++++++++++++++++++------------- widegraph.cpp | 8 ++- widegraph.h | 3 +- 4 files changed, 117 insertions(+), 44 deletions(-) diff --git a/Detector.cpp b/Detector.cpp index 8c55d68..2961caf 100644 --- a/Detector.cpp +++ b/Detector.cpp @@ -138,7 +138,6 @@ unsigned Detector::secondInPeriod () const // we take the time of the data as the following assuming no latency // delivering it to us (not true but close enough for us) qint64 now (DriftingDateTime::currentMSecsSinceEpoch ()); - unsigned secondInToday ((now % 86400000LL) / 1000); return secondInToday % m_period; } diff --git a/mainwindow.cpp b/mainwindow.cpp index 9e82b13..ab9f36b 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4265,14 +4265,94 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ // compute the next decode for each submode // enqueue those decodes that are "ready" // on an interval, issue a decode + int decodes = 0; - static qint32 currentDecodeStartA = -1; - static qint32 nextDecodeStartA = -1; + bool couldDecodeA = false; qint32 startA = -1; qint32 szA = -1; qint32 cycleA = -1; - bool couldDecodeA = false; + bool couldDecodeB = false; + qint32 startB = -1; + qint32 szB = -1; + qint32 cycleB = -1; + + bool couldDecodeC = false; + qint32 startC = -1; + qint32 szC = -1; + qint32 cycleC = -1; + + bool couldDecodeE = false; + qint32 startE = -1; + qint32 szE = -1; + qint32 cycleE = -1; + + bool couldDecodeI = false; + qint32 startI = -1; + qint32 szI = -1; + qint32 cycleI = -1; + + //unsigned msInPeriod ((QDateTime::currentMSecsSinceEpoch() % 86400000LL) % (15 * 1000)); + // k - (msInPeriod*RX_SAMPLE_RATE/1000) <- samples since beginning of period + //static qint32 lastDecodeStartK = -1; + //if(lastDecodeStartK == -1){ + // qint32 cycleStartK = computeCycleForDecode(Varicode::JS8CallNormal, k) * computeFramesPerCycleForDecode(Varicode::JS8CallNormal); + // qint32 secondStartK = ((k - cycleStartK) / RX_SAMPLE_RATE) * RX_SAMPLE_RATE; + // lastDecodeStartK = secondStartK; + //} + static qint32 lastDecodeStartK = -1; + static qint32 lastDecodeStartSec = -1; + qint32 maxSamples = NTMAX*RX_SAMPLE_RATE; + qint32 oneSecondSamples = RX_SAMPLE_RATE; + + // compute how much we've incremented since the last decode ready event + qint32 incrementedBy = k - lastDecodeStartK; + if(k < lastDecodeStartK){ + incrementedBy = maxSamples - lastDecodeStartK + k; + } + + // if we've advanced in time enough since the last decode + int thisSec = DriftingDateTime::currentDateTimeUtc().time().second(); + if(incrementedBy >= oneSecondSamples && lastDecodeStartSec != thisSec){ + qDebug() << "ready to detect decode" << incrementedBy; + + // start at now and subtract the frames in one cycle... + // for normal mode this allows us to look through the last 15 seconds of data + // + the amount that we've just incremented (say if we were caught in a decode) + // to search for decodable signals... and we do this _every_ second! + szA = computeFramesPerCycleForDecode(Varicode::JS8CallNormal) + incrementedBy; + startA = k - szA; + + // when the start position is negative, we need to start at the end of the + // buffer and wrap around. the decoder knows how to do the wrap around, so + // all we need to do is + if(startA < 0){ + startA += maxSamples; + } + + // the decoder is going to look +/- 2.48 seconds... so this may partial decode + // up to 2.48 seconds in the future...meaning if we're doing this every second + // we may actually decode this same signal 2-3 more times... but we have a + // message decode dedupe that should prevent any issues with dupes out of the + // decoder when this happens. + couldDecodeA = true; + lastDecodeStartK = k; + lastDecodeStartSec = thisSec; + + // TODO: remove this after testing + m_wideGraph->drawHorizontalLine(QColor(Qt::yellow), 0, 25); + } + + + + + + + + +#if JS8_LEGACY_DECODE_READY + static qint32 currentDecodeStartA = -1; + static qint32 nextDecodeStartA = -1; if(JS8_DEBUG_DECODE) qDebug() << "? NORMAL " << currentDecodeStartA << nextDecodeStartA; couldDecodeA = isDecodeReady(Varicode::JS8CallNormal, k, k0, ¤tDecodeStartA, &nextDecodeStartA, &startA, &szA, &cycleA); if(m_diskData){ @@ -4283,11 +4363,8 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ static qint32 currentDecodeStartB = -1; static qint32 nextDecodeStartB = -1; - qint32 startB = -1; - qint32 szB = -1; - qint32 cycleB = -1; if(JS8_DEBUG_DECODE) qDebug() << "? FAST " << currentDecodeStartB << nextDecodeStartB; - bool couldDecodeB = isDecodeReady(Varicode::JS8CallFast, k, k0, ¤tDecodeStartB, &nextDecodeStartB, &startB, &szB, &cycleB); + couldDecodeB = isDecodeReady(Varicode::JS8CallFast, k, k0, ¤tDecodeStartB, &nextDecodeStartB, &startB, &szB, &cycleB); if(m_diskData){ startB = 0; szB = NTMAX*RX_SAMPLE_RATE-1; @@ -4296,11 +4373,8 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ static qint32 currentDecodeStartC = -1; static qint32 nextDecodeStartC = -1; - qint32 startC = -1; - qint32 szC = -1; - qint32 cycleC = -1; if(JS8_DEBUG_DECODE) qDebug() << "? TURBO " << currentDecodeStartC << nextDecodeStartC; - bool couldDecodeC = isDecodeReady(Varicode::JS8CallTurbo, k, k0, ¤tDecodeStartC, &nextDecodeStartC, &startC, &szC, &cycleC); + couldDecodeC = isDecodeReady(Varicode::JS8CallTurbo, k, k0, ¤tDecodeStartC, &nextDecodeStartC, &startC, &szC, &cycleC); if(m_diskData){ startC = 0; szC = NTMAX*RX_SAMPLE_RATE-1; @@ -4310,11 +4384,8 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ #if JS8_ENABLE_JS8E static qint32 currentDecodeStartE = -1; static qint32 nextDecodeStartE = -1; - qint32 startE = -1; - qint32 szE = -1; - qint32 cycleE = -1; if(JS8_DEBUG_DECODE) qDebug() << "? SLOW " << currentDecodeStartE << nextDecodeStartE; - bool couldDecodeE = isDecodeReady(Varicode::JS8CallSlow, k, k0, ¤tDecodeStartE, &nextDecodeStartE, &startE, &szE, &cycleE); + couldDecodeE = isDecodeReady(Varicode::JS8CallSlow, k, k0, ¤tDecodeStartE, &nextDecodeStartE, &startE, &szE, &cycleE); if(m_diskData){ startE = 0; szE = NTMAX*RX_SAMPLE_RATE-1; @@ -4325,20 +4396,18 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ #if JS8_ENABLE_JS8I static qint32 currentDecodeStartI = -1; static qint32 nextDecodeStartI = -1; - qint32 startI = -1; - qint32 szI = -1; - qint32 cycleI = -1; if(JS8_DEBUG_DECODE) qDebug() << "? ULTRA " << currentDecodeStartI << nextDecodeStartI; - bool couldDecodeI = isDecodeReady(Varicode::JS8CallUltra, k, k0, ¤tDecodeStartI, &nextDecodeStartI, &startI, &szI, &cycleI); + couldDecodeI = isDecodeReady(Varicode::JS8CallUltra, k, k0, ¤tDecodeStartI, &nextDecodeStartI, &startI, &szI, &cycleI); if(m_diskData){ startI = 0; szI = NTMAX*RX_SAMPLE_RATE-1; couldDecodeI = true; } #endif +#endif - int decodes = 0; - +#define JS8_TIMING_EXPERIMENT 0 +#if JS8_TIMING_EXPERIMENT // when no other mode is being decoded, do a sync stats decode for normal mode bool experiment = true; @@ -4355,26 +4424,25 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ } if(incrementedBy >= oneSecondSamples){ + // we've incremented at least one second, so look backwards + + //startA = lastDecodeStartA + oneSecondSamples; //szA = computeFramesNeededForDecode(Varicode::JS8CallNormal) + oneSecondSamples; //lastDecodeStartA += - - startA = k - incrementedBy - computeFramesNeededForDecode(Varicode::JS8CallNormal); - if(startA < 0){ - startA += maxSamples; - } - - szA = incrementedBy + computeFramesNeededForDecode(Varicode::JS8CallNormal); - - qDebug() << "A: start:" << startA << "sz:" << szA << "stop:" << startA + szA; - - lastDecodeStartA = k; - couldDecodeA = true; + // startA = k - incrementedBy - computeFramesNeededForDecode(Varicode::JS8CallNormal); + // if(startA < 0){ + // startA += maxSamples; + // } + // + // szA = incrementedBy + computeFramesNeededForDecode(Varicode::JS8CallNormal); + // + // qDebug() << "A: start:" << startA << "sz:" << szA << "stop:" << startA + szA; + // + // lastDecodeStartA = k; + // couldDecodeA = true; } - - - //qint32 oneSecondFramesA = computeFramesPerCycleForDecode(Varicode::JS8CallNormal)/computePeriodForSubmode(Varicode::JS8CallNormal); //if(lastDecodeStartA == -1 || k < k0 || k - lastDecodeStartA > oneSecondFramesA){ // startA = k-computeFramesNeededForDecode(Varicode::JS8CallNormal); @@ -4457,6 +4525,7 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ #endif #endif } +#endif if(couldDecodeA){ DecodeParams d; @@ -4995,11 +5064,11 @@ void MainWindow::processDecodedLine(QByteArray t){ // draw candidates if(abs(xdt) <= 2){ if(s < 10){ - m_wideGraph->drawLine(QColor(Qt::darkCyan), f, f + computeBandwidthForSubmode(m)); + m_wideGraph->drawDecodeLine(QColor(Qt::darkCyan), f, f + computeBandwidthForSubmode(m)); } else if (s <= 15){ - m_wideGraph->drawLine(QColor(Qt::cyan), f, f + computeBandwidthForSubmode(m)); + m_wideGraph->drawDecodeLine(QColor(Qt::cyan), f, f + computeBandwidthForSubmode(m)); } else if (s <= 21){ - m_wideGraph->drawLine(QColor(Qt::white), f, f + computeBandwidthForSubmode(m)); + m_wideGraph->drawDecodeLine(QColor(Qt::white), f, f + computeBandwidthForSubmode(m)); } } @@ -5008,7 +5077,7 @@ void MainWindow::processDecodedLine(QByteArray t){ } // draw decodes - m_wideGraph->drawLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m)); + m_wideGraph->drawDecodeLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m)); #if 0 // use normal decodes for auto drift if we haven't already defined a new drift for this period diff --git a/widegraph.cpp b/widegraph.cpp index 71b69d4..f3d76a6 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -244,11 +244,15 @@ void WideGraph::saveSettings() //saveS m_settings->setValue ("WaterfallFPS", ui->fpsSpinBox->value()); } -void WideGraph::drawLine(const QColor &color, int ia, int ib) +void WideGraph::drawDecodeLine(const QColor &color, int ia, int ib) { ui->widePlot->drawDecodeLine(color, ia, ib); } +void WideGraph::drawHorizontalLine(const QColor &color, int x, int width){ + ui->widePlot->drawHorizontalLine(color, x, width); +} + void WideGraph::dataSink2(float s[], float df3, int ihsym, int ndiskdata) //dataSink2 { static float splot[NSMAX]; @@ -357,7 +361,7 @@ void WideGraph::drawSwide(){ } ui->widePlot->draw(swideLocal,true,false); } else if(lastSecondInPeriod != secondInPeriod) { - ui->widePlot->drawHorizontalLine(Qt::white, 0, 5); + //ui->widePlot->drawHorizontalLine(Qt::white, 0, 5); } lastSecondInPeriod=secondInPeriod; diff --git a/widegraph.h b/widegraph.h index 29768c1..7ed65f5 100644 --- a/widegraph.h +++ b/widegraph.h @@ -63,7 +63,8 @@ public: int smoothYellow(); void setRxBand (QString const& band); void setWSPRtransmitted(); - void drawLine(const QColor &color, int ia, int ib); + void drawDecodeLine(const QColor &color, int ia, int ib); + void drawHorizontalLine(const QColor &color, int x, int width); void setVHF(bool bVHF); void setRedFile(QString fRed); void setTurbo(bool turbo); From 1d51f54efd9c20b82617196a0a1317481d71b69c Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Mon, 11 May 2020 09:27:04 -0400 Subject: [PATCH 14/32] Added checkbox for displaying decode attempts in the waterfall. Gating experiments for now --- mainwindow.cpp | 283 +++++++++++++++++++++++++++---------------------- mainwindow.h | 1 + widegraph.cpp | 6 ++ widegraph.h | 1 + widegraph.ui | 188 +++++++++++++++++--------------- 5 files changed, 268 insertions(+), 211 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index ab9f36b..577e4d6 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4282,75 +4282,20 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ qint32 szC = -1; qint32 cycleC = -1; +#if JS8_ENABLE_JS8E bool couldDecodeE = false; qint32 startE = -1; qint32 szE = -1; qint32 cycleE = -1; +#endif +#if JS8_ENABLE_JS8I bool couldDecodeI = false; qint32 startI = -1; qint32 szI = -1; qint32 cycleI = -1; +#endif - //unsigned msInPeriod ((QDateTime::currentMSecsSinceEpoch() % 86400000LL) % (15 * 1000)); - // k - (msInPeriod*RX_SAMPLE_RATE/1000) <- samples since beginning of period - //static qint32 lastDecodeStartK = -1; - //if(lastDecodeStartK == -1){ - // qint32 cycleStartK = computeCycleForDecode(Varicode::JS8CallNormal, k) * computeFramesPerCycleForDecode(Varicode::JS8CallNormal); - // qint32 secondStartK = ((k - cycleStartK) / RX_SAMPLE_RATE) * RX_SAMPLE_RATE; - // lastDecodeStartK = secondStartK; - //} - static qint32 lastDecodeStartK = -1; - static qint32 lastDecodeStartSec = -1; - qint32 maxSamples = NTMAX*RX_SAMPLE_RATE; - qint32 oneSecondSamples = RX_SAMPLE_RATE; - - // compute how much we've incremented since the last decode ready event - qint32 incrementedBy = k - lastDecodeStartK; - if(k < lastDecodeStartK){ - incrementedBy = maxSamples - lastDecodeStartK + k; - } - - // if we've advanced in time enough since the last decode - int thisSec = DriftingDateTime::currentDateTimeUtc().time().second(); - if(incrementedBy >= oneSecondSamples && lastDecodeStartSec != thisSec){ - qDebug() << "ready to detect decode" << incrementedBy; - - // start at now and subtract the frames in one cycle... - // for normal mode this allows us to look through the last 15 seconds of data - // + the amount that we've just incremented (say if we were caught in a decode) - // to search for decodable signals... and we do this _every_ second! - szA = computeFramesPerCycleForDecode(Varicode::JS8CallNormal) + incrementedBy; - startA = k - szA; - - // when the start position is negative, we need to start at the end of the - // buffer and wrap around. the decoder knows how to do the wrap around, so - // all we need to do is - if(startA < 0){ - startA += maxSamples; - } - - // the decoder is going to look +/- 2.48 seconds... so this may partial decode - // up to 2.48 seconds in the future...meaning if we're doing this every second - // we may actually decode this same signal 2-3 more times... but we have a - // message decode dedupe that should prevent any issues with dupes out of the - // decoder when this happens. - couldDecodeA = true; - lastDecodeStartK = k; - lastDecodeStartSec = thisSec; - - // TODO: remove this after testing - m_wideGraph->drawHorizontalLine(QColor(Qt::yellow), 0, 25); - } - - - - - - - - -#if JS8_LEGACY_DECODE_READY static qint32 currentDecodeStartA = -1; static qint32 nextDecodeStartA = -1; if(JS8_DEBUG_DECODE) qDebug() << "? NORMAL " << currentDecodeStartA << nextDecodeStartA; @@ -4404,10 +4349,152 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ couldDecodeI = true; } #endif + + if(couldDecodeA){ + DecodeParams d; + d.submode = Varicode::JS8CallNormal; + d.cycle = cycleA; + d.start = startA; + d.sz = szA; + m_decoderQueue.append(d); + decodes++; + } + + if(couldDecodeB){ + DecodeParams d; + d.submode = Varicode::JS8CallFast; + d.cycle = cycleB; + d.start = startB; + d.sz = szB; + m_decoderQueue.append(d); + decodes++; + } + + if(couldDecodeC){ + DecodeParams d; + d.submode = Varicode::JS8CallTurbo; + d.cycle = cycleC; + d.start = startC; + d.sz = szC; + m_decoderQueue.append(d); + decodes++; + } + +#if JS8_ENABLE_JS8E + if(couldDecodeE){ + DecodeParams d; + d.submode = Varicode::JS8CallSlow; + d.cycle = cycleE; + d.start = startE; + d.sz = szE; + m_decoderQueue.append(d); + decodes++; + } #endif -#define JS8_TIMING_EXPERIMENT 0 -#if JS8_TIMING_EXPERIMENT +#if JS8_ENABLE_JS8I + if(couldDecodeI){ + DecodeParams d; + d.submode = Varicode::JS8CallUltra; + d.cycle = cycleI; + d.start = startI; + d.sz = szI; + m_decoderQueue.append(d); + decodes++; + } +#endif + + return decodes > 0; +} + +/** + * @brief MainWindow::decodeEnqueueReadyExperiment + * compute the available decoder ranges that can be processed and + * place them in the decode queue + * + * experiment with decoding on a much shorter interval than usual + * + * @param k - the current frame count + * @param k0 - the previous frame count + * @return true if decoder ranges were queued, false otherwise + */ +bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 k0){ + //unsigned msInPeriod ((QDateTime::currentMSecsSinceEpoch() % 86400000LL) % (15 * 1000)); + // k - (msInPeriod*RX_SAMPLE_RATE/1000) <- samples since beginning of period + //static qint32 lastDecodeStartK = -1; + //if(lastDecodeStartK == -1){ + // qint32 cycleStartK = computeCycleForDecode(Varicode::JS8CallNormal, k) * computeFramesPerCycleForDecode(Varicode::JS8CallNormal); + // qint32 secondStartK = ((k - cycleStartK) / RX_SAMPLE_RATE) * RX_SAMPLE_RATE; + // lastDecodeStartK = secondStartK; + //} + + static qint32 lastDecodeStartK = -1; + static qint32 lastDecodeStartSec = -1; + + int decodes = 0; + qint32 startA = 0; + qint32 szA = 0; + qint32 maxSamples = NTMAX*RX_SAMPLE_RATE; + qint32 oneSecondSamples = RX_SAMPLE_RATE; + + // compute how much we've incremented since the last decode ready event + qint32 incrementedBy = k - lastDecodeStartK; + if(k < lastDecodeStartK){ + incrementedBy = maxSamples - lastDecodeStartK + k; + } + + // if we've advanced in time enough since the last decode + int thisSec = DriftingDateTime::currentDateTimeUtc().time().second(); + if(incrementedBy >= oneSecondSamples){ // && lastDecodeStartSec != thisSec){ + qDebug() << "ready to detect decode" << incrementedBy; + + QList submodes = { + //Varicode::JS8CallSlow, + Varicode::JS8CallNormal, + //Varicode::JS8CallFast, + //Varicode::JS8CallTurbo + }; + foreach(auto submode, submodes){ + + // start at now and subtract the frames in one cycle... + // for normal mode this allows us to look through the last 15 seconds of data + // + the amount that we've just incremented (say if we were caught in a decode) + // to search for decodable signals... and we do this _every_ second! + //szA = computeFramesPerCycleForDecode(submode) + incrementedBy; + szA = computeFramesNeededForDecode(submode); + startA = k - szA; + + // when the start position is negative, we need to start at the end of the + // buffer and wrap around. the decoder knows how to do the wrap around, so + // all we need to do is + if(startA < 0){ + startA += maxSamples; + } + + // create the decode params and queue it + DecodeParams d; + d.submode = submode; + d.start = startA; + d.sz = szA; + m_decoderQueue.append(d); + decodes++; + } + + // the decoder is going to look +/- multiple seconds... so this may partial decode + // up to a few seconds in the future...meaning if we're doing this every second + // we may actually decode this same signal 2-3 more times... but we have a + // message decode dedupe that should prevent any issues with dupes out of the + // decoder when this happens. + lastDecodeStartK = k; + lastDecodeStartSec = thisSec; + + // TODO: remove this after testing + m_wideGraph->drawHorizontalLine(QColor(Qt::yellow), 0, 25); + } + + return decodes > 0; + +#if 0 // when no other mode is being decoded, do a sync stats decode for normal mode bool experiment = true; @@ -4527,61 +4614,6 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ } #endif - if(couldDecodeA){ - DecodeParams d; - d.submode = Varicode::JS8CallNormal; - d.cycle = cycleA; - d.start = startA; - d.sz = szA; - m_decoderQueue.append(d); - decodes++; - } - - if(couldDecodeB){ - DecodeParams d; - d.submode = Varicode::JS8CallFast; - d.cycle = cycleB; - d.start = startB; - d.sz = szB; - m_decoderQueue.append(d); - decodes++; - } - - if(couldDecodeC){ - DecodeParams d; - d.submode = Varicode::JS8CallTurbo; - d.cycle = cycleC; - d.start = startC; - d.sz = szC; - m_decoderQueue.append(d); - decodes++; - } - -#if JS8_ENABLE_JS8E - if(couldDecodeE){ - DecodeParams d; - d.submode = Varicode::JS8CallSlow; - d.cycle = cycleE; - d.start = startE; - d.sz = szE; - m_decoderQueue.append(d); - decodes++; - } -#endif - -#if JS8_ENABLE_JS8I - if(couldDecodeI){ - DecodeParams d; - d.submode = Varicode::JS8CallUltra; - d.cycle = cycleI; - d.start = startI; - d.sz = szI; - m_decoderQueue.append(d); - decodes++; - } -#endif - - return decodes > 0; } /** @@ -4685,7 +4717,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){ int period = computePeriodForSubmode(submode); - dec_data.params.syncStats = true; + dec_data.params.syncStats = m_wideGraph->shouldDisplayDecodeAttempts(); dec_data.params.npts8=(m_ihsym*m_nsps)/16; dec_data.params.newdat=1; dec_data.params.nagain=0; @@ -5039,8 +5071,10 @@ void MainWindow::processDecodedLine(QByteArray t){ bool bAvgMsg=false; int navg=0; +#if JS8_TIME_DRIFT_EXPERIMENT static bool hasNewDrift = false; static int newDrift = 0; +#endif if(t.indexOf("") >= 0) { auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts); @@ -5048,9 +5082,12 @@ void MainWindow::processDecodedLine(QByteArray t){ return; } + if(!m_wideGraph->shouldDisplayDecodeAttempts()){ + return; + } + auto m1 = QString(segs.at(2)); auto m = int(m1.toInt()); - auto period = computePeriodForSubmode(m); auto f1 = QString(segs.at(4)); auto f = int(f1.toFloat()); @@ -5079,7 +5116,7 @@ void MainWindow::processDecodedLine(QByteArray t){ // draw decodes m_wideGraph->drawDecodeLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m)); -#if 0 +#if JS8_TIME_DRIFT_EXPERIMENT // use normal decodes for auto drift if we haven't already defined a new drift for this period if(/*!hasNewDrift && */ (m == Varicode::JS8CallSlow || m == Varicode::JS8CallNormal)){ auto now = QDateTime::currentDateTimeUtc(); @@ -5127,12 +5164,9 @@ void MainWindow::processDecodedLine(QByteArray t){ if(t.indexOf("") >= 0) { int msec = m_decoderBusyStartTime.msecsTo(QDateTime::currentDateTimeUtc()); - qDebug() << "Decoder Duration:" << msec; - if(msec >= 1000){ - writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Decoder Duration: %1").arg(msec)); - } + if(JS8_DEBUG_DECODE) qDebug() << "decode duration" << msec << "ms"; - // TODO: decide if we should adjust here... +#if JS8_TIME_DRIFT_EXPERIMENT if(hasNewDrift){ static int driftN = 1; newDrift = ((driftN-1)*DriftingDateTime::drift() + newDrift)/driftN; @@ -5141,6 +5175,7 @@ void MainWindow::processDecodedLine(QByteArray t){ writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Drift: %1").arg(newDrift)); hasNewDrift = false; } +#endif m_bDecoded = t.mid(16).trimmed().toInt() > 0; int mswait=3*1000*m_TRperiod/4; diff --git a/mainwindow.h b/mainwindow.h index b47aa82..34fc4eb 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -244,6 +244,7 @@ private slots: bool decode(qint32 k); bool isDecodeReady(int submode, qint32 k, qint32 k0, qint32 *pCurrentDecodeStart, qint32 *pNextDecodeStart, qint32 *pStart, qint32 *pSz, qint32 *pCycle); bool decodeEnqueueReady(qint32 k, qint32 k0); + bool decodeEnqueueReadyExperiment(qint32 k, qint32 k0); bool decodeProcessQueue(qint32 *pSubmode); void decodeStart(); void decodePrepareSaveAudio(int submode); diff --git a/widegraph.cpp b/widegraph.cpp index f3d76a6..ef0b875 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -167,6 +167,7 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) : ui->controls_widget->setVisible(!m_settings->value("HideControls", false).toBool()); ui->cbControls->setChecked(!m_settings->value("HideControls", false).toBool()); ui->fpsSpinBox->setValue(m_settings->value ("WaterfallFPS", 4).toInt()); + ui->decodeAttemptCheckBox->setChecked(m_settings->value("DisplayDecodeAttempts", false).toBool()); auto splitState = m_settings->value("SplitState").toByteArray(); if(!splitState.isEmpty()){ @@ -242,6 +243,11 @@ void WideGraph::saveSettings() //saveS m_settings->setValue ("FilterOpacityPercent", ui->filterOpacitySpinBox->value()); m_settings->setValue ("SplitState", ui->splitter->saveState()); m_settings->setValue ("WaterfallFPS", ui->fpsSpinBox->value()); + m_settings->setValue ("DisplayDecodeAttempts", ui->decodeAttemptCheckBox->isChecked()); +} + +bool WideGraph::shouldDisplayDecodeAttempts(){ + return ui->decodeAttemptCheckBox->isChecked(); } void WideGraph::drawDecodeLine(const QColor &color, int ia, int ib) diff --git a/widegraph.h b/widegraph.h index 7ed65f5..bae92a6 100644 --- a/widegraph.h +++ b/widegraph.h @@ -68,6 +68,7 @@ public: void setVHF(bool bVHF); void setRedFile(QString fRed); void setTurbo(bool turbo); + bool shouldDisplayDecodeAttempts(); signals: void freezeDecode2(int n); diff --git a/widegraph.ui b/widegraph.ui index 0e0774e..989fa4b 100644 --- a/widegraph.ui +++ b/widegraph.ui @@ -427,9 +427,9 @@ 0 - -193 + -371 267 - 723 + 742 @@ -656,92 +656,106 @@ Waterfall - - - + + + + + 0 + + + + + Gain: + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + + 16777215 + 16777215 + + + + Waterfall gain + + + -50 + + + 50 + + + Qt::Horizontal + + + QSlider::TicksAbove + + + + + + + Zero: + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + + 16777215 + 16777215 + + + + Waterfall zero + + + -50 + + + 50 + + + Qt::Horizontal + + + QSlider::TicksAbove + + + + + + + - Gain: - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - - 16777215 - 16777215 - - - - Waterfall gain - - - -50 - - - 50 - - - Qt::Horizontal - - - QSlider::TicksAbove - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - - 16777215 - 16777215 - - - - Waterfall zero - - - -50 - - - 50 - - - Qt::Horizontal - - - QSlider::TicksAbove - - - - - - - Zero: + Display Decode Attempts From 22911e332af3ed8b05a73714e8906b476776fd4f Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Mon, 11 May 2020 10:49:29 -0400 Subject: [PATCH 15/32] Expose color pallete --- plotter.cpp | 4 ++++ plotter.h | 1 + widegraph.cpp | 4 ++++ widegraph.h | 1 + 4 files changed, 10 insertions(+) diff --git a/plotter.cpp b/plotter.cpp index f580585..a1fc029 100644 --- a/plotter.cpp +++ b/plotter.cpp @@ -879,6 +879,10 @@ void CPlotter::setTol(int n) //setTol() DrawOverlay(); } +QVector const& CPlotter::colors(){ + return g_ColorTbl; +} + void CPlotter::setColours(QVector const& cl) { g_ColorTbl = cl; diff --git a/plotter.h b/plotter.h index a3df2a6..60db84f 100644 --- a/plotter.h +++ b/plotter.h @@ -77,6 +77,7 @@ public: void setBreadth(qint32 w) {m_w = w;} qint32 breadth() const {return m_w;} float fSpan() const {return m_fSpan;} + QVector const& colors(); void setColours(QVector const& cl); void setFlatten(bool b1, bool b2); void setTol(int n); diff --git a/widegraph.cpp b/widegraph.cpp index ef0b875..1721502 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -690,6 +690,10 @@ void WideGraph::readPalette () //readPalette } } +QVector const& WideGraph::colors(){ + return ui->widePlot->colors(); +} + void WideGraph::on_paletteComboBox_activated (QString const& palette) //palette selector { m_waterfallPalette = palette; diff --git a/widegraph.h b/widegraph.h index bae92a6..6d34ec0 100644 --- a/widegraph.h +++ b/widegraph.h @@ -69,6 +69,7 @@ public: void setRedFile(QString fRed); void setTurbo(bool turbo); bool shouldDisplayDecodeAttempts(); + QVector const& colors(); signals: void freezeDecode2(int n); From a6647c548b8e09418113a5aeb33a54aa5c013450 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Mon, 11 May 2020 14:17:55 -0400 Subject: [PATCH 16/32] Moved experiment into own function --- lib/js8a_decode.f90 | 7 ++++++- lib/js8b_decode.f90 | 5 +++++ lib/js8c_decode.f90 | 5 +++++ lib/js8e_decode.f90 | 5 +++++ lib/js8i_decode.f90 | 5 +++++ lib/jt9.f90 | 2 ++ mainwindow.cpp | 9 +++------ 7 files changed, 31 insertions(+), 7 deletions(-) diff --git a/lib/js8a_decode.f90 b/lib/js8a_decode.f90 index 2ddd004..e3d9cfc 100644 --- a/lib/js8a_decode.f90 +++ b/lib/js8a_decode.f90 @@ -93,6 +93,11 @@ contains lsubtract=.false. endif + if(NWRITELOG.eq.1) then + write(*,*) ' pass', ipass, 'of', npass, 'subtract', lsubtract + flush(6) + endif + call timer('syncjs8 ',0) call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase) call timer('syncjs8 ',1) @@ -103,7 +108,7 @@ contains xdt=candidate(2,icand) xbase=10.0**(0.1*(sbase(nint(f1/(12000.0/NFFT1)))-40.0)) ! 3.125Hz - if(NWRITELOG.eq.1) then + if(NWRITELOG.eq.0) then write(*,*) ' candidate', icand, 'f1', f1, 'sync', sync, 'xdt', xdt, 'xbase', xbase flush(6) endif diff --git a/lib/js8b_decode.f90 b/lib/js8b_decode.f90 index eab3524..b58d28e 100644 --- a/lib/js8b_decode.f90 +++ b/lib/js8b_decode.f90 @@ -93,6 +93,11 @@ contains lsubtract=.false. endif + if(NWRITELOG.eq.1) then + write(*,*) ' pass', ipass, 'of', npass, 'subtract', lsubtract + flush(6) + endif + call timer('syncjs8 ',0) call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase) call timer('syncjs8 ',1) diff --git a/lib/js8c_decode.f90 b/lib/js8c_decode.f90 index df75c5a..586460c 100644 --- a/lib/js8c_decode.f90 +++ b/lib/js8c_decode.f90 @@ -93,6 +93,11 @@ contains lsubtract=.false. endif + if(NWRITELOG.eq.1) then + write(*,*) ' pass', ipass, 'of', npass, 'subtract', lsubtract + flush(6) + endif + call timer('syncjs8 ',0) call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase) call timer('syncjs8 ',1) diff --git a/lib/js8e_decode.f90 b/lib/js8e_decode.f90 index 1461163..ffc7a6e 100644 --- a/lib/js8e_decode.f90 +++ b/lib/js8e_decode.f90 @@ -93,6 +93,11 @@ contains lsubtract=.false. endif + if(NWRITELOG.eq.1) then + write(*,*) ' pass', ipass, 'of', npass, 'subtract', lsubtract + flush(6) + endif + call timer('syncjs8 ',0) call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase) call timer('syncjs8 ',1) diff --git a/lib/js8i_decode.f90 b/lib/js8i_decode.f90 index 044522f..4a9e085 100644 --- a/lib/js8i_decode.f90 +++ b/lib/js8i_decode.f90 @@ -93,6 +93,11 @@ contains lsubtract=.false. endif + if(NWRITELOG.eq.1) then + write(*,*) ' pass', ipass, 'of', npass, 'subtract', lsubtract + flush(6) + endif + call timer('syncjs8 ',0) call syncjs8(dd,icos,ifa,ifb,syncmin,nfqso,s,candidate,ncand,sbase) call timer('syncjs8 ',1) diff --git a/lib/jt9.f90 b/lib/jt9.f90 index 79aea64..7888afd 100644 --- a/lib/jt9.f90 +++ b/lib/jt9.f90 @@ -294,10 +294,12 @@ program jt9 shared_data%params%kposB=0 shared_data%params%kposC=0 shared_data%params%kposE=0 + shared_data%params%kposI=0 shared_data%params%kszA=NMAX-1 shared_data%params%kszB=NMAX-1 shared_data%params%kszC=NMAX-1 shared_data%params%kszE=NMAX-1 + shared_data%params%kszI=NMAX-1 call multimode_decoder(shared_data%ss,shared_data%id2,shared_data%params,nfsample) enddo diff --git a/mainwindow.cpp b/mainwindow.cpp index 577e4d6..184c906 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4418,17 +4418,14 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ * @param k0 - the previous frame count * @return true if decoder ranges were queued, false otherwise */ -bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 k0){ - //unsigned msInPeriod ((QDateTime::currentMSecsSinceEpoch() % 86400000LL) % (15 * 1000)); - // k - (msInPeriod*RX_SAMPLE_RATE/1000) <- samples since beginning of period - //static qint32 lastDecodeStartK = -1; +bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ + static qint32 lastDecodeStartK = -1; //if(lastDecodeStartK == -1){ // qint32 cycleStartK = computeCycleForDecode(Varicode::JS8CallNormal, k) * computeFramesPerCycleForDecode(Varicode::JS8CallNormal); // qint32 secondStartK = ((k - cycleStartK) / RX_SAMPLE_RATE) * RX_SAMPLE_RATE; // lastDecodeStartK = secondStartK; //} - static qint32 lastDecodeStartK = -1; static qint32 lastDecodeStartSec = -1; int decodes = 0; @@ -4460,7 +4457,7 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 k0){ // for normal mode this allows us to look through the last 15 seconds of data // + the amount that we've just incremented (say if we were caught in a decode) // to search for decodable signals... and we do this _every_ second! - //szA = computeFramesPerCycleForDecode(submode) + incrementedBy; + // szA = computeFramesPerCycleForDecode(submode) + incrementedBy; szA = computeFramesNeededForDecode(submode); startA = k - szA; From ed70ac5a1a1f76967bc19f9f2c6a70d580dc5c47 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Mon, 11 May 2020 15:11:56 -0400 Subject: [PATCH 17/32] Added proper caching of message frames during dedupe with date expiration --- mainwindow.cpp | 51 ++++++++++++++++++++++++++++++++++---------------- mainwindow.h | 8 +++++++- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 184c906..c3960a8 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -4921,12 +4921,15 @@ void MainWindow::decodeDone () m_RxLog=0; m_blankLine=true; - static int dupeClearI = 0; - if(dupeClearI > 2*m_TRperiod){ - m_messageDupeCache.clear(); - dupeClearI = 0; + // cleanup old cached messages (messages > submode period old) + for (auto it = m_messageDupeCache.begin(); it != m_messageDupeCache.end();){ + auto cached = it.value(); + if (cached.date.secsTo(QDateTime::currentDateTimeUtc()) > computePeriodForSubmode(cached.submode)){ + it = m_messageDupeCache.erase(it); + } else { + ++it; + } } - dupeClearI++; decodeBusy(false); } @@ -5201,26 +5204,39 @@ void MainWindow::processDecodedLine(QByteArray t){ } auto rawText = QString::fromUtf8 (t.constData ()).remove (QRegularExpression {"\r|\n"}); + DecodedText decodedtext {rawText, "FT8" == m_mode && ui->cbVHFcontest->isChecked(), m_config.my_grid ()}; - bool bValidFrame = decodedtext.snr() >= rxSnrThreshold(decodedtext.submode()); - // dupe check + // frames are also valid if they pass our dupe check (haven't seen the same frame in the past 1/2 decode period) auto frame = decodedtext.message(); auto frameOffset = decodedtext.frequencyOffset(); - if(m_messageDupeCache.contains(frame)){ - // check to see if the frequency is near our previous frame - auto cachedFreq = m_messageDupeCache.value(frame, 0); - if(qAbs(cachedFreq - frameOffset) <= rxThreshold(decodedtext.submode())){ - qDebug() << "duplicate frame from" << cachedFreq << "and" << frameOffset; - bValidFrame = false; + auto frameDedupeKey = QString("%1:%2").arg(decodedtext.submode()).arg(frame); + if(m_messageDupeCache.contains(frameDedupeKey)){ + auto cached = m_messageDupeCache.value(frameDedupeKey); + + // check to see if the time since last seen is > 1/2 decode period + auto cachedDate = cached.date; + if(cachedDate.secsTo(QDateTime::currentDateTimeUtc()) < 0.5*computePeriodForSubmode(decodedtext.submode())){ + qDebug() << "duplicate frame at" << cachedDate << "using key" << frameDedupeKey; + return; } - } else { - // cache for this decode cycle - m_messageDupeCache[frame] = frameOffset; + + // check to see if the frequency is near our previous frame + auto cachedFreq = cached.freq; + if(qAbs(cachedFreq - frameOffset) <= rxThreshold(decodedtext.submode())){ + qDebug() << "duplicate frame from" << cachedFreq << "and" << frameOffset << "using key" << frameDedupeKey; + return; + } + + // huzzah! + // if we make it here, the cache is invalid and will be bumped when we cache the new frame below } + // frames are valid if they meet our minimum rx threshold for the submode + bool bValidFrame = decodedtext.snr() >= rxSnrThreshold(decodedtext.submode()); + qDebug() << "valid" << bValidFrame << submodeName(decodedtext.submode()) << "decoded text" << decodedtext.message(); // skip if invalid @@ -5228,6 +5244,9 @@ void MainWindow::processDecodedLine(QByteArray t){ return; } + // if the frame is valid, cache it! + m_messageDupeCache[frameDedupeKey] = {QDateTime::currentDateTimeUtc(), decodedtext.submode(), frameOffset}; + // log valid frames to ALL.txt (and correct their timestamp format) auto freq = dialFrequency(); diff --git a/mainwindow.h b/mainwindow.h index 34fc4eb..638639f 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -859,8 +859,14 @@ private: int sz; }; + struct CachedFrame { + QDateTime date; + int submode; + int freq; + }; + QQueue m_decoderQueue; - QMap m_messageDupeCache; // message frame -> freq offset seen + QMap m_messageDupeCache; // message frame -> date seen, submode seen, freq offset seen QMap m_showColumnsCache; // table column:key -> show boolean QMap m_sortCache; // table key -> sort by QPriorityQueue m_txMessageQueue; // messages to be sent From 07cbb09219d6b61cf9bd704c223daa491e4b722a Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Mon, 11 May 2020 20:53:03 -0400 Subject: [PATCH 18/32] Trying the experimental decode timing (more liberal decoding) --- mainwindow.cpp | 137 ++++++++++++++++++++++++++++++++++++++++--------- mainwindow.h | 2 +- 2 files changed, 113 insertions(+), 26 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index c3960a8..6747c7d 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -2636,22 +2636,42 @@ int MainWindow::computeStop(int submode, int period){ return stop; } -// int MainWindow::computeCurrentCycle(int period){ -// return m_detector->secondInPeriod() / period; -// } -// -// int MainWindow::computeCycleStartForDecode(int cycle, int period){ -// qint32 samplesPerCycle = period * RX_SAMPLE_RATE; -// return cycle * samplesPerCycle; -// } - +/** + * @brief MainWindow::computeCycleForDecode + * + * compute which cycle we are currently in based on a submode frames per cycle and our current k position + * + * @param submode + * @param k + * @return + */ int MainWindow::computeCycleForDecode(int submode, int k){ - qint32 maxFrames = m_detector->period() * RX_SAMPLE_RATE; + qint32 maxFrames = NTMAX * RX_SAMPLE_RATE; qint32 cycleFrames = computeFramesPerCycleForDecode(submode); qint32 currentCycle = (k / cycleFrames) % (maxFrames / cycleFrames); // we mod here so we loop back to zero correctly return currentCycle; } +/** + * @brief MainWindow::computeAltCycleForDecode + * + * compute an alternate cycle offset by a specific number of frames + * + * e.g., if we want the 0 cycle to start at second 5, we'd provide an offset of 5*RX_SAMPLE_RATE + * + * @param submode + * @param k + * @param offsetFrames + * @return + */ +int MainWindow::computeAltCycleForDecode(int submode, int k, int offsetFrames){ + int altK = k - offsetFrames; + if(altK < 0){ + altK += NTMAX * RX_SAMPLE_RATE; + } + return computeCycleForDecode(submode, altK); +} + int MainWindow::computeFramesPerCycleForDecode(int submode){ return computePeriodForSubmode(submode) * RX_SAMPLE_RATE; } @@ -4214,10 +4234,19 @@ bool MainWindow::decode(qint32 k){ return false; } - bool ready = decodeEnqueueReady(k, kZero); + bool ready = false; + +#if JS8_USE_EXPERIMENTAL_DECODE_TIMING + ready = decodeEnqueueReady(k, kZero); if(ready || !m_decoderQueue.isEmpty()){ if(JS8_DEBUG_DECODE) qDebug() << "--> decoder is ready to be run with" << m_decoderQueue.count() << "decode periods"; } +#else + ready = decodeEnqueueReadyExperiment(k, kZero); + if(ready || !m_decoderQueue.isEmpty()){ + if(JS8_DEBUG_DECODE) qDebug() << "--> decoder is ready to be run with" << m_decoderQueue.count() << "decode periods"; + } +#endif // // TODO: what follows can likely be pulled out to an async process @@ -4353,7 +4382,6 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ if(couldDecodeA){ DecodeParams d; d.submode = Varicode::JS8CallNormal; - d.cycle = cycleA; d.start = startA; d.sz = szA; m_decoderQueue.append(d); @@ -4363,7 +4391,6 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ if(couldDecodeB){ DecodeParams d; d.submode = Varicode::JS8CallFast; - d.cycle = cycleB; d.start = startB; d.sz = szB; m_decoderQueue.append(d); @@ -4373,7 +4400,6 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ if(couldDecodeC){ DecodeParams d; d.submode = Varicode::JS8CallTurbo; - d.cycle = cycleC; d.start = startC; d.sz = szC; m_decoderQueue.append(d); @@ -4384,7 +4410,6 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ if(couldDecodeE){ DecodeParams d; d.submode = Varicode::JS8CallSlow; - d.cycle = cycleE; d.start = startE; d.sz = szE; m_decoderQueue.append(d); @@ -4396,7 +4421,6 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ if(couldDecodeI){ DecodeParams d; d.submode = Varicode::JS8CallUltra; - d.cycle = cycleI; d.start = startI; d.sz = szI; m_decoderQueue.append(d); @@ -4419,7 +4443,77 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ * @return true if decoder ranges were queued, false otherwise */ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ + // TODO: make this non-static field of MainWindow? + // map of last decode positions for each submode + static QMap lastDecodeStartMap; + + // TODO: make this non-static field of MainWindow? + // map of submodes to decode + optional alternate decode positions + static QMap> submodes = { + {Varicode::JS8CallSlow, {0}}, + {Varicode::JS8CallNormal, {0}}, + {Varicode::JS8CallFast, {0, 5}}, + {Varicode::JS8CallTurbo, {0, 3}}, + }; + + static qint32 maxSamples = NTMAX*RX_SAMPLE_RATE; + static qint32 oneSecondSamples = RX_SAMPLE_RATE; + + int decodes = 0; + + // do we have a better way to check this? + bool multi = ui->actionModeMultiDecoder->isChecked(); + + foreach(auto submode, submodes.keys()){ + // skip if multi is disabled and this mode is not the current submode + if(!multi && submode != m_nSubMode){ + continue; + } + + // check alternate decode positions + foreach(auto alt, submodes.value(submode)){ + qint32 cycle = computeAltCycleForDecode(submode, k, alt*oneSecondSamples); + qint32 cycleFrames = computeFramesPerCycleForDecode(submode); + qint32 cycleFramesNeeded = computeFramesNeededForDecode(submode) - oneSecondSamples; + qint32 cycleFramesReady = k - (cycle * cycleFrames); + + if(!lastDecodeStartMap.contains(submode)){ + lastDecodeStartMap[submode] = cycle * cycleFrames; + } + + qint32 lastDecodeStart = lastDecodeStartMap[submode]; + qint32 incrementedBy = k - lastDecodeStart; + if(k < lastDecodeStart){ + incrementedBy = maxSamples - lastDecodeStart + k; + } + + if(JS8_DEBUG_DECODE) qDebug() << submodeName(submode) << "alt" << alt << "cycle" << cycle << "cycle frames" << cycleFrames << "cycle start" << cycle*cycleFrames << "cycle end" << (cycle+1)*cycleFrames << "k" << k << "frames ready" << cycleFramesReady << "incremeted by" << incrementedBy; + + if(incrementedBy >= oneSecondSamples && cycleFramesReady >= cycleFramesNeeded){ + DecodeParams d; + d.submode = submode; + d.start = cycle*cycleFrames; + d.sz = cycleFramesReady; + m_decoderQueue.append(d); + decodes++; + + // keep track of last decode start position + lastDecodeStartMap[submode] = k; + } + } + } + + // TODO: debug + if(decodes > 0 && m_wideGraph->shouldDisplayDecodeAttempts()){ + m_wideGraph->drawHorizontalLine(QColor(Qt::yellow), 0, 5); + } + + return decodes > 0; +} + +#if 0 static qint32 lastDecodeStartK = -1; + //if(lastDecodeStartK == -1){ // qint32 cycleStartK = computeCycleForDecode(Varicode::JS8CallNormal, k) * computeFramesPerCycleForDecode(Varicode::JS8CallNormal); // qint32 secondStartK = ((k - cycleStartK) / RX_SAMPLE_RATE) * RX_SAMPLE_RATE; @@ -4612,6 +4706,7 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ #endif } +#endif /** * @brief MainWindow::decodeProcessQueue @@ -8144,19 +8239,11 @@ void MainWindow::on_actionJS8_triggered() if(m_isWideGraphMDI) m_wideGraph->show(); ui->decodedTextLabel2->setText(" UTC dB DT Freq Message"); m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe -#if JS8_RING_BUFFER + Q_ASSERT(NTMAX == 60); m_wideGraph->setPeriod(m_TRperiod, m_nsps); -#if JS8_ENABLE_JS8E && !JS8E_IS_ULTRA m_detector->setTRPeriod(NTMAX); // TODO - not thread safe -#else - m_detector->setTRPeriod(NTMAX / 2); // TODO - not thread safe -#endif -#else - m_wideGraph->setPeriod(m_TRperiod, m_nsps); - m_detector->setTRPeriod(m_TRperiod); // TODO - not thread safe -#endif ui->label_7->setText("Rx Frequency"); if(m_config.bFox()) { ui->label_6->setText("Stations calling DXpedition " + m_config.my_callsign()); diff --git a/mainwindow.h b/mainwindow.h index 638639f..8284d39 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -854,7 +854,6 @@ private: struct DecodeParams { int submode; - int cycle; int start; int sz; }; @@ -990,6 +989,7 @@ private: //int computeCurrentCycle(int period); //int computeCycleStartForDecode(int cycle, int period); int computeCycleForDecode(int submode, int k); + int computeAltCycleForDecode(int submode, int k, int offsetFrames); int computeFramesPerCycleForDecode(int submode); int computeFramesNeededForDecode(int submode); bool shortList(QString callsign); From d76bb7fbd2bd3ff0b9c3bb9a4e741e8f210ab01d Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Wed, 13 May 2020 21:14:15 -0400 Subject: [PATCH 19/32] Updated detector to reset kin on drift --- Detector.cpp | 70 +++++++++++++++++++++++++++++++++++++++++++--------- Detector.hpp | 3 +++ 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/Detector.cpp b/Detector.cpp index 2961caf..9f7f2b6 100644 --- a/Detector.cpp +++ b/Detector.cpp @@ -43,18 +43,9 @@ bool Detector::reset () void Detector::clear () { - QMutexLocker mutex(&m_lock); - #if JS8_RING_BUFFER - // set index to roughly where we are in time (1ms resolution) - qint64 now (DriftingDateTime::currentMSecsSinceEpoch ()); - unsigned msInPeriod ((now % 86400000LL) % (m_period * 1000)); - int prevKin = dec_data.params.kin; - dec_data.params.kin = qMin ((msInPeriod * m_frameRate) / 1000, static_cast (sizeof (dec_data.d2) / sizeof (dec_data.d2[0]))); - m_bufferPos = 0; - m_ns=secondInPeriod(); - memset(dec_data.d2, 0, sizeof(dec_data.d2)); - qDebug() << "advancing detector buffer from" << prevKin << "to" << dec_data.params.kin << "delta" << dec_data.params.kin - prevKin; + resetBufferPosition(); + resetBufferContent(); #else dec_data.params.kin = 0; m_bufferPos = 0; @@ -64,6 +55,63 @@ void Detector::clear () // qFill (dec_data.d2, dec_data.d2 + sizeof (dec_data.d2) / sizeof (dec_data.d2[0]), 0); } +/** + * shift the elements of an array left by n items using a temporary array for efficient moving + * + * this function moves the leading n elements to the end of the array + * + * the temporary array needs to be allocated to accept at least n items + */ +template void rotate_array_left(T array[], qint64 size, T temp[], qint64 n) { + memcpy(temp, array, n * sizeof(T)); // temporarily save leading n elements + memmove(array, array + n, (size - n) * sizeof(T)); // shift array to the left + memmove(array + size - n, temp, n * sizeof(T)); // append saved +} + +/** + * shift the elements of an array right by n items using a temporary array for efficient moving + * + * this function moves the trailing n elements to the start of the array + * + * the temporary array needs to be allocated to accept at least n items + */ +template void rotate_array_right(T array[], qint64 size, T temp[], qint64 n) { + memcpy(temp, array + size - n, n * sizeof(T)); // temporarily save trailing n elements + memmove(array + n, array, (size - n) * sizeof(T)); // shift array to the right + memcpy(array, temp, n * sizeof(T)); // prepend saved +} + +void Detector::resetBufferPosition(){ + // temporary buffer for efficient copies + static short int d0[NTMAX*RX_SAMPLE_RATE]; + + QMutexLocker mutex(&m_lock); + + // set index to roughly where we are in time (1ms resolution) + qint64 now (DriftingDateTime::currentMSecsSinceEpoch ()); + unsigned msInPeriod ((now % 86400000LL) % (m_period * 1000)); + int prevKin = dec_data.params.kin; + dec_data.params.kin = qMin ((msInPeriod * m_frameRate) / 1000, static_cast (sizeof (dec_data.d2) / sizeof (dec_data.d2[0]))); + m_bufferPos = 0; + m_ns=secondInPeriod(); + int delta = dec_data.params.kin - prevKin; + qDebug() << "advancing detector buffer from" << prevKin << "to" << dec_data.params.kin << "delta" << delta; + + // rotate buffer moving the contents that were at prevKin to the new kin position + if(delta < 0){ + rotate_array_left(dec_data.d2, NTMAX*RX_SAMPLE_RATE, d0, -delta); + } else { + rotate_array_right(dec_data.d2, NTMAX*RX_SAMPLE_RATE, d0, delta); + } +} + +void Detector::resetBufferContent(){ + QMutexLocker mutex(&m_lock); + + memset(dec_data.d2, 0, sizeof(dec_data.d2)); + qDebug() << "clearing detector buffer content"; +} + qint64 Detector::writeData (char const * data, qint64 maxSize) { QMutexLocker mutex(&m_lock); diff --git a/Detector.hpp b/Detector.hpp index f4f6b93..8f7bce9 100644 --- a/Detector.hpp +++ b/Detector.hpp @@ -35,6 +35,9 @@ public: Q_SLOT void setBlockSize (unsigned); void clear (); // discard buffer contents + void resetBufferPosition(); + void resetBufferContent(); + unsigned secondInPeriod () const; protected: From 672f0e4535b974652c5b526c0976aea726290b03 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Wed, 13 May 2020 21:15:00 -0400 Subject: [PATCH 20/32] Added decode debug of where we start the decode --- lib/decoder.f90 | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 81f7ee1..b9b9ea8 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -90,6 +90,10 @@ subroutine multimode_decoder(ss,id2,params,nfsample) id0=0 imax=int(NTMAX*12000) + if(params%syncStats) then + write(*,*) ' sync start', pos, sz + endif + if((imax-pos).lt.sz) then ! this means that the first part of the id0 is at the end of the buffer ! and the second half is at the beginning of the buffer @@ -124,6 +128,10 @@ subroutine multimode_decoder(ss,id2,params,nfsample) id0=0 imax=int(NTMAX*12000) + if(params%syncStats) then + write(*,*) ' sync start', pos, sz + endif + if((imax-pos).lt.sz) then ! this means that the first part of the id0 is at the end of the buffer ! and the second half is at the beginning of the buffer @@ -158,6 +166,10 @@ subroutine multimode_decoder(ss,id2,params,nfsample) id0=0 imax=int(NTMAX*12000) + if(params%syncStats) then + write(*,*) ' sync start', pos, sz + endif + if((imax-pos).lt.sz) then ! this means that the first part of the id0 is at the end of the buffer ! and the second half is at the beginning of the buffer @@ -192,6 +204,10 @@ subroutine multimode_decoder(ss,id2,params,nfsample) id0=0 imax=int(NTMAX*12000) + if(params%syncStats) then + write(*,*) ' sync start', pos, sz + endif + if((imax-pos).lt.sz) then ! this means that the first part of the id0 is at the end of the buffer ! and the second half is at the beginning of the buffer @@ -226,6 +242,10 @@ subroutine multimode_decoder(ss,id2,params,nfsample) id0=0 imax=int(NTMAX*12000) + if(params%syncStats) then + write(*,*) ' sync start', pos, sz + endif + if((imax-pos).lt.sz) then ! this means that the first part of the id0 is at the end of the buffer ! and the second half is at the beginning of the buffer From c44e75b20ac439bd4642a75ab51a0bf61e9d978c Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Wed, 13 May 2020 21:15:21 -0400 Subject: [PATCH 21/32] Added experimental auto-sync function for normal mode. --- mainwindow.cpp | 242 ++++++++++++++++++++++++++++++++++++++++++++----- mainwindow.h | 3 + mainwindow.ui | 10 ++ widegraph.cpp | 8 ++ widegraph.h | 2 + 5 files changed, 241 insertions(+), 24 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 6747c7d..551a222 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -810,6 +810,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, connect(m_wideGraph.data(), &WideGraph::qsy, this, &MainWindow::qsy); + connect(m_wideGraph.data(), &WideGraph::drifted, this, &MainWindow::drifted); + decodeBusy(false); QString t1[28]={"1 uW","2 uW","5 uW","10 uW","20 uW","50 uW","100 uW","200 uW","500 uW", "1 mW","2 mW","5 mW","10 mW","20 mW","50 mW","100 mW","200 mW","500 mW", @@ -2268,6 +2270,7 @@ void MainWindow::writeSettings() m_settings->setValue("SubModeHB", ui->actionModeJS8HB->isChecked()); m_settings->setValue("SubModeHBAck", ui->actionHeartbeatAcknowledgements->isChecked()); m_settings->setValue("SubModeMultiDecode", ui->actionModeMultiDecoder->isChecked()); + m_settings->setValue("SubModeAutoSync", ui->actionModeAutoSync->isChecked()); m_settings->setValue("DTtol",m_DTtol); m_settings->setValue("Ftol", ui->sbFtol->value ()); m_settings->setValue("MinSync",m_minSync); @@ -2424,6 +2427,7 @@ void MainWindow::readSettings() ui->actionModeJS8HB->setChecked(m_settings->value("SubModeHB", false).toBool()); ui->actionHeartbeatAcknowledgements->setChecked(m_settings->value("SubModeHBAck", false).toBool()); ui->actionModeMultiDecoder->setChecked(m_settings->value("SubModeMultiDecode", true).toBool()); + ui->actionModeAutoSync->setChecked(m_settings->value("SubModeAutoSync", false).toBool()); ui->sbFtol->setValue (m_settings->value("Ftol", 20).toInt()); m_minSync=m_settings->value("MinSync",0).toInt(); @@ -2676,6 +2680,18 @@ int MainWindow::computeFramesPerCycleForDecode(int submode){ return computePeriodForSubmode(submode) * RX_SAMPLE_RATE; } +int MainWindow::computePeriodStartDelayForDecode(int submode){ + int delay = 0; + switch(submode){ + case Varicode::JS8CallNormal: delay = JS8A_START_DELAY_MS; break; + case Varicode::JS8CallFast: delay = JS8B_START_DELAY_MS; break; + case Varicode::JS8CallTurbo: delay = JS8C_START_DELAY_MS; break; + case Varicode::JS8CallSlow: delay = JS8E_START_DELAY_MS; break; + case Varicode::JS8CallUltra: delay = JS8I_START_DELAY_MS; break; + } + return delay; +} + int MainWindow::computeFramesNeededForDecode(int submode){ int symbolSamples = 0; float threshold = 0.0; @@ -2706,6 +2722,8 @@ void MainWindow::dataSink(qint64 frames) k0 = k; } + qDebug() << "k" << k << "k0" << k0 << "delta" << k-k0; + #if JS8_USE_REFSPEC QString fname {QDir::toNativeSeparators(m_config.writeable_data_dir ().absoluteFilePath ("refspec.dat"))}; QByteArray bafname = fname.toLatin1(); @@ -4445,7 +4463,7 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ // TODO: make this non-static field of MainWindow? // map of last decode positions for each submode - static QMap lastDecodeStartMap; + // static QMap m_lastDecodeStartMap; // TODO: make this non-static field of MainWindow? // map of submodes to decode + optional alternate decode positions @@ -4464,24 +4482,32 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ // do we have a better way to check this? bool multi = ui->actionModeMultiDecoder->isChecked(); + // do we have a better way to check this? + bool everySecond = ui->actionModeAutoSync->isChecked(); + foreach(auto submode, submodes.keys()){ // skip if multi is disabled and this mode is not the current submode if(!multi && submode != m_nSubMode){ continue; } - // check alternate decode positions + // check all alternate decode positions foreach(auto alt, submodes.value(submode)){ + // skip alts if we are decoding every second + if(everySecond && alt != 0){ + continue; + } + qint32 cycle = computeAltCycleForDecode(submode, k, alt*oneSecondSamples); qint32 cycleFrames = computeFramesPerCycleForDecode(submode); qint32 cycleFramesNeeded = computeFramesNeededForDecode(submode) - oneSecondSamples; qint32 cycleFramesReady = k - (cycle * cycleFrames); - if(!lastDecodeStartMap.contains(submode)){ - lastDecodeStartMap[submode] = cycle * cycleFrames; + if(!m_lastDecodeStartMap.contains(submode)){ + m_lastDecodeStartMap[submode] = cycle * cycleFrames; } - qint32 lastDecodeStart = lastDecodeStartMap[submode]; + qint32 lastDecodeStart = m_lastDecodeStartMap[submode]; qint32 incrementedBy = k - lastDecodeStart; if(k < lastDecodeStart){ incrementedBy = maxSamples - lastDecodeStart + k; @@ -4489,7 +4515,21 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ if(JS8_DEBUG_DECODE) qDebug() << submodeName(submode) << "alt" << alt << "cycle" << cycle << "cycle frames" << cycleFrames << "cycle start" << cycle*cycleFrames << "cycle end" << (cycle+1)*cycleFrames << "k" << k << "frames ready" << cycleFramesReady << "incremeted by" << incrementedBy; - if(incrementedBy >= oneSecondSamples && cycleFramesReady >= cycleFramesNeeded){ + if(everySecond && incrementedBy >= oneSecondSamples){ + DecodeParams d; + d.submode = submode; + d.sz = cycleFrames; + d.start = k - d.sz; + if(d.start < 0){ + d.start += maxSamples; + } + m_decoderQueue.append(d); + decodes++; + + // keep track of last decode position + m_lastDecodeStartMap[submode] = k; + } + else if(incrementedBy >= oneSecondSamples && cycleFramesReady >= cycleFramesNeeded){ DecodeParams d; d.submode = submode; d.start = cycle*cycleFrames; @@ -4497,8 +4537,8 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ m_decoderQueue.append(d); decodes++; - // keep track of last decode start position - lastDecodeStartMap[submode] = k; + // keep track of last decode position + m_lastDecodeStartMap[submode] = k; } } } @@ -4809,7 +4849,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){ int period = computePeriodForSubmode(submode); - dec_data.params.syncStats = m_wideGraph->shouldDisplayDecodeAttempts(); + dec_data.params.syncStats = (m_wideGraph->shouldDisplayDecodeAttempts() || ui->actionModeAutoSync->isChecked()); dec_data.params.npts8=(m_ihsym*m_nsps)/16; dec_data.params.newdat=1; dec_data.params.nagain=0; @@ -5166,10 +5206,19 @@ void MainWindow::processDecodedLine(QByteArray t){ bool bAvgMsg=false; int navg=0; -#if JS8_TIME_DRIFT_EXPERIMENT - static bool hasNewDrift = false; - static int newDrift = 0; -#endif + static QList driftQueue; + + static qint32 syncStart = -1; + if(t.indexOf(" sync start") >= 0){ + auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts); + if(segs.isEmpty()){ + return; + } + + auto spos = segs.at(3); + syncStart = spos.toInt(); + return; + } if(t.indexOf("") >= 0) { auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts); @@ -5177,7 +5226,8 @@ void MainWindow::processDecodedLine(QByteArray t){ return; } - if(!m_wideGraph->shouldDisplayDecodeAttempts()){ + // only continue if we should either display decode attempts or if we should try to auto sync + if(!m_wideGraph->shouldDisplayDecodeAttempts() && !ui->actionModeAutoSync->isChecked()){ return; } @@ -5191,10 +5241,11 @@ void MainWindow::processDecodedLine(QByteArray t){ auto s = int(s1.toFloat()); auto xdt1 = QString(segs.at(8)); - auto xdt = int(xdt1.toFloat()); + auto xdt = xdt1.toFloat(); + auto xdtMs = int(xdt*1000); // draw candidates - if(abs(xdt) <= 2){ + if(abs(xdtMs) <= 2000){ if(s < 10){ m_wideGraph->drawDecodeLine(QColor(Qt::darkCyan), f, f + computeBandwidthForSubmode(m)); } else if (s <= 15){ @@ -5211,6 +5262,131 @@ void MainWindow::processDecodedLine(QByteArray t){ // draw decodes m_wideGraph->drawDecodeLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m)); + // compute time drift if needed + if(!ui->actionModeAutoSync->isChecked()){ + return; + } + + if(m != Varicode::JS8CallNormal){ + return; + } + // if we're here at this point, we _should_ be operating a decode every second + // + // so we need to figure out where: + // + // 1) this current decode started + // 2) when that cycle _should_ have started + // 3) compute the delta + // 4) apply the drift + + /// if(!m_lastDecodeStartMap.contains(m)){ + /// return; + /// } + + // this is where we started the decode + static qint32 oneSecondSamples = RX_SAMPLE_RATE; + static qint32 maxSamples = NTMAX * RX_SAMPLE_RATE; + + int periodMs = 1000 * computePeriodForSubmode(m); + + auto now = QDateTime::currentDateTimeUtc(); + writeNoticeTextToUI(now, QString("Decode at %1 (kin: %2, lastDecoded: %3)").arg(syncStart).arg(dec_data.params.kin).arg(m_lastDecodeStartMap.value(m))); + + float expectedStartDelay = computePeriodStartDelayForDecode(m)/1000.0; + + float decodedSignalTime = (float)syncStart/(float)RX_SAMPLE_RATE; + + writeNoticeTextToUI(now, QString("--> started at %1 seconds into the start of my drifted minute").arg(decodedSignalTime)); + + writeNoticeTextToUI(now, QString("--> we add a time delta of %1 seconds into the start of the cycle").arg(xdt)); + + // adjust for expected start delay + decodedSignalTime -= expectedStartDelay; + + // adjust for time delta + decodedSignalTime += xdt; + + // ensure that we are within a 60 second minute + if(decodedSignalTime < 0){ + decodedSignalTime += 60.0; + } else if(decodedSignalTime > 60){ + decodedSignalTime -= 60.0; + } + + writeNoticeTextToUI(now, QString("--> so signal adjusted started at %1 seconds into the start of my drifted minute").arg(decodedSignalTime)); + + int decodedSignalTimeMs = 1000 * decodedSignalTime; + int cycleStartTimeMs = (decodedSignalTimeMs / periodMs) * periodMs; + int driftMs = cycleStartTimeMs - decodedSignalTimeMs; + + writeNoticeTextToUI(now, QString("--> which is a drift adjustment of %1 milliseconds").arg(driftMs)); + + // if we have a large negative offset (say -14000), use the positive inverse of +1000 + if(driftMs + periodMs < qAbs(driftMs)){ + driftMs += periodMs; + } + // if we have a large positive offset (say 14000, use the negative inverse of -1000) + else if(qAbs(driftMs - periodMs) < driftMs){ + driftMs -= periodMs; + } + + writeNoticeTextToUI(now, QString("--> which is a corrected drift adjustment of %1 milliseconds").arg(driftMs)); + + int newDrift = DriftingDateTime::drift() + driftMs; + if(newDrift < 0){ + newDrift %= -periodMs; + } else { + newDrift %= periodMs; + } + + writeNoticeTextToUI(now, QString("--> which is rounded to a total drift of %1 milliseconds for this period").arg(newDrift)); + + driftQueue.append(newDrift); + + /// qint32 cycle = computeCycleForDecode(m, syncStart); + /// qint32 cycleFrames = computeFramesPerCycleForDecode(m); + /// qint32 cycleStart = cycle*cycleFrames; + /// + /// writeNoticeTextToUI(now, QString("--> cycle started at %1 and is %2 frames long").arg(cycleStart).arg(cycleFrames)); + /// writeNoticeTextToUI(now, QString("--> decode delta from cycle start is %1 frames and is %2 milliseconds after cycle start").arg(syncStart - cycleStart).arg(1000*float(syncStart-cycleStart)/RX_SAMPLE_RATE)); + /// writeNoticeTextToUI(now, QString("--> decode time delta is %1 milliseconds").arg(xdtMs)); + + // TODO: we're assuming 1 second decoding at this point + /// qint32 cycleFrames = computeFramesPerCycleForDecode(m); + /// qint32 startK = syncStart; + /// // qint32 startK = m_lastDecodeStartMap.value(m) - cycleFrames; + /// // if(startK < 0){ + /// // startK += maxSamples; + /// // } + /// qint32 cycle = computeCycleForDecode(m, startK); + /// qint32 framesIntoCycle = startK - cycle*cycleFrames; + /// + /// // TODO: do we need to know the *lastDecode* drift setting or is it safe to use this? + /// // float currentDriftSeconds = m_wideGraph->drift()/1000.0; + /// + /// // compute the relative seconds into the cycle + /// float secondsIntoCycle = (float)framesIntoCycle/(float)oneSecondSamples; + /// + /// // if we have any drift applied currently, add that to the cycle seconds since the cycle is relative + /// // float adjustedSecondsIntoCycle = secondsIntoCycle - currentDriftSeconds; + /// + /// // compute new drift adjustment + /// int periodMs = computePeriodForSubmode(m) * 1000; + /// int newDrift = (-secondsIntoCycle*1000)-xdtMs); + /// + /// int pos = newDrift + periodMs; + /// int neg = newDrift - periodMs; + /// if(qAbs(neg) < qAbs(pos)){ + /// newDrift = neg; + /// } else { + /// newDrift = pos; + /// } + /// + /// writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Decode at startK %4 and %1 seconds into cycle %5 with dt of %2 milliseconds should adjust drift by %3").arg(secondsIntoCycle).arg(xdtMs).arg(newDrift).arg(startK).arg(cycle)); + + /// setDrift(newDrift); + + #if JS8_TIME_DRIFT_EXPERIMENT // use normal decodes for auto drift if we haven't already defined a new drift for this period if(/*!hasNewDrift && */ (m == Varicode::JS8CallSlow || m == Varicode::JS8CallNormal)){ @@ -5261,16 +5437,22 @@ void MainWindow::processDecodedLine(QByteArray t){ int msec = m_decoderBusyStartTime.msecsTo(QDateTime::currentDateTimeUtc()); if(JS8_DEBUG_DECODE) qDebug() << "decode duration" << msec << "ms"; -#if JS8_TIME_DRIFT_EXPERIMENT - if(hasNewDrift){ + if(!driftQueue.isEmpty()){ static int driftN = 1; - newDrift = ((driftN-1)*DriftingDateTime::drift() + newDrift)/driftN; - setDrift(newDrift); - if(driftN < 60) driftN++; // cap it to 60 observations - writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Drift: %1").arg(newDrift)); - hasNewDrift = false; + static int driftAvg = DriftingDateTime::drift(); + + while(!driftQueue.isEmpty()){ + int newDrift = driftQueue.first(); + driftQueue.removeFirst(); + + driftAvg = ((driftN-1)*driftAvg + newDrift)/driftN; + if(driftN < 60) driftN++; // cap it to 60 observations + } + + setDrift(driftAvg); + + writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Drift: %1").arg(driftAvg)); } -#endif m_bDecoded = t.mid(16).trimmed().toInt() > 0; int mswait=3*1000*m_TRperiod/4; @@ -10051,6 +10233,12 @@ void MainWindow::qsy(int hzDelta){ displayActivity(true); } +void MainWindow::drifted(int /*prev*/, int /*cur*/){ + // here we reset the buffer position without clearing the buffer + // this makes the detected emit the correct k when drifting time + m_detector->resetBufferPosition(); +} + void MainWindow::setFreqOffsetForRestore(int freq, bool shouldRestore){ setFreq4(freq, freq); if(shouldRestore){ @@ -10541,6 +10729,7 @@ void MainWindow::updateModeButtonText(){ auto selectedCallsign = callsignSelected(); auto multi = ui->actionModeMultiDecoder->isChecked(); + auto autosync = ui->actionModeAutoSync->isChecked(); auto autoreply = ui->actionModeAutoreply->isChecked(); auto heartbeat = ui->actionModeJS8HB->isEnabled() && ui->actionModeJS8HB->isChecked(); auto ack = autoreply && ui->actionHeartbeatAcknowledgements->isChecked() && (!m_config.heartbeat_qso_pause() || selectedCallsign.isEmpty()); @@ -10550,6 +10739,10 @@ void MainWindow::updateModeButtonText(){ modeText += QString("+MULTI"); } + if(autosync){ + modeText += QString("+SYNC"); + } + if(autoreply){ if(m_config.autoreply_confirmation()){ modeText += QString("+AUTO+CONF"); @@ -10565,6 +10758,7 @@ void MainWindow::updateModeButtonText(){ modeText += QString("+HB"); } } + ui->modeButton->setText(modeText); } diff --git a/mainwindow.h b/mainwindow.h index 8284d39..9b60f3e 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -132,6 +132,7 @@ public slots: void readFromStdout(QProcess * proc); void setXIT(int n, Frequency base = 0u); void qsy(int hzDelta); + void drifted(int prev, int cur); void setFreqOffsetForRestore(int freq, bool shouldRestore); bool tryRestoreFreqOffset(); void setFreq4(int rxFreq, int txFreq); @@ -615,6 +616,7 @@ private: bool m_loopall; bool m_decoderBusy; QString m_decoderBusyBand; + QMap m_lastDecodeStartMap; Radio::Frequency m_decoderBusyFreq; QDateTime m_decoderBusyStartTime; bool m_auto; @@ -991,6 +993,7 @@ private: int computeCycleForDecode(int submode, int k); int computeAltCycleForDecode(int submode, int k, int offsetFrames); int computeFramesPerCycleForDecode(int submode); + int computePeriodStartDelayForDecode(int submode); int computeFramesNeededForDecode(int submode); bool shortList(QString callsign); void transmit (double snr = 99.); diff --git a/mainwindow.ui b/mainwindow.ui index b2daa8c..91d5d02 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -4749,10 +4749,12 @@ list. The list can be maintained in Settings (F2). + + @@ -5762,6 +5764,14 @@ list. The list can be maintained in Settings (F2). Enable Tuning Tone (T&UNE) + + + true + + + Enable Automatic Synchronization (S&YNC) + + diff --git a/widegraph.cpp b/widegraph.cpp index 1721502..bc03166 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -921,6 +921,8 @@ void WideGraph::on_driftSyncResetButton_clicked(){ } void WideGraph::setDrift(int n){ + int prev = drift(); + DriftingDateTime::setDrift(n); qDebug() << qSetRealNumberPrecision(12) << "Drift milliseconds:" << n; @@ -930,6 +932,12 @@ void WideGraph::setDrift(int n){ if(ui->driftSpinBox->value() != n){ ui->driftSpinBox->setValue(n); } + + emit drifted(prev, n); +} + +int WideGraph::drift(){ + return DriftingDateTime::drift(); } void WideGraph::setQSYEnabled(bool enabled){ diff --git a/widegraph.h b/widegraph.h index 6d34ec0..b73540a 100644 --- a/widegraph.h +++ b/widegraph.h @@ -77,6 +77,7 @@ signals: void setXIT2(int n); void setFreq3(int rxFreq, int txFreq); void qsy(int hzDelta); + void drifted(int prev, int cur); public slots: void wideFreezeDecode(int n); @@ -85,6 +86,7 @@ public slots: void setControlsVisible(bool visible); bool controlsVisible(); void setDrift(int n); + int drift(); void setQSYEnabled(bool enabled); void setPaused(bool paused){ m_paused = paused; } From 864db3efaf7827e29e2f3d017d52303b53410c0e Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Wed, 13 May 2020 22:20:20 -0400 Subject: [PATCH 22/32] Record time drift when autosyncing as the drifted time --- mainwindow.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 551a222..55c4bc9 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -5572,7 +5572,7 @@ void MainWindow::processDecodedLine(QByteArray t){ d.utcTimestamp = DriftingDateTime::currentDateTimeUtc(); d.snr = decodedtext.snr(); d.isBuffered = false; - d.tdrift = decodedtext.dt(); + d.tdrift = ui->actionModeAutoSync->isChecked() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); d.submode = decodedtext.submode(); // if we have any "first" frame, and a buffer is already established, clear it... @@ -5610,7 +5610,7 @@ void MainWindow::processDecodedLine(QByteArray t){ cd.offset = decodedtext.frequencyOffset(); cd.utcTimestamp = DriftingDateTime::currentDateTimeUtc(); cd.bits = decodedtext.bits(); - cd.tdrift = decodedtext.dt(); + cd.tdrift = ui->actionModeAutoSync->isChecked() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); cd.submode = decodedtext.submode(); // Only respond to HEARTBEATS...remember that CQ messages are "Alt" pings @@ -5689,7 +5689,7 @@ void MainWindow::processDecodedLine(QByteArray t){ cmd.utcTimestamp = DriftingDateTime::currentDateTimeUtc(); cmd.bits = decodedtext.bits(); cmd.extra = parts.length() > 2 ? parts.mid(3).join(" ") : ""; - cmd.tdrift = decodedtext.dt(); + cmd.tdrift = ui->actionModeAutoSync->isChecked() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); cmd.submode = decodedtext.submode(); // if the command is a buffered command and its not the last frame OR we have from or to in a separate message (compound call) From 0195285e12920aff7ecc0677631e82079dcc336e Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Wed, 13 May 2020 22:22:51 -0400 Subject: [PATCH 23/32] Cleaned up autosync/autodrift --- mainwindow.cpp | 103 +++++-------------------------------------------- 1 file changed, 9 insertions(+), 94 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 55c4bc9..a3f77a0 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -2722,7 +2722,7 @@ void MainWindow::dataSink(qint64 frames) k0 = k; } - qDebug() << "k" << k << "k0" << k0 << "delta" << k-k0; + //qDebug() << "k" << k << "k0" << k0 << "delta" << k-k0; #if JS8_USE_REFSPEC QString fname {QDir::toNativeSeparators(m_config.writeable_data_dir ().absoluteFilePath ("refspec.dat"))}; @@ -5279,26 +5279,17 @@ void MainWindow::processDecodedLine(QByteArray t){ // 3) compute the delta // 4) apply the drift - /// if(!m_lastDecodeStartMap.contains(m)){ - /// return; - /// } - - // this is where we started the decode - static qint32 oneSecondSamples = RX_SAMPLE_RATE; - static qint32 maxSamples = NTMAX * RX_SAMPLE_RATE; - int periodMs = 1000 * computePeriodForSubmode(m); - auto now = QDateTime::currentDateTimeUtc(); - writeNoticeTextToUI(now, QString("Decode at %1 (kin: %2, lastDecoded: %3)").arg(syncStart).arg(dec_data.params.kin).arg(m_lastDecodeStartMap.value(m))); + //writeNoticeTextToUI(now, QString("Decode at %1 (kin: %2, lastDecoded: %3)").arg(syncStart).arg(dec_data.params.kin).arg(m_lastDecodeStartMap.value(m))); float expectedStartDelay = computePeriodStartDelayForDecode(m)/1000.0; float decodedSignalTime = (float)syncStart/(float)RX_SAMPLE_RATE; - writeNoticeTextToUI(now, QString("--> started at %1 seconds into the start of my drifted minute").arg(decodedSignalTime)); + //writeNoticeTextToUI(now, QString("--> started at %1 seconds into the start of my drifted minute").arg(decodedSignalTime)); - writeNoticeTextToUI(now, QString("--> we add a time delta of %1 seconds into the start of the cycle").arg(xdt)); + //writeNoticeTextToUI(now, QString("--> we add a time delta of %1 seconds into the start of the cycle").arg(xdt)); // adjust for expected start delay decodedSignalTime -= expectedStartDelay; @@ -5313,13 +5304,13 @@ void MainWindow::processDecodedLine(QByteArray t){ decodedSignalTime -= 60.0; } - writeNoticeTextToUI(now, QString("--> so signal adjusted started at %1 seconds into the start of my drifted minute").arg(decodedSignalTime)); + //writeNoticeTextToUI(now, QString("--> so signal adjusted started at %1 seconds into the start of my drifted minute").arg(decodedSignalTime)); int decodedSignalTimeMs = 1000 * decodedSignalTime; int cycleStartTimeMs = (decodedSignalTimeMs / periodMs) * periodMs; int driftMs = cycleStartTimeMs - decodedSignalTimeMs; - writeNoticeTextToUI(now, QString("--> which is a drift adjustment of %1 milliseconds").arg(driftMs)); + //writeNoticeTextToUI(now, QString("--> which is a drift adjustment of %1 milliseconds").arg(driftMs)); // if we have a large negative offset (say -14000), use the positive inverse of +1000 if(driftMs + periodMs < qAbs(driftMs)){ @@ -5330,7 +5321,7 @@ void MainWindow::processDecodedLine(QByteArray t){ driftMs -= periodMs; } - writeNoticeTextToUI(now, QString("--> which is a corrected drift adjustment of %1 milliseconds").arg(driftMs)); + //writeNoticeTextToUI(now, QString("--> which is a corrected drift adjustment of %1 milliseconds").arg(driftMs)); int newDrift = DriftingDateTime::drift() + driftMs; if(newDrift < 0){ @@ -5339,86 +5330,10 @@ void MainWindow::processDecodedLine(QByteArray t){ newDrift %= periodMs; } - writeNoticeTextToUI(now, QString("--> which is rounded to a total drift of %1 milliseconds for this period").arg(newDrift)); + //writeNoticeTextToUI(now, QString("--> which is rounded to a total drift of %1 milliseconds for this period").arg(newDrift)); driftQueue.append(newDrift); - /// qint32 cycle = computeCycleForDecode(m, syncStart); - /// qint32 cycleFrames = computeFramesPerCycleForDecode(m); - /// qint32 cycleStart = cycle*cycleFrames; - /// - /// writeNoticeTextToUI(now, QString("--> cycle started at %1 and is %2 frames long").arg(cycleStart).arg(cycleFrames)); - /// writeNoticeTextToUI(now, QString("--> decode delta from cycle start is %1 frames and is %2 milliseconds after cycle start").arg(syncStart - cycleStart).arg(1000*float(syncStart-cycleStart)/RX_SAMPLE_RATE)); - /// writeNoticeTextToUI(now, QString("--> decode time delta is %1 milliseconds").arg(xdtMs)); - - // TODO: we're assuming 1 second decoding at this point - /// qint32 cycleFrames = computeFramesPerCycleForDecode(m); - /// qint32 startK = syncStart; - /// // qint32 startK = m_lastDecodeStartMap.value(m) - cycleFrames; - /// // if(startK < 0){ - /// // startK += maxSamples; - /// // } - /// qint32 cycle = computeCycleForDecode(m, startK); - /// qint32 framesIntoCycle = startK - cycle*cycleFrames; - /// - /// // TODO: do we need to know the *lastDecode* drift setting or is it safe to use this? - /// // float currentDriftSeconds = m_wideGraph->drift()/1000.0; - /// - /// // compute the relative seconds into the cycle - /// float secondsIntoCycle = (float)framesIntoCycle/(float)oneSecondSamples; - /// - /// // if we have any drift applied currently, add that to the cycle seconds since the cycle is relative - /// // float adjustedSecondsIntoCycle = secondsIntoCycle - currentDriftSeconds; - /// - /// // compute new drift adjustment - /// int periodMs = computePeriodForSubmode(m) * 1000; - /// int newDrift = (-secondsIntoCycle*1000)-xdtMs); - /// - /// int pos = newDrift + periodMs; - /// int neg = newDrift - periodMs; - /// if(qAbs(neg) < qAbs(pos)){ - /// newDrift = neg; - /// } else { - /// newDrift = pos; - /// } - /// - /// writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Decode at startK %4 and %1 seconds into cycle %5 with dt of %2 milliseconds should adjust drift by %3").arg(secondsIntoCycle).arg(xdtMs).arg(newDrift).arg(startK).arg(cycle)); - - /// setDrift(newDrift); - - -#if JS8_TIME_DRIFT_EXPERIMENT - // use normal decodes for auto drift if we haven't already defined a new drift for this period - if(/*!hasNewDrift && */ (m == Varicode::JS8CallSlow || m == Varicode::JS8CallNormal)){ - auto now = QDateTime::currentDateTimeUtc(); - float n = 0; - float nNeg = ((now.time().second()) % period) - period; - float nPos = period - ((now.time().second()) % period); - if(qAbs(nNeg) < nPos){ - n = nNeg + 1; - } else { - n = nPos - 1; - } - - // subtract to the previous period - n -= (float)period; - - // subtract the actual tx duration - n += computeFramesNeededForDecode(m)/RX_SAMPLE_RATE; - - // subtract the time delta - n -= xdt; - - int xdtmin = qMin(n*1000, (float)DriftingDateTime::drift()); - int xdtmax = qMax(n*1000, (float)DriftingDateTime::drift()); - - int oldNewDrift = newDrift; - newDrift = (xdtmin + (xdtmax-xdtmin)/2); - newDrift = qMin(oldNewDrift, newDrift) + (qMax(oldNewDrift, newDrift)-qMin(oldNewDrift, newDrift))/2; - hasNewDrift = true; - } -#endif - if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists()); return; @@ -5451,7 +5366,7 @@ void MainWindow::processDecodedLine(QByteArray t){ setDrift(driftAvg); - writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Drift: %1").arg(driftAvg)); + //writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Automatic Drift: %1").arg(driftAvg)); } m_bDecoded = t.mid(16).trimmed().toInt() > 0; From 0869bc3f57da2561ad1c7fcf5c43a683c3bc4ff7 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Thu, 14 May 2020 12:05:16 -0400 Subject: [PATCH 24/32] Fixed total frames needed for decode in the experimental decoder --- mainwindow.cpp | 40 ++++++++++++++++++++++++++-------------- mainwindow.h | 1 + 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index a3f77a0..89a5b73 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -2692,16 +2692,21 @@ int MainWindow::computePeriodStartDelayForDecode(int submode){ return delay; } -int MainWindow::computeFramesNeededForDecode(int submode){ +int MainWindow::computeFramesPerSymbolForDecode(int submode){ int symbolSamples = 0; - float threshold = 0.0; switch(submode){ - case Varicode::JS8CallNormal: symbolSamples = JS8A_SYMBOL_SAMPLES; threshold = JS8A_START_DELAY_MS/1000.0 + 0.5; break; - case Varicode::JS8CallFast: symbolSamples = JS8B_SYMBOL_SAMPLES; threshold = JS8B_START_DELAY_MS/1000.0 + 0.5; break; - case Varicode::JS8CallTurbo: symbolSamples = JS8C_SYMBOL_SAMPLES; threshold = JS8C_START_DELAY_MS/1000.0 + 0.5; break; - case Varicode::JS8CallSlow: symbolSamples = JS8E_SYMBOL_SAMPLES; threshold = JS8E_START_DELAY_MS/1000.0 + 0.5; break; - case Varicode::JS8CallUltra: symbolSamples = JS8I_SYMBOL_SAMPLES; threshold = JS8I_START_DELAY_MS/1000.0 + 0.5; break; + case Varicode::JS8CallNormal: symbolSamples = JS8A_SYMBOL_SAMPLES; break; + case Varicode::JS8CallFast: symbolSamples = JS8B_SYMBOL_SAMPLES; break; + case Varicode::JS8CallTurbo: symbolSamples = JS8C_SYMBOL_SAMPLES; break; + case Varicode::JS8CallSlow: symbolSamples = JS8E_SYMBOL_SAMPLES; break; + case Varicode::JS8CallUltra: symbolSamples = JS8I_SYMBOL_SAMPLES; break; } + return symbolSamples; +} + +int MainWindow::computeFramesNeededForDecode(int submode){ + float threshold = 0.5 + computePeriodStartDelayForDecode(submode)/1000.0; + int symbolSamples = computeFramesPerSymbolForDecode(submode); return int(qFloor(float(symbolSamples*JS8_NUM_SYMBOLS + threshold*RX_SAMPLE_RATE))); } @@ -4485,6 +4490,9 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ // do we have a better way to check this? bool everySecond = ui->actionModeAutoSync->isChecked(); + // do we need to process alternate positions? + bool skipAlt = true; + foreach(auto submode, submodes.keys()){ // skip if multi is disabled and this mode is not the current submode if(!multi && submode != m_nSubMode){ @@ -4498,9 +4506,14 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ continue; } + // skip alt decode positions if needed + if(skipAlt && alt != 0){ + continue; + } + qint32 cycle = computeAltCycleForDecode(submode, k, alt*oneSecondSamples); qint32 cycleFrames = computeFramesPerCycleForDecode(submode); - qint32 cycleFramesNeeded = computeFramesNeededForDecode(submode) - oneSecondSamples; + qint32 cycleFramesNeeded = computeFramesPerSymbolForDecode(submode)*JS8_NUM_SYMBOLS; //computeFramesNeededForDecode(submode) - oneSecondSamples; qint32 cycleFramesReady = k - (cycle * cycleFrames); if(!m_lastDecodeStartMap.contains(submode)){ @@ -4529,7 +4542,7 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ // keep track of last decode position m_lastDecodeStartMap[submode] = k; } - else if(incrementedBy >= oneSecondSamples && cycleFramesReady >= cycleFramesNeeded){ + else if(incrementedBy >= oneSecondSamples && (cycleFramesReady >= cycleFramesNeeded || cycleFramesReady < oneSecondSamples)){ DecodeParams d; d.submode = submode; d.start = cycle*cycleFrames; @@ -4543,11 +4556,6 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ } } - // TODO: debug - if(decodes > 0 && m_wideGraph->shouldDisplayDecodeAttempts()){ - m_wideGraph->drawHorizontalLine(QColor(Qt::yellow), 0, 5); - } - return decodes > 0; } @@ -5340,6 +5348,10 @@ void MainWindow::processDecodedLine(QByteArray t){ } if(t.indexOf("") >= 0) { + if(m_wideGraph->shouldDisplayDecodeAttempts()){ + m_wideGraph->drawHorizontalLine(QColor(Qt::yellow), 0, 5); + } + if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists()); return; } diff --git a/mainwindow.h b/mainwindow.h index 9b60f3e..ef3b302 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -994,6 +994,7 @@ private: int computeAltCycleForDecode(int submode, int k, int offsetFrames); int computeFramesPerCycleForDecode(int submode); int computePeriodStartDelayForDecode(int submode); + int computeFramesPerSymbolForDecode(int submode); int computeFramesNeededForDecode(int submode); bool shortList(QString callsign); void transmit (double snr = 99.); From 97dfc7f52f460f18a2ad6d1a55cac51d3c2f9f7f Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Thu, 14 May 2020 21:14:01 -0400 Subject: [PATCH 25/32] Cleaning up frame dedupe, decode debug, and decode buffer thresholds --- commons.h | 1 + decodedtext.cpp | 4 +++- decodedtext.h | 2 ++ lib/decoder.f90 | 10 +++++----- lib/js8a_decode.f90 | 2 +- mainwindow.cpp | 24 ++++++++++++++++++------ mainwindow.h | 2 +- 7 files changed, 31 insertions(+), 14 deletions(-) diff --git a/commons.h b/commons.h index d7ec425..0839428 100644 --- a/commons.h +++ b/commons.h @@ -13,6 +13,7 @@ #define JS8_DECODE_THREAD 1 // use a separate thread for decode process handling #define JS8_ALLOW_EXTENDED 1 // allow extended latin-1 capital charset #define JS8_SAVE_AUDIO 0 // enable the save menu +#define JS8_AUTO_SYNC 0 // enable the experimental auto sync feature #ifdef QT_DEBUG #define JS8_DEBUG_DECODE 0 // emit debug statements for the decode pipeline diff --git a/decodedtext.cpp b/decodedtext.cpp index e37e974..f2e8e0a 100644 --- a/decodedtext.cpp +++ b/decodedtext.cpp @@ -26,6 +26,7 @@ DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString , isAlt_(false) , bits_{0} , submode_{ string_.mid(column_mode + padding_, 3).trimmed().at(0).cell() - 'A' } + , frame_ { string_.mid (column_qsoText + padding_, 12).trimmed () } { if(message_.length() >= 1) { message_ = message_.left (21).remove (QRegularExpression {"[<>]"}); @@ -71,7 +72,8 @@ DecodedText::DecodedText (QString const& js8callmessage, int bits, int submode): isHeartbeat_(false), isAlt_(false), bits_(bits), - submode_(submode) + submode_(submode), + frame_(js8callmessage) { is_standard_ = QRegularExpression("^(CQ|DE|QRZ)\\s").match(message_).hasMatch(); diff --git a/decodedtext.h b/decodedtext.h index 1eb51af..868e16f 100644 --- a/decodedtext.h +++ b/decodedtext.h @@ -41,6 +41,7 @@ public: bool tryUnpackFastData(); quint8 frameType() const { return frameType_; } + QString frame() const { return frame_; } QString extra() const { return extra_; } QString compoundCall() const { return compound_; } @@ -113,6 +114,7 @@ private: bool is_standard_; int bits_; int submode_; + QString frame_; }; #endif // DECODEDTEXT_H diff --git a/lib/decoder.f90 b/lib/decoder.f90 index b9b9ea8..020e7f9 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -91,7 +91,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) imax=int(NTMAX*12000) if(params%syncStats) then - write(*,*) ' sync start', pos, sz + write(*,*) ' sync start', pos, sz endif if((imax-pos).lt.sz) then @@ -129,7 +129,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) imax=int(NTMAX*12000) if(params%syncStats) then - write(*,*) ' sync start', pos, sz + write(*,*) ' sync start', pos, sz endif if((imax-pos).lt.sz) then @@ -167,7 +167,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) imax=int(NTMAX*12000) if(params%syncStats) then - write(*,*) ' sync start', pos, sz + write(*,*) ' sync start', pos, sz endif if((imax-pos).lt.sz) then @@ -205,7 +205,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) imax=int(NTMAX*12000) if(params%syncStats) then - write(*,*) ' sync start', pos, sz + write(*,*) ' sync start', pos, sz endif if((imax-pos).lt.sz) then @@ -243,7 +243,7 @@ subroutine multimode_decoder(ss,id2,params,nfsample) imax=int(NTMAX*12000) if(params%syncStats) then - write(*,*) ' sync start', pos, sz + write(*,*) ' sync start', pos, sz endif if((imax-pos).lt.sz) then diff --git a/lib/js8a_decode.f90 b/lib/js8a_decode.f90 index e3d9cfc..22ddc7b 100644 --- a/lib/js8a_decode.f90 +++ b/lib/js8a_decode.f90 @@ -108,7 +108,7 @@ contains xdt=candidate(2,icand) xbase=10.0**(0.1*(sbase(nint(f1/(12000.0/NFFT1)))-40.0)) ! 3.125Hz - if(NWRITELOG.eq.0) then + if(NWRITELOG.eq.1) then write(*,*) ' candidate', icand, 'f1', f1, 'sync', sync, 'xdt', xdt, 'xbase', xbase flush(6) endif diff --git a/mainwindow.cpp b/mainwindow.cpp index 89a5b73..8a4e9a4 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1022,6 +1022,11 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, if(!JS8_ENABLE_JS8I){ ui->actionModeJS8Ultra->setVisible(false); } + if(!JS8_AUTO_SYNC){ + ui->actionModeAutoSync->setVisible(false); + ui->actionModeAutoSync->setEnabled(false); + ui->actionModeAutoSync->setChecked(false); + } // prep prepareMonitorControls(); @@ -4284,6 +4289,11 @@ bool MainWindow::decode(qint32 k){ return false; } + if(m_decoderBusyStartTime.isValid() && m_decoderBusyStartTime.msecsTo(QDateTime::currentDateTimeUtc()) < 1000){ + if(JS8_DEBUG_DECODE) qDebug() << "--> decoder paused for 1000 ms after last decode start"; + return false; + } + int threshold = m_nSubMode == Varicode::JS8CallSlow ? 4000 : 2000; // two seconds if(isInDecodeDelayThreshold(threshold)){ if(JS8_DEBUG_DECODE) qDebug() << "--> decoder paused for" << threshold << "ms after transmit stop"; @@ -4491,7 +4501,7 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ bool everySecond = ui->actionModeAutoSync->isChecked(); // do we need to process alternate positions? - bool skipAlt = true; + bool skipAlt = false; foreach(auto submode, submodes.keys()){ // skip if multi is disabled and this mode is not the current submode @@ -4542,7 +4552,10 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ // keep track of last decode position m_lastDecodeStartMap[submode] = k; } - else if(incrementedBy >= oneSecondSamples && (cycleFramesReady >= cycleFramesNeeded || cycleFramesReady < oneSecondSamples)){ + else if( + (incrementedBy >= 2*oneSecondSamples && cycleFramesReady >= cycleFramesNeeded ) || + (incrementedBy >= oneSecondSamples && cycleFramesReady < 1.25*oneSecondSamples) + ){ DecodeParams d; d.submode = submode; d.start = cycle*cycleFrames; @@ -5209,7 +5222,7 @@ void MainWindow::readFromStdout(QProcess * proc) //r } void MainWindow::processDecodedLine(QByteArray t){ - qDebug() << "JS8: " << QString(t); + if(JS8_DEBUG_DECODE) qDebug() << "JS8: " << QString(t); bool bAvgMsg=false; int navg=0; @@ -5217,7 +5230,7 @@ void MainWindow::processDecodedLine(QByteArray t){ static QList driftQueue; static qint32 syncStart = -1; - if(t.indexOf(" sync start") >= 0){ + if(t.indexOf(" sync start") >= 0){ auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts); if(segs.isEmpty()){ return; @@ -5414,9 +5427,8 @@ void MainWindow::processDecodedLine(QByteArray t){ // frames are also valid if they pass our dupe check (haven't seen the same frame in the past 1/2 decode period) - auto frame = decodedtext.message(); auto frameOffset = decodedtext.frequencyOffset(); - auto frameDedupeKey = QString("%1:%2").arg(decodedtext.submode()).arg(frame); + auto frameDedupeKey = QString("%1:%2").arg(decodedtext.submode()).arg(decodedtext.frame()); if(m_messageDupeCache.contains(frameDedupeKey)){ auto cached = m_messageDupeCache.value(frameDedupeKey); diff --git a/mainwindow.h b/mainwindow.h index ef3b302..1fdc867 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -616,7 +616,7 @@ private: bool m_loopall; bool m_decoderBusy; QString m_decoderBusyBand; - QMap m_lastDecodeStartMap; + QMap m_lastDecodeStartMap; // submode, decode k start position Radio::Frequency m_decoderBusyFreq; QDateTime m_decoderBusyStartTime; bool m_auto; From f0b5508c1e35d478c384b4291eec4b1c4d44388a Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Thu, 14 May 2020 21:21:38 -0400 Subject: [PATCH 26/32] Enable autosync for testing --- commons.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commons.h b/commons.h index 0839428..a1a2d03 100644 --- a/commons.h +++ b/commons.h @@ -13,7 +13,7 @@ #define JS8_DECODE_THREAD 1 // use a separate thread for decode process handling #define JS8_ALLOW_EXTENDED 1 // allow extended latin-1 capital charset #define JS8_SAVE_AUDIO 0 // enable the save menu -#define JS8_AUTO_SYNC 0 // enable the experimental auto sync feature +#define JS8_AUTO_SYNC 1 // enable the experimental auto sync feature #ifdef QT_DEBUG #define JS8_DEBUG_DECODE 0 // emit debug statements for the decode pipeline From 1b6a4dd301924a4c3d507cbf44e2f5fb84060cec Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Thu, 14 May 2020 22:17:20 -0400 Subject: [PATCH 27/32] Updated menu label --- mainwindow.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainwindow.ui b/mainwindow.ui index 91d5d02..4a18e75 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -5769,7 +5769,7 @@ list. The list can be maintained in Settings (F2). true - Enable Automatic Synchronization (S&YNC) + Enable Automatic Timing Synchronization (S&YNC) From 9728fe52c21bcf2e0d5cc91c766cfe9e5a90eef7 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Fri, 15 May 2020 21:30:10 -0400 Subject: [PATCH 28/32] Moved auto sync experiment into the timing tab --- mainwindow.cpp | 28 ++++++++-------------------- mainwindow.ui | 1 - widegraph.cpp | 13 +++++++++++++ widegraph.h | 2 ++ widegraph.ui | 22 +++++++++++++++++++--- 5 files changed, 42 insertions(+), 24 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 8a4e9a4..e4104b4 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1022,11 +1022,6 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, if(!JS8_ENABLE_JS8I){ ui->actionModeJS8Ultra->setVisible(false); } - if(!JS8_AUTO_SYNC){ - ui->actionModeAutoSync->setVisible(false); - ui->actionModeAutoSync->setEnabled(false); - ui->actionModeAutoSync->setChecked(false); - } // prep prepareMonitorControls(); @@ -2275,7 +2270,6 @@ void MainWindow::writeSettings() m_settings->setValue("SubModeHB", ui->actionModeJS8HB->isChecked()); m_settings->setValue("SubModeHBAck", ui->actionHeartbeatAcknowledgements->isChecked()); m_settings->setValue("SubModeMultiDecode", ui->actionModeMultiDecoder->isChecked()); - m_settings->setValue("SubModeAutoSync", ui->actionModeAutoSync->isChecked()); m_settings->setValue("DTtol",m_DTtol); m_settings->setValue("Ftol", ui->sbFtol->value ()); m_settings->setValue("MinSync",m_minSync); @@ -2432,7 +2426,6 @@ void MainWindow::readSettings() ui->actionModeJS8HB->setChecked(m_settings->value("SubModeHB", false).toBool()); ui->actionHeartbeatAcknowledgements->setChecked(m_settings->value("SubModeHBAck", false).toBool()); ui->actionModeMultiDecoder->setChecked(m_settings->value("SubModeMultiDecode", true).toBool()); - ui->actionModeAutoSync->setChecked(m_settings->value("SubModeAutoSync", false).toBool()); ui->sbFtol->setValue (m_settings->value("Ftol", 20).toInt()); m_minSync=m_settings->value("MinSync",0).toInt(); @@ -4498,7 +4491,7 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ bool multi = ui->actionModeMultiDecoder->isChecked(); // do we have a better way to check this? - bool everySecond = ui->actionModeAutoSync->isChecked(); + bool everySecond = m_wideGraph->shouldAutoSync(); // do we need to process alternate positions? bool skipAlt = false; @@ -4870,7 +4863,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){ int period = computePeriodForSubmode(submode); - dec_data.params.syncStats = (m_wideGraph->shouldDisplayDecodeAttempts() || ui->actionModeAutoSync->isChecked()); + dec_data.params.syncStats = (m_wideGraph->shouldDisplayDecodeAttempts() || m_wideGraph->shouldAutoSync()); dec_data.params.npts8=(m_ihsym*m_nsps)/16; dec_data.params.newdat=1; dec_data.params.nagain=0; @@ -5248,7 +5241,7 @@ void MainWindow::processDecodedLine(QByteArray t){ } // only continue if we should either display decode attempts or if we should try to auto sync - if(!m_wideGraph->shouldDisplayDecodeAttempts() && !ui->actionModeAutoSync->isChecked()){ + if(!m_wideGraph->shouldDisplayDecodeAttempts() && !m_wideGraph->shouldAutoSync()){ return; } @@ -5284,7 +5277,7 @@ void MainWindow::processDecodedLine(QByteArray t){ m_wideGraph->drawDecodeLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m)); // compute time drift if needed - if(!ui->actionModeAutoSync->isChecked()){ + if(!m_wideGraph->shouldAutoSync()){ return; } @@ -5511,7 +5504,7 @@ void MainWindow::processDecodedLine(QByteArray t){ d.utcTimestamp = DriftingDateTime::currentDateTimeUtc(); d.snr = decodedtext.snr(); d.isBuffered = false; - d.tdrift = ui->actionModeAutoSync->isChecked() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); + d.tdrift = m_wideGraph->shouldAutoSync() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); d.submode = decodedtext.submode(); // if we have any "first" frame, and a buffer is already established, clear it... @@ -5549,7 +5542,7 @@ void MainWindow::processDecodedLine(QByteArray t){ cd.offset = decodedtext.frequencyOffset(); cd.utcTimestamp = DriftingDateTime::currentDateTimeUtc(); cd.bits = decodedtext.bits(); - cd.tdrift = ui->actionModeAutoSync->isChecked() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); + cd.tdrift = m_wideGraph->shouldAutoSync() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); cd.submode = decodedtext.submode(); // Only respond to HEARTBEATS...remember that CQ messages are "Alt" pings @@ -5628,7 +5621,7 @@ void MainWindow::processDecodedLine(QByteArray t){ cmd.utcTimestamp = DriftingDateTime::currentDateTimeUtc(); cmd.bits = decodedtext.bits(); cmd.extra = parts.length() > 2 ? parts.mid(3).join(" ") : ""; - cmd.tdrift = ui->actionModeAutoSync->isChecked() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); + cmd.tdrift = m_wideGraph->shouldAutoSync() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); cmd.submode = decodedtext.submode(); // if the command is a buffered command and its not the last frame OR we have from or to in a separate message (compound call) @@ -6457,7 +6450,7 @@ void MainWindow::guiUpdate() auto drift = DriftingDateTime::drift(); QDateTime t = DriftingDateTime::currentDateTimeUtc(); QStringList parts; - parts << (t.time().toString() + (!drift ? " " : QString(" (%1%2ms)").arg(drift > 0 ? "+" : "").arg(drift))); + parts << (t.time().toString() + (!drift ? " " : QString(" (%1%2ms%3)").arg(drift > 0 ? "+" : "").arg(drift).arg(m_wideGraph->shouldAutoSync() ? " auto" : ""))); parts << t.date().toString("yyyy MMM dd"); ui->labUTC->setText(parts.join("\n")); @@ -10668,7 +10661,6 @@ void MainWindow::updateModeButtonText(){ auto selectedCallsign = callsignSelected(); auto multi = ui->actionModeMultiDecoder->isChecked(); - auto autosync = ui->actionModeAutoSync->isChecked(); auto autoreply = ui->actionModeAutoreply->isChecked(); auto heartbeat = ui->actionModeJS8HB->isEnabled() && ui->actionModeJS8HB->isChecked(); auto ack = autoreply && ui->actionHeartbeatAcknowledgements->isChecked() && (!m_config.heartbeat_qso_pause() || selectedCallsign.isEmpty()); @@ -10678,10 +10670,6 @@ void MainWindow::updateModeButtonText(){ modeText += QString("+MULTI"); } - if(autosync){ - modeText += QString("+SYNC"); - } - if(autoreply){ if(m_config.autoreply_confirmation()){ modeText += QString("+AUTO+CONF"); diff --git a/mainwindow.ui b/mainwindow.ui index 4a18e75..2107591 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -4749,7 +4749,6 @@ list. The list can be maintained in Settings (F2). - diff --git a/widegraph.cpp b/widegraph.cpp index bc03166..914453d 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -250,6 +250,19 @@ bool WideGraph::shouldDisplayDecodeAttempts(){ return ui->decodeAttemptCheckBox->isChecked(); } +bool WideGraph::shouldAutoSync(){ + return ui->autoDriftButton->isChecked(); +} + +void WideGraph::on_autoDriftButton_checked(bool checked){ + auto text = ui->autoDriftButton->text(); + if(checked){ + ui->autoDriftButton->setText(text.replace("Start", "Stop")); + } else { + ui->autoDriftButton->setText(text.replace("Stop", "Start")); + } +} + void WideGraph::drawDecodeLine(const QColor &color, int ia, int ib) { ui->widePlot->drawDecodeLine(color, ia, ib); diff --git a/widegraph.h b/widegraph.h index b73540a..26d873f 100644 --- a/widegraph.h +++ b/widegraph.h @@ -69,6 +69,7 @@ public: void setRedFile(QString fRed); void setTurbo(bool turbo); bool shouldDisplayDecodeAttempts(); + bool shouldAutoSync(); QVector const& colors(); signals: @@ -122,6 +123,7 @@ private slots: void on_filterCheckBox_toggled(bool b); void on_filterOpacitySpinBox_valueChanged(int n); + void on_autoDriftButton_checked(bool checked); void on_driftSpinBox_valueChanged(int n); void on_driftSyncButton_clicked(); void on_driftSyncEndButton_clicked(); diff --git a/widegraph.ui b/widegraph.ui index 989fa4b..53d91f3 100644 --- a/widegraph.ui +++ b/widegraph.ui @@ -427,8 +427,8 @@ 0 - -371 - 267 + 0 + 240 742 @@ -976,7 +976,7 @@ 0 0 281 - 198 + 234 @@ -1020,6 +1020,22 @@ + + + + + 0 + 30 + + + + Start Automatic Drift Sync + + + true + + + From cfca8144763eaba2cb9b4908c6244830b6547213 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Fri, 15 May 2020 21:31:50 -0400 Subject: [PATCH 29/32] Allow slow mode to auto sync as well --- mainwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index e4104b4..44c021d 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -5281,7 +5281,7 @@ void MainWindow::processDecodedLine(QByteArray t){ return; } - if(m != Varicode::JS8CallNormal){ + if(m != Varicode::JS8CallNormal && m != Varicode::JS8CallSlow){ return; } // if we're here at this point, we _should_ be operating a decode every second From 64d9a303b3e8c30ef5d9eb8d14718d624e785d23 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Fri, 15 May 2020 21:36:16 -0400 Subject: [PATCH 30/32] Fixed display bug of sync button --- widegraph.cpp | 2 +- widegraph.h | 2 +- widegraph.ui | 20 +++++++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/widegraph.cpp b/widegraph.cpp index 914453d..8ca7c31 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -254,7 +254,7 @@ bool WideGraph::shouldAutoSync(){ return ui->autoDriftButton->isChecked(); } -void WideGraph::on_autoDriftButton_checked(bool checked){ +void WideGraph::on_autoDriftButton_toggled(bool checked){ auto text = ui->autoDriftButton->text(); if(checked){ ui->autoDriftButton->setText(text.replace("Start", "Stop")); diff --git a/widegraph.h b/widegraph.h index 26d873f..38a84f7 100644 --- a/widegraph.h +++ b/widegraph.h @@ -123,7 +123,7 @@ private slots: void on_filterCheckBox_toggled(bool b); void on_filterOpacitySpinBox_valueChanged(int n); - void on_autoDriftButton_checked(bool checked); + void on_autoDriftButton_toggled(bool checked); void on_driftSpinBox_valueChanged(int n); void on_driftSyncButton_clicked(); void on_driftSyncEndButton_clicked(); diff --git a/widegraph.ui b/widegraph.ui index 53d91f3..fb0b91d 100644 --- a/widegraph.ui +++ b/widegraph.ui @@ -428,7 +428,7 @@ 0 0 - 240 + 267 742 @@ -976,7 +976,7 @@ 0 0 281 - 234 + 252 @@ -1020,6 +1020,13 @@ + + + + Qt::Horizontal + + + @@ -1029,13 +1036,20 @@ - Start Automatic Drift Sync + Start Automatic Sync Time Drift true + + + + Qt::Horizontal + + + From 9c2b5b989a1d0c2fb139b0f2fd969c7b4fd5571c Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Fri, 15 May 2020 21:39:25 -0400 Subject: [PATCH 31/32] Added tooltip for sync button --- widegraph.ui | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/widegraph.ui b/widegraph.ui index fb0b91d..1ff363a 100644 --- a/widegraph.ui +++ b/widegraph.ui @@ -975,7 +975,7 @@ 0 0 - 281 + 273 252 @@ -1035,8 +1035,11 @@ 30 + + <html><head/><body><p>Automatically synchronize time drift every second to decodes of signals observed.</p><p>This process is CPU intensive and may cause abnormal decoder behavior if run for extended periods of time.</p></body></html> + - Start Automatic Sync Time Drift + Start Automatic Time Drift true @@ -1062,7 +1065,7 @@ <html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the start of a minute.</p></body></html> - Sync Time Drift to Now (Minute Start) + Set Time Drift to Now (Minute Start) @@ -1078,7 +1081,7 @@ <html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the start of a TX cycle in the current transmission speed.</p></body></html> - Sync Time Drift to Now (TX Start) + Set Time Drift to Now (TX Start) @@ -1094,7 +1097,7 @@ <html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the end of a TX cycle in the current transmission speed.</p></body></html> - Sync Time Drift to Now (TX End) + Set Time Drift to Now (TX End) From 7e70122cb5940344d8ee6bc2ce32a561fb86b486 Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Sat, 16 May 2020 11:57:27 -0400 Subject: [PATCH 32/32] Automatically stop time drift sync after decode by default --- mainwindow.cpp | 8 ++ widegraph.cpp | 54 ++++++++++++- widegraph.h | 4 + widegraph.ui | 200 ++++++++++++++++++++++++++----------------------- 4 files changed, 170 insertions(+), 96 deletions(-) diff --git a/mainwindow.cpp b/mainwindow.cpp index 44c021d..78d38a0 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -5281,9 +5281,13 @@ void MainWindow::processDecodedLine(QByteArray t){ return; } + // TODO: can we do this for FAST & TURBO + // if fast/turbo is observed and we're in a period post 15 seconds (i.e., second 18 turbo decode) + // then make the drift relative to the first cycle instead if(m != Varicode::JS8CallNormal && m != Varicode::JS8CallSlow){ return; } + // if we're here at this point, we _should_ be operating a decode every second // // so we need to figure out where: @@ -5374,6 +5378,9 @@ void MainWindow::processDecodedLine(QByteArray t){ static int driftN = 1; static int driftAvg = DriftingDateTime::drift(); + // let the widegraph know for timing control + m_wideGraph->notifyDriftedSignalsDecoded(driftQueue.count()); + while(!driftQueue.isEmpty()){ int newDrift = driftQueue.first(); driftQueue.removeFirst(); @@ -5384,6 +5391,7 @@ void MainWindow::processDecodedLine(QByteArray t){ setDrift(driftAvg); + //writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Automatic Drift: %1").arg(driftAvg)); } diff --git a/widegraph.cpp b/widegraph.cpp index 8ca7c31..66aa649 100644 --- a/widegraph.cpp +++ b/widegraph.cpp @@ -168,6 +168,7 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) : ui->cbControls->setChecked(!m_settings->value("HideControls", false).toBool()); ui->fpsSpinBox->setValue(m_settings->value ("WaterfallFPS", 4).toInt()); ui->decodeAttemptCheckBox->setChecked(m_settings->value("DisplayDecodeAttempts", false).toBool()); + ui->autoDriftAutoStopCheckBox->setChecked(m_settings->value ("StopAutoSyncOnDecode", true).toBool()); auto splitState = m_settings->value("SplitState").toByteArray(); if(!splitState.isEmpty()){ @@ -244,6 +245,7 @@ void WideGraph::saveSettings() //saveS m_settings->setValue ("SplitState", ui->splitter->saveState()); m_settings->setValue ("WaterfallFPS", ui->fpsSpinBox->value()); m_settings->setValue ("DisplayDecodeAttempts", ui->decodeAttemptCheckBox->isChecked()); + m_settings->setValue ("StopAutoSyncOnDecode", ui->autoDriftAutoStopCheckBox->isChecked()); } bool WideGraph::shouldDisplayDecodeAttempts(){ @@ -254,12 +256,58 @@ bool WideGraph::shouldAutoSync(){ return ui->autoDriftButton->isChecked(); } +void WideGraph::notifyDriftedSignalsDecoded(int /*signalsDecoded*/){ + if(ui->autoDriftAutoStopCheckBox->isChecked()){ + ui->autoDriftButton->setChecked(false); + } +} + void WideGraph::on_autoDriftButton_toggled(bool checked){ + static bool connected = false; + if(!connected){ + connect(&m_autoSyncTimer, &QTimer::timeout, this, [this](){ + // if auto drift isn't checked, don't worry about this... + if(!ui->autoDriftButton->isChecked()){ + return; + } + + // uncheck after timeout + if(m_autoSyncTimeLeft == 0){ + ui->autoDriftButton->setChecked(false); + return; + } + + // set new text and decrement timeleft + auto text = ui->autoDriftButton->text(); + auto newText = QString("%1 (%2)").arg(text.left(text.indexOf("(")).trimmed()).arg(m_autoSyncTimeLeft--); + ui->autoDriftButton->setText(newText); + }); + connected = true; + } + + // if in the future we want to auto sync timeout after a time period + bool autoSyncTimeout = false; + auto text = ui->autoDriftButton->text(); - if(checked){ - ui->autoDriftButton->setText(text.replace("Start", "Stop")); + + if(autoSyncTimeout){ + if(checked){ + m_autoSyncTimeLeft = 120; + m_autoSyncTimer.setInterval(1000); + m_autoSyncTimer.start(); + ui->autoDriftButton->setText(QString("%1 (%2)").arg(text.replace("Start", "Stop")).arg(m_autoSyncTimeLeft--)); + } else { + m_autoSyncTimeLeft = 0; + m_autoSyncTimer.stop(); + ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Stop", "Start")); + } + return; } else { - ui->autoDriftButton->setText(text.replace("Stop", "Start")); + if(checked){ + ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Start", "Stop")); + } else { + ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Stop", "Start")); + } } } diff --git a/widegraph.h b/widegraph.h index 38a84f7..f5facc7 100644 --- a/widegraph.h +++ b/widegraph.h @@ -90,6 +90,7 @@ public slots: int drift(); void setQSYEnabled(bool enabled); void setPaused(bool paused){ m_paused = paused; } + void notifyDriftedSignalsDecoded(int signalsDecoded); protected: void keyPressEvent (QKeyEvent *e) override; @@ -165,6 +166,9 @@ private: bool m_bRef; bool m_bHaveTransmitted; //Set true at end of a WSPR transmission + QTimer m_autoSyncTimer; + int m_autoSyncTimeLeft; + QTimer m_drawTimer; QMutex m_drawLock; diff --git a/widegraph.ui b/widegraph.ui index 1ff363a..ce7b81b 100644 --- a/widegraph.ui +++ b/widegraph.ui @@ -179,7 +179,7 @@ 0 0 - 270 + 323 372 @@ -428,7 +428,7 @@ 0 0 - 267 + 323 742 @@ -975,8 +975,8 @@ 0 0 - 273 - 252 + 337 + 351 @@ -1021,100 +1021,114 @@ - - - Qt::Horizontal + + + Automatic + + + + + + 0 + 30 + + + + <html><head/><body><p>Automatically synchronize time drift every second to decodes of NORMAL and SLOW signals observed.</p><p>This process is CPU intensive and may cause abnormal decoder behavior if run for extended periods of time. Default operation should be paired with stopping automatic time drift after signals have been decoded. </p></body></html> + + + Start Automatic Time Drift + + + true + + + + + + + Stop Automatic Drift After First Decode + + + true + + + + - - - - 0 - 30 - - - - <html><head/><body><p>Automatically synchronize time drift every second to decodes of signals observed.</p><p>This process is CPU intensive and may cause abnormal decoder behavior if run for extended periods of time.</p></body></html> - - - Start Automatic Time Drift - - - true - - - - - - - Qt::Horizontal - - - - - - - - 0 - 30 - - - - <html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the start of a minute.</p></body></html> - - - Set Time Drift to Now (Minute Start) - - - - - - - - 0 - 30 - - - - <html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the start of a TX cycle in the current transmission speed.</p></body></html> - - - Set Time Drift to Now (TX Start) - - - - - - - - 0 - 30 - - - - <html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the end of a TX cycle in the current transmission speed.</p></body></html> - - - Set Time Drift to Now (TX End) - - - - - - - - 0 - 30 - - - - Reset your time drift to zero. - - - Reset Time Drift + + + Manual + + + + + + 0 + 30 + + + + <html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the start of a minute.</p></body></html> + + + Set Time Drift to Now (Minute Start) + + + + + + + + 0 + 30 + + + + <html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the end of a TX cycle in the current transmission speed.</p></body></html> + + + Set Time Drift to Now (TX End) + + + + + + + + 0 + 30 + + + + <html><head/><body><p>Observe signals in the waterfall and click this to synchronize your time drift with the start of a TX cycle in the current transmission speed.</p></body></html> + + + Set Time Drift to Now (TX Start) + + + + + + + + 0 + 30 + + + + Reset your time drift to zero. + + + Reset Time Drift + + + +