From 002121cff15c1aaff8a5613fdb95d6c2ec729909 Mon Sep 17 00:00:00 2001 From: Ezra Taimuty-Loomis Date: Wed, 10 Feb 2021 12:30:11 -0500 Subject: [PATCH] Implemented tuner manager --- src/PiScan.h | 1 + src/common/Configuration.h | 14 +++ src/piscan_server.cpp | 4 + src/sigproc/CMakeLists.txt | 1 + src/sigproc/Demodulator.cpp | 51 ++------- src/sigproc/Demodulator.h | 2 + src/sigproc/TunerManager.cpp | 149 +++++++++++++++++++++++++ src/sigproc/TunerManager.h | 49 ++++++++ src/sigproc/sigproc_types.h | 1 + src/sigproc/test/CMakeLists.txt | 1 + src/sigproc/test/TunerManager_test.cpp | 0 11 files changed, 229 insertions(+), 44 deletions(-) create mode 100644 src/sigproc/TunerManager.cpp create mode 100644 src/sigproc/TunerManager.h create mode 100644 src/sigproc/test/TunerManager_test.cpp diff --git a/src/PiScan.h b/src/PiScan.h index af3c932..fa58e80 100644 --- a/src/PiScan.h +++ b/src/PiScan.h @@ -49,6 +49,7 @@ void setDemodSquelch(float level); piscan::server::context::DemodContext getDemodContext(); void squelchBreak(bool mute = true); long long getTunerSampleRate(); +//void setTunerPPM(int ppm); } /* server functions */ diff --git a/src/common/Configuration.h b/src/common/Configuration.h index c331d9a..68adbce 100644 --- a/src/common/Configuration.h +++ b/src/common/Configuration.h @@ -59,6 +59,20 @@ struct DemodConfig { int squelchType = DEFAULT_SQUELCH_MODE; }; +#define DEFAULT_RANK 0 +#define DEFAULT_DESC "NULL" +#define DEFAULT_DRIVER "null" +#define DEFAULT_PPM 0 +#define DEFAULT_TUNER_SAMPLE_RATE 2048000 + +struct TunerConfig { + int rank = DEFAULT_RANK; + std::string description = DEFAULT_DESC; + std::string driver = DEFAULT_DRIVER; + int ppmCorrection = DEFAULT_PPM; + long int sampleRate = DEFAULT_TUNER_SAMPLE_RATE; +}; + #define DEFAULT_RTSP_PORT 8554 #define DEFAULT_RTSP_OVER_HTTP false diff --git a/src/piscan_server.cpp b/src/piscan_server.cpp index 7e228aa..9613838 100644 --- a/src/piscan_server.cpp +++ b/src/piscan_server.cpp @@ -150,6 +150,10 @@ long long app::demod::getTunerSampleRate() { return demodInst.getTunerSampleRate(); } +/*void app::demod::setTunerPPM(int ppm) { + // TODO +}*/ + const piscan::server::context::SystemInfo app::system::getSystemInfo(){ piscan::server::context::SystemInfo info = { .version = PISCAN_VERSION, diff --git a/src/sigproc/CMakeLists.txt b/src/sigproc/CMakeLists.txt index 7318044..b98f9ac 100644 --- a/src/sigproc/CMakeLists.txt +++ b/src/sigproc/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(sigproc ${CMAKE_CURRENT_SOURCE_DIR}/Demodulator.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TunerManager.cpp ) add_subdirectory(test) diff --git a/src/sigproc/Demodulator.cpp b/src/sigproc/Demodulator.cpp index 9cfbae2..2108ac4 100644 --- a/src/sigproc/Demodulator.cpp +++ b/src/sigproc/Demodulator.cpp @@ -12,7 +12,6 @@ #include "Demodulator.h" #include "loguru.hpp" -#define DEFAULT_SDR_SAMPLE_RATE 2048000 #define INIT_FREQUENCY 100000000 #define NUM_RATES_DEFAULT 4 #define SIGLEVEL_REFRESH_INTERVAL 100 // milliseconds @@ -20,56 +19,19 @@ namespace piscan { namespace sigproc { -Demodulator::Demodulator() : _cubic(makeCubic()), _demodMgr(_cubic->getDemodMgr()) {}; +Demodulator::Demodulator() : _cubic(makeCubic()), _demodMgr(_cubic->getDemodMgr()), _tunerManager(_cubic) {}; void Demodulator::start(){ piscan::config::DemodState& state = app::system::getConfig().getDemodState(); _squelchLevel = state.squelch; _gain = state.gain; + // TODO assertions will be used until a soft abort is made available CHECK_F(_cubic->OnInit()); - while(_cubic->areDevicesEnumerating()); - - std::vector* devs = _cubic->getDevices(); - - CHECK_F(devs->size() > 0, "No SDR devices are available"); - - //TODO config file for this - LOG_F(INFO, "Auto-selecting SDR device"); - size_t i; - for(i = 0; i < devs->size();){ - if(devs->at(i)->getDriver() != "audio" && devs->at(i)->isAvailable()) - break; - - i++; - } - - CHECK_F(i < devs->size(), "No valid or available devices found"); - - auto dev = devs->at(i); - - CHECK_F(dev->getDriver() != "audio"); - - LOG_F(INFO, "Auto selected: %s", dev->getName().c_str()); - - LOG_F(INFO, "Auto selecting sample rate"); - std::vector srates = dev->getSampleRates(SOAPY_SDR_RX, 0); - long srate = 0; - for(i = 0; i < srates.size() - 1; i++){ - if(srates[i] <= DEFAULT_SDR_SAMPLE_RATE && srates[i+1] > DEFAULT_SDR_SAMPLE_RATE){ - break; - } - } - - srate = srates[i]; - LOG_F(INFO, "Setting device sample rate to %li", srate); - - //_cubic->setGain(name, gain_in); - - _cubic->setDevice(dev, 2000); - _cubic->setSampleRate(srate); - + CHECK_F(_tunerManager.enumerateDevices()); + CHECK_F(_tunerManager.autoSelectTuner()); + CHECK_F(_tunerManager.startSelectedTuner()); //sets sample rate for outputs - imported from cubic unsigned int desired_rates[NUM_RATES_DEFAULT] = { 48000, 44100, 96000, 192000 }; @@ -166,7 +128,8 @@ void Demodulator::stop(){ _sigLevelRefresher.stop(); //delete _sigLevelRefresher; - _cubic->stopDevice(false, 2000); + //_cubic->stopDevice(false, 2000); + _tunerManager.stopSelectedTuner(); _cubic->OnExit(); piscan::config::DemodState& state = app::system::getConfig().getDemodState(); diff --git a/src/sigproc/Demodulator.h b/src/sigproc/Demodulator.h index aaa60c1..3d61ea7 100644 --- a/src/sigproc/Demodulator.h +++ b/src/sigproc/Demodulator.h @@ -18,6 +18,7 @@ #include "Configuration.h" #include "synchronize.h" #include "IntervalTimer.h" +#include "TunerManager.h" namespace piscan { @@ -56,6 +57,7 @@ private: std::shared_ptr _cubic; DemodulatorMgr& _demodMgr; + TunerManager _tunerManager; std::map _demods; diff --git a/src/sigproc/TunerManager.cpp b/src/sigproc/TunerManager.cpp new file mode 100644 index 0000000..f770934 --- /dev/null +++ b/src/sigproc/TunerManager.cpp @@ -0,0 +1,149 @@ +#include + +#include "TunerManager.h" +#include "CubicSDR.h" +#include "loguru.hpp" + +#define AUDIO_DRIVER "audio" + + +namespace piscan{ +namespace sigproc { + +/* + * @param cubic pointer to CubicSDR instance + */ +TunerManager::TunerManager(std::shared_ptr cubic) : _cubic(cubic) { +} + +/* + * @return True if at least one SDR device was discovered and is available, + * False if no SDRs are available or if an error occured + */ +bool TunerManager::enumerateDevices() { + while(_cubic->areDevicesEnumerating()) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + std::vector* devs = _cubic->getDevices(); + + //CHECK_F(devs->size() > 0, "No SDR devices are available"); + if (devs->empty()) { + LOG_F(WARNING, "No devices are available"); + return false; + } + + // Place devices into map by serial number and filter out audio devices + for (auto device : *devs) { + if (device->getDriver() != AUDIO_DRIVER) { + const std::string& name = device->getName(); + if (name.size()) { + _devs[name] = device; + } + } + } + + return true; +} + +bool TunerManager::deviceAvailable() { + return true; +} + +bool TunerManager::selectFirstAvailableDevice() { + //TODO this is temporary - will eventually be replaced by a config interface + LOG_F(INFO, "Selecting first available device"); + SDRDeviceInfo* device = (*(_devs.begin())).second; + _selectedTuner.device = device; + _selectedTuner.config.driver = device->getDriver(); + _selectedTuner.config.description = device->getName(); + return true; +} + +/*std::vector TunerManager::getKnownDevices() { + +} + +std::vector TunerManager::getAllDevices() { + +}*/ + +/* + * @param desired The desired sample rate + * @param supportedRates Vector containing all sample rates supported by a tuner + * @return The supported sample rate closest to desired, or -1 if no rates are supported + */ +long TunerManager::nearestSampleRate(long desired, std::vector& supportedRates) { + long minDiff = LONG_MAX; + long result = -1; + long diff; + + for (long rate : supportedRates) { + diff = std::abs(desired - rate); + if (diff < minDiff) { + result = rate; + minDiff = diff; + } + } + + return result; +} + +/* + * Allow the tuner to choose the tuner to use - by default the first available tuner found in config + * @return False if a tuner could not be selected + */ +bool TunerManager::autoSelectTuner() { + if(_devs.empty()) { + LOG_F(WARNING, "No SDR devices were found"); + return false; + } + + if (_savedTuners.empty()) { + LOG_F(INFO, "No saved tuners found in config, defaulting to first available"); + selectFirstAvailableDevice(); + + LOG_F(INFO, "New tuner will be saved to config"); + _savedTuners.push(_selectedTuner.config); + } + + LOG_F(INFO, "Auto selecting sample rate"); + std::vector srates(std::move(_selectedTuner.device->getSampleRates(SOAPY_SDR_RX, 0))); + + RAW_LOG_F(1, "Supported sample rates for tuner:"); + for (long rate : srates) + RAW_LOG_F(1, "\t%li", rate); + + _selectedTuner.config.sampleRate = nearestSampleRate(DEFAULT_TUNER_SAMPLE_RATE, srates); // TODO hardcoded sample rate temporary + + LOG_F(INFO, "Auto selected: %s", _selectedTuner.device->getName().c_str()); + + return true; +} + +/* + * Start the tuner's operation + * @return False if no tuner is selected or an error occured during init + */ +bool TunerManager::startSelectedTuner() { + LOG_F(INFO, "Starting tuner %s", _selectedTuner.device->getName().c_str()); + _cubic->setDevice(_selectedTuner.device, 2000); + LOG_F(INFO, "Setting tuner PPM correction to %i", _selectedTuner.config.ppmCorrection); + _cubic->setPPM(_selectedTuner.config.ppmCorrection); + LOG_F(INFO, "Setting device sample rate to %li", _selectedTuner.config.sampleRate); + _cubic->setSampleRate(_selectedTuner.config.sampleRate); + + return true; +} + +/* + * Stop the tuner's operation + * @return False if no tuner is selected, the tuner wasn't started, or an error occured during deinit + */ +bool TunerManager::stopSelectedTuner() { + _cubic->stopDevice(false, 2000); + return true; +} + +} +} diff --git a/src/sigproc/TunerManager.h b/src/sigproc/TunerManager.h new file mode 100644 index 0000000..0743bea --- /dev/null +++ b/src/sigproc/TunerManager.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include +#include + +#include "Configuration.h" + +class CubicSDR; +class SDRDeviceInfo; + +namespace piscan { +namespace sigproc { + class TunerComparator : public std::less { + constexpr bool operator()(piscan::config::TunerConfig& left, piscan::config::TunerConfig& right) const { + return left.rank < right.rank; + } + }; + + struct ConfiguredTuner { + piscan::config::TunerConfig config; // TODO may be changed to ref or shared_ptr + SDRDeviceInfo* device; + }; + + class TunerManager { + public: + TunerManager(std::shared_ptr cubic); + + bool enumerateDevices(); + bool deviceAvailable(); + bool selectFirstAvailableDevice(); + /*std::vector getKnownDevices(); + std::vector getAllDevices();*/ + + static long nearestSampleRate(long desired, std::vector& supportedRates); + + bool autoSelectTuner(); + bool startSelectedTuner(); + bool stopSelectedTuner(); + + private: + std::shared_ptr _cubic; + std::unordered_map _devs; + std::priority_queue, TunerComparator> _savedTuners; + ConfiguredTuner _selectedTuner; + }; +} +} diff --git a/src/sigproc/sigproc_types.h b/src/sigproc/sigproc_types.h index 8e6bea1..5e0cd7e 100644 --- a/src/sigproc/sigproc_types.h +++ b/src/sigproc/sigproc_types.h @@ -25,5 +25,6 @@ namespace sigproc { }; class Demodulator; + class TunerManager; } } diff --git a/src/sigproc/test/CMakeLists.txt b/src/sigproc/test/CMakeLists.txt index 140d8b0..e07ca31 100644 --- a/src/sigproc/test/CMakeLists.txt +++ b/src/sigproc/test/CMakeLists.txt @@ -1,4 +1,5 @@ package_add_test(sigproc_tests ${CMAKE_CURRENT_SOURCE_DIR}/AudioManager_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Demodulator_test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TunerManager_test.cpp ) diff --git a/src/sigproc/test/TunerManager_test.cpp b/src/sigproc/test/TunerManager_test.cpp new file mode 100644 index 0000000..e69de29