Added experimental auto-sync function for normal mode.

This commit is contained in:
Jordan Sherer 2020-05-13 21:15:21 -04:00
parent 672f0e4535
commit c44e75b20a
5 changed files with 241 additions and 24 deletions

View File

@ -810,6 +810,8 @@ MainWindow::MainWindow(QDir const& temp_directory, bool multiple,
connect(m_wideGraph.data(), &WideGraph::qsy, this, &MainWindow::qsy); connect(m_wideGraph.data(), &WideGraph::qsy, this, &MainWindow::qsy);
connect(m_wideGraph.data(), &WideGraph::drifted, this, &MainWindow::drifted);
decodeBusy(false); decodeBusy(false);
QString t1[28]={"1 uW","2 uW","5 uW","10 uW","20 uW","50 uW","100 uW","200 uW","500 uW", QString t1[28]={"1 uW","2 uW","5 uW","10 uW","20 uW","50 uW","100 uW","200 uW","500 uW",
"1 mW","2 mW","5 mW","10 mW","20 mW","50 mW","100 mW","200 mW","500 mW", "1 mW","2 mW","5 mW","10 mW","20 mW","50 mW","100 mW","200 mW","500 mW",
@ -2268,6 +2270,7 @@ void MainWindow::writeSettings()
m_settings->setValue("SubModeHB", ui->actionModeJS8HB->isChecked()); m_settings->setValue("SubModeHB", ui->actionModeJS8HB->isChecked());
m_settings->setValue("SubModeHBAck", ui->actionHeartbeatAcknowledgements->isChecked()); m_settings->setValue("SubModeHBAck", ui->actionHeartbeatAcknowledgements->isChecked());
m_settings->setValue("SubModeMultiDecode", ui->actionModeMultiDecoder->isChecked()); m_settings->setValue("SubModeMultiDecode", ui->actionModeMultiDecoder->isChecked());
m_settings->setValue("SubModeAutoSync", ui->actionModeAutoSync->isChecked());
m_settings->setValue("DTtol",m_DTtol); m_settings->setValue("DTtol",m_DTtol);
m_settings->setValue("Ftol", ui->sbFtol->value ()); m_settings->setValue("Ftol", ui->sbFtol->value ());
m_settings->setValue("MinSync",m_minSync); m_settings->setValue("MinSync",m_minSync);
@ -2424,6 +2427,7 @@ void MainWindow::readSettings()
ui->actionModeJS8HB->setChecked(m_settings->value("SubModeHB", false).toBool()); ui->actionModeJS8HB->setChecked(m_settings->value("SubModeHB", false).toBool());
ui->actionHeartbeatAcknowledgements->setChecked(m_settings->value("SubModeHBAck", false).toBool()); ui->actionHeartbeatAcknowledgements->setChecked(m_settings->value("SubModeHBAck", false).toBool());
ui->actionModeMultiDecoder->setChecked(m_settings->value("SubModeMultiDecode", true).toBool()); ui->actionModeMultiDecoder->setChecked(m_settings->value("SubModeMultiDecode", true).toBool());
ui->actionModeAutoSync->setChecked(m_settings->value("SubModeAutoSync", false).toBool());
ui->sbFtol->setValue (m_settings->value("Ftol", 20).toInt()); ui->sbFtol->setValue (m_settings->value("Ftol", 20).toInt());
m_minSync=m_settings->value("MinSync",0).toInt(); m_minSync=m_settings->value("MinSync",0).toInt();
@ -2676,6 +2680,18 @@ int MainWindow::computeFramesPerCycleForDecode(int submode){
return computePeriodForSubmode(submode) * RX_SAMPLE_RATE; return computePeriodForSubmode(submode) * RX_SAMPLE_RATE;
} }
int MainWindow::computePeriodStartDelayForDecode(int submode){
int delay = 0;
switch(submode){
case Varicode::JS8CallNormal: delay = JS8A_START_DELAY_MS; break;
case Varicode::JS8CallFast: delay = JS8B_START_DELAY_MS; break;
case Varicode::JS8CallTurbo: delay = JS8C_START_DELAY_MS; break;
case Varicode::JS8CallSlow: delay = JS8E_START_DELAY_MS; break;
case Varicode::JS8CallUltra: delay = JS8I_START_DELAY_MS; break;
}
return delay;
}
int MainWindow::computeFramesNeededForDecode(int submode){ int MainWindow::computeFramesNeededForDecode(int submode){
int symbolSamples = 0; int symbolSamples = 0;
float threshold = 0.0; float threshold = 0.0;
@ -2706,6 +2722,8 @@ void MainWindow::dataSink(qint64 frames)
k0 = k; k0 = k;
} }
qDebug() << "k" << k << "k0" << k0 << "delta" << k-k0;
#if JS8_USE_REFSPEC #if JS8_USE_REFSPEC
QString fname {QDir::toNativeSeparators(m_config.writeable_data_dir ().absoluteFilePath ("refspec.dat"))}; QString fname {QDir::toNativeSeparators(m_config.writeable_data_dir ().absoluteFilePath ("refspec.dat"))};
QByteArray bafname = fname.toLatin1(); QByteArray bafname = fname.toLatin1();
@ -4445,7 +4463,7 @@ bool MainWindow::decodeEnqueueReady(qint32 k, qint32 k0){
bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){
// TODO: make this non-static field of MainWindow? // TODO: make this non-static field of MainWindow?
// map of last decode positions for each submode // map of last decode positions for each submode
static QMap<qint32, qint32> lastDecodeStartMap; // static QMap<qint32, qint32> m_lastDecodeStartMap;
// TODO: make this non-static field of MainWindow? // TODO: make this non-static field of MainWindow?
// map of submodes to decode + optional alternate decode positions // map of submodes to decode + optional alternate decode positions
@ -4464,24 +4482,32 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){
// do we have a better way to check this? // 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 = ui->actionModeAutoSync->isChecked();
foreach(auto submode, submodes.keys()){ foreach(auto submode, submodes.keys()){
// skip if multi is disabled and this mode is not the current submode // skip if multi is disabled and this mode is not the current submode
if(!multi && submode != m_nSubMode){ if(!multi && submode != m_nSubMode){
continue; continue;
} }
// check 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
if(everySecond && alt != 0){
continue;
}
qint32 cycle = computeAltCycleForDecode(submode, k, alt*oneSecondSamples); qint32 cycle = computeAltCycleForDecode(submode, k, alt*oneSecondSamples);
qint32 cycleFrames = computeFramesPerCycleForDecode(submode); qint32 cycleFrames = computeFramesPerCycleForDecode(submode);
qint32 cycleFramesNeeded = computeFramesNeededForDecode(submode) - oneSecondSamples; qint32 cycleFramesNeeded = computeFramesNeededForDecode(submode) - oneSecondSamples;
qint32 cycleFramesReady = k - (cycle * cycleFrames); qint32 cycleFramesReady = k - (cycle * cycleFrames);
if(!lastDecodeStartMap.contains(submode)){ if(!m_lastDecodeStartMap.contains(submode)){
lastDecodeStartMap[submode] = cycle * cycleFrames; m_lastDecodeStartMap[submode] = cycle * cycleFrames;
} }
qint32 lastDecodeStart = lastDecodeStartMap[submode]; qint32 lastDecodeStart = m_lastDecodeStartMap[submode];
qint32 incrementedBy = k - lastDecodeStart; qint32 incrementedBy = k - lastDecodeStart;
if(k < lastDecodeStart){ if(k < lastDecodeStart){
incrementedBy = maxSamples - lastDecodeStart + k; incrementedBy = maxSamples - lastDecodeStart + k;
@ -4489,7 +4515,21 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){
if(JS8_DEBUG_DECODE) qDebug() << submodeName(submode) << "alt" << alt << "cycle" << cycle << "cycle frames" << cycleFrames << "cycle start" << cycle*cycleFrames << "cycle end" << (cycle+1)*cycleFrames << "k" << k << "frames ready" << cycleFramesReady << "incremeted by" << incrementedBy; if(JS8_DEBUG_DECODE) qDebug() << submodeName(submode) << "alt" << alt << "cycle" << cycle << "cycle frames" << cycleFrames << "cycle start" << cycle*cycleFrames << "cycle end" << (cycle+1)*cycleFrames << "k" << k << "frames ready" << cycleFramesReady << "incremeted by" << incrementedBy;
if(incrementedBy >= oneSecondSamples && cycleFramesReady >= cycleFramesNeeded){ if(everySecond && incrementedBy >= oneSecondSamples){
DecodeParams d;
d.submode = submode;
d.sz = cycleFrames;
d.start = k - d.sz;
if(d.start < 0){
d.start += maxSamples;
}
m_decoderQueue.append(d);
decodes++;
// keep track of last decode position
m_lastDecodeStartMap[submode] = k;
}
else if(incrementedBy >= oneSecondSamples && cycleFramesReady >= cycleFramesNeeded){
DecodeParams d; DecodeParams d;
d.submode = submode; d.submode = submode;
d.start = cycle*cycleFrames; d.start = cycle*cycleFrames;
@ -4497,8 +4537,8 @@ bool MainWindow::decodeEnqueueReadyExperiment(qint32 k, qint32 /*k0*/){
m_decoderQueue.append(d); m_decoderQueue.append(d);
decodes++; decodes++;
// keep track of last decode start position // keep track of last decode position
lastDecodeStartMap[submode] = k; m_lastDecodeStartMap[submode] = k;
} }
} }
} }
@ -4809,7 +4849,7 @@ bool MainWindow::decodeProcessQueue(qint32 *pSubmode){
int period = computePeriodForSubmode(submode); int period = computePeriodForSubmode(submode);
dec_data.params.syncStats = m_wideGraph->shouldDisplayDecodeAttempts(); dec_data.params.syncStats = (m_wideGraph->shouldDisplayDecodeAttempts() || ui->actionModeAutoSync->isChecked());
dec_data.params.npts8=(m_ihsym*m_nsps)/16; dec_data.params.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;
@ -5166,10 +5206,19 @@ void MainWindow::processDecodedLine(QByteArray t){
bool bAvgMsg=false; bool bAvgMsg=false;
int navg=0; int navg=0;
#if JS8_TIME_DRIFT_EXPERIMENT static QList<int> driftQueue;
static bool hasNewDrift = false;
static int newDrift = 0; static qint32 syncStart = -1;
#endif if(t.indexOf("<DecodeDebug> sync start") >= 0){
auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts);
if(segs.isEmpty()){
return;
}
auto spos = segs.at(3);
syncStart = spos.toInt();
return;
}
if(t.indexOf("<DecodeSyncStat>") >= 0) { if(t.indexOf("<DecodeSyncStat>") >= 0) {
auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts); auto segs = QString(t.trimmed()).split(QRegExp("[\\s\\t]+"), QString::SkipEmptyParts);
@ -5177,7 +5226,8 @@ void MainWindow::processDecodedLine(QByteArray t){
return; return;
} }
if(!m_wideGraph->shouldDisplayDecodeAttempts()){ // only continue if we should either display decode attempts or if we should try to auto sync
if(!m_wideGraph->shouldDisplayDecodeAttempts() && !ui->actionModeAutoSync->isChecked()){
return; return;
} }
@ -5191,10 +5241,11 @@ void MainWindow::processDecodedLine(QByteArray t){
auto s = int(s1.toFloat()); auto s = int(s1.toFloat());
auto xdt1 = QString(segs.at(8)); auto xdt1 = QString(segs.at(8));
auto xdt = int(xdt1.toFloat()); auto xdt = xdt1.toFloat();
auto xdtMs = int(xdt*1000);
// draw candidates // draw candidates
if(abs(xdt) <= 2){ if(abs(xdtMs) <= 2000){
if(s < 10){ if(s < 10){
m_wideGraph->drawDecodeLine(QColor(Qt::darkCyan), f, f + computeBandwidthForSubmode(m)); m_wideGraph->drawDecodeLine(QColor(Qt::darkCyan), f, f + computeBandwidthForSubmode(m));
} else if (s <= 15){ } else if (s <= 15){
@ -5211,6 +5262,131 @@ 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(!ui->actionModeAutoSync->isChecked()){
return;
}
if(m != Varicode::JS8CallNormal){
return;
}
// if we're here at this point, we _should_ be operating a decode every second
//
// so we need to figure out where:
//
// 1) this current decode started
// 2) when that cycle _should_ have started
// 3) compute the delta
// 4) apply the drift
/// if(!m_lastDecodeStartMap.contains(m)){
/// return;
/// }
// this is where we started the decode
static qint32 oneSecondSamples = RX_SAMPLE_RATE;
static qint32 maxSamples = NTMAX * RX_SAMPLE_RATE;
int periodMs = 1000 * computePeriodForSubmode(m);
auto now = QDateTime::currentDateTimeUtc();
writeNoticeTextToUI(now, QString("Decode at %1 (kin: %2, lastDecoded: %3)").arg(syncStart).arg(dec_data.params.kin).arg(m_lastDecodeStartMap.value(m)));
float expectedStartDelay = computePeriodStartDelayForDecode(m)/1000.0;
float decodedSignalTime = (float)syncStart/(float)RX_SAMPLE_RATE;
writeNoticeTextToUI(now, QString("--> started at %1 seconds into the start of my drifted minute").arg(decodedSignalTime));
writeNoticeTextToUI(now, QString("--> we add a time delta of %1 seconds into the start of the cycle").arg(xdt));
// adjust for expected start delay
decodedSignalTime -= expectedStartDelay;
// adjust for time delta
decodedSignalTime += xdt;
// ensure that we are within a 60 second minute
if(decodedSignalTime < 0){
decodedSignalTime += 60.0;
} else if(decodedSignalTime > 60){
decodedSignalTime -= 60.0;
}
writeNoticeTextToUI(now, QString("--> so signal adjusted started at %1 seconds into the start of my drifted minute").arg(decodedSignalTime));
int decodedSignalTimeMs = 1000 * decodedSignalTime;
int cycleStartTimeMs = (decodedSignalTimeMs / periodMs) * periodMs;
int driftMs = cycleStartTimeMs - decodedSignalTimeMs;
writeNoticeTextToUI(now, QString("--> which is a drift adjustment of %1 milliseconds").arg(driftMs));
// if we have a large negative offset (say -14000), use the positive inverse of +1000
if(driftMs + periodMs < qAbs(driftMs)){
driftMs += periodMs;
}
// if we have a large positive offset (say 14000, use the negative inverse of -1000)
else if(qAbs(driftMs - periodMs) < driftMs){
driftMs -= periodMs;
}
writeNoticeTextToUI(now, QString("--> which is a corrected drift adjustment of %1 milliseconds").arg(driftMs));
int newDrift = DriftingDateTime::drift() + driftMs;
if(newDrift < 0){
newDrift %= -periodMs;
} else {
newDrift %= periodMs;
}
writeNoticeTextToUI(now, QString("--> which is rounded to a total drift of %1 milliseconds for this period").arg(newDrift));
driftQueue.append(newDrift);
/// qint32 cycle = computeCycleForDecode(m, syncStart);
/// qint32 cycleFrames = computeFramesPerCycleForDecode(m);
/// qint32 cycleStart = cycle*cycleFrames;
///
/// writeNoticeTextToUI(now, QString("--> cycle started at %1 and is %2 frames long").arg(cycleStart).arg(cycleFrames));
/// writeNoticeTextToUI(now, QString("--> decode delta from cycle start is %1 frames and is %2 milliseconds after cycle start").arg(syncStart - cycleStart).arg(1000*float(syncStart-cycleStart)/RX_SAMPLE_RATE));
/// writeNoticeTextToUI(now, QString("--> decode time delta is %1 milliseconds").arg(xdtMs));
// TODO: we're assuming 1 second decoding at this point
/// qint32 cycleFrames = computeFramesPerCycleForDecode(m);
/// qint32 startK = syncStart;
/// // qint32 startK = m_lastDecodeStartMap.value(m) - cycleFrames;
/// // if(startK < 0){
/// // startK += maxSamples;
/// // }
/// qint32 cycle = computeCycleForDecode(m, startK);
/// qint32 framesIntoCycle = startK - cycle*cycleFrames;
///
/// // TODO: do we need to know the *lastDecode* drift setting or is it safe to use this?
/// // float currentDriftSeconds = m_wideGraph->drift()/1000.0;
///
/// // compute the relative seconds into the cycle
/// float secondsIntoCycle = (float)framesIntoCycle/(float)oneSecondSamples;
///
/// // if we have any drift applied currently, add that to the cycle seconds since the cycle is relative
/// // float adjustedSecondsIntoCycle = secondsIntoCycle - currentDriftSeconds;
///
/// // compute new drift adjustment
/// int periodMs = computePeriodForSubmode(m) * 1000;
/// int newDrift = (-secondsIntoCycle*1000)-xdtMs);
///
/// int pos = newDrift + periodMs;
/// int neg = newDrift - periodMs;
/// if(qAbs(neg) < qAbs(pos)){
/// newDrift = neg;
/// } else {
/// newDrift = pos;
/// }
///
/// writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Decode at startK %4 and %1 seconds into cycle %5 with dt of %2 milliseconds should adjust drift by %3").arg(secondsIntoCycle).arg(xdtMs).arg(newDrift).arg(startK).arg(cycle));
/// setDrift(newDrift);
#if JS8_TIME_DRIFT_EXPERIMENT #if JS8_TIME_DRIFT_EXPERIMENT
// use normal decodes for auto drift if we haven't already defined a new drift for this period // use normal decodes for auto drift if we haven't already defined a new drift for this period
if(/*!hasNewDrift && */ (m == Varicode::JS8CallSlow || m == Varicode::JS8CallNormal)){ if(/*!hasNewDrift && */ (m == Varicode::JS8CallSlow || m == Varicode::JS8CallNormal)){
@ -5261,16 +5437,22 @@ void MainWindow::processDecodedLine(QByteArray t){
int msec = m_decoderBusyStartTime.msecsTo(QDateTime::currentDateTimeUtc()); int msec = m_decoderBusyStartTime.msecsTo(QDateTime::currentDateTimeUtc());
if(JS8_DEBUG_DECODE) qDebug() << "decode duration" << msec << "ms"; if(JS8_DEBUG_DECODE) qDebug() << "decode duration" << msec << "ms";
#if JS8_TIME_DRIFT_EXPERIMENT if(!driftQueue.isEmpty()){
if(hasNewDrift){
static int driftN = 1; static int driftN = 1;
newDrift = ((driftN-1)*DriftingDateTime::drift() + newDrift)/driftN; static int driftAvg = DriftingDateTime::drift();
setDrift(newDrift);
if(driftN < 60) driftN++; // cap it to 60 observations while(!driftQueue.isEmpty()){
writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Drift: %1").arg(newDrift)); int newDrift = driftQueue.first();
hasNewDrift = false; driftQueue.removeFirst();
driftAvg = ((driftN-1)*driftAvg + newDrift)/driftN;
if(driftN < 60) driftN++; // cap it to 60 observations
}
setDrift(driftAvg);
writeNoticeTextToUI(QDateTime::currentDateTimeUtc(), QString("Drift: %1").arg(driftAvg));
} }
#endif
m_bDecoded = t.mid(16).trimmed().toInt() > 0; m_bDecoded = t.mid(16).trimmed().toInt() > 0;
int mswait=3*1000*m_TRperiod/4; int mswait=3*1000*m_TRperiod/4;
@ -10051,6 +10233,12 @@ void MainWindow::qsy(int hzDelta){
displayActivity(true); displayActivity(true);
} }
void MainWindow::drifted(int /*prev*/, int /*cur*/){
// here we reset the buffer position without clearing the buffer
// this makes the detected emit the correct k when drifting time
m_detector->resetBufferPosition();
}
void MainWindow::setFreqOffsetForRestore(int freq, bool shouldRestore){ void MainWindow::setFreqOffsetForRestore(int freq, bool shouldRestore){
setFreq4(freq, freq); setFreq4(freq, freq);
if(shouldRestore){ if(shouldRestore){
@ -10541,6 +10729,7 @@ void MainWindow::updateModeButtonText(){
auto selectedCallsign = callsignSelected(); auto selectedCallsign = callsignSelected();
auto multi = ui->actionModeMultiDecoder->isChecked(); auto multi = ui->actionModeMultiDecoder->isChecked();
auto autosync = ui->actionModeAutoSync->isChecked();
auto autoreply = ui->actionModeAutoreply->isChecked(); auto autoreply = ui->actionModeAutoreply->isChecked();
auto heartbeat = ui->actionModeJS8HB->isEnabled() && ui->actionModeJS8HB->isChecked(); auto heartbeat = ui->actionModeJS8HB->isEnabled() && ui->actionModeJS8HB->isChecked();
auto ack = autoreply && ui->actionHeartbeatAcknowledgements->isChecked() && (!m_config.heartbeat_qso_pause() || selectedCallsign.isEmpty()); auto ack = autoreply && ui->actionHeartbeatAcknowledgements->isChecked() && (!m_config.heartbeat_qso_pause() || selectedCallsign.isEmpty());
@ -10550,6 +10739,10 @@ void MainWindow::updateModeButtonText(){
modeText += QString("+MULTI"); modeText += QString("+MULTI");
} }
if(autosync){
modeText += QString("+SYNC");
}
if(autoreply){ if(autoreply){
if(m_config.autoreply_confirmation()){ if(m_config.autoreply_confirmation()){
modeText += QString("+AUTO+CONF"); modeText += QString("+AUTO+CONF");
@ -10565,6 +10758,7 @@ void MainWindow::updateModeButtonText(){
modeText += QString("+HB"); modeText += QString("+HB");
} }
} }
ui->modeButton->setText(modeText); ui->modeButton->setText(modeText);
} }

View File

@ -132,6 +132,7 @@ public slots:
void readFromStdout(QProcess * proc); void readFromStdout(QProcess * proc);
void setXIT(int n, Frequency base = 0u); void setXIT(int n, Frequency base = 0u);
void qsy(int hzDelta); void qsy(int hzDelta);
void drifted(int prev, int cur);
void setFreqOffsetForRestore(int freq, bool shouldRestore); void setFreqOffsetForRestore(int freq, bool shouldRestore);
bool tryRestoreFreqOffset(); bool tryRestoreFreqOffset();
void setFreq4(int rxFreq, int txFreq); void setFreq4(int rxFreq, int txFreq);
@ -615,6 +616,7 @@ private:
bool m_loopall; bool m_loopall;
bool m_decoderBusy; bool m_decoderBusy;
QString m_decoderBusyBand; QString m_decoderBusyBand;
QMap<qint32, qint32> m_lastDecodeStartMap;
Radio::Frequency m_decoderBusyFreq; Radio::Frequency m_decoderBusyFreq;
QDateTime m_decoderBusyStartTime; QDateTime m_decoderBusyStartTime;
bool m_auto; bool m_auto;
@ -991,6 +993,7 @@ private:
int computeCycleForDecode(int submode, int k); int computeCycleForDecode(int submode, int k);
int computeAltCycleForDecode(int submode, int k, int offsetFrames); int computeAltCycleForDecode(int submode, int k, int offsetFrames);
int computeFramesPerCycleForDecode(int submode); int computeFramesPerCycleForDecode(int submode);
int computePeriodStartDelayForDecode(int submode);
int computeFramesNeededForDecode(int submode); int computeFramesNeededForDecode(int submode);
bool shortList(QString callsign); bool shortList(QString callsign);
void transmit (double snr = 99.); void transmit (double snr = 99.);

View File

@ -4749,10 +4749,12 @@ list. The list can be maintained in Settings (F2).</string>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="menu_Decode_Passes"/> <addaction name="menu_Decode_Passes"/>
<addaction name="actionModeMultiDecoder"/> <addaction name="actionModeMultiDecoder"/>
<addaction name="actionModeAutoSync"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionModeAutoreply"/> <addaction name="actionModeAutoreply"/>
<addaction name="actionModeJS8HB"/> <addaction name="actionModeJS8HB"/>
<addaction name="actionHeartbeatAcknowledgements"/> <addaction name="actionHeartbeatAcknowledgements"/>
<addaction name="separator"/>
</widget> </widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuConfig"/> <addaction name="menuConfig"/>
@ -5762,6 +5764,14 @@ list. The list can be maintained in Settings (F2).</string>
<string>Enable Tuning Tone (T&amp;UNE)</string> <string>Enable Tuning Tone (T&amp;UNE)</string>
</property> </property>
</action> </action>
<action name="actionModeAutoSync">
<property name="checkable">
<bool>true</bool>
</property>
<property name="text">
<string>Enable Automatic Synchronization (S&amp;YNC)</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<customwidgets> <customwidgets>

View File

@ -921,6 +921,8 @@ void WideGraph::on_driftSyncResetButton_clicked(){
} }
void WideGraph::setDrift(int n){ void WideGraph::setDrift(int n){
int prev = drift();
DriftingDateTime::setDrift(n); DriftingDateTime::setDrift(n);
qDebug() << qSetRealNumberPrecision(12) << "Drift milliseconds:" << n; qDebug() << qSetRealNumberPrecision(12) << "Drift milliseconds:" << n;
@ -930,6 +932,12 @@ void WideGraph::setDrift(int n){
if(ui->driftSpinBox->value() != n){ if(ui->driftSpinBox->value() != n){
ui->driftSpinBox->setValue(n); ui->driftSpinBox->setValue(n);
} }
emit drifted(prev, n);
}
int WideGraph::drift(){
return DriftingDateTime::drift();
} }
void WideGraph::setQSYEnabled(bool enabled){ void WideGraph::setQSYEnabled(bool enabled){

View File

@ -77,6 +77,7 @@ signals:
void setXIT2(int n); void setXIT2(int n);
void setFreq3(int rxFreq, int txFreq); void setFreq3(int rxFreq, int txFreq);
void qsy(int hzDelta); void qsy(int hzDelta);
void drifted(int prev, int cur);
public slots: public slots:
void wideFreezeDecode(int n); void wideFreezeDecode(int n);
@ -85,6 +86,7 @@ public slots:
void setControlsVisible(bool visible); void setControlsVisible(bool visible);
bool controlsVisible(); bool controlsVisible();
void setDrift(int n); void setDrift(int n);
int drift();
void setQSYEnabled(bool enabled); void setQSYEnabled(bool enabled);
void setPaused(bool paused){ m_paused = paused; } void setPaused(bool paused){ m_paused = paused; }