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 = {
|
static QMap<qint32, QList<qint32>> submodes = {
|
||||||
{Varicode::JS8CallSlow, {0}},
|
{Varicode::JS8CallSlow, {0}},
|
||||||
{Varicode::JS8CallNormal, {0}},
|
{Varicode::JS8CallNormal, {0}},
|
||||||
{Varicode::JS8CallFast, {0, 5}},
|
{Varicode::JS8CallFast, {0, 5}}, // NORMAL: 0, 10, 20 --- ALT: 15, 25
|
||||||
{Varicode::JS8CallTurbo, {0, 3}},
|
{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;
|
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?
|
// do we have a better way to check this?
|
||||||
bool multi = ui->actionModeMultiDecoder->isChecked();
|
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?
|
// do we need to process alternate positions?
|
||||||
bool skipAlt = false;
|
bool skipAlt = true;
|
||||||
|
|
||||||
foreach(auto submode, submodes.keys()){
|
foreach(auto submode, submodes.keys()){
|
||||||
// skip if multi is disabled and this mode is not the current submode
|
// do we have a better way to check this?
|
||||||
if(!multi && submode != m_nSubMode){
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check all alternate decode positions
|
// check all alternate decode positions
|
||||||
foreach(auto alt, submodes.value(submode)){
|
foreach(auto alt, submodes.value(submode)){
|
||||||
// skip alts if we are decoding every second
|
// skip alt decode positions if needed
|
||||||
if(everySecond && alt != 0){
|
if(skipAlt && alt != 0){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip alt decode positions if needed
|
// skip alts if we are decoding every second
|
||||||
if(skipAlt && alt != 0){
|
if(everySecond && alt != 0){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4545,6 +4548,9 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){
|
|||||||
qint32 cycleFrames = computeFramesPerCycleForDecode(submode);
|
qint32 cycleFrames = computeFramesPerCycleForDecode(submode);
|
||||||
qint32 cycleFramesNeeded = computeFramesPerSymbolForDecode(submode)*JS8_NUM_SYMBOLS; //computeFramesNeededForDecode(submode) - oneSecondSamples;
|
qint32 cycleFramesNeeded = computeFramesPerSymbolForDecode(submode)*JS8_NUM_SYMBOLS; //computeFramesNeededForDecode(submode) - oneSecondSamples;
|
||||||
qint32 cycleFramesReady = k - (cycle * cycleFrames);
|
qint32 cycleFramesReady = k - (cycle * cycleFrames);
|
||||||
|
if(cycleFramesReady < 0){
|
||||||
|
cycleFramesReady = k + (maxSamples - (cycle * cycleFrames));
|
||||||
|
}
|
||||||
|
|
||||||
if(!m_lastDecodeStartMap.contains(submode)){
|
if(!m_lastDecodeStartMap.contains(submode)){
|
||||||
m_lastDecodeStartMap[submode] = cycle * cycleFrames;
|
m_lastDecodeStartMap[submode] = cycle * cycleFrames;
|
||||||
@ -4556,7 +4562,7 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){
|
|||||||
incrementedBy = maxSamples - lastDecodeStart + k;
|
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){
|
if(everySecond && incrementedBy >= oneSecondSamples){
|
||||||
DecodeParams d;
|
DecodeParams d;
|
||||||
@ -4573,8 +4579,9 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){
|
|||||||
m_lastDecodeStartMap[submode] = k;
|
m_lastDecodeStartMap[submode] = k;
|
||||||
}
|
}
|
||||||
else if(
|
else if(
|
||||||
(incrementedBy >= 2*oneSecondSamples && cycleFramesReady >= cycleFramesNeeded ) ||
|
(incrementedBy >= 1.5*oneSecondSamples && cycleFramesReady >= cycleFramesNeeded) || // within every 3/2 seconds for normal positions
|
||||||
(incrementedBy >= oneSecondSamples && cycleFramesReady < 1.25*oneSecondSamples)
|
(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;
|
DecodeParams d;
|
||||||
d.submode = submode;
|
d.submode = submode;
|
||||||
@ -4592,203 +4599,6 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){
|
|||||||
return decodes > 0;
|
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
|
* @brief MainWindow::decodeProcessQueue
|
||||||
* process the decode queue by merging available decode ranges
|
* process the decode queue by merging available decode ranges
|
||||||
@ -4890,7 +4700,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){
|
|||||||
|
|
||||||
int period = computePeriodForSubmode(submode);
|
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.npts8=(m_ihsym*m_nsps)/16;
|
||||||
dec_data.params.newdat=1;
|
dec_data.params.newdat=1;
|
||||||
dec_data.params.nagain=0;
|
dec_data.params.nagain=0;
|
||||||
@ -5267,8 +5077,8 @@ void MainWindow::processDecodedLine(QByteArray t){
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only continue if we should either display decode attempts or if we should try to auto sync
|
// only continue if we should either display decode attempts
|
||||||
if(!m_wideGraph->shouldDisplayDecodeAttempts() && !m_wideGraph->shouldAutoSync()){
|
if(!m_wideGraph->shouldDisplayDecodeAttempts()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5303,18 +5113,123 @@ void MainWindow::processDecodedLine(QByteArray t){
|
|||||||
// draw decodes
|
// draw decodes
|
||||||
m_wideGraph->drawDecodeLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m));
|
m_wideGraph->drawDecodeLine(QColor(Qt::red), f, f + computeBandwidthForSubmode(m));
|
||||||
|
|
||||||
// compute time drift if needed
|
if(JS8_DEBUG_DECODE) qDebug() << "--> busy?" << m_decoderBusy << "lock exists?" << ( QFile{m_config.temp_dir ().absoluteFilePath (".lock")}.exists());
|
||||||
if(!m_wideGraph->shouldAutoSync()){
|
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: can we do this for FAST & TURBO
|
// check to see if the frequency is near our previous frame
|
||||||
// if fast/turbo is observed and we're in a period post 15 seconds (i.e., second 18 turbo decode)
|
auto cachedFreq = cached.freq;
|
||||||
// then make the drift relative to the first cycle instead
|
if(qAbs(cachedFreq - frameOffset) <= rxThreshold(decodedtext.submode())){
|
||||||
if(m != Varicode::JS8CallNormal && m != Varicode::JS8CallSlow){
|
qDebug() << "duplicate frame from" << cachedFreq << "and" << frameOffset << "using key" << frameDedupeKey;
|
||||||
return;
|
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
|
// if we're here at this point, we _should_ be operating a decode every second
|
||||||
//
|
//
|
||||||
// so we need to figure out where:
|
// 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));
|
//writeNoticeTextToUI(now, QString("--> which is rounded to a total drift of %1 milliseconds for this period").arg(newDrift));
|
||||||
|
|
||||||
driftQueue.append(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!
|
// if the frame is valid, cache it!
|
||||||
@ -5540,8 +5346,8 @@ void MainWindow::processDecodedLine(QByteArray t){
|
|||||||
d.utcTimestamp = DriftingDateTime::currentDateTimeUtc();
|
d.utcTimestamp = DriftingDateTime::currentDateTimeUtc();
|
||||||
d.snr = decodedtext.snr();
|
d.snr = decodedtext.snr();
|
||||||
d.isBuffered = false;
|
d.isBuffered = false;
|
||||||
d.tdrift = m_wideGraph->shouldAutoSync() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt();
|
|
||||||
d.submode = decodedtext.submode();
|
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...
|
// if we have any "first" frame, and a buffer is already established, clear it...
|
||||||
int prevBufferOffset = -1;
|
int prevBufferOffset = -1;
|
||||||
@ -5578,8 +5384,8 @@ void MainWindow::processDecodedLine(QByteArray t){
|
|||||||
cd.offset = decodedtext.frequencyOffset();
|
cd.offset = decodedtext.frequencyOffset();
|
||||||
cd.utcTimestamp = DriftingDateTime::currentDateTimeUtc();
|
cd.utcTimestamp = DriftingDateTime::currentDateTimeUtc();
|
||||||
cd.bits = decodedtext.bits();
|
cd.bits = decodedtext.bits();
|
||||||
cd.tdrift = m_wideGraph->shouldAutoSync() ? DriftingDateTime::drift()/1000.0 : decodedtext.dt();
|
|
||||||
cd.submode = decodedtext.submode();
|
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
|
// Only respond to HEARTBEATS...remember that CQ messages are "Alt" pings
|
||||||
if(decodedtext.isHeartbeat()){
|
if(decodedtext.isHeartbeat()){
|
||||||
@ -5657,8 +5463,8 @@ void MainWindow::processDecodedLine(QByteArray t){
|
|||||||
cmd.utcTimestamp = DriftingDateTime::currentDateTimeUtc();
|
cmd.utcTimestamp = DriftingDateTime::currentDateTimeUtc();
|
||||||
cmd.bits = decodedtext.bits();
|
cmd.bits = decodedtext.bits();
|
||||||
cmd.extra = parts.length() > 2 ? parts.mid(3).join(" ") : "";
|
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.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 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 == "<....>"){
|
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();
|
auto drift = DriftingDateTime::drift();
|
||||||
QDateTime t = DriftingDateTime::currentDateTimeUtc();
|
QDateTime t = DriftingDateTime::currentDateTimeUtc();
|
||||||
QStringList parts;
|
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");
|
parts << t.date().toString("yyyy MMM dd");
|
||||||
ui->labUTC->setText(parts.join("\n"));
|
ui->labUTC->setText(parts.join("\n"));
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "DriftingDateTime.h"
|
#include "DriftingDateTime.h"
|
||||||
#include "keyeater.h"
|
#include "keyeater.h"
|
||||||
|
#include "varicode.h"
|
||||||
|
|
||||||
#include "moc_widegraph.cpp"
|
#include "moc_widegraph.cpp"
|
||||||
|
|
||||||
@ -169,6 +170,7 @@ WideGraph::WideGraph(QSettings * settings, QWidget *parent) :
|
|||||||
ui->fpsSpinBox->setValue(m_settings->value ("WaterfallFPS", 4).toInt());
|
ui->fpsSpinBox->setValue(m_settings->value ("WaterfallFPS", 4).toInt());
|
||||||
ui->decodeAttemptCheckBox->setChecked(m_settings->value("DisplayDecodeAttempts", false).toBool());
|
ui->decodeAttemptCheckBox->setChecked(m_settings->value("DisplayDecodeAttempts", false).toBool());
|
||||||
ui->autoDriftAutoStopCheckBox->setChecked(m_settings->value ("StopAutoSyncOnDecode", true).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();
|
auto splitState = m_settings->value("SplitState").toByteArray();
|
||||||
if(!splitState.isEmpty()){
|
if(!splitState.isEmpty()){
|
||||||
@ -246,18 +248,33 @@ void WideGraph::saveSettings() //saveS
|
|||||||
m_settings->setValue ("WaterfallFPS", ui->fpsSpinBox->value());
|
m_settings->setValue ("WaterfallFPS", ui->fpsSpinBox->value());
|
||||||
m_settings->setValue ("DisplayDecodeAttempts", ui->decodeAttemptCheckBox->isChecked());
|
m_settings->setValue ("DisplayDecodeAttempts", ui->decodeAttemptCheckBox->isChecked());
|
||||||
m_settings->setValue ("StopAutoSyncOnDecode", ui->autoDriftAutoStopCheckBox->isChecked());
|
m_settings->setValue ("StopAutoSyncOnDecode", ui->autoDriftAutoStopCheckBox->isChecked());
|
||||||
|
m_settings->setValue ("StopAutoSyncAfter", ui->autoDriftStopSpinBox->value());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WideGraph::shouldDisplayDecodeAttempts(){
|
bool WideGraph::shouldDisplayDecodeAttempts(){
|
||||||
return ui->decodeAttemptCheckBox->isChecked();
|
return ui->decodeAttemptCheckBox->isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WideGraph::shouldAutoSync(){
|
bool WideGraph::isAutoSyncEnabled(){
|
||||||
return ui->autoDriftButton->isChecked();
|
return ui->autoDriftButton->isChecked() && m_autoSyncDecodesLeft > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WideGraph::notifyDriftedSignalsDecoded(int /*signalsDecoded*/){
|
bool WideGraph::shouldAutoSyncSubmode(int submode){
|
||||||
if(ui->autoDriftAutoStopCheckBox->isChecked()){
|
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);
|
ui->autoDriftButton->setChecked(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -304,9 +321,13 @@ void WideGraph::on_autoDriftButton_toggled(bool checked){
|
|||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if(checked){
|
if(checked){
|
||||||
|
m_autoSyncDecodesLeft = ui->autoDriftStopSpinBox->value();
|
||||||
ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Start", "Stop"));
|
ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Start", "Stop"));
|
||||||
|
ui->autoDriftStopSpinBox->setEnabled(false);
|
||||||
} else {
|
} else {
|
||||||
|
m_autoSyncDecodesLeft = 0;
|
||||||
ui->autoDriftButton->setText(text.left(text.indexOf("(")).trimmed().replace("Stop", "Start"));
|
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 setRedFile(QString fRed);
|
||||||
void setTurbo(bool turbo);
|
void setTurbo(bool turbo);
|
||||||
bool shouldDisplayDecodeAttempts();
|
bool shouldDisplayDecodeAttempts();
|
||||||
bool shouldAutoSync();
|
bool shouldAutoSyncSubmode(int submode);
|
||||||
|
bool isAutoSyncEnabled();
|
||||||
QVector<QColor> const& colors();
|
QVector<QColor> const& colors();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@ -170,6 +171,7 @@ private:
|
|||||||
|
|
||||||
QTimer m_autoSyncTimer;
|
QTimer m_autoSyncTimer;
|
||||||
int m_autoSyncTimeLeft;
|
int m_autoSyncTimeLeft;
|
||||||
|
int m_autoSyncDecodesLeft;
|
||||||
|
|
||||||
QTimer m_drawTimer;
|
QTimer m_drawTimer;
|
||||||
QMutex m_drawLock;
|
QMutex m_drawLock;
|
||||||
|
43
widegraph.ui
43
widegraph.ui
@ -179,7 +179,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>323</width>
|
<width>521</width>
|
||||||
<height>372</height>
|
<height>372</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@ -428,7 +428,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>323</width>
|
<width>521</width>
|
||||||
<height>742</height>
|
<height>742</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@ -974,9 +974,9 @@
|
|||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>-73</y>
|
||||||
<width>337</width>
|
<width>521</width>
|
||||||
<height>351</height>
|
<height>356</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
@ -1052,14 +1052,31 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="autoDriftAutoStopCheckBox">
|
<layout class="QFormLayout" name="formLayout_4">
|
||||||
<property name="text">
|
<item row="0" column="0">
|
||||||
<string>Stop Automatic Drift After First Decode</string>
|
<widget class="QCheckBox" name="autoDriftAutoStopCheckBox">
|
||||||
</property>
|
<property name="text">
|
||||||
<property name="checked">
|
<string>Stop Automatic Drift After:</string>
|
||||||
<bool>true</bool>
|
</property>
|
||||||
</property>
|
<property name="checked">
|
||||||
</widget>
|
<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>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
Loading…
Reference in New Issue
Block a user