diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 0918122..fa16fb3 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -53,6 +53,7 @@ AppFrame::AppFrame() : wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL); demodTray = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL); + wxBoxSizer *demodTunerTray = new wxBoxSizer(wxHORIZONTAL); int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 }; @@ -156,10 +157,23 @@ AppFrame::AppFrame() : demodScopeTray->AddSpacer(1); + deltaLockButton = new ModeSelectorCanvas(demodPanel, attribList); + deltaLockButton->addChoice(1, "V"); + deltaLockButton->setPadding(-1,-1); + deltaLockButton->setHighlightColor(RGBA4f(0.8,0.8,0.2)); + deltaLockButton->setHelpTip("Delta Lock Toggle (V) - Enable to lock modem relative to center frequency."); + deltaLockButton->setToggleMode(true); + deltaLockButton->setSelection(-1); + deltaLockButton->SetMinSize(wxSize(20,28)); + + demodTunerTray->Add(deltaLockButton, 0, wxEXPAND | wxALL, 0); + demodTunerTray->AddSpacer(1); + demodTuner = new TuningCanvas(demodPanel, attribList); - demodTuner->setHelpTip("Testing tuner"); demodTuner->SetMinClientSize(wxSize(200,28)); - demodScopeTray->Add(demodTuner, 1, wxEXPAND | wxALL, 0); + demodTunerTray->Add(demodTuner, 1, wxEXPAND | wxALL, 0); + + demodScopeTray->Add(demodTunerTray, 1, wxEXPAND | wxALL, 0); demodTray->Add(demodScopeTray, 30, wxEXPAND | wxALL, 0); @@ -1078,6 +1092,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { #ifdef ENABLE_DIGITAL_LAB demodModeSelectorAdv->setSelection(dType); #endif + deltaLockButton->setSelection(demod->isDeltaLock()?1:-1); demodMuteButton->setSelection(demod->isMuted()?1:-1); modemPropertiesUpdated.store(true); demodTuner->setHalfBand(dType=="USB" || dType=="LSB"); @@ -1144,11 +1159,34 @@ void AppFrame::OnIdle(wxIdleEvent& event) { if (demod->isMuted() && muteMode == -1) { demodMuteButton->setSelection(1); wxGetApp().getDemodMgr().setLastMuted(demod->isMuted()); + demodMuteButton->Refresh(); } else if (!demod->isMuted() && muteMode == 1) { demodMuteButton->setSelection(-1); wxGetApp().getDemodMgr().setLastMuted(demod->isMuted()); + demodMuteButton->Refresh(); + } + } + + int deltaMode = deltaLockButton->getSelection(); + if (deltaLockButton->modeChanged()) { + if (demod->isDeltaLock() && deltaMode == -1) { + demod->setDeltaLock(false); + } else if (!demod->isDeltaLock() && deltaMode == 1) { + demod->setDeltaLockOfs(demod->getFrequency()-wxGetApp().getFrequency()); + demod->setDeltaLock(true); + } + wxGetApp().getDemodMgr().setLastDeltaLock(demod->isDeltaLock()); + deltaLockButton->clearModeChanged(); + } else { + if (demod->isDeltaLock() && deltaMode == -1) { + deltaLockButton->setSelection(1); + wxGetApp().getDemodMgr().setLastDeltaLock(true); + deltaLockButton->Refresh(); + } else if (!demod->isDeltaLock() && deltaMode == 1) { + deltaLockButton->setSelection(-1); + wxGetApp().getDemodMgr().setLastDeltaLock(false); + deltaLockButton->Refresh(); } - demodMuteButton->Refresh(); } int soloMode = soloModeButton->getSelection(); @@ -1377,6 +1415,10 @@ void AppFrame::saveSession(std::string fileName) { *demod->newChild("output_device") = outputDevices[(*instance_i)->getOutputDevice()].name; *demod->newChild("gain") = (*instance_i)->getGain(); *demod->newChild("muted") = (*instance_i)->isMuted() ? 1 : 0; + if ((*instance_i)->isDeltaLock()) { + *demod->newChild("delta_lock") = (*instance_i)->isDeltaLock() ? 1 : 0; + *demod->newChild("delta_ofs") = (*instance_i)->getDeltaLockOfs(); + } if ((*instance_i) == wxGetApp().getDemodMgr().getLastActiveDemodulator()) { *demod->newChild("active") = 1; } @@ -1450,9 +1492,11 @@ bool AppFrame::loadSession(std::string fileName) { float squelch_level = demod->hasAnother("squelch_level") ? (float) *demod->getNext("squelch_level") : 0; int squelch_enabled = demod->hasAnother("squelch_enabled") ? (int) *demod->getNext("squelch_enabled") : 0; int muted = demod->hasAnother("muted") ? (int) *demod->getNext("muted") : 0; + int delta_locked = demod->hasAnother("delta_lock") ? (int) *demod->getNext("delta_lock") : 0; + int delta_ofs = demod->hasAnother("delta_ofs") ? (int) *demod->getNext("delta_ofs") : 0; std::string output_device = demod->hasAnother("output_device") ? string(*(demod->getNext("output_device"))) : ""; float gain = demod->hasAnother("gain") ? (float) *demod->getNext("gain") : 1.0; - + std::string type = "FM"; DataNode *demodTypeNode = demod->hasAnother("type")?demod->getNext("type"):nullptr; @@ -1512,6 +1556,10 @@ bool AppFrame::loadSession(std::string fileName) { newDemod->setGain(gain); newDemod->updateLabel(freq); newDemod->setMuted(muted?true:false); + if (delta_locked) { + newDemod->setDeltaLock(true); + newDemod->setDeltaLockOfs(delta_ofs); + } if (squelch_enabled) { newDemod->setSquelchEnabled(true); newDemod->setSquelchLevel(squelch_level); @@ -1645,6 +1693,8 @@ int AppFrame::OnGlobalKeyDown(wxKeyEvent &event) { case WXK_NUMPAD_RIGHT: waterfallCanvas->OnKeyDown(event); // TODO: Move the stuff from there to here return 1; + case 'V': + return 1; case ']': if (lastDemod) { lastDemod->setFrequency(lastDemod->getFrequency()+snap); @@ -1709,6 +1759,8 @@ int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) { return -1; } + DemodulatorInstance *lastDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); + switch (event.GetKeyCode()) { case WXK_SPACE: if (!demodTuner->getMouseTracker()->mouseInView()) { @@ -1726,6 +1778,14 @@ int AppFrame::OnGlobalKeyUp(wxKeyEvent &event) { case WXK_NUMPAD_RIGHT: waterfallCanvas->OnKeyUp(event); return 1; + case 'V': + if (lastDemod && lastDemod->isDeltaLock()) { + lastDemod->setDeltaLock(false); + } else if (lastDemod) { + lastDemod->setDeltaLockOfs(lastDemod->getFrequency() - wxGetApp().getFrequency()); + lastDemod->setDeltaLock(true); + } + break; case 'A': demodModeSelector->setSelection("AM"); return 1; diff --git a/src/AppFrame.h b/src/AppFrame.h index 1fcbc37..ed957a3 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -110,7 +110,7 @@ private: // UITestCanvas *testCanvas; MeterCanvas *spectrumAvgMeter; MeterCanvas *waterfallSpeedMeter; - ModeSelectorCanvas *demodMuteButton, *peakHoldButton, *soloModeButton; + ModeSelectorCanvas *demodMuteButton, *peakHoldButton, *soloModeButton, *deltaLockButton; GainCanvas *gainCanvas; wxSizerItem *gainSizerItem, *gainSpacerItem; wxSplitterWindow *mainVisSplitter, *mainSplitter; diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index e53cd8e..e3565d8 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -373,6 +373,22 @@ void DemodulatorInstance::setTracking(bool tracking) { this->tracking = tracking; } +bool DemodulatorInstance::isDeltaLock() { + return deltaLock.load(); +} + +void DemodulatorInstance::setDeltaLock(bool lock) { + deltaLock.store(lock); +} + +void DemodulatorInstance::setDeltaLockOfs(int lockOfs) { + deltaLockOfs.store(lockOfs); +} + +int DemodulatorInstance::getDeltaLockOfs() { + return deltaLockOfs.load(); +} + bool DemodulatorInstance::isMuted() { return demodulatorThread->isMuted(); } diff --git a/src/demod/DemodulatorInstance.h b/src/demod/DemodulatorInstance.h index b03184e..18c5362 100644 --- a/src/demod/DemodulatorInstance.h +++ b/src/demod/DemodulatorInstance.h @@ -80,7 +80,12 @@ public: bool isTracking(); void setTracking(bool tracking); - + + bool isDeltaLock(); + void setDeltaLock(bool lock); + void setDeltaLockOfs(int lockOfs); + int getDeltaLockOfs(); + bool isMuted(); void setMuted(bool muted); @@ -122,6 +127,8 @@ private: std::atomic_bool active; std::atomic_bool squelch; std::atomic_bool muted; + std::atomic_bool deltaLock; + std::atomic_int deltaLockOfs; std::atomic_int currentOutputDevice; std::atomic currentAudioGain; diff --git a/src/demod/DemodulatorMgr.cpp b/src/demod/DemodulatorMgr.cpp index 932b72d..ea8af51 100644 --- a/src/demod/DemodulatorMgr.cpp +++ b/src/demod/DemodulatorMgr.cpp @@ -11,7 +11,7 @@ bool inactiveCompare (DemodulatorInstance *i, DemodulatorInstance *j) { return ( DemodulatorMgr::DemodulatorMgr() : activeDemodulator(NULL), lastActiveDemodulator(NULL), activeVisualDemodulator(NULL), lastBandwidth(DEFAULT_DEMOD_BW), lastDemodType( - DEFAULT_DEMOD_TYPE), lastSquelchEnabled(false), lastSquelch(-100), lastGain(1.0), lastMuted(false) { + DEFAULT_DEMOD_TYPE), lastSquelchEnabled(false), lastSquelch(-100), lastGain(1.0), lastMuted(false), lastDeltaLock(false) { } DemodulatorMgr::~DemodulatorMgr() { @@ -284,6 +284,15 @@ void DemodulatorMgr::setLastGain(float lastGain) { this->lastGain = lastGain; } + +bool DemodulatorMgr::getLastDeltaLock() const { + return lastDeltaLock; +} + +void DemodulatorMgr::setLastDeltaLock(bool lock) { + lastDeltaLock = lock; +} + float DemodulatorMgr::getLastSquelchLevel() const { return lastSquelch; } diff --git a/src/demod/DemodulatorMgr.h b/src/demod/DemodulatorMgr.h index 780d7d3..8d72711 100644 --- a/src/demod/DemodulatorMgr.h +++ b/src/demod/DemodulatorMgr.h @@ -37,6 +37,9 @@ public: float getLastGain() const; void setLastGain(float lastGain); + bool getLastDeltaLock() const; + void setLastDeltaLock(bool lock); + float getLastSquelchLevel() const; void setLastSquelchLevel(float lastSquelch); @@ -67,6 +70,7 @@ private: float lastSquelch; float lastGain; bool lastMuted; + bool lastDeltaLock; std::map lastModemSettings; }; diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index 1d908c1..ef02c87 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -69,11 +69,22 @@ void SDRPostThread::updateActiveDemodulators() { nRunDemods = 0; + long long centerFreq = wxGetApp().getFrequency(); + for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) { DemodulatorInstance *demod = *demod_i; DemodulatorThreadInputQueue *demodQueue = demod->getIQInputDataPipe(); // not in range? + if (demod->isDeltaLock()) { + if (demod->getFrequency() != centerFreq + demod->getDeltaLockOfs()) { + demod->setFrequency(centerFreq + demod->getDeltaLockOfs()); + demod->updateLabel(demod->getFrequency()); + demod->setFollow(false); + demod->setTracking(false); + } + } + if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) { // deactivate if active if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) { @@ -85,7 +96,7 @@ void SDRPostThread::updateActiveDemodulators() { } // follow if follow mode - if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) { + if (demod->isFollow() && centerFreq != demod->getFrequency()) { wxGetApp().setFrequency(demod->getFrequency()); demod->setFollow(false); } diff --git a/src/visual/ModeSelectorCanvas.cpp b/src/visual/ModeSelectorCanvas.cpp index 4d00f0a..188e702 100644 --- a/src/visual/ModeSelectorCanvas.cpp +++ b/src/visual/ModeSelectorCanvas.cpp @@ -183,6 +183,7 @@ void ModeSelectorCanvas::setSelection(int value) { for (int i = 0; i < numChoices; i++) { if (selections[i].value == value) { currentSelection = i; + Refresh(); return; } } diff --git a/src/visual/PrimaryGLContext.cpp b/src/visual/PrimaryGLContext.cpp index 19fa7bc..25893c5 100644 --- a/src/visual/PrimaryGLContext.cpp +++ b/src/visual/PrimaryGLContext.cpp @@ -148,6 +148,10 @@ void PrimaryGLContext::DrawDemodInfo(DemodulatorInstance *demod, RGBA4f color, l demodLabel = std::string("[S] ") + demodLabel; } + if (demod->isDeltaLock()) { + demodLabel.append(" [V]"); + } + if (demod->getDemodulatorType() == "USB") { GLFont::getFont(GLFont::GLFONT_SIZE16).drawString(demodLabel, uxPos, hPos, 16, GLFont::GLFONT_ALIGN_LEFT, GLFont::GLFONT_ALIGN_CENTER); } else if (demod->getDemodulatorType() == "LSB") { diff --git a/src/visual/TuningCanvas.cpp b/src/visual/TuningCanvas.cpp index 709eaff..34f694f 100644 --- a/src/visual/TuningCanvas.cpp +++ b/src/visual/TuningCanvas.cpp @@ -186,6 +186,9 @@ void TuningCanvas::StepTuner(ActiveState state, int exponent, bool up) { activeDemod->setTracking(true); activeDemod->setFollow(true); activeDemod->setFrequency(freq); + if (activeDemod->isDeltaLock()) { + activeDemod->setDeltaLockOfs(activeDemod->getFrequency() - wxGetApp().getFrequency()); + } activeDemod->updateLabel(freq); } diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index b2e7c1b..c09e55a 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -612,6 +612,9 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { if (bwDiff) { demod->setFrequency(currentFreq + bwDiff); + if (demod->isDeltaLock()) { + demod->setDeltaLockOfs(demod->getFrequency() - wxGetApp().getFrequency()); + } currentFreq = demod->getFrequency(); demod->updateLabel(currentFreq); } @@ -699,6 +702,12 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) { demod->setSquelchEnabled(mgr->isLastSquelchEnabled()); demod->setGain(mgr->getLastGain()); demod->setMuted(mgr->isLastMuted()); + if (mgr->getLastDeltaLock()) { + demod->setDeltaLock(true); + demod->setDeltaLockOfs(wxGetApp().getFrequency()-freq); + } else { + demod->setDeltaLock(false); + } demod->writeModemSettings(mgr->getLastModemSettings(mgr->getLastDemodulatorType())); demod->run(); @@ -712,6 +721,9 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) { demod->updateLabel(freq); demod->setFrequency(freq); + if (demod->isDeltaLock()) { + demod->setDeltaLockOfs(demod->getFrequency() - wxGetApp().getFrequency()); + } if (isNew) { setStatusText("New demodulator at frequency: %s", freq); @@ -788,6 +800,12 @@ void WaterfallCanvas::OnMouseReleased(wxMouseEvent& event) { demod->setSquelchEnabled(mgr->isLastSquelchEnabled()); demod->setGain(mgr->getLastGain()); demod->setMuted(mgr->isLastMuted()); + if (mgr->getLastDeltaLock()) { + demod->setDeltaLock(true); + demod->setDeltaLockOfs(wxGetApp().getFrequency()-freq); + } else { + demod->setDeltaLock(false); + } demod->writeModemSettings(mgr->getLastModemSettings(mgr->getLastDemodulatorType())); demod->run();