From bd357ed4c4c6469f25ea0a7e7eb116a583bdba5d Mon Sep 17 00:00:00 2001 From: Jordan Sherer Date: Mon, 4 Nov 2019 14:38:00 -0500 Subject: [PATCH] Working multidecoder using relative positioning in the frame buffer --- commons.h | 15 ++++++-- lib/constants.f90 | 2 +- lib/decoder.f90 | 73 +++++++++++++++++++++++++---------- lib/jt9.f90 | 4 ++ lib/jt9a.f90 | 2 +- lib/jt9com.f90 | 12 ++++-- mainwindow.cpp | 96 +++++++++++++++++++++++++++++++++++++++-------- mainwindow.h | 2 +- 8 files changed, 161 insertions(+), 45 deletions(-) diff --git a/commons.h b/commons.h index 9ca69cf..c79f0d7 100644 --- a/commons.h +++ b/commons.h @@ -2,7 +2,7 @@ #define COMMONS_H #define NSMAX 6827 -#define NTMAX 30 +#define NTMAX 60 #define RX_SAMPLE_RATE 12000 @@ -13,6 +13,7 @@ #define JS8_DECODER_E2S 0 // decode every 2 seconds instead of at the half symbol stop #define JS8_NUM_SYMBOLS 79 +#define JS8_ENABLE_JS8A 1 #define JS8_ENABLE_JS8B 1 #define JS8_ENABLE_JS8C 1 #define JS8_ENABLE_JS8D 0 @@ -75,7 +76,6 @@ extern struct dec_data { float ss[184*NSMAX]; // symbol spectra float savg[NSMAX]; float sred[5760]; - short int d1[NTMAX*RX_SAMPLE_RATE]; // sample frame buffer for decoding (copied from d2 for the frame period) short int d2[NTMAX*RX_SAMPLE_RATE]; // sample frame buffer for sample collection struct { @@ -93,10 +93,17 @@ extern struct dec_data { int nfb; //High decode limit (Hz) int ntol; //+/- decoding range around fQSO (Hz) int kin; // number of frames written to d2 - int kpos; // starting position of decode - int kout; // number of frames for decode + int kposA; // starting position of decode for submode A + int kposB; // starting position of decode for submode B + int kposC; // starting position of decode for submode C + int kposD; // starting position of decode for submode D + int kszA; // number of frames for decode for submode A + int kszB; // number of frames for decode for submode B + int kszC; // number of frames for decode for submode C + int kszD; // number of frames for decode for submode D int nzhsym; // half symbol stop index int nsubmode; + int nsubmodes; bool nagain; int ndepth; bool lft8apon; diff --git a/lib/constants.f90 b/lib/constants.f90 index 6f7b027..1690295 100644 --- a/lib/constants.f90 +++ b/lib/constants.f90 @@ -1,4 +1,4 @@ - integer, parameter :: NTMAX=30 + integer, parameter :: NTMAX=60 integer, parameter :: NMAX=NTMAX*12000 !Total sample intervals (one minute) integer, parameter :: NDMAX=NTMAX*1500 !Sample intervals at 1500 Hz rate integer, parameter :: NSMAX=6827 !Max length of saved spectra diff --git a/lib/decoder.f90 b/lib/decoder.f90 index 8e8a039..ac57b3d 100644 --- a/lib/decoder.f90 +++ b/lib/decoder.f90 @@ -1,4 +1,4 @@ -subroutine multimode_decoder(ss,id1,params,nfsample) +subroutine multimode_decoder(ss,id2,params,nfsample) !$ use omp_lib use prog_args @@ -28,8 +28,9 @@ subroutine multimode_decoder(ss,id1,params,nfsample) end type counting_js8d_decoder real ss(184,NSMAX) - logical baddata,newdat65,newdat9,single_decode,bVHF,bad0,newdat - integer*2 id1(NTMAX*12000) + logical baddata,newdat65,newdat9,single_decode,bVHF,bad0,newdat,trydecode + integer*2 id0(NTMAX*12000) + integer*2 id2(NTMAX*12000) type(params_block) :: params character(len=20) :: datetime character(len=12) :: mycall, hiscall @@ -78,60 +79,92 @@ subroutine multimode_decoder(ss,id1,params,nfsample) endif endif - if(params%nmode.eq.8 .and. params%nsubmode.eq.4) then + trydecode=.false. + + if(params%nmode.eq.8 .and. (params%nsubmode.eq.4 .or. iand(params%nsubmodes, 8).eq.8)) then ! We're in JS8 mode D call timer('decjs8d ',0) newdat=params%newdat - call my_js8d%decode(js8d_decoded,id1,params%nQSOProgress,params%nfqso, & + trydecode=.true. + + ! copy the relevant frames for decoding + pos = params%kposD + sz = params%kszD + id0=0 + id0(1:sz+1)=id2(pos+1:pos+sz+1) + + call my_js8d%decode(js8d_decoded,id0,params%nQSOProgress,params%nfqso, & 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) call timer('decjs8d ',1) - go to 800 endif - if(params%nmode.eq.8 .and. params%nsubmode.eq.2) then + if(params%nmode.eq.8 .and. (params%nsubmode.eq.2 .or. iand(params%nsubmodes, 4).eq.4)) then ! We're in JS8 mode C call timer('decjs8c ',0) newdat=params%newdat - call my_js8c%decode(js8c_decoded,id1,params%nQSOProgress,params%nfqso, & + trydecode=.true. + + ! copy the relevant frames for decoding + pos = params%kposC + sz = params%kszC + id0=0 + id0(1:sz+1)=id2(pos+1:pos+sz+1) + + call my_js8c%decode(js8c_decoded,id0,params%nQSOProgress,params%nfqso, & 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) call timer('decjs8c ',1) - go to 800 endif - if(params%nmode.eq.8 .and. params%nsubmode.eq.1) then + if(params%nmode.eq.8 .and. (params%nsubmode.eq.1 .or. iand(params%nsubmodes, 2).eq.2)) then ! We're in JS8 mode B call timer('decjs8b ',0) newdat=params%newdat - call my_js8b%decode(js8b_decoded,id1,params%nQSOProgress,params%nfqso, & + trydecode=.true. + + ! copy the relevant frames for decoding + pos = params%kposB + sz = params%kszB + id0=0 + id0(1:sz+1)=id2(pos+1:pos+sz+1) + + call my_js8b%decode(js8b_decoded,id0,params%nQSOProgress,params%nfqso, & 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) call timer('decjs8b ',1) - go to 800 endif - if(params%nmode.eq.8 .and. params%nsubmode.eq.0) then + if(params%nmode.eq.8 .and. (params%nsubmode.eq.0 .or. iand(params%nsubmodes, 1).eq.1)) then ! We're in JS8 mode A call timer('decjs8a ',0) newdat=params%newdat - call my_js8a%decode(js8a_decoded,id1,params%nQSOProgress,params%nfqso, & + trydecode=.true. + + ! copy the relevant frames for decoding + pos = params%kposA + sz = params%kszA + id0=0 + id0(1:sz+1)=id2(pos+1:pos+sz+1) + + call my_js8a%decode(js8a_decoded,id0,params%nQSOProgress,params%nfqso, & 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) call timer('decjs8a ',1) - go to 800 endif - rms=sqrt(dot_product(float(id1(300000:310000)), & - float(id1(300000:310000)))/10000.0) + if(trydecode) go to 800 + + rms=sqrt(dot_product(float(id2(300000:310000)), & + float(id2(300000:310000)))/10000.0) if(rms.lt.2.0) go to 800 ! Zap data at start that might come from T/R switching transient? @@ -142,7 +175,7 @@ subroutine multimode_decoder(ss,id1,params,nfsample) sq=0. do n=1,nadd k=k+1 - sq=sq + float(id1(k))**2 + sq=sq + float(id2(k))**2 enddo rms=sqrt(sq/nadd) if(rms.gt.10000.0) then @@ -153,11 +186,11 @@ subroutine multimode_decoder(ss,id1,params,nfsample) enddo if(bad0) then nz=min(NTMAX*12000,kbad+100) -! id1(1:nz)=0 ! temporarily disabled as it can breaak the JT9 decoder, maybe others +! id2(1:nz)=0 ! temporarily disabled as it can breaak the JT9 decoder, maybe others endif npts65=52*12000 - if(baddata(id1,npts65)) then + if(baddata(id2,npts65)) then nsynced=0 ndecoded=0 go to 800 diff --git a/lib/jt9.f90 b/lib/jt9.f90 index 2131440..577fc22 100644 --- a/lib/jt9.f90 +++ b/lib/jt9.f90 @@ -297,6 +297,10 @@ program jt9 datetime="2013-Apr-16 15:13" !### Temp shared_data%params%datetime=transfer(datetime,shared_data%params%datetime) if(mode.eq.9 .and. fsplit.ne.2700) shared_data%params%nfa=fsplit + shared_data%params%kszA=NMAX-1 + shared_data%params%kszB=NMAX-1 + shared_data%params%kszC=NMAX-1 + shared_data%params%kszD=NMAX-1 call multimode_decoder(shared_data%ss,shared_data%id2,shared_data%params,nfsample) enddo diff --git a/lib/jt9a.f90 b/lib/jt9a.f90 index ad45bc3..9729e08 100644 --- a/lib/jt9a.f90 +++ b/lib/jt9a.f90 @@ -61,7 +61,7 @@ subroutine jt9a() local_params=shared_data%params !save a copy because wsjtx carries on accessing call flush(6) call timer('decoder ',0) - call multimode_decoder(shared_data%ss,shared_data%id1,local_params,12000) + call multimode_decoder(shared_data%ss,shared_data%id2,local_params,12000) call timer('decoder ',1) 100 inquire(file=trim(temp_dir)//'/.lock',exist=fileExists) diff --git a/lib/jt9com.f90 b/lib/jt9com.f90 index d22ac88..e5d9e6a 100644 --- a/lib/jt9com.f90 +++ b/lib/jt9com.f90 @@ -19,10 +19,17 @@ integer(c_int) :: nfb integer(c_int) :: ntol integer(c_int) :: kin - integer(c_int) :: kpos - integer(c_int) :: kout + integer(c_int) :: kposA + integer(c_int) :: kposB + integer(c_int) :: kposC + integer(c_int) :: kposD + integer(c_int) :: kszA + integer(c_int) :: kszB + integer(c_int) :: kszC + integer(c_int) :: kszD integer(c_int) :: nzhsym integer(c_int) :: nsubmode + integer(c_int) :: nsubmodes logical(c_bool) :: nagain integer(c_int) :: ndepth logical(c_bool) :: lft8apon @@ -54,7 +61,6 @@ real(c_float) :: ss(184,NSMAX) real(c_float) :: savg(NSMAX) real(c_float) :: sred(5760) - integer(c_short) :: id1(NMAX) integer(c_short) :: id2(NMAX) type(params_block) :: params end type dec_data diff --git a/mainwindow.cpp b/mainwindow.cpp index 26e1d7d..5cab40a 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1015,6 +1015,9 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple, if(pProcessed) *pProcessed = true; }); ui->modeButton->installEventFilter(mbmp); + if(!JS8_ENABLE_JS8A){ + ui->actionModeJS8Normal->setVisible(false); + } if(!JS8_ENABLE_JS8B){ ui->actionModeJS8Fast->setVisible(false); } @@ -2480,7 +2483,7 @@ int MainWindow::computeStop(int submode, int period){ case Varicode::JS8CallNormal: symbolSamples = JS8A_SYMBOL_SAMPLES; /* threshold = 1.00 */ ; break; case Varicode::JS8CallFast: symbolSamples = JS8B_SYMBOL_SAMPLES; /* threshold = 1.08 */ ; break; case Varicode::JS8CallTurbo: symbolSamples = JS8C_SYMBOL_SAMPLES; /* threshold = 0.50 */ ; break; - case Varicode::JS8CallUltra: symbolSamples = JS8D_SYMBOL_SAMPLES; /* threshold = 0.00 */ ; break; + case Varicode::JS8CallUltra: symbolSamples = JS8D_SYMBOL_SAMPLES; threshold = 0.00; break; } stop = qFloor(float(symbolSamples*JS8_NUM_SYMBOLS + threshold*RX_SAMPLE_RATE)/(float)m_nsps*2.0); #endif @@ -2580,6 +2583,14 @@ void MainWindow::dataSink(qint64 frames) dec_data.params.nagain=0; dec_data.params.nzhsym=m_ihsym; + // only decode once per second + static int lastn = -1; + int n = m_detector->secondInPeriod(); + if(n == lastn){ + return; + } + lastn = n; + decode(); } @@ -3845,7 +3856,7 @@ void MainWindow::decode(){ qint32 submode = m_nSubMode; qint32 period = m_TRperiod; - bool ready = decodeReady(submode, period); + bool ready = decodeReady(submode, period, &submode, &period); if(!ready){ return; } @@ -3854,28 +3865,80 @@ void MainWindow::decode(){ decodePrepareSaveAudio(submode, period); } -bool MainWindow::decodeReady(int submode, int period){ +bool MainWindow::decodeReady(int submode, int period, int *pSubmode, int *pPeriod){ if(period == 0){ return false; } #if JS8_RING_BUFFER - qint32 cycle = computeCurrentCycle(period); - qint32 cycleSampleStart = computeCycleStartForDecode(cycle, period); - qint32 framesNeeded = computeFramesNeededForDecode(submode, period); + int k = dec_data.params.kin; - static int lastCycle = -1; - if(cycle < lastCycle){ - lastCycle = -1; + qint32 cycleSampleStartA = computeCycleStartForDecode(computeCurrentCycle(JS8A_TX_SECONDS), JS8A_TX_SECONDS); + qint32 framesNeededA = computeFramesNeededForDecode(Varicode::JS8CallNormal, JS8A_TX_SECONDS); + bool couldDecodeA = k >= cycleSampleStartA + framesNeededA; + + qint32 cycleSampleStartB = computeCycleStartForDecode(computeCurrentCycle(JS8B_TX_SECONDS), JS8B_TX_SECONDS); + qint32 framesNeededB = computeFramesNeededForDecode(Varicode::JS8CallFast, JS8B_TX_SECONDS); + bool couldDecodeB = k >= cycleSampleStartB + framesNeededB; + + qint32 cycleSampleStartC = computeCycleStartForDecode(computeCurrentCycle(JS8C_TX_SECONDS), JS8C_TX_SECONDS); + qint32 framesNeededC = computeFramesNeededForDecode(Varicode::JS8CallTurbo, JS8C_TX_SECONDS); + bool couldDecodeC = k >= cycleSampleStartC + framesNeededC; + +#if JS8_ENABLE_JS8D + qint32 cycleSampleStartD = computeCycleStartForDecode(computeCurrentCycle(JS8D_TX_SECONDS), JS8D_TX_SECONDS); + qint32 framesNeededD = computeFramesNeededForDecode(Varicode::JS8CallUltra, JS8D_TX_SECONDS); + bool couldDecodeD = k >= cycleSampleStartD + framesNeededD; +#else + qint32 cycleSampleStartD = 0; + qint32 framesNeededD = 0; + bool couldDecodeD = false; +#endif + + // set the params for starting positions and sizes for decode + dec_data.params.kposA = cycleSampleStartA; + dec_data.params.kposB = cycleSampleStartB; + dec_data.params.kposC = cycleSampleStartC; + dec_data.params.kposD = cycleSampleStartD; + dec_data.params.kszA = qMax(framesNeededA, k-cycleSampleStartA); + dec_data.params.kszB = qMax(framesNeededB, k-cycleSampleStartB); + dec_data.params.kszC = qMax(framesNeededC, k-cycleSampleStartC); + dec_data.params.kszD = qMax(framesNeededD, k-cycleSampleStartD); + dec_data.params.nsubmodes = 0; + + int decodes = int(couldDecodeA) + int(couldDecodeB) + int(couldDecodeC) + int(couldDecodeD); + + if(couldDecodeD){ + qDebug() << "could decode D from" << cycleSampleStartD << "to" << cycleSampleStartD + framesNeededD; + submode = Varicode::JS8CallUltra; + period = JS8D_TX_SECONDS; + dec_data.params.nsubmodes |= (Varicode::JS8CallUltra << 1); + } + if(couldDecodeC){ + qDebug() << "could decode C from" << cycleSampleStartC << "to" << cycleSampleStartC + framesNeededC; + submode = Varicode::JS8CallTurbo; + period = JS8C_TX_SECONDS; + dec_data.params.nsubmodes |= (Varicode::JS8CallTurbo << 1); + } + if(couldDecodeB){ + qDebug() << "could decode B from" << cycleSampleStartB << "to" << cycleSampleStartB + framesNeededB; + submode = Varicode::JS8CallFast; + period = JS8B_TX_SECONDS; + dec_data.params.nsubmodes |= (Varicode::JS8CallFast << 1); + } + if(couldDecodeA){ + qDebug() << "could decode A from" << cycleSampleStartA << "to" << cycleSampleStartA + framesNeededA; + submode = Varicode::JS8CallNormal; + period = JS8A_TX_SECONDS; + dec_data.params.nsubmodes |= (Varicode::JS8CallNormal + 1); } - int k = dec_data.params.kin; - if(!m_diskData && (cycle == lastCycle || k < cycleSampleStart + framesNeeded)){ + if(pSubmode) *pSubmode=submode; + if(pPeriod) *pPeriod=period; + + if(!m_diskData && decodes == 0){ return false; } - - qDebug() << "cycle" << cycle << "start" << cycleSampleStart << "k" << k << "needed" << framesNeeded; - lastCycle = cycle; #endif #if JS8_DECODER_E2S @@ -4011,6 +4074,7 @@ bool MainWindow::decodeReady(int submode, int period){ strncpy(dec_data.params.hiscall,(hisCall + " ").toLatin1 ().constData (), 12); strncpy(dec_data.params.hisgrid,(hisGrid + " ").toLatin1 ().constData (), 6); +#if JS8_TWO_BUFFERS #if JS8_RING_BUFFER // clear out d1 memset(dec_data.d1, 0, sizeof(dec_data.d1)); @@ -4047,6 +4111,7 @@ bool MainWindow::decodeReady(int submode, int period){ qDebug() << "try decode from" << 0 << "to" << dec_data.params.kin; memset(dec_data.d1, 0, sizeof(dec_data.d1)); memcpy(dec_data.d1, dec_data.d2, sizeof(dec_data.d2)); +#endif #endif return true; @@ -7066,7 +7131,8 @@ void MainWindow::on_actionJS8_triggered() m_wideGraph->setPeriod(m_TRperiod, m_nsps); m_modulator->setTRPeriod(m_TRperiod); // TODO - not thread safe #if JS8_RING_BUFFER - m_detector->setTRPeriod(NTMAX); // TODO - not thread safe + Q_ASSERT(NTMAX == 60); + m_detector->setTRPeriod(NTMAX / 2); // TODO - not thread safe #else m_detector->setTRPeriod(m_TRperiod); // TODO - not thread safe #endif diff --git a/mainwindow.h b/mainwindow.h index a8f8ab6..49f7e25 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -230,7 +230,7 @@ private slots: void on_actionCopyright_Notice_triggered(); void on_DecodeButton_clicked (bool); void decode(); - bool decodeReady(int submode, int period); + bool decodeReady(int submode, int period, int *pSubmode, int *pPeriod); void decodeStart(int submode, int period); void decodePrepareSaveAudio(int submode, int period); void decodeBusy(bool b);