diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 75989d2..fc901fc 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -943,7 +943,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; @@ -952,9 +951,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); @@ -1242,7 +1238,7 @@ bool AppFrame::actionOnMenuSettings(wxCommandEvent& event) { menuIdx++; } } //end for - + return true; } diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 898f53f..e6b63b1 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -947,8 +947,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 c6394be..d097829 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..b5ae9e5 100644 --- a/src/forms/SDRDevices/SDRDevices.cpp +++ b/src/forms/SDRDevices/SDRDevices.cpp @@ -395,7 +395,6 @@ void SDRDevicesDialog::OnUseSelected( wxMouseEvent& event) { wxGetApp().setDeviceArgs(settingArgs); wxGetApp().setStreamArgs(streamArgs); wxGetApp().setDevice(dev,0); - wxGetApp().notifyMainUIOfDeviceChange(); Close(); } event.Skip(); @@ -494,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(); @@ -549,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++) { @@ -569,14 +568,10 @@ void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) { if (dev->isActive()) { wxGetApp().getSDRThread()->writeSetting(rtp->first, settingValue); } - - wxGetApp().notifyMainUIOfDeviceChange(); return; } } } - // general refresh. - wxGetApp().notifyMainUIOfDeviceChange(); } 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/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/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 c5a3bef..70010cf 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) @@ -41,6 +42,8 @@ GainCanvas::GainCanvas(wxWindow *parent, std::vector dispAttrs) : startPos = spacing/2.0; barHeight = 0.8f; refreshCounter = 0; + + userGainAsChanged = false; } GainCanvas::~GainCanvas() { @@ -69,13 +72,40 @@ void GainCanvas::OnIdle(wxIdleEvent &event) { } else { event.Skip(); } + + bool areGainsChangedHere = false; for (auto gi : gainPanels) { if (gi->getChanged()) { - wxGetApp().setGain(gi->getName(), gi->getValue()); + 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 rebuild the gain UI + gi->setChanged(false); } } + + //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; + } + } } void GainCanvas::SetLevel() { @@ -86,8 +116,7 @@ void GainCanvas::SetLevel() { float value = gi->getMeterHitValue(mpos); gi->setValue(value); - gi->setChanged(true); - + gi->setChanged(true); break; } } @@ -167,13 +196,12 @@ void GainCanvas::OnMouseEnterWindow(wxMouseEvent& event) { #endif } - - void GainCanvas::setHelpTip(std::string tip) { helpTip = tip; } void GainCanvas::updateGainUI() { + SDRDeviceInfo *devInfo = wxGetApp().getDevice(); //possible if we 'Refresh Devices' then devInfo becomes null @@ -183,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; @@ -205,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))); @@ -217,10 +250,67 @@ void GainCanvas::updateGainUI() { gainPanels.push_back(mPanel); i++; } - + 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. + //also, do not attempt an update with the device is not started. + if (devInfo == nullptr || !devInfo->isActive()) { + 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, 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 ((int)actualRoundedGain != (int)(gainPanels[panelIndex]->getValue())) { + + 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, actualRoundedGain); + + 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..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,9 +26,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); @@ -50,6 +55,9 @@ private: float spacing, barWidth, startPos, barHeight, numGains; int refreshCounter; wxSize clientSize; + + std::atomic_bool userGainAsChanged; + Timer userGainAsChangedDelayTimer; // wxDECLARE_EVENT_TABLE(); };