Added time drift auto stop after a number of decoders. Edited decode positions
This commit is contained in:
		
							parent
							
								
									bdcd350aa4
								
							
						
					
					
						commit
						47ad3adcd4
					
				
							
								
								
									
										476
									
								
								mainwindow.cpp
									
									
									
									
									
								
							
							
						
						
									
										476
									
								
								mainwindow.cpp
									
									
									
									
									
								
							| @ -4505,8 +4505,11 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ | ||||
|     static QMap<qint32, QList<qint32>> submodes = { | ||||
|         {Varicode::JS8CallSlow,   {0}}, | ||||
|         {Varicode::JS8CallNormal, {0}}, | ||||
|         {Varicode::JS8CallFast,   {0, 5}}, | ||||
|         {Varicode::JS8CallTurbo,  {0, 3}}, | ||||
|         {Varicode::JS8CallFast,   {0, 5}}, // NORMAL: 0, 10, 20    --- ALT: 15, 25
 | ||||
|         {Varicode::JS8CallTurbo,  {0, 3}}, // NORMAL: 0, 6, 12, 18 --- ALT: 15, 21, 27
 | ||||
| #if JS8_ENABLE_JS8I | ||||
|         {Varicode::JS8CallUltra,  {0, 1}}, | ||||
| #endif | ||||
|     }; | ||||
| 
 | ||||
|     static qint32 maxSamples = NTMAX*RX_SAMPLE_RATE; | ||||
| @ -4517,27 +4520,27 @@ 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 = m_wideGraph->shouldAutoSync(); | ||||
| 
 | ||||
|     // do we need to process alternate positions?
 | ||||
|     bool skipAlt = false; | ||||
|     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){ | ||||
|         // do we have a better way to check this?
 | ||||
|         bool everySecond = m_wideGraph->shouldAutoSyncSubmode(submode); | ||||
| 
 | ||||
|         // skip if multi is disabled and this mode is not the current submode and we're not autosyncing this mode
 | ||||
|         if(!everySecond && !multi && submode != m_nSubMode){ | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         // check all alternate decode positions
 | ||||
|         foreach(auto alt, submodes.value(submode)){ | ||||
|             // skip alts if we are decoding every second
 | ||||
|             if(everySecond && alt != 0){ | ||||
|             // skip alt decode positions if needed
 | ||||
|             if(skipAlt && alt != 0){ | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             // skip alt decode positions if needed
 | ||||
|             if(skipAlt && alt != 0){ | ||||
|             // skip alts if we are decoding every second
 | ||||
|             if(everySecond && alt != 0){ | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
| @ -4545,6 +4548,9 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ | ||||
|             qint32 cycleFrames = computeFramesPerCycleForDecode(submode); | ||||
|             qint32 cycleFramesNeeded = computeFramesPerSymbolForDecode(submode)*JS8_NUM_SYMBOLS; //computeFramesNeededForDecode(submode) - oneSecondSamples;
 | ||||
|             qint32 cycleFramesReady = k - (cycle * cycleFrames); | ||||
|             if(cycleFramesReady < 0){ | ||||
|                 cycleFramesReady = k + (maxSamples - (cycle * cycleFrames)); | ||||
|             } | ||||
| 
 | ||||
|             if(!m_lastDecodeStartMap.contains(submode)){ | ||||
|                 m_lastDecodeStartMap[submode] = cycle * cycleFrames; | ||||
| @ -4556,7 +4562,7 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ | ||||
|                 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(alt > 0 || 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(everySecond && incrementedBy >= oneSecondSamples){ | ||||
|                 DecodeParams d; | ||||
| @ -4573,8 +4579,9 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ | ||||
|                 m_lastDecodeStartMap[submode] = k; | ||||
|             } | ||||
|             else if( | ||||
|                 (incrementedBy >= 2*oneSecondSamples && cycleFramesReady >= cycleFramesNeeded ) || | ||||
|                 (incrementedBy >= oneSecondSamples && cycleFramesReady < 1.25*oneSecondSamples) | ||||
|                 (incrementedBy >= 1.5*oneSecondSamples && cycleFramesReady >= cycleFramesNeeded)                        || // within every 3/2 seconds for normal positions
 | ||||
|                 (incrementedBy >= oneSecondSamples     && cycleFramesReady >= cycleFramesNeeded - 1.5*oneSecondSamples) || // within the last 3/2 seconds of a new cycle
 | ||||
|                 (incrementedBy >= oneSecondSamples     && cycleFramesReady < 1.5*oneSecondSamples)                         // within the first 3/2 seconds of a new cycle
 | ||||
|             ){ | ||||
|                 DecodeParams d; | ||||
|                 d.submode = submode; | ||||
| @ -4592,203 +4599,6 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ | ||||
|     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;
 | ||||
|     //    lastDecodeStartK = secondStartK;
 | ||||
|     //}
 | ||||
| 
 | ||||
|     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<int> 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; | ||||
| 
 | ||||
|     if(experiment){ | ||||
|         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){ | ||||
|             // 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;
 | ||||
|         } | ||||
| 
 | ||||
|         //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 | ||||
|         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 | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief MainWindow::decodeProcessQueue | ||||
|  *        process the decode queue by merging available decode ranges | ||||
| @ -4890,7 +4700,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){ | ||||
| 
 | ||||
|     int period = computePeriodForSubmode(submode); | ||||
| 
 | ||||
|     dec_data.params.syncStats = (m_wideGraph->shouldDisplayDecodeAttempts() || m_wideGraph->shouldAutoSync()); | ||||
|     dec_data.params.syncStats = (m_wideGraph->shouldDisplayDecodeAttempts() || m_wideGraph->isAutoSyncEnabled()); | ||||
|     dec_data.params.npts8=(m_ihsym*m_nsps)/16; | ||||
|     dec_data.params.newdat=1; | ||||
|     dec_data.params.nagain=0; | ||||
| @ -5267,8 +5077,8 @@ void MainWindow::processDecodedLine(QByteArray t){ | ||||
|           return; | ||||
|       } | ||||
| 
 | ||||
|       // only continue if we should either display decode attempts or if we should try to auto sync
 | ||||
|       if(!m_wideGraph->shouldDisplayDecodeAttempts() && !m_wideGraph->shouldAutoSync()){ | ||||
|       // only continue if we should either display decode attempts
 | ||||
|       if(!m_wideGraph->shouldDisplayDecodeAttempts()){ | ||||
|           return; | ||||
|       } | ||||
| 
 | ||||
| @ -5303,18 +5113,123 @@ void MainWindow::processDecodedLine(QByteArray t){ | ||||
|       // draw decodes
 | ||||
|       m_wideGraph->drawDecodeLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m)); | ||||
| 
 | ||||
|       // compute time drift if needed
 | ||||
|       if(!m_wideGraph->shouldAutoSync()){ | ||||
|       if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists()); | ||||
| 
 | ||||
|       return; | ||||
|   } | ||||
| 
 | ||||
|   if(t.indexOf("<DecodeStarted>") >= 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; | ||||
|   } | ||||
| 
 | ||||
|   if(t.indexOf("<DecodeDebug>") >= 0) { | ||||
|       return; | ||||
|   } | ||||
| 
 | ||||
|   if(t.indexOf("<DecodeFinished>") >= 0) { | ||||
|     int msec = m_decoderBusyStartTime.msecsTo(QDateTime::currentDateTimeUtc()); | ||||
|     if(JS8_DEBUG_DECODE) qDebug() << "decode duration" << msec << "ms"; | ||||
| 
 | ||||
|     // TODO: move this into a function
 | ||||
|     if(!driftQueue.isEmpty()){ | ||||
|         if(m_driftMsMMA_N == 0){ | ||||
|             m_driftMsMMA_N = 1; | ||||
|             m_driftMsMMA = DriftingDateTime::drift(); | ||||
|         } | ||||
| 
 | ||||
|         // let the widegraph know for timing control
 | ||||
|         m_wideGraph->notifyDriftedSignalsDecoded(driftQueue.count()); | ||||
| 
 | ||||
|         while(!driftQueue.isEmpty()){ | ||||
|             int newDrift = driftQueue.first(); | ||||
|             driftQueue.removeFirst(); | ||||
| 
 | ||||
|             m_driftMsMMA = ((m_driftMsMMA_N-1)*m_driftMsMMA + newDrift)/m_driftMsMMA_N; | ||||
|             if(m_driftMsMMA_N < 60) m_driftMsMMA_N++; // cap it to 60 observations
 | ||||
|         } | ||||
| 
 | ||||
|         setDrift(m_driftMsMMA); | ||||
| 
 | ||||
|         //writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Automatic Drift: %1").arg(driftAvg));
 | ||||
|     } | ||||
| 
 | ||||
|     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
 | ||||
|     decodeDone(); | ||||
|     m_startAnother=m_loopall; | ||||
|     if(m_bNoMoreFiles) { | ||||
|       MessageBox::information_message(this, tr("No more files to open.")); | ||||
|       m_bNoMoreFiles=false; | ||||
|     } | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   if(m_mode=="JT4" or m_mode=="JT65" or m_mode=="QRA64" or m_mode=="FT8") { | ||||
|     int n=t.indexOf("f"); | ||||
|     if(n<0) n=t.indexOf("d"); | ||||
|     if(n>0) { | ||||
|       QString tt=t.mid(n+1,1); | ||||
|       navg=tt.toInt(); | ||||
|       if(navg==0) { | ||||
|         char c = tt.data()->toLatin1(); | ||||
|         if(int(c)>=65 and int(c)<=90) navg=int(c)-54; | ||||
|       } | ||||
|       if(navg>1 or t.indexOf("f*")>0) bAvgMsg=true; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   auto rawText = QString::fromUtf8 (t.constData ()).remove (QRegularExpression {"\r|\n"}); | ||||
| 
 | ||||
|   DecodedText decodedtext {rawText, "FT8" == m_mode && | ||||
|         ui->cbVHFcontest->isChecked(), m_config.my_grid ()}; | ||||
| 
 | ||||
|   // TODO: move this into a function
 | ||||
|   // frames are valid if they pass our dupe check (haven't seen the same frame in the past 1/2 decode period)
 | ||||
|   auto frameOffset = decodedtext.frequencyOffset(); | ||||
|   auto frameDedupeKey = QString("%1:%2").arg(decodedtext.submode()).arg(decodedtext.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; | ||||
|       } | ||||
| 
 | ||||
|       // 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; | ||||
|       // 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
 | ||||
|   if(!bValidFrame) { | ||||
|       return; | ||||
|   } | ||||
| 
 | ||||
|   // TODO: move this into a function
 | ||||
|   // compute time drift for non-dupe messages
 | ||||
|   if(m_wideGraph->shouldAutoSyncSubmode(decodedtext.submode())){ | ||||
|       int m = decodedtext.submode(); | ||||
|       float xdt = decodedtext.dt(); | ||||
| 
 | ||||
|       // if we're here at this point, we _should_ be operating a decode every second
 | ||||
|       //
 | ||||
|       // so we need to figure out where:
 | ||||
| @ -5378,115 +5293,6 @@ void MainWindow::processDecodedLine(QByteArray t){ | ||||
|       //writeNoticeTextToUI(now, QString("--> which is rounded to a total drift of %1 milliseconds for this period").arg(newDrift));
 | ||||
| 
 | ||||
|       driftQueue.append(newDrift); | ||||
| 
 | ||||
|       if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists()); | ||||
| 
 | ||||
|       return; | ||||
|   } | ||||
| 
 | ||||
|   if(t.indexOf("<DecodeStarted>") >= 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; | ||||
|   } | ||||
| 
 | ||||
|   if(t.indexOf("<DecodeDebug>") >= 0) { | ||||
|       return; | ||||
|   } | ||||
| 
 | ||||
|   if(t.indexOf("<DecodeFinished>") >= 0) { | ||||
|     int msec = m_decoderBusyStartTime.msecsTo(QDateTime::currentDateTimeUtc()); | ||||
|     if(JS8_DEBUG_DECODE) qDebug() << "decode duration" << msec << "ms"; | ||||
| 
 | ||||
|     if(!driftQueue.isEmpty()){ | ||||
|         if(m_driftMsMMA_N == 0){ | ||||
|             m_driftMsMMA_N = 1; | ||||
|             m_driftMsMMA = DriftingDateTime::drift(); | ||||
|         } | ||||
| 
 | ||||
|         // let the widegraph know for timing control
 | ||||
|         m_wideGraph->notifyDriftedSignalsDecoded(driftQueue.count()); | ||||
| 
 | ||||
|         while(!driftQueue.isEmpty()){ | ||||
|             int newDrift = driftQueue.first(); | ||||
|             driftQueue.removeFirst(); | ||||
| 
 | ||||
|             m_driftMsMMA = ((m_driftMsMMA_N-1)*m_driftMsMMA + newDrift)/m_driftMsMMA_N; | ||||
|             if(m_driftMsMMA_N < 60) m_driftMsMMA_N++; // cap it to 60 observations
 | ||||
|         } | ||||
| 
 | ||||
|         setDrift(m_driftMsMMA); | ||||
| 
 | ||||
|         //writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Automatic Drift: %1").arg(driftAvg));
 | ||||
|     } | ||||
| 
 | ||||
|     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
 | ||||
|     decodeDone(); | ||||
|     m_startAnother=m_loopall; | ||||
|     if(m_bNoMoreFiles) { | ||||
|       MessageBox::information_message(this, tr("No more files to open.")); | ||||
|       m_bNoMoreFiles=false; | ||||
|     } | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   if(m_mode=="JT4" or m_mode=="JT65" or m_mode=="QRA64" or m_mode=="FT8") { | ||||
|     int n=t.indexOf("f"); | ||||
|     if(n<0) n=t.indexOf("d"); | ||||
|     if(n>0) { | ||||
|       QString tt=t.mid(n+1,1); | ||||
|       navg=tt.toInt(); | ||||
|       if(navg==0) { | ||||
|         char c = tt.data()->toLatin1(); | ||||
|         if(int(c)>=65 and int(c)<=90) navg=int(c)-54; | ||||
|       } | ||||
|       if(navg>1 or t.indexOf("f*")>0) bAvgMsg=true; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   auto rawText = QString::fromUtf8 (t.constData ()).remove (QRegularExpression {"\r|\n"}); | ||||
| 
 | ||||
|   DecodedText decodedtext {rawText, "FT8" == m_mode && | ||||
|         ui->cbVHFcontest->isChecked(), m_config.my_grid ()}; | ||||
| 
 | ||||
| 
 | ||||
|   // frames are also valid if they pass our dupe check (haven't seen the same frame in the past 1/2 decode period)
 | ||||
|   auto frameOffset = decodedtext.frequencyOffset(); | ||||
|   auto frameDedupeKey = QString("%1:%2").arg(decodedtext.submode()).arg(decodedtext.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; | ||||
|       } | ||||
| 
 | ||||
|       // 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
 | ||||
|   if(!bValidFrame) { | ||||
|       return; | ||||
|   } | ||||
| 
 | ||||
|   // if the frame is valid, cache it!
 | ||||
| @ -5540,8 +5346,8 @@ void MainWindow::processDecodedLine(QByteArray t){ | ||||
|     d.utcTimestamp = DriftingDateTime::currentDateTimeUtc(); | ||||
|     d.snr = decodedtext.snr(); | ||||
|     d.isBuffered = false; | ||||
|     d.tdrift = m_wideGraph->shouldAutoSync() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); | ||||
|     d.submode = decodedtext.submode(); | ||||
|     d.tdrift = m_wideGraph->shouldAutoSyncSubmode(d.submode) ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); | ||||
| 
 | ||||
|     // if we have any "first" frame, and a buffer is already established, clear it...
 | ||||
|     int prevBufferOffset = -1; | ||||
| @ -5578,8 +5384,8 @@ void MainWindow::processDecodedLine(QByteArray t){ | ||||
|     cd.offset = decodedtext.frequencyOffset(); | ||||
|     cd.utcTimestamp = DriftingDateTime::currentDateTimeUtc(); | ||||
|     cd.bits = decodedtext.bits(); | ||||
|     cd.tdrift = m_wideGraph->shouldAutoSync() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); | ||||
|     cd.submode = decodedtext.submode(); | ||||
|     cd.tdrift = m_wideGraph->shouldAutoSyncSubmode(d.submode) ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); | ||||
| 
 | ||||
|     // Only respond to HEARTBEATS...remember that CQ messages are "Alt" pings
 | ||||
|     if(decodedtext.isHeartbeat()){ | ||||
| @ -5657,8 +5463,8 @@ void MainWindow::processDecodedLine(QByteArray t){ | ||||
|       cmd.utcTimestamp = DriftingDateTime::currentDateTimeUtc(); | ||||
|       cmd.bits = decodedtext.bits(); | ||||
|       cmd.extra = parts.length() > 2 ? parts.mid(3).join(" ") : ""; | ||||
|       cmd.tdrift = m_wideGraph->shouldAutoSync() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); | ||||
|       cmd.submode = decodedtext.submode(); | ||||
|       cmd.tdrift = m_wideGraph->shouldAutoSyncSubmode(cmd.submode) ? DriftingDateTime::drift()/1000.0 : decodedtext.dt(); | ||||
| 
 | ||||
|       // 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)
 | ||||
|       if((Varicode::isCommandBuffered(cmd.cmd) && (cmd.bits & Varicode::JS8CallLast) != Varicode::JS8CallLast) || cmd.from == "<....>" || cmd.to == "<....>"){ | ||||
| @ -6486,7 +6292,7 @@ void MainWindow::guiUpdate() | ||||
|     auto drift = DriftingDateTime::drift(); | ||||
|     QDateTime t = DriftingDateTime::currentDateTimeUtc(); | ||||
|     QStringList parts; | ||||
|     parts << (t.time().toString() + (!drift ? " " : QString(" (%1%2ms%3)").arg(drift > 0 ? "+" : "").arg(drift).arg(m_wideGraph->shouldAutoSync() ? " auto" : ""))); | ||||
|     parts << (t.time().toString() + (!drift ? " " : QString(" (%1%2ms)").arg(drift > 0 ? "+" : "").arg(drift))); | ||||
|     parts << t.date().toString("yyyy MMM dd"); | ||||
|     ui->labUTC->setText(parts.join("\n")); | ||||
| 
 | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
| 
 | ||||
| #include "DriftingDateTime.h" | ||||
| #include "keyeater.h" | ||||
| #include "varicode.h" | ||||
| 
 | ||||
| #include "moc_widegraph.cpp" | ||||
| 
 | ||||
| @ -169,6 +170,7 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) : | ||||
|     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()); | ||||
|     ui->autoDriftStopSpinBox->setValue(m_settings->value ("StopAutoSyncAfter", 1).toInt()); | ||||
| 
 | ||||
|     auto splitState = m_settings->value("SplitState").toByteArray(); | ||||
|     if(!splitState.isEmpty()){ | ||||
| @ -246,18 +248,33 @@ void WideGraph::saveSettings()                                           //saveS | ||||
|   m_settings->setValue ("WaterfallFPS", ui->fpsSpinBox->value()); | ||||
|   m_settings->setValue ("DisplayDecodeAttempts", ui->decodeAttemptCheckBox->isChecked()); | ||||
|   m_settings->setValue ("StopAutoSyncOnDecode", ui->autoDriftAutoStopCheckBox->isChecked()); | ||||
|   m_settings->setValue ("StopAutoSyncAfter", ui->autoDriftStopSpinBox->value()); | ||||
| } | ||||
| 
 | ||||
| bool WideGraph::shouldDisplayDecodeAttempts(){ | ||||
|     return ui->decodeAttemptCheckBox->isChecked(); | ||||
| } | ||||
| 
 | ||||
| bool WideGraph::shouldAutoSync(){ | ||||
|     return ui->autoDriftButton->isChecked(); | ||||
| bool WideGraph::isAutoSyncEnabled(){ | ||||
|     return ui->autoDriftButton->isChecked() && m_autoSyncDecodesLeft > 0; | ||||
| } | ||||
| 
 | ||||
| void WideGraph::notifyDriftedSignalsDecoded(int /*signalsDecoded*/){ | ||||
|     if(ui->autoDriftAutoStopCheckBox->isChecked()){ | ||||
| bool WideGraph::shouldAutoSyncSubmode(int submode){ | ||||
|     return isAutoSyncEnabled() && ( | ||||
|            submode == Varicode::JS8CallSlow | ||||
|         || submode == Varicode::JS8CallNormal | ||||
|     //  || submode == Varicode::JS8CallFast
 | ||||
|     //  || submode == Varicode::JS8CallTurbo
 | ||||
|     //  || submode == Varicode::JS8CallUltra
 | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| void WideGraph::notifyDriftedSignalsDecoded(int signalsDecoded){ | ||||
|     //qDebug() << "decoded" << signalsDecoded << "with" << m_autoSyncDecodesLeft << "left";
 | ||||
| 
 | ||||
|     m_autoSyncDecodesLeft -= signalsDecoded; | ||||
| 
 | ||||
|     if(ui->autoDriftAutoStopCheckBox->isChecked() && m_autoSyncDecodesLeft <= 0){ | ||||
|         ui->autoDriftButton->setChecked(false); | ||||
|     } | ||||
| } | ||||
| @ -304,9 +321,13 @@ void WideGraph::on_autoDriftButton_toggled(bool checked){ | ||||
|         return; | ||||
|     } else { | ||||
|         if(checked){ | ||||
|             m_autoSyncDecodesLeft = ui->autoDriftStopSpinBox->value(); | ||||
|             ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Start", "Stop")); | ||||
|             ui->autoDriftStopSpinBox->setEnabled(false); | ||||
|         } else { | ||||
|             m_autoSyncDecodesLeft = 0; | ||||
|             ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Stop", "Start")); | ||||
|             ui->autoDriftStopSpinBox->setEnabled(true); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -69,7 +69,8 @@ public: | ||||
|   void   setRedFile(QString fRed); | ||||
|   void   setTurbo(bool turbo); | ||||
|   bool   shouldDisplayDecodeAttempts(); | ||||
|   bool   shouldAutoSync(); | ||||
|   bool   shouldAutoSyncSubmode(int submode); | ||||
|   bool   isAutoSyncEnabled(); | ||||
|   QVector<QColor> const& colors(); | ||||
| 
 | ||||
| signals: | ||||
| @ -170,6 +171,7 @@ private: | ||||
| 
 | ||||
|   QTimer m_autoSyncTimer; | ||||
|   int m_autoSyncTimeLeft; | ||||
|   int m_autoSyncDecodesLeft; | ||||
| 
 | ||||
|   QTimer m_drawTimer; | ||||
|   QMutex m_drawLock; | ||||
|  | ||||
							
								
								
									
										43
									
								
								widegraph.ui
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								widegraph.ui
									
									
									
									
									
								
							| @ -179,7 +179,7 @@ | ||||
|                <rect> | ||||
|                 <x>0</x> | ||||
|                 <y>0</y> | ||||
|                 <width>323</width> | ||||
|                 <width>521</width> | ||||
|                 <height>372</height> | ||||
|                </rect> | ||||
|               </property> | ||||
| @ -428,7 +428,7 @@ | ||||
|                <rect> | ||||
|                 <x>0</x> | ||||
|                 <y>0</y> | ||||
|                 <width>323</width> | ||||
|                 <width>521</width> | ||||
|                 <height>742</height> | ||||
|                </rect> | ||||
|               </property> | ||||
| @ -974,9 +974,9 @@ | ||||
|               <property name="geometry"> | ||||
|                <rect> | ||||
|                 <x>0</x> | ||||
|                 <y>0</y> | ||||
|                 <width>337</width> | ||||
|                 <height>351</height> | ||||
|                 <y>-73</y> | ||||
|                 <width>521</width> | ||||
|                 <height>356</height> | ||||
|                </rect> | ||||
|               </property> | ||||
|               <layout class="QVBoxLayout" name="verticalLayout"> | ||||
| @ -1052,14 +1052,31 @@ | ||||
|                    </widget> | ||||
|                   </item> | ||||
|                   <item> | ||||
|                    <widget class="QCheckBox" name="autoDriftAutoStopCheckBox"> | ||||
|                     <property name="text"> | ||||
|                      <string>Stop Automatic Drift After First Decode</string> | ||||
|                     </property> | ||||
|                     <property name="checked"> | ||||
|                      <bool>true</bool> | ||||
|                     </property> | ||||
|                    </widget> | ||||
|                    <layout class="QFormLayout" name="formLayout_4"> | ||||
|                     <item row="0" column="0"> | ||||
|                      <widget class="QCheckBox" name="autoDriftAutoStopCheckBox"> | ||||
|                       <property name="text"> | ||||
|                        <string>Stop Automatic Drift After:</string> | ||||
|                       </property> | ||||
|                       <property name="checked"> | ||||
|                        <bool>true</bool> | ||||
|                       </property> | ||||
|                      </widget> | ||||
|                     </item> | ||||
|                     <item row="0" column="1"> | ||||
|                      <widget class="QSpinBox" name="autoDriftStopSpinBox"> | ||||
|                       <property name="suffix"> | ||||
|                        <string> decode(s)</string> | ||||
|                       </property> | ||||
|                       <property name="minimum"> | ||||
|                        <number>1</number> | ||||
|                       </property> | ||||
|                       <property name="maximum"> | ||||
|                        <number>1024</number> | ||||
|                       </property> | ||||
|                      </widget> | ||||
|                     </item> | ||||
|                    </layout> | ||||
|                   </item> | ||||
|                  </layout> | ||||
|                 </widget> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Jordan Sherer
						Jordan Sherer