mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-12-25 02:18:30 +01:00
pushing work for the future new source system
This commit is contained in:
parent
5b47f900a6
commit
c488d72ce2
@ -15,10 +15,6 @@ namespace sourcemenu {
|
|||||||
bool iqCorrection = false;
|
bool iqCorrection = false;
|
||||||
bool invertIQ = false;
|
bool invertIQ = false;
|
||||||
|
|
||||||
EventHandler<std::string> sourceRegisteredHandler;
|
|
||||||
EventHandler<std::string> sourceUnregisterHandler;
|
|
||||||
EventHandler<std::string> sourceUnregisteredHandler;
|
|
||||||
|
|
||||||
std::vector<std::string> sourceNames;
|
std::vector<std::string> sourceNames;
|
||||||
std::string sourceNamesTxt;
|
std::string sourceNamesTxt;
|
||||||
std::string selectedSource;
|
std::string selectedSource;
|
||||||
@ -99,10 +95,10 @@ namespace sourcemenu {
|
|||||||
}
|
}
|
||||||
sourceId = std::distance(sourceNames.begin(), it);
|
sourceId = std::distance(sourceNames.begin(), it);
|
||||||
selectedSource = sourceNames[sourceId];
|
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();
|
refreshSources();
|
||||||
|
|
||||||
if (selectedSource.empty()) {
|
if (selectedSource.empty()) {
|
||||||
@ -114,13 +110,13 @@ namespace sourcemenu {
|
|||||||
sourceId = std::distance(sourceNames.begin(), std::find(sourceNames.begin(), sourceNames.end(), selectedSource));
|
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; }
|
if (name != selectedSource) { return; }
|
||||||
|
|
||||||
// TODO: Stop everything
|
// TODO: Stop everything
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSourceUnregistered(std::string name, void* ctx) {
|
void onSourceUnregistered(std::string name) {
|
||||||
refreshSources();
|
refreshSources();
|
||||||
|
|
||||||
if (sourceNames.empty()) {
|
if (sourceNames.empty()) {
|
||||||
@ -153,12 +149,9 @@ namespace sourcemenu {
|
|||||||
selectSource(selected);
|
selectSource(selected);
|
||||||
sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
|
sigpath::iqFrontEnd.setDecimation(1 << decimationPower);
|
||||||
|
|
||||||
sourceRegisteredHandler.handler = onSourceRegistered;
|
sigpath::sourceManager.onSourceRegistered.bind(onSourceRegistered);
|
||||||
sourceUnregisterHandler.handler = onSourceUnregister;
|
sigpath::sourceManager.onSourceUnregister.bind(onSourceUnregister);
|
||||||
sourceUnregisteredHandler.handler = onSourceUnregistered;
|
sigpath::sourceManager.onSourceUnregistered.bind(onSourceUnregistered);
|
||||||
sigpath::sourceManager.onSourceRegistered.bindHandler(&sourceRegisteredHandler);
|
|
||||||
sigpath::sourceManager.onSourceUnregister.bindHandler(&sourceUnregisterHandler);
|
|
||||||
sigpath::sourceManager.onSourceUnregistered.bindHandler(&sourceUnregisteredHandler);
|
|
||||||
|
|
||||||
core::configManager.release();
|
core::configManager.release();
|
||||||
}
|
}
|
||||||
@ -179,7 +172,7 @@ namespace sourcemenu {
|
|||||||
|
|
||||||
if (running) { style::endDisabled(); }
|
if (running) { style::endDisabled(); }
|
||||||
|
|
||||||
sigpath::sourceManager.showSelectedMenu();
|
sigpath::sourceManager.showMenu();
|
||||||
|
|
||||||
if (ImGui::Checkbox("IQ Correction##_sdrpp_iq_corr", &iqCorrection)) {
|
if (ImGui::Checkbox("IQ Correction##_sdrpp_iq_corr", &iqCorrection)) {
|
||||||
sigpath::iqFrontEnd.setDCBlocking(iqCorrection);
|
sigpath::iqFrontEnd.setDCBlocking(iqCorrection);
|
||||||
|
@ -146,7 +146,7 @@ namespace server {
|
|||||||
// Load sourceId from config
|
// Load sourceId from config
|
||||||
sourceId = 0;
|
sourceId = 0;
|
||||||
if (sourceList.keyExists(sourceName)) { sourceId = sourceList.keyId(sourceName); }
|
if (sourceList.keyExists(sourceName)) { sourceId = sourceList.keyId(sourceName); }
|
||||||
sigpath::sourceManager.selectSource(sourceList[sourceId]);
|
sigpath::sourceManager.select(sourceList[sourceId]);
|
||||||
|
|
||||||
// TODO: Use command line option
|
// TODO: Use command line option
|
||||||
std::string host = (std::string)core::args["addr"];
|
std::string host = (std::string)core::args["addr"];
|
||||||
@ -280,8 +280,7 @@ namespace server {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (cmd == COMMAND_START) {
|
else if (cmd == COMMAND_START) {
|
||||||
sigpath::sourceManager.start();
|
running = sigpath::sourceManager.start();
|
||||||
running = true;
|
|
||||||
}
|
}
|
||||||
else if (cmd == COMMAND_STOP) {
|
else if (cmd == COMMAND_STOP) {
|
||||||
sigpath::sourceManager.stop();
|
sigpath::sourceManager.stop();
|
||||||
@ -309,14 +308,14 @@ namespace server {
|
|||||||
SmGui::FillWidth();
|
SmGui::FillWidth();
|
||||||
SmGui::ForceSync();
|
SmGui::ForceSync();
|
||||||
if (SmGui::Combo("##sdrpp_server_src_sel", &sourceId, sourceList.txt)) {
|
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.acquire();
|
||||||
core::configManager.conf["source"] = sourceList.key(sourceId);
|
core::configManager.conf["source"] = sourceList.key(sourceId);
|
||||||
core::configManager.release(true);
|
core::configManager.release(true);
|
||||||
}
|
}
|
||||||
if (running) { SmGui::EndDisabled(); }
|
if (running) { SmGui::EndDisabled(); }
|
||||||
|
|
||||||
sigpath::sourceManager.showSelectedMenu();
|
sigpath::sourceManager.showMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderUI(SmGui::DrawList* dl, std::string diffId, SmGui::DrawListElem diffValue) {
|
void renderUI(SmGui::DrawList* dl, std::string diffId, SmGui::DrawListElem diffValue) {
|
||||||
|
@ -1,106 +1,186 @@
|
|||||||
#include <server.h>
|
#include "source.h"
|
||||||
#include <signal_path/source.h>
|
|
||||||
#include <utils/flog.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()) {
|
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;
|
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()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
onSourceUnregister.emit(name);
|
|
||||||
if (name == selectedName) {
|
// Notify event listeners of the imminent deletion
|
||||||
if (selectedHandler != NULL) {
|
onSourceUnregister(name);
|
||||||
sources[selectedName]->deselectHandler(sources[selectedName]->ctx);
|
|
||||||
}
|
// Delete from lists
|
||||||
sigpath::iqFrontEnd.setInput(&nullSource);
|
sourceNames.erase(std::find(sourceNames.begin(), sourceNames.end(), name));
|
||||||
selectedHandler = NULL;
|
|
||||||
}
|
|
||||||
sources.erase(name);
|
sources.erase(name);
|
||||||
onSourceUnregistered.emit(name);
|
|
||||||
|
// Notify event listeners of the deletion
|
||||||
|
onSourceUnregistered(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> SourceManager::getSourceNames() {
|
const std::vector<std::string>& SourceManager::getSourceNames() {
|
||||||
std::vector<std::string> names;
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
for (auto const& [name, src] : sources) { names.push_back(name); }
|
return sourceNames;
|
||||||
return names;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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()) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
if (selectedHandler != NULL) {
|
|
||||||
sources[selectedName]->deselectHandler(sources[selectedName]->ctx);
|
// Select the source
|
||||||
}
|
selectedSourceName = name;
|
||||||
selectedHandler = sources[name];
|
selectedSource = sources[name];
|
||||||
selectedHandler->selectHandler(selectedHandler->ctx);
|
|
||||||
selectedName = name;
|
// Call the selected source
|
||||||
if (core::args["server"].b()) {
|
selectedSource->select();
|
||||||
server::setInput(selectedHandler->stream);
|
|
||||||
}
|
// Retune to make sure the source has the latest frequency
|
||||||
else {
|
tune(frequency);
|
||||||
sigpath::iqFrontEnd.setInput(selectedHandler->stream);
|
|
||||||
}
|
|
||||||
// Set server input here
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceManager::showSelectedMenu() {
|
const std::string& SourceManager::getSelected() {
|
||||||
if (selectedHandler == NULL) {
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
return;
|
return selectedSourceName;
|
||||||
}
|
|
||||||
selectedHandler->menuHandler(selectedHandler->ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceManager::start() {
|
bool SourceManager::start() {
|
||||||
if (selectedHandler == NULL) {
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
return;
|
|
||||||
}
|
// Check if not already running
|
||||||
selectedHandler->startHandler(selectedHandler->ctx);
|
if (running) { return true; }
|
||||||
|
|
||||||
|
// Call source if selected and save if started
|
||||||
|
running = (!selectedSource) ? false : selectedSource->start();
|
||||||
|
|
||||||
|
return running;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceManager::stop() {
|
void SourceManager::stop() {
|
||||||
if (selectedHandler == NULL) {
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
return;
|
|
||||||
}
|
// Check if running
|
||||||
selectedHandler->stopHandler(selectedHandler->ctx);
|
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) {
|
void SourceManager::tune(double freq) {
|
||||||
if (selectedHandler == NULL) {
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
return;
|
|
||||||
|
// 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) {
|
void SourceManager::setTuningOffset(double offset) {
|
||||||
tuneOffset = offset;
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
tune(currentFreq);
|
|
||||||
|
// Update offset
|
||||||
|
this->offset = offset;
|
||||||
|
|
||||||
|
// Retune to take affect
|
||||||
|
tune(frequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceManager::setTuningMode(TuningMode mode) {
|
void SourceManager::setTuningMode(TuningMode mode) {
|
||||||
tuneMode = mode;
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
tune(currentFreq);
|
|
||||||
|
// Update mode
|
||||||
|
this->mode = mode;
|
||||||
|
|
||||||
|
// Retune to take affect
|
||||||
|
tune(frequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceManager::setPanadpterIF(double freq) {
|
void SourceManager::setPanadpterIF(double freq) {
|
||||||
ifFreq = freq;
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
tune(currentFreq);
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
@ -1,56 +1,153 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <dsp/stream.h>
|
#include <mutex>
|
||||||
#include <dsp/types.h>
|
#include <dsp/types.h>
|
||||||
|
#include <dsp/stream.h>
|
||||||
#include <utils/event.h>
|
#include <utils/event.h>
|
||||||
|
|
||||||
|
enum TuningMode {
|
||||||
|
TUNING_MODE_NORMAL,
|
||||||
|
TUNING_MODE_PANADAPTER
|
||||||
|
};
|
||||||
|
|
||||||
|
class Source;
|
||||||
|
|
||||||
class SourceManager {
|
class SourceManager {
|
||||||
|
friend Source;
|
||||||
public:
|
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;
|
* Unregister a source.
|
||||||
void (*menuHandler)(void* ctx);
|
* @param name Name of the source.
|
||||||
void (*selectHandler)(void* ctx);
|
*/
|
||||||
void (*deselectHandler)(void* ctx);
|
void unregisterSource(const std::string& name);
|
||||||
void (*startHandler)(void* ctx);
|
|
||||||
void (*stopHandler)(void* ctx);
|
|
||||||
void (*tuneHandler)(double freq, void* ctx);
|
|
||||||
void* ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum TuningMode {
|
/**
|
||||||
NORMAL,
|
* Get a list of source names.
|
||||||
PANADAPTER
|
* @return List of source names.
|
||||||
};
|
*/
|
||||||
|
const std::vector<std::string>& getSourceNames();
|
||||||
|
|
||||||
void registerSource(std::string name, SourceHandler* handler);
|
/**
|
||||||
void unregisterSource(std::string name);
|
* Select a source.
|
||||||
void selectSource(std::string name);
|
* @param name Name of the source.
|
||||||
void showSelectedMenu();
|
*/
|
||||||
void start();
|
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();
|
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);
|
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);
|
void setTuningOffset(double offset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set tuning mode.
|
||||||
|
* @param mode Tuning mode.
|
||||||
|
*/
|
||||||
void setTuningMode(TuningMode mode);
|
void setTuningMode(TuningMode mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set panadapter mode IF frequency.
|
||||||
|
* @param freq IF frequency in Hz.
|
||||||
|
*/
|
||||||
void setPanadpterIF(double freq);
|
void setPanadpterIF(double freq);
|
||||||
|
|
||||||
std::vector<std::string> getSourceNames();
|
// =============================================================================
|
||||||
|
|
||||||
|
// Emitted after a new source has been registered.
|
||||||
Event<std::string> onSourceRegistered;
|
Event<std::string> onSourceRegistered;
|
||||||
|
|
||||||
|
// Emitted when a source is about to be unregistered.
|
||||||
Event<std::string> onSourceUnregister;
|
Event<std::string> onSourceUnregister;
|
||||||
|
|
||||||
|
// Emitted after a source has been unregistered.
|
||||||
Event<std::string> onSourceUnregistered;
|
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;
|
Event<double> onRetune;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<std::string, SourceHandler*> sources;
|
void deselect();
|
||||||
std::string selectedName;
|
void setSamplerate(double samplerate);
|
||||||
SourceHandler* selectedHandler = NULL;
|
|
||||||
double tuneOffset;
|
std::vector<std::string> sourceNames;
|
||||||
double currentFreq;
|
std::map<std::string, Source*> sources;
|
||||||
double ifFreq = 0.0;
|
|
||||||
TuningMode tuneMode = TuningMode::NORMAL;
|
std::string selectedSourceName = "";
|
||||||
dsp::stream<dsp::complex_t> nullSource;
|
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;
|
||||||
};
|
};
|
@ -1,43 +1,51 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <vector>
|
#include <functional>
|
||||||
#include <utils/flog.h>
|
#include <stdexcept>
|
||||||
|
#include <mutex>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
template <class T>
|
typedef int HandlerID;
|
||||||
struct EventHandler {
|
|
||||||
EventHandler() {}
|
|
||||||
EventHandler(void (*handler)(T, void*), void* ctx) {
|
|
||||||
this->handler = handler;
|
|
||||||
this->ctx = ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
void (*handler)(T, void*);
|
template <typename... Args>
|
||||||
void* ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
class Event {
|
class Event {
|
||||||
|
using Handler = std::function<void(Args...)>;
|
||||||
public:
|
public:
|
||||||
Event() {}
|
HandlerID bind(Handler handler) {
|
||||||
~Event() {}
|
std::lock_guard<std::mutex> lck(mtx);
|
||||||
|
HandlerID id = genID();
|
||||||
void emit(T value) {
|
handlers[id] = handler;
|
||||||
for (auto const& handler : handlers) {
|
return id;
|
||||||
handler->handler(value, handler->ctx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void bindHandler(EventHandler<T>* handler) {
|
template<typename MHandler, class T>
|
||||||
handlers.push_back(handler);
|
HandlerID bind(MHandler handler, T* ctx) {
|
||||||
|
return bind([=](Args... args){
|
||||||
|
(ctx->*handler)(args...);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void unbindHandler(EventHandler<T>* handler) {
|
void unbind(HandlerID id) {
|
||||||
if (std::find(handlers.begin(), handlers.end(), handler) == handlers.end()) {
|
std::lock_guard<std::mutex> lck(mtx);
|
||||||
flog::error("Tried to remove a non-existent event handler");
|
if (handlers.find(id) == handlers.end()) {
|
||||||
return;
|
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:
|
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;
|
||||||
};
|
};
|
@ -45,9 +45,6 @@ public:
|
|||||||
}
|
}
|
||||||
config.release();
|
config.release();
|
||||||
|
|
||||||
_retuneHandler.ctx = this;
|
|
||||||
_retuneHandler.handler = retuneHandler;
|
|
||||||
|
|
||||||
gui::menu.registerEntry(name, menuHandler, this, NULL);
|
gui::menu.registerEntry(name, menuHandler, this, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,8 +84,8 @@ public:
|
|||||||
|
|
||||||
// Switch source to panadapter mode
|
// Switch source to panadapter mode
|
||||||
sigpath::sourceManager.setPanadpterIF(ifFreq);
|
sigpath::sourceManager.setPanadpterIF(ifFreq);
|
||||||
sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::PANADAPTER);
|
sigpath::sourceManager.setTuningMode(TUNING_MODE_PANADAPTER);
|
||||||
sigpath::sourceManager.onRetune.bindHandler(&_retuneHandler);
|
retuneHandlerId = sigpath::sourceManager.onRetune.bind(retuneHandler, this);
|
||||||
|
|
||||||
running = true;
|
running = true;
|
||||||
}
|
}
|
||||||
@ -98,8 +95,8 @@ public:
|
|||||||
if (!running) { return; }
|
if (!running) { return; }
|
||||||
|
|
||||||
// Switch source back to normal mode
|
// Switch source back to normal mode
|
||||||
sigpath::sourceManager.onRetune.unbindHandler(&_retuneHandler);
|
sigpath::sourceManager.onRetune.unbind(retuneHandlerId);
|
||||||
sigpath::sourceManager.setTuningMode(SourceManager::TuningMode::NORMAL);
|
sigpath::sourceManager.setTuningMode(TUNING_MODE_NORMAL);
|
||||||
|
|
||||||
// Disconnect from rigctl server
|
// Disconnect from rigctl server
|
||||||
client->close();
|
client->close();
|
||||||
@ -159,10 +156,9 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void retuneHandler(double freq, void* ctx) {
|
void retuneHandler(double freq) {
|
||||||
RigctlClientModule* _this = (RigctlClientModule*)ctx;
|
if (!client || !client->isOpen()) { return; }
|
||||||
if (!_this->client || !_this->client->isOpen()) { return; }
|
if (client->setFreq(freq)) {
|
||||||
if (_this->client->setFreq(freq)) {
|
|
||||||
flog::error("Could not set frequency");
|
flog::error("Could not set frequency");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,7 +174,7 @@ private:
|
|||||||
|
|
||||||
double ifFreq = 8830000.0;
|
double ifFreq = 8830000.0;
|
||||||
|
|
||||||
EventHandler<double> _retuneHandler;
|
HandlerID retuneHandlerId;
|
||||||
};
|
};
|
||||||
|
|
||||||
MOD_EXPORT void _INIT_() {
|
MOD_EXPORT void _INIT_() {
|
||||||
|
@ -25,7 +25,7 @@ SDRPP_MOD_INFO{
|
|||||||
|
|
||||||
ConfigManager config;
|
ConfigManager config;
|
||||||
|
|
||||||
class AirspySourceModule : public ModuleManager::Instance {
|
class AirspySourceModule : public ModuleManager::Instance, public Source {
|
||||||
public:
|
public:
|
||||||
AirspySourceModule(std::string name) {
|
AirspySourceModule(std::string name) {
|
||||||
this->name = name;
|
this->name = name;
|
||||||
@ -34,15 +34,6 @@ public:
|
|||||||
|
|
||||||
sampleRate = 10000000.0;
|
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();
|
refresh();
|
||||||
if (sampleRateList.size() > 0) {
|
if (sampleRateList.size() > 0) {
|
||||||
sampleRate = sampleRateList[0];
|
sampleRate = sampleRateList[0];
|
||||||
@ -54,11 +45,11 @@ public:
|
|||||||
config.release();
|
config.release();
|
||||||
selectByString(devSerial);
|
selectByString(devSerial);
|
||||||
|
|
||||||
sigpath::sourceManager.registerSource("Airspy", &handler);
|
sigpath::sourceManager.registerSource("Airspy", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
~AirspySourceModule() {
|
~AirspySourceModule() {
|
||||||
stop(this);
|
stop();
|
||||||
sigpath::sourceManager.unregisterSource("Airspy");
|
sigpath::sourceManager.unregisterSource("Airspy");
|
||||||
airspy_exit();
|
airspy_exit();
|
||||||
}
|
}
|
||||||
@ -231,6 +222,315 @@ public:
|
|||||||
airspy_close(dev);
|
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:
|
private:
|
||||||
std::string getBandwdithScaled(double bw) {
|
std::string getBandwdithScaled(double bw) {
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
@ -246,322 +546,6 @@ private:
|
|||||||
return std::string(buf);
|
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) {
|
static int callback(airspy_transfer_t* transfer) {
|
||||||
AirspySourceModule* _this = (AirspySourceModule*)transfer->ctx;
|
AirspySourceModule* _this = (AirspySourceModule*)transfer->ctx;
|
||||||
memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t));
|
memcpy(_this->stream.writeBuf, transfer->samples, transfer->sample_count * sizeof(dsp::complex_t));
|
||||||
@ -574,7 +558,6 @@ private:
|
|||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
dsp::stream<dsp::complex_t> stream;
|
dsp::stream<dsp::complex_t> stream;
|
||||||
double sampleRate;
|
double sampleRate;
|
||||||
SourceManager::SourceHandler handler;
|
|
||||||
bool running = false;
|
bool running = false;
|
||||||
double freq;
|
double freq;
|
||||||
uint64_t selectedSerial = 0;
|
uint64_t selectedSerial = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user