diff --git a/core/src/gui/menus/source.cpp b/core/src/gui/menus/source.cpp index da8285ac..41021c9e 100644 --- a/core/src/gui/menus/source.cpp +++ b/core/src/gui/menus/source.cpp @@ -15,10 +15,6 @@ namespace sourcemenu { bool iqCorrection = false; bool invertIQ = false; - EventHandler sourceRegisteredHandler; - EventHandler sourceUnregisterHandler; - EventHandler sourceUnregisteredHandler; - std::vector sourceNames; std::string sourceNamesTxt; std::string selectedSource; @@ -99,10 +95,10 @@ namespace sourcemenu { } sourceId = std::distance(sourceNames.begin(), it); selectedSource = sourceNames[sourceId]; - sigpath::sourceManager.selectSource(sourceNames[sourceId]); + sigpath::sourceManager.select(sourceNames[sourceId]); } - void onSourceRegistered(std::string name, void* ctx) { + void onSourceRegistered(std::string name) { refreshSources(); if (selectedSource.empty()) { @@ -114,13 +110,13 @@ namespace sourcemenu { sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource)); } - void onSourceUnregister(std::string name, void* ctx) { + void onSourceUnregister(std::string name) { if (name != selectedSource) { return; } // TODO: Stop everything } - void onSourceUnregistered(std::string name, void* ctx) { + void onSourceUnregistered(std::string name) { refreshSources(); if (sourceNames.empty()) { @@ -153,12 +149,9 @@ namespace sourcemenu { selectSource(selected); sigpath::iqFrontEnd.setDecimation(1 << decimationPower); - sourceRegisteredHandler.handler = onSourceRegistered; - sourceUnregisterHandler.handler = onSourceUnregister; - sourceUnregisteredHandler.handler = onSourceUnregistered; - sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler); - sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler); - sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler); + sigpath::sourceManager.onSourceRegistered.bind(onSourceRegistered); + sigpath::sourceManager.onSourceUnregister.bind(onSourceUnregister); + sigpath::sourceManager.onSourceUnregistered.bind(onSourceUnregistered); core::configManager.release(); } @@ -179,7 +172,7 @@ namespace sourcemenu { if (running) { style::endDisabled(); } - sigpath::sourceManager.showSelectedMenu(); + sigpath::sourceManager.showMenu(); if (ImGui::Checkbox("IQ Correction##_sdrpp_iq_corr", &iqCorrection)) { sigpath::iqFrontEnd.setDCBlocking(iqCorrection); diff --git a/core/src/server.cpp b/core/src/server.cpp index a780a384..fb89a238 100644 --- a/core/src/server.cpp +++ b/core/src/server.cpp @@ -146,7 +146,7 @@ namespace server { // Load sourceId from config sourceId = 0; if (sourceList.keyExists(sourceName)) { sourceId = sourceList.keyId(sourceName); } - sigpath::sourceManager.selectSource(sourceList[sourceId]); + sigpath::sourceManager.select(sourceList[sourceId]); // TODO: Use command line option std::string host = (std::string)core::args["addr"]; @@ -280,8 +280,7 @@ namespace server { } } else if (cmd == COMMAND_START) { - sigpath::sourceManager.start(); - running = true; + running = sigpath::sourceManager.start(); } else if (cmd == COMMAND_STOP) { sigpath::sourceManager.stop(); @@ -309,14 +308,14 @@ namespace server { SmGui::FillWidth(); SmGui::ForceSync(); if (SmGui::Combo("##sdrpp_server_src_sel", &sourceId, sourceList.txt)) { - sigpath::sourceManager.selectSource(sourceList[sourceId]); + sigpath::sourceManager.select(sourceList[sourceId]); core::configManager.acquire(); core::configManager.conf["source"] = sourceList.key(sourceId); core::configManager.release(true); } if (running) { SmGui::EndDisabled(); } - sigpath::sourceManager.showSelectedMenu(); + sigpath::sourceManager.showMenu(); } void renderUI(SmGui::DrawList* dl, std::string diffId, SmGui::DrawListElem diffValue) { diff --git a/core/src/signal_path/source.cpp b/core/src/signal_path/source.cpp index 5b02a4c5..4fe81f48 100644 --- a/core/src/signal_path/source.cpp +++ b/core/src/signal_path/source.cpp @@ -1,106 +1,186 @@ -#include -#include +#include "source.h" #include -#include -#include -SourceManager::SourceManager() { -} +void SourceManager::registerSource(const std::string& name, Source* source) { + std::lock_guard lck(mtx); -void SourceManager::registerSource(std::string name, SourceHandler* handler) { + // Check arguments + if (source || name.empty()) { + flog::error("Invalid argument to register source", name); + return; + } + + // Check that a source with that name doesn't already exist if (sources.find(name) != sources.end()) { - flog::error("Tried to register new source with existing name: {0}", name); + flog::error("Tried to register source with existing name: {}", name); return; } - sources[name] = handler; - onSourceRegistered.emit(name); + + // Add source to map + sources[name] = source; + + // Add source to lists + sourceNames.push_back(name); + onSourceRegistered(name); } -void SourceManager::unregisterSource(std::string name) { +void SourceManager::unregisterSource(const std::string& name) { + std::lock_guard lck(mtx); + + // Check that a source with that name exists if (sources.find(name) == sources.end()) { - flog::error("Tried to unregister non existent source: {0}", name); + flog::error("Tried to unregister a non-existent source: {}", name); return; } - onSourceUnregister.emit(name); - if (name == selectedName) { - if (selectedHandler != NULL) { - sources[selectedName]->deselectHandler(sources[selectedName]->ctx); - } - sigpath::iqFrontEnd.setInput(&nullSource); - selectedHandler = NULL; - } + + // Notify event listeners of the imminent deletion + onSourceUnregister(name); + + // Delete from lists + sourceNames.erase(std::find(sourceNames.begin(), sourceNames.end(), name)); sources.erase(name); - onSourceUnregistered.emit(name); + + // Notify event listeners of the deletion + onSourceUnregistered(name); } -std::vector SourceManager::getSourceNames() { - std::vector names; - for (auto const& [name, src] : sources) { names.push_back(name); } - return names; +const std::vector& SourceManager::getSourceNames() { + std::lock_guard lck(mtx); + return sourceNames; } -void SourceManager::selectSource(std::string name) { +void SourceManager::select(const std::string& name) { + std::lock_guard lck(mtx); + + // make sure that source isn't currently selected + if (selectedSourceName == name) { return; } + + // Deselect current source + deselect(); + + // Check that a source with that name exists if (sources.find(name) == sources.end()) { - flog::error("Tried to select non existent source: {0}", name); + flog::error("Tried to select a non-existent source: {}", name); return; } - if (selectedHandler != NULL) { - sources[selectedName]->deselectHandler(sources[selectedName]->ctx); - } - selectedHandler = sources[name]; - selectedHandler->selectHandler(selectedHandler->ctx); - selectedName = name; - if (core::args["server"].b()) { - server::setInput(selectedHandler->stream); - } - else { - sigpath::iqFrontEnd.setInput(selectedHandler->stream); - } - // Set server input here + + // Select the source + selectedSourceName = name; + selectedSource = sources[name]; + + // Call the selected source + selectedSource->select(); + + // Retune to make sure the source has the latest frequency + tune(frequency); } -void SourceManager::showSelectedMenu() { - if (selectedHandler == NULL) { - return; - } - selectedHandler->menuHandler(selectedHandler->ctx); +const std::string& SourceManager::getSelected() { + std::lock_guard lck(mtx); + return selectedSourceName; } -void SourceManager::start() { - if (selectedHandler == NULL) { - return; - } - selectedHandler->startHandler(selectedHandler->ctx); +bool SourceManager::start() { + std::lock_guard lck(mtx); + + // Check if not already running + if (running) { return true; } + + // Call source if selected and save if started + running = (!selectedSource) ? false : selectedSource->start(); + + return running; } void SourceManager::stop() { - if (selectedHandler == NULL) { - return; - } - selectedHandler->stopHandler(selectedHandler->ctx); + std::lock_guard lck(mtx); + + // Check if running + if (!running) { return; } + + // Call source if selected and save state + if (selectedSource) { selectedSource->stop(); } + running = false; +} + +bool SourceManager::isRunning() { + std::lock_guard lck(mtx); + return running; } void SourceManager::tune(double freq) { - if (selectedHandler == NULL) { - return; + std::lock_guard lck(mtx); + + // Save frequency + frequency = freq; + + // Call source if selected + if (selectedSource) { + selectedSource->tune(((mode == TUNING_MODE_NORMAL) ? freq : ifFrequency) + offset); } - // TODO: No need to always retune the hardware in panadpter mode - selectedHandler->tuneHandler(((tuneMode == TuningMode::NORMAL) ? freq : ifFreq) + tuneOffset, selectedHandler->ctx); - onRetune.emit(freq); - currentFreq = freq; } +void SourceManager::showMenu() { + std::lock_guard lck(mtx); + + // Call source if selected + if (selectedSource) { selectedSource->showMenu(); } +} + +double SourceManager::getSamplerate() { + std::lock_guard lck(mtx); + return samplerate; +} + +// =========== TODO: These functions should not happen in this class =========== + void SourceManager::setTuningOffset(double offset) { - tuneOffset = offset; - tune(currentFreq); + std::lock_guard lck(mtx); + + // Update offset + this->offset = offset; + + // Retune to take affect + tune(frequency); } void SourceManager::setTuningMode(TuningMode mode) { - tuneMode = mode; - tune(currentFreq); + std::lock_guard lck(mtx); + + // Update mode + this->mode = mode; + + // Retune to take affect + tune(frequency); } void SourceManager::setPanadpterIF(double freq) { - ifFreq = freq; - tune(currentFreq); + std::lock_guard lck(mtx); + + // Update offset + ifFrequency = freq; + + // Return to take affect if in panadapter mode + if (mode == TUNING_MODE_PANADAPTER) { tune(frequency); } +} + +// ============================================================================= + +void SourceManager::deselect() { + std::lock_guard lck(mtx); + + // Call source if selected + if (selectedSource) { selectedSource->deselect(); } + + // Mark as deselected + selectedSourceName.clear(); + selectedSource = NULL; +} + +void SourceManager::setSamplerate(double samplerate) { + std::lock_guard lck(mtx); + + // Save samplerate and emit event + this->samplerate = samplerate; + onSamplerateChanged(samplerate); } \ No newline at end of file diff --git a/core/src/signal_path/source.h b/core/src/signal_path/source.h index 5021ea2d..563f5c47 100644 --- a/core/src/signal_path/source.h +++ b/core/src/signal_path/source.h @@ -1,56 +1,153 @@ #pragma once #include -#include +#include #include -#include +#include #include +#include #include +enum TuningMode { + TUNING_MODE_NORMAL, + TUNING_MODE_PANADAPTER +}; + +class Source; + class SourceManager { + friend Source; public: - SourceManager(); + /** + * Register a source. + * @param name Name of the source. + * @param source Pointer to the source instance. + */ + void registerSource(const std::string& name, Source* source); - struct SourceHandler { - dsp::stream* stream; - void (*menuHandler)(void* ctx); - void (*selectHandler)(void* ctx); - void (*deselectHandler)(void* ctx); - void (*startHandler)(void* ctx); - void (*stopHandler)(void* ctx); - void (*tuneHandler)(double freq, void* ctx); - void* ctx; - }; + /** + * Unregister a source. + * @param name Name of the source. + */ + void unregisterSource(const std::string& name); - enum TuningMode { - NORMAL, - PANADAPTER - }; + /** + * Get a list of source names. + * @return List of source names. + */ + const std::vector& getSourceNames(); - void registerSource(std::string name, SourceHandler* handler); - void unregisterSource(std::string name); - void selectSource(std::string name); - void showSelectedMenu(); - void start(); + /** + * Select a source. + * @param name Name of the source. + */ + void select(const std::string& name); + + /** + * Get the name of the currently selected source. + * @return Name of the source or empty if no source is selected. + */ + const std::string& getSelected(); + + /** + * Start the radio. + * @return True if the radio started successfully, false if not. + */ + bool start(); + + /** + * Stop the radio. + */ void stop(); + + /** + * Check if the radio is running. + * @return True if the radio is running, false if not. + */ + bool isRunning(); + + /** + * Tune the radio. + * @param freq Frequency in Hz. + */ void tune(double freq); + + /** + * Tune the radio. + * @param freq Frequency to tune the radio to. + */ + void showMenu(); + + /** + * Get the current samplerate of the radio. + * @return Samplerate in Hz. + */ + double getSamplerate(); + + // =========== TODO: These functions should not happen in this class =========== + + /** + * Set offset to add to the tuned frequency. + * @param offset Offset in Hz. + */ void setTuningOffset(double offset); + + /** + * Set tuning mode. + * @param mode Tuning mode. + */ void setTuningMode(TuningMode mode); + + /** + * Set panadapter mode IF frequency. + * @param freq IF frequency in Hz. + */ void setPanadpterIF(double freq); - std::vector getSourceNames(); + // ============================================================================= + // Emitted after a new source has been registered. Event onSourceRegistered; + + // Emitted when a source is about to be unregistered. Event onSourceUnregister; + + // Emitted after a source has been unregistered. Event onSourceUnregistered; + + // Emitted when the samplerate of the incoming IQ has changed. + Event onSamplerateChanged; + + // Emitted when the source manager is instructed to tune the radio. Event onRetune; private: - std::map sources; - std::string selectedName; - SourceHandler* selectedHandler = NULL; - double tuneOffset; - double currentFreq; - double ifFreq = 0.0; - TuningMode tuneMode = TuningMode::NORMAL; - dsp::stream nullSource; + void deselect(); + void setSamplerate(double samplerate); + + std::vector sourceNames; + std::map sources; + + std::string selectedSourceName = ""; + Source* selectedSource = NULL; + + bool running = false; + double samplerate = 1e6; + double frequency = 100e6; + double offset = 0; + double ifFrequency = 8.830e6; + TuningMode mode = TUNING_MODE_NORMAL; + + std::recursive_mutex mtx; +}; + +class Source { +public: + virtual void showMenu() {} + virtual void select() = 0; + virtual void deselect() {} + virtual bool start() = 0; + virtual void stop() = 0; + virtual void tune(double freq) {} + + dsp::stream stream; }; \ No newline at end of file diff --git a/core/src/utils/event.h b/core/src/utils/event.h index 312d38c2..e07d7986 100644 --- a/core/src/utils/event.h +++ b/core/src/utils/event.h @@ -1,43 +1,51 @@ #pragma once -#include -#include +#include +#include +#include +#include -template -struct EventHandler { - EventHandler() {} - EventHandler(void (*handler)(T, void*), void* ctx) { - this->handler = handler; - this->ctx = ctx; - } +typedef int HandlerID; - void (*handler)(T, void*); - void* ctx; -}; - -template +template class Event { + using Handler = std::function; public: - Event() {} - ~Event() {} - - void emit(T value) { - for (auto const& handler : handlers) { - handler->handler(value, handler->ctx); - } + HandlerID bind(Handler handler) { + std::lock_guard lck(mtx); + HandlerID id = genID(); + handlers[id] = handler; + return id; } - void bindHandler(EventHandler* handler) { - handlers.push_back(handler); + template + HandlerID bind(MHandler handler, T* ctx) { + return bind([=](Args... args){ + (ctx->*handler)(args...); + }); } - void unbindHandler(EventHandler* handler) { - if (std::find(handlers.begin(), handlers.end(), handler) == handlers.end()) { - flog::error("Tried to remove a non-existent event handler"); - return; + void unbind(HandlerID id) { + std::lock_guard lck(mtx); + if (handlers.find(id) == handlers.end()) { + throw std::runtime_error("Could not unbind handler, unknown ID"); + } + handlers.erase(id); + } + + void operator()(Args... args) { + std::lock_guard lck(mtx); + for (const auto& [desc, handler] : handlers) { + handler(args...); } - handlers.erase(std::remove(handlers.begin(), handlers.end(), handler), handlers.end()); } private: - std::vector*> handlers; + HandlerID genID() { + int id; + for (id = 1; handlers.find(id) != handlers.end(); id++); + return id; + } + + std::map handlers; + std::mutex mtx; }; \ No newline at end of file diff --git a/misc_modules/rigctl_client/src/main.cpp b/misc_modules/rigctl_client/src/main.cpp index 3df6c077..157771ec 100644 --- a/misc_modules/rigctl_client/src/main.cpp +++ b/misc_modules/rigctl_client/src/main.cpp @@ -45,9 +45,6 @@ public: } config.release(); - _retuneHandler.ctx = this; - _retuneHandler.handler = retuneHandler; - gui::menu.registerEntry(name, menuHandler, this, NULL); } @@ -87,8 +84,8 @@ public: // Switch source to panadapter mode sigpath::sourceManager.setPanadpterIF(ifFreq); - sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::PANADAPTER); - sigpath::sourceManager.onRetune.bindHandler(&_retuneHandler); + sigpath::sourceManager.setTuningMode(TUNING_MODE_PANADAPTER); + retuneHandlerId = sigpath::sourceManager.onRetune.bind(retuneHandler, this); running = true; } @@ -98,8 +95,8 @@ public: if (!running) { return; } // Switch source back to normal mode - sigpath::sourceManager.onRetune.unbindHandler(&_retuneHandler); - sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::NORMAL); + sigpath::sourceManager.onRetune.unbind(retuneHandlerId); + sigpath::sourceManager.setTuningMode(TUNING_MODE_NORMAL); // Disconnect from rigctl server client->close(); @@ -159,10 +156,9 @@ private: } } - static void retuneHandler(double freq, void* ctx) { - RigctlClientModule* _this = (RigctlClientModule*)ctx; - if (!_this->client || !_this->client->isOpen()) { return; } - if (_this->client->setFreq(freq)) { + void retuneHandler(double freq) { + if (!client || !client->isOpen()) { return; } + if (client->setFreq(freq)) { flog::error("Could not set frequency"); } } @@ -178,7 +174,7 @@ private: double ifFreq = 8830000.0; - EventHandler _retuneHandler; + HandlerID retuneHandlerId; }; MOD_EXPORT void _INIT_() { diff --git a/source_modules/airspy_source/src/main.cpp b/source_modules/airspy_source/src/main.cpp index 5457e1dd..6ac8e928 100644 --- a/source_modules/airspy_source/src/main.cpp +++ b/source_modules/airspy_source/src/main.cpp @@ -25,7 +25,7 @@ SDRPP_MOD_INFO{ ConfigManager config; -class AirspySourceModule : public ModuleManager::Instance { +class AirspySourceModule : public ModuleManager::Instance, public Source { public: AirspySourceModule(std::string name) { this->name = name; @@ -34,15 +34,6 @@ public: sampleRate = 10000000.0; - handler.ctx = this; - handler.selectHandler = menuSelected; - handler.deselectHandler = menuDeselected; - handler.menuHandler = menuHandler; - handler.startHandler = start; - handler.stopHandler = stop; - handler.tuneHandler = tune; - handler.stream = &stream; - refresh(); if (sampleRateList.size() > 0) { sampleRate = sampleRateList[0]; @@ -54,11 +45,11 @@ public: config.release(); selectByString(devSerial); - sigpath::sourceManager.registerSource("Airspy", &handler); + sigpath::sourceManager.registerSource("Airspy", this); } ~AirspySourceModule() { - stop(this); + stop(); sigpath::sourceManager.unregisterSource("Airspy"); airspy_exit(); } @@ -231,6 +222,315 @@ public: airspy_close(dev); } + void select() { + core::setInputSampleRate(sampleRate); + flog::info("AirspySourceModule '{0}': Select!", name); + } + + void deselect() { + flog::info("AirspySourceModule '{0}': Deselect!", name); + } + + bool start() { + if (running) { return true; } + if (selectedSerial == 0) { + flog::error("Tried to start Airspy source with null serial"); + return false; + } + +#ifndef __ANDROID__ + int err = airspy_open_sn(&openDev, selectedSerial); +#else + int err = airspy_open_fd(&openDev, devFd); +#endif + if (err != 0) { + char buf[1024]; + sprintf(buf, "%016" PRIX64, selectedSerial); + flog::error("Could not open Airspy {0}", buf); + return false; + } + + airspy_set_samplerate(openDev, sampleRateList[srId]); + airspy_set_freq(openDev, freq); + + if (gainMode == 0) { + airspy_set_lna_agc(openDev, 0); + airspy_set_mixer_agc(openDev, 0); + airspy_set_sensitivity_gain(openDev, sensitiveGain); + } + else if (gainMode == 1) { + airspy_set_lna_agc(openDev, 0); + airspy_set_mixer_agc(openDev, 0); + airspy_set_linearity_gain(openDev, linearGain); + } + else if (gainMode == 2) { + if (lnaAgc) { + airspy_set_lna_agc(openDev, 1); + } + else { + airspy_set_lna_agc(openDev, 0); + airspy_set_lna_gain(openDev, lnaGain); + } + if (mixerAgc) { + airspy_set_mixer_agc(openDev, 1); + } + else { + airspy_set_mixer_agc(openDev, 0); + airspy_set_mixer_gain(openDev, mixerGain); + } + airspy_set_vga_gain(openDev, vgaGain); + } + + airspy_set_rf_bias(openDev, biasT); + + airspy_start_rx(openDev, callback, this); + + running = true; + flog::info("AirspySourceModule '{0}': Start!", name); + } + + void stop() { + if (!running) { return; } + running = false; + stream.stopWriter(); + airspy_close(openDev); + stream.clearWriteStop(); + flog::info("AirspySourceModule '{0}': Stop!", name); + } + + void tune(double freq) { + this->freq = freq; + if (running) { + airspy_set_freq(openDev, freq); + } + flog::info("AirspySourceModule '{0}': Tune: {1}!", name, freq); + } + + void showMenu() { + if (running) { SmGui::BeginDisabled(); } + + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Combo(CONCAT("##_airspy_dev_sel_", name), &devId, devListTxt.c_str())) { + selectBySerial(devList[devId]); + core::setInputSampleRate(sampleRate); + if (selectedSerStr != "") { + config.acquire(); + config.conf["device"] = selectedSerStr; + config.release(true); + } + } + + if (SmGui::Combo(CONCAT("##_airspy_sr_sel_", name), &srId, sampleRateListTxt.c_str())) { + sampleRate = sampleRateList[srId]; + core::setInputSampleRate(sampleRate); + if (selectedSerStr != "") { + config.acquire(); + config.conf["devices"][selectedSerStr]["sampleRate"] = sampleRate; + config.release(true); + } + } + + SmGui::SameLine(); + SmGui::FillWidth(); + SmGui::ForceSync(); + if (SmGui::Button(CONCAT("Refresh##_airspy_refr_", name))) { + refresh(); + config.acquire(); + std::string devSerial = config.conf["device"]; + config.release(); + selectByString(devSerial); + core::setInputSampleRate(sampleRate); + } + + if (running) { SmGui::EndDisabled(); } + + SmGui::BeginGroup(); + SmGui::Columns(3, CONCAT("AirspyGainModeColumns##_", name), false); + SmGui::ForceSync(); + if (SmGui::RadioButton(CONCAT("Sensitive##_airspy_gm_", name), gainMode == 0)) { + gainMode = 0; + if (running) { + airspy_set_lna_agc(openDev, 0); + airspy_set_mixer_agc(openDev, 0); + airspy_set_sensitivity_gain(openDev, sensitiveGain); + } + if (selectedSerStr != "") { + config.acquire(); + config.conf["devices"][selectedSerStr]["gainMode"] = 0; + config.release(true); + } + } + SmGui::NextColumn(); + SmGui::ForceSync(); + if (SmGui::RadioButton(CONCAT("Linear##_airspy_gm_", name), gainMode == 1)) { + gainMode = 1; + if (running) { + airspy_set_lna_agc(openDev, 0); + airspy_set_mixer_agc(openDev, 0); + airspy_set_linearity_gain(openDev, linearGain); + } + if (selectedSerStr != "") { + config.acquire(); + config.conf["devices"][selectedSerStr]["gainMode"] = 1; + config.release(true); + } + } + SmGui::NextColumn(); + SmGui::ForceSync(); + if (SmGui::RadioButton(CONCAT("Free##_airspy_gm_", name), gainMode == 2)) { + gainMode = 2; + if (running) { + if (lnaAgc) { + airspy_set_lna_agc(openDev, 1); + } + else { + airspy_set_lna_agc(openDev, 0); + airspy_set_lna_gain(openDev, lnaGain); + } + if (mixerAgc) { + airspy_set_mixer_agc(openDev, 1); + } + else { + airspy_set_mixer_agc(openDev, 0); + airspy_set_mixer_gain(openDev, mixerGain); + } + airspy_set_vga_gain(openDev, vgaGain); + } + if (selectedSerStr != "") { + config.acquire(); + config.conf["devices"][selectedSerStr]["gainMode"] = 2; + config.release(true); + } + } + SmGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", name), false); + SmGui::EndGroup(); + + // Gain menus + + if (gainMode == 0) { + SmGui::LeftLabel("Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_airspy_sens_gain_", name), &sensitiveGain, 0, 21)) { + if (running) { + airspy_set_sensitivity_gain(openDev, sensitiveGain); + } + if (selectedSerStr != "") { + config.acquire(); + config.conf["devices"][selectedSerStr]["sensitiveGain"] = sensitiveGain; + config.release(true); + } + } + } + else if (gainMode == 1) { + SmGui::LeftLabel("Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_airspy_lin_gain_", name), &linearGain, 0, 21)) { + if (running) { + airspy_set_linearity_gain(openDev, linearGain); + } + if (selectedSerStr != "") { + config.acquire(); + config.conf["devices"][selectedSerStr]["linearGain"] = linearGain; + config.release(true); + } + } + } + else if (gainMode == 2) { + // TODO: Switch to a table for alignment + if (lnaAgc) { SmGui::BeginDisabled(); } + SmGui::LeftLabel("LNA Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_airspy_lna_gain_", name), &lnaGain, 0, 15)) { + if (running) { + airspy_set_lna_gain(openDev, lnaGain); + } + if (selectedSerStr != "") { + config.acquire(); + config.conf["devices"][selectedSerStr]["lnaGain"] = lnaGain; + config.release(true); + } + } + if (lnaAgc) { SmGui::EndDisabled(); } + + if (mixerAgc) { SmGui::BeginDisabled(); } + SmGui::LeftLabel("Mixer Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_airspy_mix_gain_", name), &mixerGain, 0, 15)) { + if (running) { + airspy_set_mixer_gain(openDev, mixerGain); + } + if (selectedSerStr != "") { + config.acquire(); + config.conf["devices"][selectedSerStr]["mixerGain"] = mixerGain; + config.release(true); + } + } + if (mixerAgc) { SmGui::EndDisabled(); } + + SmGui::LeftLabel("VGA Gain"); + SmGui::FillWidth(); + if (SmGui::SliderInt(CONCAT("##_airspy_vga_gain_", name), &vgaGain, 0, 15)) { + if (running) { + airspy_set_vga_gain(openDev, vgaGain); + } + if (selectedSerStr != "") { + config.acquire(); + config.conf["devices"][selectedSerStr]["vgaGain"] = vgaGain; + config.release(true); + } + } + + // AGC Control + SmGui::ForceSync(); + if (SmGui::Checkbox(CONCAT("LNA AGC##_airspy_", name), &lnaAgc)) { + if (running) { + if (lnaAgc) { + airspy_set_lna_agc(openDev, 1); + } + else { + airspy_set_lna_agc(openDev, 0); + airspy_set_lna_gain(openDev, lnaGain); + } + } + if (selectedSerStr != "") { + config.acquire(); + config.conf["devices"][selectedSerStr]["lnaAgc"] = lnaAgc; + config.release(true); + } + } + SmGui::ForceSync(); + if (SmGui::Checkbox(CONCAT("Mixer AGC##_airspy_", name), &mixerAgc)) { + if (running) { + if (mixerAgc) { + airspy_set_mixer_agc(openDev, 1); + } + else { + airspy_set_mixer_agc(openDev, 0); + airspy_set_mixer_gain(openDev, mixerGain); + } + } + if (selectedSerStr != "") { + config.acquire(); + config.conf["devices"][selectedSerStr]["mixerAgc"] = mixerAgc; + config.release(true); + } + } + } + + // Bias T + if (SmGui::Checkbox(CONCAT("Bias T##_airspy_", name), &biasT)) { + if (running) { + airspy_set_rf_bias(openDev, biasT); + } + if (selectedSerStr != "") { + config.acquire(); + config.conf["devices"][selectedSerStr]["biasT"] = biasT; + config.release(true); + } + } + } + private: std::string getBandwdithScaled(double bw) { char buf[1024]; @@ -246,322 +546,6 @@ private: return std::string(buf); } - static void menuSelected(void* ctx) { - AirspySourceModule* _this = (AirspySourceModule*)ctx; - core::setInputSampleRate(_this->sampleRate); - flog::info("AirspySourceModule '{0}': Menu Select!", _this->name); - } - - static void menuDeselected(void* ctx) { - AirspySourceModule* _this = (AirspySourceModule*)ctx; - flog::info("AirspySourceModule '{0}': Menu Deselect!", _this->name); - } - - static void start(void* ctx) { - AirspySourceModule* _this = (AirspySourceModule*)ctx; - if (_this->running) { return; } - if (_this->selectedSerial == 0) { - flog::error("Tried to start Airspy source with null serial"); - return; - } - -#ifndef __ANDROID__ - int err = airspy_open_sn(&_this->openDev, _this->selectedSerial); -#else - int err = airspy_open_fd(&_this->openDev, _this->devFd); -#endif - if (err != 0) { - char buf[1024]; - sprintf(buf, "%016" PRIX64, _this->selectedSerial); - flog::error("Could not open Airspy {0}", buf); - return; - } - - airspy_set_samplerate(_this->openDev, _this->sampleRateList[_this->srId]); - airspy_set_freq(_this->openDev, _this->freq); - - if (_this->gainMode == 0) { - airspy_set_lna_agc(_this->openDev, 0); - airspy_set_mixer_agc(_this->openDev, 0); - airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain); - } - else if (_this->gainMode == 1) { - airspy_set_lna_agc(_this->openDev, 0); - airspy_set_mixer_agc(_this->openDev, 0); - airspy_set_linearity_gain(_this->openDev, _this->linearGain); - } - else if (_this->gainMode == 2) { - if (_this->lnaAgc) { - airspy_set_lna_agc(_this->openDev, 1); - } - else { - airspy_set_lna_agc(_this->openDev, 0); - airspy_set_lna_gain(_this->openDev, _this->lnaGain); - } - if (_this->mixerAgc) { - airspy_set_mixer_agc(_this->openDev, 1); - } - else { - airspy_set_mixer_agc(_this->openDev, 0); - airspy_set_mixer_gain(_this->openDev, _this->mixerGain); - } - airspy_set_vga_gain(_this->openDev, _this->vgaGain); - } - - airspy_set_rf_bias(_this->openDev, _this->biasT); - - airspy_start_rx(_this->openDev, callback, _this); - - _this->running = true; - flog::info("AirspySourceModule '{0}': Start!", _this->name); - } - - static void stop(void* ctx) { - AirspySourceModule* _this = (AirspySourceModule*)ctx; - if (!_this->running) { return; } - _this->running = false; - _this->stream.stopWriter(); - airspy_close(_this->openDev); - _this->stream.clearWriteStop(); - flog::info("AirspySourceModule '{0}': Stop!", _this->name); - } - - static void tune(double freq, void* ctx) { - AirspySourceModule* _this = (AirspySourceModule*)ctx; - if (_this->running) { - airspy_set_freq(_this->openDev, freq); - } - _this->freq = freq; - flog::info("AirspySourceModule '{0}': Tune: {1}!", _this->name, freq); - } - - static void menuHandler(void* ctx) { - AirspySourceModule* _this = (AirspySourceModule*)ctx; - - if (_this->running) { SmGui::BeginDisabled(); } - - SmGui::FillWidth(); - SmGui::ForceSync(); - if (SmGui::Combo(CONCAT("##_airspy_dev_sel_", _this->name), &_this->devId, _this->devListTxt.c_str())) { - _this->selectBySerial(_this->devList[_this->devId]); - core::setInputSampleRate(_this->sampleRate); - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["device"] = _this->selectedSerStr; - config.release(true); - } - } - - if (SmGui::Combo(CONCAT("##_airspy_sr_sel_", _this->name), &_this->srId, _this->sampleRateListTxt.c_str())) { - _this->sampleRate = _this->sampleRateList[_this->srId]; - core::setInputSampleRate(_this->sampleRate); - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["devices"][_this->selectedSerStr]["sampleRate"] = _this->sampleRate; - config.release(true); - } - } - - SmGui::SameLine(); - SmGui::FillWidth(); - SmGui::ForceSync(); - if (SmGui::Button(CONCAT("Refresh##_airspy_refr_", _this->name))) { - _this->refresh(); - config.acquire(); - std::string devSerial = config.conf["device"]; - config.release(); - _this->selectByString(devSerial); - core::setInputSampleRate(_this->sampleRate); - } - - if (_this->running) { SmGui::EndDisabled(); } - - SmGui::BeginGroup(); - SmGui::Columns(3, CONCAT("AirspyGainModeColumns##_", _this->name), false); - SmGui::ForceSync(); - if (SmGui::RadioButton(CONCAT("Sensitive##_airspy_gm_", _this->name), _this->gainMode == 0)) { - _this->gainMode = 0; - if (_this->running) { - airspy_set_lna_agc(_this->openDev, 0); - airspy_set_mixer_agc(_this->openDev, 0); - airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain); - } - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["devices"][_this->selectedSerStr]["gainMode"] = 0; - config.release(true); - } - } - SmGui::NextColumn(); - SmGui::ForceSync(); - if (SmGui::RadioButton(CONCAT("Linear##_airspy_gm_", _this->name), _this->gainMode == 1)) { - _this->gainMode = 1; - if (_this->running) { - airspy_set_lna_agc(_this->openDev, 0); - airspy_set_mixer_agc(_this->openDev, 0); - airspy_set_linearity_gain(_this->openDev, _this->linearGain); - } - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["devices"][_this->selectedSerStr]["gainMode"] = 1; - config.release(true); - } - } - SmGui::NextColumn(); - SmGui::ForceSync(); - if (SmGui::RadioButton(CONCAT("Free##_airspy_gm_", _this->name), _this->gainMode == 2)) { - _this->gainMode = 2; - if (_this->running) { - if (_this->lnaAgc) { - airspy_set_lna_agc(_this->openDev, 1); - } - else { - airspy_set_lna_agc(_this->openDev, 0); - airspy_set_lna_gain(_this->openDev, _this->lnaGain); - } - if (_this->mixerAgc) { - airspy_set_mixer_agc(_this->openDev, 1); - } - else { - airspy_set_mixer_agc(_this->openDev, 0); - airspy_set_mixer_gain(_this->openDev, _this->mixerGain); - } - airspy_set_vga_gain(_this->openDev, _this->vgaGain); - } - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["devices"][_this->selectedSerStr]["gainMode"] = 2; - config.release(true); - } - } - SmGui::Columns(1, CONCAT("EndAirspyGainModeColumns##_", _this->name), false); - SmGui::EndGroup(); - - // Gain menus - - if (_this->gainMode == 0) { - SmGui::LeftLabel("Gain"); - SmGui::FillWidth(); - if (SmGui::SliderInt(CONCAT("##_airspy_sens_gain_", _this->name), &_this->sensitiveGain, 0, 21)) { - if (_this->running) { - airspy_set_sensitivity_gain(_this->openDev, _this->sensitiveGain); - } - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["devices"][_this->selectedSerStr]["sensitiveGain"] = _this->sensitiveGain; - config.release(true); - } - } - } - else if (_this->gainMode == 1) { - SmGui::LeftLabel("Gain"); - SmGui::FillWidth(); - if (SmGui::SliderInt(CONCAT("##_airspy_lin_gain_", _this->name), &_this->linearGain, 0, 21)) { - if (_this->running) { - airspy_set_linearity_gain(_this->openDev, _this->linearGain); - } - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["devices"][_this->selectedSerStr]["linearGain"] = _this->linearGain; - config.release(true); - } - } - } - else if (_this->gainMode == 2) { - // TODO: Switch to a table for alignment - if (_this->lnaAgc) { SmGui::BeginDisabled(); } - SmGui::LeftLabel("LNA Gain"); - SmGui::FillWidth(); - if (SmGui::SliderInt(CONCAT("##_airspy_lna_gain_", _this->name), &_this->lnaGain, 0, 15)) { - if (_this->running) { - airspy_set_lna_gain(_this->openDev, _this->lnaGain); - } - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["devices"][_this->selectedSerStr]["lnaGain"] = _this->lnaGain; - config.release(true); - } - } - if (_this->lnaAgc) { SmGui::EndDisabled(); } - - if (_this->mixerAgc) { SmGui::BeginDisabled(); } - SmGui::LeftLabel("Mixer Gain"); - SmGui::FillWidth(); - if (SmGui::SliderInt(CONCAT("##_airspy_mix_gain_", _this->name), &_this->mixerGain, 0, 15)) { - if (_this->running) { - airspy_set_mixer_gain(_this->openDev, _this->mixerGain); - } - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["devices"][_this->selectedSerStr]["mixerGain"] = _this->mixerGain; - config.release(true); - } - } - if (_this->mixerAgc) { SmGui::EndDisabled(); } - - SmGui::LeftLabel("VGA Gain"); - SmGui::FillWidth(); - if (SmGui::SliderInt(CONCAT("##_airspy_vga_gain_", _this->name), &_this->vgaGain, 0, 15)) { - if (_this->running) { - airspy_set_vga_gain(_this->openDev, _this->vgaGain); - } - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["devices"][_this->selectedSerStr]["vgaGain"] = _this->vgaGain; - config.release(true); - } - } - - // AGC Control - SmGui::ForceSync(); - if (SmGui::Checkbox(CONCAT("LNA AGC##_airspy_", _this->name), &_this->lnaAgc)) { - if (_this->running) { - if (_this->lnaAgc) { - airspy_set_lna_agc(_this->openDev, 1); - } - else { - airspy_set_lna_agc(_this->openDev, 0); - airspy_set_lna_gain(_this->openDev, _this->lnaGain); - } - } - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["devices"][_this->selectedSerStr]["lnaAgc"] = _this->lnaAgc; - config.release(true); - } - } - SmGui::ForceSync(); - if (SmGui::Checkbox(CONCAT("Mixer AGC##_airspy_", _this->name), &_this->mixerAgc)) { - if (_this->running) { - if (_this->mixerAgc) { - airspy_set_mixer_agc(_this->openDev, 1); - } - else { - airspy_set_mixer_agc(_this->openDev, 0); - airspy_set_mixer_gain(_this->openDev, _this->mixerGain); - } - } - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["devices"][_this->selectedSerStr]["mixerAgc"] = _this->mixerAgc; - config.release(true); - } - } - } - - // Bias T - if (SmGui::Checkbox(CONCAT("Bias T##_airspy_", _this->name), &_this->biasT)) { - if (_this->running) { - airspy_set_rf_bias(_this->openDev, _this->biasT); - } - if (_this->selectedSerStr != "") { - config.acquire(); - config.conf["devices"][_this->selectedSerStr]["biasT"] = _this->biasT; - config.release(true); - } - } - } - static int callback(airspy_transfer_t* transfer) { AirspySourceModule* _this = (AirspySourceModule*)transfer->ctx; memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t)); @@ -574,7 +558,6 @@ private: bool enabled = true; dsp::stream stream; double sampleRate; - SourceManager::SourceHandler handler; bool running = false; double freq; uint64_t selectedSerial = 0;