From 68b095ef8d8045d9a698e85c25b2d3be8f2b0957 Mon Sep 17 00:00:00 2001 From: "Charles J. Cliffe" Date: Wed, 11 May 2016 22:37:25 -0400 Subject: [PATCH] Save sample rate, gain levels and AGC state per-device, add sample rate to device dialog --- src/AppConfig.cpp | 113 ++++++++++++++++++++++++---- src/AppConfig.h | 17 ++++- src/AppFrame.cpp | 6 +- src/CubicSDR.cpp | 20 +++-- src/forms/SDRDevices/SDRDevices.cpp | 40 ++++++++++ src/forms/SDRDevices/SDRDevices.h | 1 + src/sdr/SoapySDRThread.cpp | 30 +++++++- src/util/DataTree.cpp | 2 + src/visual/GainCanvas.cpp | 7 +- src/visual/GainCanvas.h | 4 +- 10 files changed, 206 insertions(+), 34 deletions(-) diff --git a/src/AppConfig.cpp b/src/AppConfig.cpp index 506c4c9..0c20f83 100644 --- a/src/AppConfig.cpp +++ b/src/AppConfig.cpp @@ -4,6 +4,8 @@ DeviceConfig::DeviceConfig() : deviceId("") { ppm.store(0); offset.store(0); + agcMode.store(true); + sampleRate.store(0); } DeviceConfig::DeviceConfig(std::string deviceId) : DeviceConfig() { @@ -26,6 +28,24 @@ long long DeviceConfig::getOffset() { return offset.load(); } + +void DeviceConfig::setSampleRate(long srate) { + sampleRate.store(srate); +} + +long DeviceConfig::getSampleRate() { + return sampleRate.load(); +} + +void DeviceConfig::setAGCMode(bool agcMode) { + this->agcMode.store(agcMode); +} + +bool DeviceConfig::getAGCMode() { + return agcMode.load(); +} + + void DeviceConfig::setDeviceId(std::string deviceId) { busy_lock.lock(); this->deviceId = deviceId; @@ -62,21 +82,38 @@ void DeviceConfig::save(DataNode *node) { busy_lock.lock(); *node->newChild("id") = deviceId; *node->newChild("name") = deviceName; - *node->newChild("ppm") = (int)ppm; - *node->newChild("offset") = offset; - DataNode *streamOptsNode = node->newChild("streamOpts"); - for (ConfigSettings::const_iterator opt_i = streamOpts.begin(); opt_i != streamOpts.end(); opt_i++) { - *streamOptsNode->newChild(opt_i->first.c_str()) = opt_i->second; + *node->newChild("ppm") = (int)ppm.load(); + *node->newChild("offset") = offset.load(); + *node->newChild("sample_rate") = sampleRate.load(); + *node->newChild("agc_mode") = agcMode.load()?1:0; + + if (streamOpts.size()) { + DataNode *streamOptsNode = node->newChild("streamOpts"); + for (ConfigSettings::const_iterator opt_i = streamOpts.begin(); opt_i != streamOpts.end(); opt_i++) { + *streamOptsNode->newChild(opt_i->first.c_str()) = opt_i->second; + } } - DataNode *settingsNode = node->newChild("settings"); - for (ConfigSettings::const_iterator set_i = settings.begin(); set_i != settings.end(); set_i++) { - *settingsNode->newChild(set_i->first.c_str()) = set_i->second; + if (settings.size()) { + DataNode *settingsNode = node->newChild("settings"); + for (ConfigSettings::const_iterator set_i = settings.begin(); set_i != settings.end(); set_i++) { + *settingsNode->newChild(set_i->first.c_str()) = set_i->second; + } } - DataNode *rigIFs = node->newChild("rig_ifs"); - for (std::map::const_iterator rigIF_i = rigIF.begin(); rigIF_i != rigIF.end(); rigIF_i++) { - DataNode *ifNode = rigIFs->newChild("rig_if"); - *ifNode->newChild("model") = rigIF_i->first; - *ifNode->newChild("sdr_if") = rigIF_i->second; + if (rigIF.size()) { + DataNode *rigIFs = node->newChild("rig_ifs"); + for (std::map::const_iterator rigIF_i = rigIF.begin(); rigIF_i != rigIF.end(); rigIF_i++) { + DataNode *ifNode = rigIFs->newChild("rig_if"); + *ifNode->newChild("model") = rigIF_i->first; + *ifNode->newChild("sdr_if") = rigIF_i->second; + } + } + if (gains.size()) { + DataNode *gainsNode = node->newChild("gains"); + for (ConfigGains::const_iterator gain_i = gains.begin(); gain_i != gains.end(); gain_i++) { + DataNode *gainNode = gainsNode->newChild("gain"); + *gainNode->newChild("id") = gain_i->first; + *gainNode->newChild("value") = gain_i->second; + } } busy_lock.unlock(); } @@ -91,14 +128,24 @@ void DeviceConfig::load(DataNode *node) { int ppmValue = 0; ppm_node->element()->get(ppmValue); setPPM(ppmValue); - std::cout << "Loaded PPM for device '" << deviceId << "' at " << ppmValue << "ppm" << std::endl; } if (node->hasAnother("offset")) { DataNode *offset_node = node->getNext("offset"); long long offsetValue = 0; offset_node->element()->get(offsetValue); setOffset(offsetValue); - std::cout << "Loaded offset for device '" << deviceId << "' at " << offsetValue << "Hz" << std::endl; + } + if (node->hasAnother("agc_mode")) { + DataNode *agc_node = node->getNext("agc_mode"); + int agcModeValue = 0; + agc_node->element()->get(agcModeValue); + setAGCMode(agcModeValue?true:false); + } + if (node->hasAnother("sample_rate")) { + DataNode *sample_rate_node = node->getNext("sample_rate"); + long sampleRateValue = 0; + sample_rate_node->element()->get(sampleRateValue); + setSampleRate(sampleRateValue); } if (node->hasAnother("streamOpts")) { DataNode *streamOptsNode = node->getNext("streamOpts"); @@ -139,6 +186,21 @@ void DeviceConfig::load(DataNode *node) { } } } + if (node->hasAnother("gains")) { + DataNode *gainsNode = node->getNext("gains"); + while (gainsNode->hasAnother("gain")) { + DataNode *gainNode = gainsNode->getNext("gain"); + std::string keyName; + float fltSettingValue; + + gainNode->getNext("id")->element()->get(keyName); + gainNode->getNext("value")->element()->get(fltSettingValue); + + if (keyName != "" && !(fltSettingValue!=fltSettingValue)) { + setGain(keyName, fltSettingValue); + } + } + } busy_lock.unlock(); } @@ -181,6 +243,27 @@ ConfigSettings DeviceConfig::getSettings() { return settings; } + +void DeviceConfig::setGains(ConfigGains gains) { + this->gains = gains; +} + +ConfigGains DeviceConfig::getGains() { + return gains; +} + +void DeviceConfig::setGain(std::string key, float value) { + gains[key] = value; +} + +float DeviceConfig::getGain(std::string key, float defaultValue) { + if (gains.find(key) != gains.end()) { + return gains[key]; + } + return defaultValue; +} + + void DeviceConfig::setRigIF(int rigType, long long freq) { rigIF[rigType] = freq; } diff --git a/src/AppConfig.h b/src/AppConfig.h index 4b1198d..1b39b29 100644 --- a/src/AppConfig.h +++ b/src/AppConfig.h @@ -12,6 +12,7 @@ #include "SDRDeviceInfo.h" typedef std::map ConfigSettings; +typedef std::map ConfigGains; class DeviceConfig { public: @@ -24,6 +25,12 @@ public: void setOffset(long long offset); long long getOffset(); + void setSampleRate(long srate); + long getSampleRate(); + + void setAGCMode(bool agcMode); + bool getAGCMode(); + void setDeviceId(std::string deviceId); std::string getDeviceId(); @@ -39,7 +46,12 @@ public: ConfigSettings getSettings(); void setSetting(std::string key, std::string value); std::string getSetting(std::string key, std::string defaultValue); - + + void setGains(ConfigGains gains); + ConfigGains getGains(); + void setGain(std::string key, float value); + float getGain(std::string key, float defaultValue); + void setRigIF(int rigType, long long freq); long long getRigIF(int rigType); @@ -53,7 +65,10 @@ private: std::atomic_int ppm; std::atomic_llong offset; + std::atomic_bool agcMode; + std::atomic_long sampleRate; ConfigSettings streamOpts; + ConfigGains gains; std::map settings; std::map rigIF; }; diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 84d9154..5382d71 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -1597,7 +1597,9 @@ bool AppFrame::loadSession(std::string fileName) { long long center_freq = *header->getNext("center_freq"); std::cout << "\tCenter Frequency: " << center_freq << std::endl; - + + wxGetApp().setFrequency(center_freq); + if (header->hasAnother("sample_rate")) { int sample_rate = *header->getNext("sample_rate"); @@ -1613,8 +1615,6 @@ bool AppFrame::loadSession(std::string fileName) { } - wxGetApp().setFrequency(center_freq); - DataNode *demodulators = l.rootNode()->getNext("demodulators"); int numDemodulators = 0; diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 4821ee3..b2239b6 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -544,14 +544,11 @@ void CubicSDR::setDevice(SDRDeviceInfo *dev) { SoapySDR::Device *soapyDev = dev->getSoapyDevice(); if (soapyDev) { - //long long freqHigh, freqLow; + if (long devSampleRate = devConfig->getSampleRate()) { + sampleRate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, devSampleRate); + sampleRateInitialized.store(true); + } - //SoapySDR::RangeList freqRange = soapyDev->getFrequencyRange(SOAPY_SDR_RX, 0); - - //freqLow = freqRange[0].minimum(); - //freqHigh = freqRange[freqRange.size()-1].maximum(); - - // Try for a reasonable default sample rate. if (!sampleRateInitialized.load()) { sampleRate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, DEFAULT_SAMPLE_RATE); sampleRateInitialized.store(true); @@ -568,9 +565,16 @@ void CubicSDR::setDevice(SDRDeviceInfo *dev) { setPPM(devConfig->getPPM()); setOffset(devConfig->getOffset()); + + + if (devConfig->getAGCMode()) { + setAGCMode(true); + } else { + setAGCMode(false); + } t_SDR = new std::thread(&SDRThread::threadMain, sdrThread); - } +} stoppedDev = nullptr; } diff --git a/src/forms/SDRDevices/SDRDevices.cpp b/src/forms/SDRDevices/SDRDevices.cpp index 8bcec51..d22e5b9 100644 --- a/src/forms/SDRDevices/SDRDevices.cpp +++ b/src/forms/SDRDevices/SDRDevices.cpp @@ -113,6 +113,30 @@ void SDRDevicesDialog::refreshDeviceProperties() { devSettings["name"] = m_propertyGrid->Append( new wxStringProperty("Name", wxPG_LABEL, devConfig->getDeviceName()) ); devSettings["offset"] = m_propertyGrid->Append( new wxIntProperty("Offset (Hz)", wxPG_LABEL, devConfig->getOffset()) ); + int currentSampleRate = wxGetApp().getSampleRate(); + int deviceSampleRate = devConfig->getSampleRate(); + + if (!deviceSampleRate) { + deviceSampleRate = selDev->getSampleRateNear(SOAPY_SDR_RX, 0, currentSampleRate); + } + + SoapySDR::ArgInfo sampleRateArg; + std::vector rateOpts = selDev->getSampleRates(SOAPY_SDR_RX, 0); + + for (std::vector::iterator rate_i = rateOpts.begin(); rate_i != rateOpts.end(); rate_i++) { + sampleRateArg.options.push_back(std::to_string(*rate_i)); + sampleRateArg.optionNames.push_back(frequencyToStr(*rate_i)); + } + + sampleRateArg.type = SoapySDR::ArgInfo::STRING; + sampleRateArg.units = "Hz"; + sampleRateArg.name = "Sample Rate"; + sampleRateArg.key = "sample_rate"; + sampleRateArg.value = std::to_string(deviceSampleRate); + + devSettings["sample_rate"] = addArgInfoProperty(m_propertyGrid, sampleRateArg); + deviceArgs["sample_rate"] = sampleRateArg; + runtimeArgs.erase(runtimeArgs.begin(), runtimeArgs.end()); runtimeProps.erase(runtimeProps.begin(), runtimeProps.end()); streamProps.erase(streamProps.begin(), streamProps.end()); @@ -291,6 +315,7 @@ void SDRDevicesDialog::OnUseSelected( wxMouseEvent& event) { wxGetApp().setDeviceArgs(settingArgs); wxGetApp().setStreamArgs(streamArgs); wxGetApp().setDevice(dev); + Close(); } event.Skip(); @@ -406,6 +431,21 @@ void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) { long offset = event.GetPropertyValue().GetInteger(); devConfig->setOffset(offset); + } else if (dev && event.GetProperty() == devSettings["sample_rate"]) { + DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId()); + + std::string strRate = deviceArgs["sample_rate"].options[event.GetPropertyValue().GetInteger()]; + int srate = 0; + try { + srate = std::stoi(strRate); + devConfig->setSampleRate(srate); + + if (dev->isActive() || !wxGetApp().getDevice()) { + wxGetApp().setSampleRate(srate); + } + } catch (std::invalid_argument e) { + // nop + } } else if (editId && dev) { wxPGProperty *prop = event.GetProperty(); diff --git a/src/forms/SDRDevices/SDRDevices.h b/src/forms/SDRDevices/SDRDevices.h index ad8f71d..199abbe 100644 --- a/src/forms/SDRDevices/SDRDevices.h +++ b/src/forms/SDRDevices/SDRDevices.h @@ -36,6 +36,7 @@ private: std::map devItems; std::map::iterator devItems_i; SDRDeviceInfo *dev; + std::map deviceArgs; std::map runtimeProps; std::map runtimeArgs; std::map streamProps; diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp index ac17723..66c60ee 100644 --- a/src/sdr/SoapySDRThread.cpp +++ b/src/sdr/SoapySDRThread.cpp @@ -302,6 +302,15 @@ void SDRThread::updateSettings() { agc_mode_changed.store(false); if (!agc_mode.load()) { updateGains(); + + 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); + } + } } doUpdate = true; } @@ -394,7 +403,7 @@ int SDRThread::getOptimalElementCount(long long sampleRate, int fps) { int elemCount = (int)floor((double)sampleRate/(double)fps); int nch = numChannels.load(); elemCount = int(ceil((double)elemCount/(double)nch))*nch; - std::cout << "Calculated optimal " << numChannels.load() << " channel element count of " << elemCount << std::endl; +// std::cout << "Calculated optimal " << numChannels.load() << " channel element count of " << elemCount << std::endl; return elemCount; } @@ -450,7 +459,7 @@ void SDRThread::unlockFrequency() { void SDRThread::setOffset(long long ofs) { offset.store(ofs); offset_changed.store(true); - std::cout << "Set offset: " << offset.load() << std::endl; +// std::cout << "Set offset: " << offset.load() << std::endl; } long long SDRThread::getOffset() { @@ -460,7 +469,11 @@ long long SDRThread::getOffset() { void SDRThread::setSampleRate(int rate) { sampleRate.store(rate); rate_changed = true; - std::cout << "Set sample rate: " << sampleRate.load() << std::endl; + DeviceConfig *devConfig = deviceConfig.load(); + if (devConfig) { + devConfig->setSampleRate(rate); + } +// std::cout << "Set sample rate: " << sampleRate.load() << std::endl; } int SDRThread::getSampleRate() { return sampleRate.load(); @@ -469,7 +482,7 @@ int SDRThread::getSampleRate() { void SDRThread::setPPM(int ppm) { this->ppm.store(ppm); ppm_changed.store(true); - std::cout << "Set PPM: " << this->ppm.load() << std::endl; +// std::cout << "Set PPM: " << this->ppm.load() << std::endl; } int SDRThread::getPPM() { @@ -479,6 +492,10 @@ int SDRThread::getPPM() { void SDRThread::setAGCMode(bool mode) { agc_mode.store(mode); agc_mode_changed.store(true); + DeviceConfig *devConfig = deviceConfig.load(); + if (devConfig) { + devConfig->setAGCMode(mode); + } } bool SDRThread::getAGCMode() { @@ -499,6 +516,11 @@ void SDRThread::setGain(std::string name, float value) { gainChanged[name] = true; gain_value_changed.store(true); gain_busy.unlock(); + + DeviceConfig *devConfig = deviceConfig.load(); + if (devConfig) { + devConfig->setGain(name, value); + } } float SDRThread::getGain(std::string name) { diff --git a/src/util/DataTree.cpp b/src/util/DataTree.cpp index bda9cf1..968d371 100755 --- a/src/util/DataTree.cpp +++ b/src/util/DataTree.cpp @@ -371,6 +371,8 @@ std::string DataElement::toString() { double floatSettingValue; get(floatSettingValue); strValue = std::to_string(floatSettingValue); + } else if (dataType == DATA_NULL) { + strValue = ""; } else { std::cout << "Unhandled DataElement toString for type: " << dataType << std::endl; } diff --git a/src/visual/GainCanvas.cpp b/src/visual/GainCanvas.cpp index 6c6784f..e72aa66 100644 --- a/src/visual/GainCanvas.cpp +++ b/src/visual/GainCanvas.cpp @@ -36,6 +36,7 @@ GainCanvas::GainCanvas(wxWindow *parent, int *dispAttrs) : barWidth = (1.0/numGains)*0.8; startPos = spacing/2.0; barHeight = 0.8; + refreshCounter = 0; } GainCanvas::~GainCanvas() { @@ -186,8 +187,9 @@ void GainCanvas::updateGainUI() { const wxSize ClientSize = GetClientSize(); SDRDeviceInfo *devInfo = wxGetApp().getDevice(); + DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(devInfo->getDeviceId()); - SDRRangeMap gains = devInfo->getGains(SOAPY_SDR_RX, 0); + gains = devInfo->getGains(SOAPY_SDR_RX, 0); SDRRangeMap::iterator gi; numGains = gains.size(); @@ -223,7 +225,8 @@ void GainCanvas::updateGainUI() { gInfo->name = gi->first; gInfo->low = gi->second.minimum(); gInfo->high = gi->second.maximum(); - gInfo->current = wxGetApp().getGain(gInfo->name); + gInfo->current = devConfig->getGain(gInfo->name,wxGetApp().getGain(gInfo->name)); + gInfo->changed = false; gInfo->panel.setBorderPx(1); gInfo->panel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X); diff --git a/src/visual/GainCanvas.h b/src/visual/GainCanvas.h index f79ca38..ed23a8f 100644 --- a/src/visual/GainCanvas.h +++ b/src/visual/GainCanvas.h @@ -10,7 +10,7 @@ #include "MouseTracker.h" #include "GLPanel.h" #include "PrimaryGLContext.h" - +#include "SDRDeviceInfo.h" #include "Timer.h" class GainInfo { @@ -53,8 +53,10 @@ private: std::string helpTip; std::vector gainInfo; GLPanel bgPanel; + SDRRangeMap gains; float spacing, barWidth, startPos, barHeight, numGains; + int refreshCounter; wxSize clientSize; // wxDECLARE_EVENT_TABLE();