diff --git a/Registration.cpp b/Registration.cpp index bd74679..cd6ee11 100644 --- a/Registration.cpp +++ b/Registration.cpp @@ -31,49 +31,77 @@ //the tuner is cached because the device cannot be opened twice in the same process, //and we require that findRTLSDR() yield the same results for SoapySDR device cache. //if another process attempts to find an open rtlsdr, it will be marked unavailable -static std::string get_tuner(const std::string &serial, const size_t deviceIndex) +// USB indexes identify receivers on every platform, including devices with +// duplicate or empty serials. Only tuner metadata probing is browser-specific: +// native libusb can safely open devices during discovery, while WebUSB cannot. +#ifndef __EMSCRIPTEN__ +static std::string get_tuner(const size_t deviceIndex) { static std::mutex mutex; std::lock_guard lock(mutex); - static std::map cache; - auto it = cache.find(serial); + static std::map cache; + auto it = cache.find(deviceIndex); if (it != cache.end()) return it->second; rtlsdr_dev_t *devTest; if (rtlsdr_open(&devTest, deviceIndex) != 0) return "unavailable"; const auto tuner = SoapyRTLSDR::rtlTunerToString(rtlsdr_get_tuner_type(devTest)); rtlsdr_close(devTest); - cache[serial] = tuner; + cache[deviceIndex] = tuner; return tuner; } +#endif static std::vector findRTLSDR(const SoapySDR::Kwargs &args) { std::vector results; - char manufact[256], product[256], serial[256]; + char manufact[256] = {}, product[256] = {}, serial[256] = {}; const size_t this_count = rtlsdr_get_device_count(); for (size_t i = 0; i < this_count; i++) { +#ifdef __EMSCRIPTEN__ + // Do not open every receiver merely to populate discovery metadata. + // WebUSB serializes asynchronous access to each authorized USBDevice, + // so probing one active or unresponsive receiver can block the others. + // Keep explicit legacy serial filtering, but use the cross-platform + // USB index as the normal device identity. + if (args.count("serial") != 0 && + rtlsdr_get_device_usb_strings(i, manufact, product, serial) != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, "rtlsdr_get_device_usb_strings(%zu) failed", i); + continue; + } +#else if (rtlsdr_get_device_usb_strings(i, manufact, product, serial) != 0) { SoapySDR_logf(SOAPY_SDR_ERROR, "rtlsdr_get_device_usb_strings(%zu) failed", i); continue; } SoapySDR_logf(SOAPY_SDR_DEBUG, "\tManufacturer: %s, Product Name: %s, Serial: %s", manufact, product, serial); +#endif + + const std::string index = std::to_string(i); + + // Index filtering works on every platform; serial remains a + // compatibility path for existing device strings. + if (args.count("index") != 0 and args.at("index") != index) continue; + if (args.count("serial") != 0 and args.at("serial") != serial) continue; SoapySDR::Kwargs devInfo; +#ifdef __EMSCRIPTEN__ + devInfo["label"] = std::string(rtlsdr_get_device_name(i)) + " [USB index " + index + "]"; +#else devInfo["label"] = std::string(rtlsdr_get_device_name(i)) + " :: " + serial; devInfo["product"] = product; devInfo["serial"] = serial; devInfo["manufacturer"] = manufact; - devInfo["tuner"] = get_tuner(serial, i); - - //filtering by serial - if (args.count("serial") != 0 and args.at("serial") != serial) continue; + devInfo["tuner"] = get_tuner(i); +#endif + devInfo["index"] = index; results.push_back(devInfo); } diff --git a/Settings.cpp b/Settings.cpp index d4f1c59..68ee526 100644 --- a/Settings.cpp +++ b/Settings.cpp @@ -27,6 +27,7 @@ #include #include #include +#include SoapyRTLSDR::SoapyRTLSDR(const SoapySDR::Kwargs &args): deviceId(-1), @@ -60,20 +61,50 @@ SoapyRTLSDR::SoapyRTLSDR(const SoapySDR::Kwargs &args): { if (args.count("label") != 0) SoapySDR_logf(SOAPY_SDR_INFO, "Opening %s...", args.at("label").c_str()); - //if a serial is not present, then findRTLSDR had zero devices enumerated - if (args.count("serial") == 0) throw std::runtime_error("No RTL-SDR devices found!"); + // Prefer the enumerated USB index on every platform so receivers with + // duplicate or empty serial descriptors can still be selected uniquely. + // Retain serial lookup for compatibility with existing configurations. + if (args.count("index") != 0) + { + const auto index = args.at("index"); + size_t parsedCharacters = 0; + unsigned long parsedIndex = 0; + try + { + parsedIndex = std::stoul(index, &parsedCharacters); + } + catch (const std::exception &) + { + throw std::runtime_error("Invalid RTL-SDR device index: " + index); + } - const auto serial = args.at("serial"); - deviceId = rtlsdr_get_index_by_serial(serial.c_str()); - if (deviceId < 0) throw std::runtime_error("rtlsdr_get_index_by_serial("+serial+") - " + std::to_string(deviceId)); + if (parsedCharacters != index.size() || + parsedIndex > std::numeric_limits::max() || + parsedIndex >= rtlsdr_get_device_count()) + { + throw std::runtime_error("Invalid RTL-SDR device index: " + index); + } + deviceId = static_cast(parsedIndex); + } + else if (args.count("serial") != 0) + { + const auto serial = args.at("serial"); + deviceId = rtlsdr_get_index_by_serial(serial.c_str()); + if (deviceId < 0) throw std::runtime_error("rtlsdr_get_index_by_serial("+serial+") - " + std::to_string(deviceId)); + } + else + { + throw std::runtime_error("No RTL-SDR device index or serial specified!"); + } if (args.count("tuner") != 0) tunerType = rtlStringToTuner(args.at("tuner")); - SoapySDR_logf(SOAPY_SDR_DEBUG, "RTL-SDR Tuner type: %s", rtlTunerToString(tunerType).c_str()); SoapySDR_logf(SOAPY_SDR_DEBUG, "RTL-SDR opening device %d", deviceId); if (rtlsdr_open(&dev, deviceId) != 0) { throw std::runtime_error("Unable to open RTL-SDR device"); } + tunerType = rtlsdr_get_tuner_type(dev); + SoapySDR_logf(SOAPY_SDR_DEBUG, "RTL-SDR Tuner type: %s", rtlTunerToString(tunerType).c_str()); //extract min/max overall gain range int num_gains = rtlsdr_get_tuner_gains(dev, nullptr); @@ -847,4 +878,3 @@ rtlsdr_tuner SoapyRTLSDR::rtlStringToTuner(std::string tunerType) return deviceTuner; } -