From 196c664cf44fc1c303783aed15ba6ff0194813b2 Mon Sep 17 00:00:00 2001 From: vsonnier Date: Sat, 21 Jan 2017 11:26:51 +0100 Subject: [PATCH] Device Sample Rate improvements: UI, changed device sample rates to be expressed as long instead of int at some places for safety/consistency. Also pedantic harmless traces changes. --- src/AppFrame.cpp | 158 +++++++++++++++++++++------- src/AppFrame.h | 8 ++ src/CubicSDR.cpp | 4 + src/CubicSDR.h | 2 + src/CubicSDRDefs.h | 2 + src/audio/AudioThread.cpp | 2 +- src/forms/SDRDevices/SDRDevices.cpp | 11 +- src/sdr/SoapySDRThread.cpp | 13 ++- src/sdr/SoapySDRThread.h | 6 +- src/visual/GainCanvas.cpp | 7 ++ src/visual/InteractiveCanvas.cpp | 6 +- src/visual/InteractiveCanvas.h | 10 +- 12 files changed, 171 insertions(+), 58 deletions(-) diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index 91fd5b7..b01bd58 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -709,9 +709,13 @@ void AppFrame::initDeviceParams(SDRDeviceInfo *devInfo) { deviceChanged.store(true); } +void AppFrame::notifyDeviceChanged() { + deviceChanged.store(true); +} + void AppFrame::updateDeviceParams() { - if (!deviceChanged.load()) { + if (!deviceChanged.load() || devInfo == nullptr) { return; } @@ -800,16 +804,35 @@ void AppFrame::updateDeviceParams() { menuBar->Replace(1, newSettingsMenu, wxT("&Settings")); settingsMenu = newSettingsMenu; - // Build sample rate menu + // Build/Rebuild the sample rate menu : sampleRates = devInfo->getSampleRates(SOAPY_SDR_RX, 0); - sampleRateMenuItems.erase(sampleRateMenuItems.begin(),sampleRateMenuItems.end()); + sampleRateMenuItems.clear(); wxMenu *newSampleRateMenu = new wxMenu; int ofs = 0; + + //Current sample rate, try to keep it as is. long sampleRate = wxGetApp().getSampleRate(); + + long minRate = sampleRates.front(); + long maxRate = sampleRates.back(); + + //If it is beyond limits, make device choose a reasonable value + if (sampleRate < minRate || sampleRate > maxRate) { + sampleRate = devInfo->getSampleRateNear(SOAPY_SDR_RX, 0, sampleRate); + } + + //Check if a manual entry was previously set: if so, check its value is still within the limits of the device. If not so, reset it. + if (manualSampleRate > 0 && + manualSampleRate < minRate || manualSampleRate > maxRate) { + manualSampleRate = -1; + } + bool checked = false; for (vector::iterator i = sampleRates.begin(); i != sampleRates.end(); i++) { + sampleRateMenuItems[wxID_BANDWIDTH_BASE+ofs] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_BASE+ofs, frequencyToStr(*i)); + if (sampleRate == (*i)) { sampleRateMenuItems[wxID_BANDWIDTH_BASE+ofs]->Check(true); checked = true; @@ -817,10 +840,26 @@ void AppFrame::updateDeviceParams() { ofs++; } - sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual Entry")); + //Add a manual sample value radio button, but disabled by default in case the user + //never ever uses manual entry. + if (manualSampleRate <= 0) { + sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual : N/A")); + sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(false); + } + else { + sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual : ") + frequencyToStr(manualSampleRate)); + sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(true); + } + + //We apply the current sample rate after all if (!checked) { sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true); } + + //Append a normal button (NOT a radio-button) for manual entry dialog at the end + 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; @@ -890,7 +929,6 @@ void AppFrame::disableRig() { } #endif - void AppFrame::OnMenu(wxCommandEvent& event) { // if (event.GetId() >= wxID_RT_AUDIO_DEVICE && event.GetId() < wxID_RT_AUDIO_DEVICE + (int)devices.size()) { @@ -1129,41 +1167,51 @@ void AppFrame::OnMenu(wxCommandEvent& event) { bookmarkView->updateTheme(); } - switch (event.GetId()) { - case wxID_BANDWIDTH_MANUAL: - int rateHigh, rateLow; - - SDRDeviceInfo *dev = wxGetApp().getDevice(); - if (dev == NULL) { - break; - } - + if (event.GetId() == wxID_BANDWIDTH_MANUAL) { + wxGetApp().setSampleRate(manualSampleRate); + } + else if (event.GetId() == wxID_BANDWIDTH_MANUAL_DIALOG) { + + int rateHigh, rateLow; + + SDRDeviceInfo *dev = wxGetApp().getDevice(); + if (dev != nullptr) { + std::vector sampleRates = dev->getSampleRates(SOAPY_SDR_RX, 0); - - rateLow = 2000000; - rateHigh = 30000000; - + + //default + rateLow = MANUAL_SAMPLE_RATE_MIN; + rateHigh = MANUAL_SAMPLE_RATE_MAX; + if (sampleRates.size()) { - rateLow = sampleRates[0]; - rateHigh = sampleRates[sampleRates.size()-1]; + rateLow = sampleRates.front(); + rateHigh = sampleRates.back(); } long bw = wxGetNumberFromUser("\n" + dev->getName() + "\n\n " - + "min: " + std::to_string(rateLow) + " Hz" - + ", max: " + std::to_string(rateHigh) + " Hz\n", - "Sample Rate in Hz", - "Manual Sample Rate Entry", - wxGetApp().getSampleRate(), - rateLow, - rateHigh, - this); + + "min: " + std::to_string(rateLow) + " Hz" + + ", max: " + std::to_string(rateHigh) + " Hz\n", + "Sample Rate in Hz", + "Manual Sample Rate Entry", + //If a manual sample rate has already been input, recall this one. + manualSampleRate > 0? manualSampleRate :wxGetApp().getSampleRate(), + rateLow, + rateHigh, + this); + if (bw != -1) { - wxGetApp().setSampleRate(bw); + + manualSampleRate = bw; + sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(true); + + sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->SetItemLabel(wxT("Manual : ") + frequencyToStr(manualSampleRate)); + sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true); + wxGetApp().setSampleRate(manualSampleRate); } - break; + } } - - if (event.GetId() >= wxID_BANDWIDTH_BASE && event.GetId() < wxID_BANDWIDTH_BASE + (int)sampleRates.size()) { + else if (event.GetId() >= wxID_BANDWIDTH_BASE && event.GetId() < wxID_BANDWIDTH_BASE + (int)sampleRates.size()) { + wxGetApp().setSampleRate(sampleRates[event.GetId()-wxID_BANDWIDTH_BASE]); } @@ -1842,18 +1890,54 @@ bool AppFrame::loadSession(std::string fileName) { } if (header->hasAnother("sample_rate")) { - int sample_rate = *header->getNext("sample_rate"); - + + long sample_rate = *header->getNext("sample_rate"); + SDRDeviceInfo *dev = wxGetApp().getSDRThread()->getDevice(); if (dev) { - // Try for a reasonable default sample rate. - sample_rate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, sample_rate); + //retreive the available sample rates. A valid previously chosen manual + //value is constrained within these limits. If it doesn't behave, lets the device choose + //for us. + long minRate = MANUAL_SAMPLE_RATE_MIN; + long maxRate = MANUAL_SAMPLE_RATE_MAX; + + std::vector sampleRates = dev->getSampleRates(SOAPY_SDR_RX, 0); + + if (sampleRates.size()) { + minRate = sampleRates.front(); + maxRate = sampleRates.back(); + } + + //If it is beyond limits, make device choose a reasonable value + if (sample_rate < minRate || sample_rate > maxRate) { + sample_rate = dev->getSampleRateNear(SOAPY_SDR_RX, 0, sample_rate); + } + + //scan the available sample rates and see if it matches a predifined one + int menuIndex = -1; + for (auto discreteRate : sampleRates) { + if (discreteRate == sample_rate) { + menuIndex++; + //activate Bandwidth Menu entry matching this predefined sample_rate. + sampleRateMenuItems[wxID_BANDWIDTH_BASE + menuIndex]->Check(true); + break; + } + } //end for + //this is a manual entry + if (menuIndex == -1) { + manualSampleRate = sample_rate; + sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Enable(true); + // Apply the manual value, activate the menu entry + + sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->SetItemLabel(wxString("Manual Entry : ") + frequencyToStr(sample_rate)); + sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true); + } + //update applied value wxGetApp().setSampleRate(sample_rate); deviceChanged.store(true); } else { wxGetApp().setSampleRate(sample_rate); } - } DemodulatorInstance *loadedActiveDemod = nullptr; diff --git a/src/AppFrame.h b/src/AppFrame.h index e45974a..97562d4 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -55,6 +55,7 @@ #define wxID_DISPLAY_BOOKMARKS 2107 #define wxID_BANDWIDTH_BASE 2150 +#define wxID_BANDWIDTH_MANUAL_DIALOG 2199 #define wxID_BANDWIDTH_MANUAL 2200 #define wxID_DISPLAY_BASE 2250 @@ -118,6 +119,12 @@ public: BookmarkView *getBookmarkView(); void disableSave(bool state); + + //call this in case the main UI is not + //the origin of device changes / sample rate by operator, + //and must be notified back to update its UI elements + //(ex: SDR Devices dialog changing the configuration) + void notifyDeviceChanged(); #ifdef _WIN32 bool canFocus(); @@ -174,6 +181,7 @@ private: SoapySDR::ArgInfoList settingArgs; int settingsIdMax; std::vector sampleRates; + long manualSampleRate = -1; std::string currentSessionFile; diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 522fd3b..18afff8 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -917,6 +917,10 @@ bool CubicSDR::areDevicesReady() { return devicesReady.load(); } +void CubicSDR::notifyMainUIOfDeviceChange() { + appframe->notifyDeviceChanged(); +} + bool CubicSDR::areDevicesEnumerating() { return !sdrEnum->isTerminated(); } diff --git a/src/CubicSDR.h b/src/CubicSDR.h index 35136bd..33bfa31 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -138,6 +138,8 @@ public: bool areDevicesEnumerating(); bool areModulesMissing(); std::string getNotification(); + + void notifyMainUIOfDeviceChange(); void addRemote(std::string remoteAddr); void removeRemote(std::string remoteAddr); diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index 2f172ae..17980bd 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -40,4 +40,6 @@ const char filePathSeparator = #define CHANNELIZER_RATE_MAX 500000 +#define MANUAL_SAMPLE_RATE_MIN 2000000 // 2MHz +#define MANUAL_SAMPLE_RATE_MAX 200000000 // 200MHz (We are 2017+ after all) diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp index 9344b6e..bb8b6d0 100644 --- a/src/audio/AudioThread.cpp +++ b/src/audio/AudioThread.cpp @@ -260,7 +260,7 @@ void AudioThread::enumerateDevices(std::vector &devs) { std::cout << "\t\t32-bit float normalized between plus/minus 1.0." << std::endl; } if (nFormats & RTAUDIO_FLOAT64) { - std::cout << "\t\t32-bit float normalized between plus/minus 1.0." << std::endl; + std::cout << "\t\t64-bit float normalized between plus/minus 1.0." << std::endl; } std::vector::iterator srate; diff --git a/src/forms/SDRDevices/SDRDevices.cpp b/src/forms/SDRDevices/SDRDevices.cpp index f1554ab..a0ff00a 100644 --- a/src/forms/SDRDevices/SDRDevices.cpp +++ b/src/forms/SDRDevices/SDRDevices.cpp @@ -116,8 +116,8 @@ 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(); + long currentSampleRate = wxGetApp().getSampleRate(); + long deviceSampleRate = devConfig->getSampleRate(); if (!deviceSampleRate) { deviceSampleRate = selDev->getSampleRateNear(SOAPY_SDR_RX, 0, currentSampleRate); @@ -318,7 +318,7 @@ void SDRDevicesDialog::OnUseSelected( wxMouseEvent& event) { wxGetApp().setDeviceArgs(settingArgs); wxGetApp().setStreamArgs(streamArgs); wxGetApp().setDevice(dev,0); - + wxGetApp().notifyMainUIOfDeviceChange(); Close(); } event.Skip(); @@ -438,13 +438,14 @@ void SDRDevicesDialog::OnPropGridChanged( wxPropertyGridEvent& event ) { DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId()); std::string strRate = deviceArgs["sample_rate"].options[event.GetPropertyValue().GetInteger()]; - int srate = 0; + long srate = 0; try { - srate = std::stoi(strRate); + srate = std::stol(strRate); devConfig->setSampleRate(srate); if (dev->isActive() || !wxGetApp().getDevice()) { wxGetApp().setSampleRate(srate); + wxGetApp().notifyMainUIOfDeviceChange(); } } catch (std::invalid_argument e) { // nop diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp index a79d12f..0c1e36f 100644 --- a/src/sdr/SoapySDRThread.cpp +++ b/src/sdr/SoapySDRThread.cpp @@ -208,7 +208,12 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) { int n_stream_read = device->readStream(stream, buffs, mtElems, flags, timeNs); //if the n_stream_read <= 0, bail out from reading. - if (n_stream_read <= 0) { + if (n_stream_read == 0) { + std::cout << "SDRThread::readStream(): read blocking..." << std::endl; + break; + } + else if (n_stream_read < 0) { + std::cout << "SDRThread::readStream(): read failed with code: " << n_stream_read << std::endl; break; } @@ -226,7 +231,7 @@ void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) { } else { break; } - } + } //end while if (n_read > 0 && !stopping && !iqDataOutQueue->full()) { SDRThreadIQData *dataOut = buffers.getBuffer(); @@ -515,7 +520,7 @@ long long SDRThread::getOffset() { return offset.load(); } -void SDRThread::setSampleRate(int rate) { +void SDRThread::setSampleRate(long rate) { sampleRate.store(rate); rate_changed = true; DeviceConfig *devConfig = deviceConfig.load(); @@ -524,7 +529,7 @@ void SDRThread::setSampleRate(int rate) { } // std::cout << "Set sample rate: " << sampleRate.load() << std::endl; } -int SDRThread::getSampleRate() { +long SDRThread::getSampleRate() { return sampleRate.load(); } diff --git a/src/sdr/SoapySDRThread.h b/src/sdr/SoapySDRThread.h index 3bf1b38..794b837 100644 --- a/src/sdr/SoapySDRThread.h +++ b/src/sdr/SoapySDRThread.h @@ -70,8 +70,8 @@ public: void setOffset(long long ofs); long long getOffset(); - void setSampleRate(int rate); - int getSampleRate(); + void setSampleRate(long rate); + long getSampleRate(); void setPPM(int ppm); int getPPM(); @@ -109,7 +109,7 @@ protected: std::map settings; std::map settingChanged; - std::atomic sampleRate; + std::atomic_llong sampleRate; std::atomic_llong frequency, offset, lock_freq; std::atomic_int ppm, numElems, mtuElems, numChannels; std::atomic_bool hasPPM, hasHardwareDC; diff --git a/src/visual/GainCanvas.cpp b/src/visual/GainCanvas.cpp index 7ef1b1b..1087bfe 100644 --- a/src/visual/GainCanvas.cpp +++ b/src/visual/GainCanvas.cpp @@ -170,6 +170,13 @@ void GainCanvas::setHelpTip(std::string tip) { void GainCanvas::updateGainUI() { SDRDeviceInfo *devInfo = wxGetApp().getDevice(); + + //possible if we 'Refresh Devices' then devInfo becomes null + //until a new device is selected. + if (devInfo == nullptr) { + return; + } + DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(devInfo->getDeviceId()); gains = devInfo->getGains(SOAPY_SDR_RX, 0); diff --git a/src/visual/InteractiveCanvas.cpp b/src/visual/InteractiveCanvas.cpp index 38f6765..add3938 100644 --- a/src/visual/InteractiveCanvas.cpp +++ b/src/visual/InteractiveCanvas.cpp @@ -30,7 +30,7 @@ InteractiveCanvas::InteractiveCanvas(wxWindow *parent, int *dispAttrs) : InteractiveCanvas::~InteractiveCanvas() { } -void InteractiveCanvas::setView(long long center_freq_in, int bandwidth_in) { +void InteractiveCanvas::setView(long long center_freq_in, long long bandwidth_in) { isView = true; centerFreq = center_freq_in; bandwidth = bandwidth_in; @@ -74,11 +74,11 @@ long long InteractiveCanvas::getCenterFrequency() { } } -void InteractiveCanvas::setBandwidth(unsigned int bandwidth_in) { +void InteractiveCanvas::setBandwidth(long long bandwidth_in) { bandwidth = bandwidth_in; } -unsigned int InteractiveCanvas::getBandwidth() { +long long InteractiveCanvas::getBandwidth() { if (isView) { return bandwidth; } else { diff --git a/src/visual/InteractiveCanvas.h b/src/visual/InteractiveCanvas.h index 07346f7..769b622 100644 --- a/src/visual/InteractiveCanvas.h +++ b/src/visual/InteractiveCanvas.h @@ -17,15 +17,15 @@ public: long long getFrequencyAt(float x); long long getFrequencyAt(float x, long long iqCenterFreq, long long iqBandwidth); - virtual void setView(long long center_freq_in, int bandwidth_in); + virtual void setView(long long center_freq_in, long long bandwidth_in); virtual void disableView(); bool getViewState(); void setCenterFrequency(long long center_freq_in); long long getCenterFrequency(); - void setBandwidth(unsigned int bandwidth_in); - unsigned int getBandwidth(); + void setBandwidth(long long bandwidth_in); + long long getBandwidth(); MouseTracker *getMouseTracker(); bool isMouseInView(); @@ -59,8 +59,8 @@ protected: bool ctrlDown; long long centerFreq; - unsigned int bandwidth; - unsigned int lastBandwidth; + long long bandwidth; + long long lastBandwidth; bool isView; std::string lastToolTip;