pushing work for the future new source system

This commit is contained in:
AlexandreRouma 2023-03-04 14:18:52 +01:00
parent 5b47f900a6
commit c488d72ce2
7 changed files with 643 additions and 487 deletions

View File

@ -15,10 +15,6 @@ namespace sourcemenu {
bool iqCorrection = false;
bool invertIQ = false;
EventHandler<std::string> sourceRegisteredHandler;
EventHandler<std::string> sourceUnregisterHandler;
EventHandler<std::string> sourceUnregisteredHandler;
std::vector<std::string> 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);

View File

@ -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) {

View File

@ -1,106 +1,186 @@
#include <server.h>
#include <signal_path/source.h>
#include "source.h"
#include <utils/flog.h>
#include <signal_path/signal_path.h>
#include <core.h>
SourceManager::SourceManager() {
}
void SourceManager::registerSource(const std::string& name, Source* source) {
std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> 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<std::string> SourceManager::getSourceNames() {
std::vector<std::string> names;
for (auto const& [name, src] : sources) { names.push_back(name); }
return names;
const std::vector<std::string>& SourceManager::getSourceNames() {
std::lock_guard<std::recursive_mutex> lck(mtx);
return sourceNames;
}
void SourceManager::selectSource(std::string name) {
void SourceManager::select(const std::string& name) {
std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> lck(mtx);
return selectedSourceName;
}
void SourceManager::start() {
if (selectedHandler == NULL) {
return;
}
selectedHandler->startHandler(selectedHandler->ctx);
bool SourceManager::start() {
std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> lck(mtx);
return running;
}
void SourceManager::tune(double freq) {
if (selectedHandler == NULL) {
return;
std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> lck(mtx);
// Call source if selected
if (selectedSource) { selectedSource->showMenu(); }
}
double SourceManager::getSamplerate() {
std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> 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<std::recursive_mutex> lck(mtx);
// Save samplerate and emit event
this->samplerate = samplerate;
onSamplerateChanged(samplerate);
}

View File

@ -1,56 +1,153 @@
#pragma once
#include <string>
#include <vector>
#include <functional>
#include <map>
#include <dsp/stream.h>
#include <mutex>
#include <dsp/types.h>
#include <dsp/stream.h>
#include <utils/event.h>
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<dsp::complex_t>* 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<std::string>& 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<std::string> getSourceNames();
// =============================================================================
// Emitted after a new source has been registered.
Event<std::string> onSourceRegistered;
// Emitted when a source is about to be unregistered.
Event<std::string> onSourceUnregister;
// Emitted after a source has been unregistered.
Event<std::string> onSourceUnregistered;
// Emitted when the samplerate of the incoming IQ has changed.
Event<double> onSamplerateChanged;
// Emitted when the source manager is instructed to tune the radio.
Event<double> onRetune;
private:
std::map<std::string, SourceHandler*> sources;
std::string selectedName;
SourceHandler* selectedHandler = NULL;
double tuneOffset;
double currentFreq;
double ifFreq = 0.0;
TuningMode tuneMode = TuningMode::NORMAL;
dsp::stream<dsp::complex_t> nullSource;
void deselect();
void setSamplerate(double samplerate);
std::vector<std::string> sourceNames;
std::map<std::string, Source*> 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<dsp::complex_t> stream;
};

View File

@ -1,43 +1,51 @@
#pragma once
#include <vector>
#include <utils/flog.h>
#include <functional>
#include <stdexcept>
#include <mutex>
#include <map>
template <class T>
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 <class T>
template <typename... Args>
class Event {
using Handler = std::function<void(Args...)>;
public:
Event() {}
~Event() {}
void emit(T value) {
for (auto const& handler : handlers) {
handler->handler(value, handler->ctx);
}
HandlerID bind(Handler handler) {
std::lock_guard<std::mutex> lck(mtx);
HandlerID id = genID();
handlers[id] = handler;
return id;
}
void bindHandler(EventHandler<T>* handler) {
handlers.push_back(handler);
template<typename MHandler, class T>
HandlerID bind(MHandler handler, T* ctx) {
return bind([=](Args... args){
(ctx->*handler)(args...);
});
}
void unbindHandler(EventHandler<T>* 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<std::mutex> 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<std::mutex> lck(mtx);
for (const auto& [desc, handler] : handlers) {
handler(args...);
}
handlers.erase(std::remove(handlers.begin(), handlers.end(), handler), handlers.end());
}
private:
std::vector<EventHandler<T>*> handlers;
HandlerID genID() {
int id;
for (id = 1; handlers.find(id) != handlers.end(); id++);
return id;
}
std::map<HandlerID, Handler> handlers;
std::mutex mtx;
};

View File

@ -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<double> _retuneHandler;
HandlerID retuneHandlerId;
};
MOD_EXPORT void _INIT_() {

View File

@ -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<dsp::complex_t> stream;
double sampleRate;
SourceManager::SourceHandler handler;
bool running = false;
double freq;
uint64_t selectedSerial = 0;