From e44af72b00938f015670ef364902a47a33ac9e04 Mon Sep 17 00:00:00 2001 From: vsonnier Date: Sat, 18 Nov 2017 22:38:05 +0100 Subject: [PATCH 1/3] Assure Gain control <==> Settings menu synchronization in case of mutually depdendent updates --- src/AppFrame.cpp | 4 -- src/CubicSDR.cpp | 6 ++- src/CubicSDR.h | 2 +- src/forms/SDRDevices/SDRDevices.cpp | 7 +-- src/sdr/SDRDeviceInfo.cpp | 21 +++++++++ src/sdr/SDRDeviceInfo.h | 4 ++ src/visual/GainCanvas.cpp | 70 ++++++++++++++++++++++++++++- src/visual/GainCanvas.h | 6 ++- 8 files changed, 108 insertions(+), 12 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 4864c0d..1926c96 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -941,7 +941,6 @@ void AppFrame::updateDeviceParams() { newSampleRateMenu->AppendSeparator(); sampleRateMenuItems[wxID_BANDWIDTH_MANUAL_DIALOG] = newSampleRateMenu->Append(wxID_BANDWIDTH_MANUAL_DIALOG, wxT("Manual Entry...")); - menuBar->Replace(2, newSampleRateMenu, wxT("Sample &Rate")); sampleRateMenu = newSampleRateMenu; @@ -950,9 +949,6 @@ void AppFrame::updateDeviceParams() { gainSizerItem->Show(true); gainSizerItem->SetMinSize(devInfo->getSoapyDevice()->listGains(SOAPY_SDR_RX,0).size()*50,0); demodTray->Layout(); - gainCanvas->updateGainUI(); - gainCanvas->Refresh(); - gainCanvas->Refresh(); } else { gainSpacerItem->Show(false); gainSizerItem->Show(false); diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index f4cb6aa..aface75 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -944,8 +944,12 @@ bool CubicSDR::areDevicesReady() { return devicesReady.load(); } -void CubicSDR::notifyMainUIOfDeviceChange() { +void CubicSDR::notifyMainUIOfDeviceChange(bool forceRefreshOfGains) { appframe->notifyDeviceChanged(); + + if (forceRefreshOfGains) { + appframe->refreshGainUI(); + } } bool CubicSDR::areDevicesEnumerating() { diff --git a/src/CubicSDR.h b/src/CubicSDR.h index c714ad7..798089e 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -143,7 +143,7 @@ public: bool areModulesMissing(); std::string getNotification(); - void notifyMainUIOfDeviceChange(); + void notifyMainUIOfDeviceChange(bool forceRefreshOfGains = false); void addRemote(std::string remoteAddr); void removeRemote(std::string remoteAddr); diff --git a/src/forms/SDRDevices/SDRDevices.cpp b/src/forms/SDRDevices/SDRDevices.cpp index ea50024..cabebee 100644 --- a/src/forms/SDRDevices/SDRDevices.cpp +++ b/src/forms/SDRDevices/SDRDevices.cpp @@ -395,7 +395,8 @@ void SDRDevicesDialog::OnUseSelected( wxMouseEvent& event) { wxGetApp().setDeviceArgs(settingArgs); wxGetApp().setStreamArgs(streamArgs); wxGetApp().setDevice(dev,0); - wxGetApp().notifyMainUIOfDeviceChange(); + wxGetApp().notifyMainUIOfDeviceChange(true); + Refresh(); Close(); } event.Skip(); @@ -570,13 +571,13 @@ void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) { wxGetApp().getSDRThread()->writeSetting(rtp->first, settingValue); } - wxGetApp().notifyMainUIOfDeviceChange(); + wxGetApp().notifyMainUIOfDeviceChange(true); return; } } } // general refresh. - wxGetApp().notifyMainUIOfDeviceChange(); + wxGetApp().notifyMainUIOfDeviceChange(true); } void SDRDevicesDialog::OnPropGridFocus( wxFocusEvent& /* event */) { diff --git a/src/sdr/SDRDeviceInfo.cpp b/src/sdr/SDRDeviceInfo.cpp index 6bf415b..2dbaf03 100644 --- a/src/sdr/SDRDeviceInfo.cpp +++ b/src/sdr/SDRDeviceInfo.cpp @@ -269,3 +269,24 @@ SDRRangeMap SDRDeviceInfo::getGains(int direction, size_t channel) { return gainMap; } + +//read the current gain of name gainName (must exit in getGains(), else return 0) +//in the device. +double SDRDeviceInfo::getCurrentGain(int direction, size_t channel, const std::string& gainName) { + + SoapySDR::Device *dev = getSoapyDevice(); + + if (dev) { + + std::vector gainNames = dev->listGains(direction, channel); + + auto itFoundName = std::find(gainNames.begin(), gainNames.end(), gainName); + + if (itFoundName != gainNames.end()) { + + return dev->getGain(direction, channel, gainName); + } + } + + return 0.0; +} diff --git a/src/sdr/SDRDeviceInfo.h b/src/sdr/SDRDeviceInfo.h index e5d9bc2..622d56f 100644 --- a/src/sdr/SDRDeviceInfo.h +++ b/src/sdr/SDRDeviceInfo.h @@ -92,6 +92,10 @@ public: SDRRangeMap getGains(int direction, size_t channel); + //read the current gain of name gainName (must exist in getGains(), else return 0) + //in the device. + double getCurrentGain(int direction, size_t channel, const std::string& gainName); + private: int index = 0; std::string name, serial, product, manufacturer, tuner; diff --git a/src/visual/GainCanvas.cpp b/src/visual/GainCanvas.cpp index c5a3bef..393fce7 100644 --- a/src/visual/GainCanvas.cpp +++ b/src/visual/GainCanvas.cpp @@ -69,13 +69,25 @@ void GainCanvas::OnIdle(wxIdleEvent &event) { } else { event.Skip(); } + + bool areGainsChangedHere = false; for (auto gi : gainPanels) { if (gi->getChanged()) { + areGainsChangedHere = true; wxGetApp().setGain(gi->getName(), gi->getValue()); + //A gain may be exposed as setting also so assure refresh of the menu also. + wxGetApp().notifyMainUIOfDeviceChange(false); //do not piggyback to us... + gi->setChanged(false); } } + + if (!areGainsChangedHere) { + if (updateGainValues()) { + Refresh(); + } + } } void GainCanvas::SetLevel() { @@ -167,8 +179,6 @@ void GainCanvas::OnMouseEnterWindow(wxMouseEvent& event) { #endif } - - void GainCanvas::setHelpTip(std::string tip) { helpTip = tip; } @@ -221,6 +231,62 @@ void GainCanvas::updateGainUI() { setThemeColors(); } +// call this to refresh the gain values only, not the whole UI. +bool GainCanvas::updateGainValues() { + + bool isRefreshNeeded = false; + + SDRDeviceInfo *devInfo = wxGetApp().getDevice(); + + //possible if we 'Refresh Devices' then devInfo becomes null + //until a new device is selected. + if (devInfo == nullptr) { + return false; + } + + DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(devInfo->getDeviceId()); + + gains = devInfo->getGains(SOAPY_SDR_RX, 0); + SDRRangeMap::iterator gi; + + size_t numGainsToRefresh = std::min(gains.size(), gainPanels.size()); + size_t panelIndex = 0; + + //actually the order of gains iteration should be constant because map of string, + //and gainPanels were built in that order in updateGainUI() + for (auto gi : gains) { + + if (panelIndex >= numGainsToRefresh) { + break; + } + + // do not update if a change is already pending. + if (!gainPanels[panelIndex]->getChanged()) { + + //read the actual gain from the device. + float actualGain = (float)devInfo->getCurrentGain(SOAPY_SDR_RX, 0, gi.first); + + //do nothing if the difference is less than 1.0, since the panel do not show it anyway. + if (std::abs(actualGain - gainPanels[panelIndex]->getValue()) > 1.0) { + + gainPanels[panelIndex]->setValue(actualGain); + + //update the config with this value : + //a consequence of such updates is that the use setting + // is overriden by the current one in AGC mode. + //TODO: if it not desirable, do not update in AGC mode. + devConfig->setGain(gi.first, actualGain); + + isRefreshNeeded = true; + } + } //end if no external change pending. + + panelIndex++; + } + + return isRefreshNeeded; +} + void GainCanvas::setThemeColors() { RGBA4f c1, c2; diff --git a/src/visual/GainCanvas.h b/src/visual/GainCanvas.h index 2e7a552..9ef9f3f 100644 --- a/src/visual/GainCanvas.h +++ b/src/visual/GainCanvas.h @@ -25,9 +25,13 @@ public: void setHelpTip(std::string tip); void updateGainUI(); - void setThemeColors(); + void setThemeColors(); private: + + // call this to refresh the gain values only, return true if refresh is needed + bool updateGainValues(); + void OnPaint(wxPaintEvent& event); void OnIdle(wxIdleEvent &event); From 4b077af8735f76892d6f53a645e4d001774101d7 Mon Sep 17 00:00:00 2001 From: vsonnier Date: Mon, 20 Nov 2017 01:33:18 +0100 Subject: [PATCH 2/3] Fix for gain sliders not perfectly following integer gain steps from the menu (i.e RSPx RF gains) --- src/visual/GainCanvas.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/visual/GainCanvas.cpp b/src/visual/GainCanvas.cpp index 393fce7..83e2f50 100644 --- a/src/visual/GainCanvas.cpp +++ b/src/visual/GainCanvas.cpp @@ -17,6 +17,7 @@ #include "CubicSDRDefs.h" #include "AppFrame.h" #include +#include wxBEGIN_EVENT_TABLE(GainCanvas, wxGLCanvas) EVT_PAINT(GainCanvas::OnPaint) EVT_IDLE(GainCanvas::OnIdle) @@ -75,7 +76,9 @@ void GainCanvas::OnIdle(wxIdleEvent &event) { for (auto gi : gainPanels) { if (gi->getChanged()) { areGainsChangedHere = true; - wxGetApp().setGain(gi->getName(), gi->getValue()); + // Gain only displays integer gain values, so the applied gain + //value to exactly that. + wxGetApp().setGain(gi->getName(), (int)(gi->getValue())); //A gain may be exposed as setting also so assure refresh of the menu also. wxGetApp().notifyMainUIOfDeviceChange(false); //do not piggyback to us... @@ -263,19 +266,19 @@ bool GainCanvas::updateGainValues() { // do not update if a change is already pending. if (!gainPanels[panelIndex]->getChanged()) { - //read the actual gain from the device. - float actualGain = (float)devInfo->getCurrentGain(SOAPY_SDR_RX, 0, gi.first); + //read the actual gain from the device, round it + float actualRoundedGain = (float)std::round(devInfo->getCurrentGain(SOAPY_SDR_RX, 0, gi.first)); //do nothing if the difference is less than 1.0, since the panel do not show it anyway. - if (std::abs(actualGain - gainPanels[panelIndex]->getValue()) > 1.0) { + if ((int)actualRoundedGain != (int)(gainPanels[panelIndex]->getValue())) { - gainPanels[panelIndex]->setValue(actualGain); + gainPanels[panelIndex]->setValue(actualRoundedGain); //update the config with this value : //a consequence of such updates is that the use setting // is overriden by the current one in AGC mode. //TODO: if it not desirable, do not update in AGC mode. - devConfig->setGain(gi.first, actualGain); + devConfig->setGain(gi.first, actualRoundedGain); isRefreshNeeded = true; } From ada56440fccd2c90560e0166ccc729d5c02d9169 Mon Sep 17 00:00:00 2001 From: vsonnier Date: Mon, 20 Nov 2017 01:33:18 +0100 Subject: [PATCH 3/3] Fix reloading of gains at startup. --- src/AppFrame.cpp | 2 +- src/forms/SDRDevices/SDRDevices.cpp | 10 ++---- src/panel/MeterPanel.h | 2 +- src/sdr/SoapySDRThread.cpp | 19 +++++++----- src/visual/GainCanvas.cpp | 47 +++++++++++++++++++++-------- src/visual/GainCanvas.h | 6 +++- 6 files changed, 54 insertions(+), 32 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 1926c96..76a26a7 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -1236,7 +1236,7 @@ bool AppFrame::actionOnMenuSettings(wxCommandEvent& event) { menuIdx++; } } //end for - + return true; } diff --git a/src/forms/SDRDevices/SDRDevices.cpp b/src/forms/SDRDevices/SDRDevices.cpp index cabebee..b5ae9e5 100644 --- a/src/forms/SDRDevices/SDRDevices.cpp +++ b/src/forms/SDRDevices/SDRDevices.cpp @@ -395,8 +395,6 @@ void SDRDevicesDialog::OnUseSelected( wxMouseEvent& event) { wxGetApp().setDeviceArgs(settingArgs); wxGetApp().setStreamArgs(streamArgs); wxGetApp().setDevice(dev,0); - wxGetApp().notifyMainUIOfDeviceChange(true); - Refresh(); Close(); } event.Skip(); @@ -495,7 +493,7 @@ void SDRDevicesDialog::OnRefreshDevices( wxMouseEvent& /* event */) { void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) { - if (editId && event.GetProperty() == devSettings["name"]) { + if (event.GetProperty() == devSettings["name"]) { DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId()); wxString devName = event.GetPropertyValue().GetString(); @@ -550,7 +548,7 @@ void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) { // nop } } - else if (editId && dev) { + else if (dev) { wxPGProperty *prop = event.GetProperty(); //change value of RuntimeProps for (std::map::iterator rtp = runtimeProps.begin(); rtp != runtimeProps.end(); rtp++) { @@ -570,14 +568,10 @@ void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) { if (dev->isActive()) { wxGetApp().getSDRThread()->writeSetting(rtp->first, settingValue); } - - wxGetApp().notifyMainUIOfDeviceChange(true); return; } } } - // general refresh. - wxGetApp().notifyMainUIOfDeviceChange(true); } void SDRDevicesDialog::OnPropGridFocus( wxFocusEvent& /* event */) { diff --git a/src/panel/MeterPanel.h b/src/panel/MeterPanel.h index c8c81f6..86a737d 100644 --- a/src/panel/MeterPanel.h +++ b/src/panel/MeterPanel.h @@ -32,7 +32,7 @@ protected: private: std::string name; float low, high, current; - bool changed; + bool changed = false; GLPanel bgPanel; GLPanel levelPanel; GLPanel highlightPanel; diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp index 7513612..3ca5f7b 100644 --- a/src/sdr/SoapySDRThread.cpp +++ b/src/sdr/SoapySDRThread.cpp @@ -153,13 +153,14 @@ bool SDRThread::init() { settingChanged.erase(settingChanged.begin(), settingChanged.end()); } + //apply settings. { //enter scoped-lock std::lock_guard < std::mutex > lock(setting_busy); for (settings_i = settingsInfo.begin(); settings_i != settingsInfo.end(); settings_i++) { SoapySDR::ArgInfo setting = (*settings_i); - if ((settingChanged.find(setting.key) != settingChanged.end()) && (settings.find(setting.key) != settings.end())) { - device->writeSetting(setting.key, settings[setting.key]); + if ((settingChanged.find(setting.key) != settingChanged.end()) && (settings.find(setting.key) != settings.end())) { + device->writeSetting(setting.key, settings[setting.key]); settingChanged[setting.key] = false; } else { settings[setting.key] = device->readSetting(setting.key); @@ -173,7 +174,10 @@ bool SDRThread::init() { updateSettings(); wxGetApp().sdrThreadNotify(SDRThread::SDR_THREAD_INITIALIZED, std::string("Device Initialized.")); - + + //rebuild menu now that settings are really been applied. + wxGetApp().notifyMainUIOfDeviceChange(true); + return true; } @@ -486,13 +490,12 @@ void SDRThread::updateSettings() { if (!agc_mode.load()) { updateGains(); + //re-apply the saved configuration gains: DeviceConfig *devConfig = deviceConfig.load(); ConfigGains gains = devConfig->getGains(); - if (gains.size()) { - for (ConfigGains::iterator gain_i = gains.begin(); gain_i != gains.end(); gain_i++) { - setGain(gain_i->first, gain_i->second); - } + for (ConfigGains::iterator gain_i = gains.begin(); gain_i != gains.end(); gain_i++) { + setGain(gain_i->first, gain_i->second); } } doUpdate = true; @@ -544,7 +547,7 @@ void SDRThread::run() { SDRDeviceInfo *activeDev = deviceInfo.load(); - if (activeDev != NULL) { + if (activeDev != nullptr) { std::cout << "device init()" << std::endl; if (!init()) { std::cout << "SDR Thread stream init error." << std::endl; diff --git a/src/visual/GainCanvas.cpp b/src/visual/GainCanvas.cpp index 83e2f50..70010cf 100644 --- a/src/visual/GainCanvas.cpp +++ b/src/visual/GainCanvas.cpp @@ -42,6 +42,8 @@ GainCanvas::GainCanvas(wxWindow *parent, std::vector dispAttrs) : startPos = spacing/2.0; barHeight = 0.8f; refreshCounter = 0; + + userGainAsChanged = false; } GainCanvas::~GainCanvas() { @@ -75,20 +77,33 @@ void GainCanvas::OnIdle(wxIdleEvent &event) { for (auto gi : gainPanels) { if (gi->getChanged()) { - areGainsChangedHere = true; - // Gain only displays integer gain values, so the applied gain + areGainsChangedHere = true; + // Gain only displays integer gain values, so set the applied gain //value to exactly that. wxGetApp().setGain(gi->getName(), (int)(gi->getValue())); //A gain may be exposed as setting also so assure refresh of the menu also. - wxGetApp().notifyMainUIOfDeviceChange(false); //do not piggyback to us... + wxGetApp().notifyMainUIOfDeviceChange(false); //do not rebuild the gain UI gi->setChanged(false); } } - if (!areGainsChangedHere) { - if (updateGainValues()) { - Refresh(); + //User input has changed the gain, so schedule an update of values + //in 150ms in the future, else the device may not have taken the value into account. + if (areGainsChangedHere) { + userGainAsChanged = true; + userGainAsChangedDelayTimer.start(); + } + else { + userGainAsChangedDelayTimer.update(); + + if (!userGainAsChanged || (userGainAsChanged && userGainAsChangedDelayTimer.getMilliseconds() > 150)) { + + if (updateGainValues()) { + Refresh(); + } + + userGainAsChanged = false; } } } @@ -101,8 +116,7 @@ void GainCanvas::SetLevel() { float value = gi->getMeterHitValue(mpos); gi->setValue(value); - gi->setChanged(true); - + gi->setChanged(true); break; } } @@ -187,6 +201,7 @@ void GainCanvas::setHelpTip(std::string tip) { } void GainCanvas::updateGainUI() { + SDRDeviceInfo *devInfo = wxGetApp().getDevice(); //possible if we 'Refresh Devices' then devInfo becomes null @@ -196,9 +211,14 @@ void GainCanvas::updateGainUI() { } DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(devInfo->getDeviceId()); - + + //read the gains from the device. + //This may be wrong because the device is not started, or has yet + //to take into account a user gain change. Doesn't matter, + //UpdateGainValues() takes cares of updating the true value realtime. gains = devInfo->getGains(SOAPY_SDR_RX, 0); - SDRRangeMap::iterator gi; + + SDRRangeMap::iterator gi; numGains = gains.size(); float i = 0; @@ -218,7 +238,7 @@ void GainCanvas::updateGainUI() { bgPanel.removeChild(mDel); delete mDel; } - + for (auto gi : gains) { MeterPanel *mPanel = new MeterPanel(gi.first, gi.second.minimum(), gi.second.maximum(), devConfig->getGain(gi.first,wxGetApp().getGain(gi.first))); @@ -230,7 +250,7 @@ void GainCanvas::updateGainUI() { gainPanels.push_back(mPanel); i++; } - + setThemeColors(); } @@ -243,7 +263,8 @@ bool GainCanvas::updateGainValues() { //possible if we 'Refresh Devices' then devInfo becomes null //until a new device is selected. - if (devInfo == nullptr) { + //also, do not attempt an update with the device is not started. + if (devInfo == nullptr || !devInfo->isActive()) { return false; } diff --git a/src/visual/GainCanvas.h b/src/visual/GainCanvas.h index 9ef9f3f..323724b 100644 --- a/src/visual/GainCanvas.h +++ b/src/visual/GainCanvas.h @@ -8,6 +8,7 @@ #include #include +#include #include "InteractiveCanvas.h" #include "MouseTracker.h" @@ -25,7 +26,7 @@ public: void setHelpTip(std::string tip); void updateGainUI(); - void setThemeColors(); + void setThemeColors(); private: @@ -54,6 +55,9 @@ private: float spacing, barWidth, startPos, barHeight, numGains; int refreshCounter; wxSize clientSize; + + std::atomic_bool userGainAsChanged; + Timer userGainAsChangedDelayTimer; // wxDECLARE_EVENT_TABLE(); };