From d5103ed1401d6f2003e1bc12f49068ad4b51c0ba Mon Sep 17 00:00:00 2001 From: Sergi Aliaga Date: Fri, 21 Aug 2026 11:45:49 -0400 Subject: [PATCH] Fix crash selecting devices whose driver reports sample rates as a range Drivers that only implement getSampleRateRange() (e.g. the usdr module for Wavelet Lab xSDR) crash CubicSDR on device start with: terminating due to uncaught exception of type std::runtime_error: SoapyUSDR::setSampleRate() unable to set samplerate! Two problems in SDRDeviceInfo::getSampleRates(): - SoapySDR's deprecated-API shim expands a continuous rate range into a stepped list whose first entry is 0. The 0 survives decimation, getSampleRateNear() picks it, and the driver throws on setSampleRate(0). Filter out non-positive rates. - If a driver returns an empty rate list, the existing code calls sampleRates.back() on an empty vector (undefined behavior). Synthesize a candidate list from getSampleRateRange() instead, fall back to the range endpoints, and bail out cleanly if there is no rate information at all. Verified on a Wavelet Lab xSDR over USB: device selection previously aborted the app, now starts and streams normally. --- src/sdr/SDRDeviceInfo.cpp | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/sdr/SDRDeviceInfo.cpp b/src/sdr/SDRDeviceInfo.cpp index d7c55de..9b56862 100644 --- a/src/sdr/SDRDeviceInfo.cpp +++ b/src/sdr/SDRDeviceInfo.cpp @@ -196,6 +196,48 @@ std::vector SDRDeviceInfo::getSampleRates(int direction, size_t channel) { //the original list returned from the driver: std::vector sampleRates = dev->listSampleRates(direction, channel); + //drop bogus non-positive rates: range-only drivers (e.g. usdr/xsdr) go through + //the SoapySDR deprecated-API shim, which can emit a 0 entry that later gets + //selected and makes the driver throw on setSampleRate(0). + sampleRates.erase(std::remove_if(sampleRates.begin(), sampleRates.end(), + [](double r) { return r <= 0; }), sampleRates.end()); + + //some drivers (e.g. usdr/xsdr) only report a continuous range and return an + //empty discrete list; synthesize candidate rates from the range instead. + if (sampleRates.empty()) { + + SoapySDR::RangeList rateRanges = dev->getSampleRateRange(direction, channel); + + const double candidateRates[] = { 250000, 500000, 1000000, 2000000, 2500000, + 4000000, 5000000, 8000000, 10000000, 16000000, 20000000, 25000000, + 32000000, 40000000, 50000000, 64000000, 80000000, 100000000, 125000000 }; + + for (double candidate : candidateRates) { + for (const auto& range : rateRanges) { + if (candidate >= range.minimum() && candidate <= range.maximum()) { + sampleRates.push_back(candidate); + break; + } + } + } + + //still nothing usable: fall back to the range endpoints themselves + if (sampleRates.empty()) { + for (const auto& range : rateRanges) { + sampleRates.push_back(range.minimum()); + if (range.maximum() != range.minimum()) { + sampleRates.push_back(range.maximum()); + } + } + } + } + + //no rate info at all: bail out with an empty list rather than reading + //past the end of an empty vector below. + if (sampleRates.empty()) { + return result; + } + //be paranoid, sort by increasing rates... std::sort(sampleRates.begin(), sampleRates.end(), [](double a, double b) -> bool { return a < b; });