1 Commits

Author SHA1 Message Date
c488d72ce2 pushing work for the future new source system 2023-03-04 14:18:52 +01:00
43 changed files with 738 additions and 1181 deletions

View File

@ -1,7 +0,0 @@
# Important
Only minor bug fixes and bandplans are accepted.
Pull requests adding features or any bug fix that requires significant code changes will be automatically rejected.
Open an issue requesting a feature or discussing a possible bugfix instead.

View File

@ -13,7 +13,6 @@ endif (USE_BUNDLE_DEFAULTS)
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
add_definitions(-DSDRPP_IS_CORE)
add_definitions(-DFLOG_ANDROID_TAG="SDR++")
if (MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif ()

View File

@ -55,33 +55,24 @@ namespace sdrpp_credits {
"Dale L Puckett (K0HYD)",
"Daniele D'Agnelli",
"D. Jones",
"Dexruus",
"EB3FRN",
"Eric Johnson",
"Ernest Murphy (NH7L)",
"Flinger Films",
"Frank Werner (HB9FXQ)",
"gringogrigio",
"Jeff Moe",
"Joe Cupano",
"KD1SQ",
"Kezza",
"Krys Kamieniecki",
"Lee Donaghy",
"Lee KD1SQ",
".lozenge. (Hank Hill)",
"Martin Herren (HB9FXX)",
"ON4MU",
"Passion-Radio.com",
"Paul Maine",
"Peter Betz",
"Scanner School",
"Scott Palmer",
"SignalsEverywhere",
"Syne Ardwin (WI9SYN)",
"W4IPA",
"William Arcand (W1WRA)",
"Yves Rougy",
"Zipper"
};

View File

@ -67,6 +67,10 @@ namespace dsp::buffer {
sizes[writeCur] = count;
writeCur++;
writeCur = ((writeCur) % TEST_BUFFER_SIZE);
// if (((writeCur - readCur + TEST_BUFFER_SIZE) % TEST_BUFFER_SIZE) >= (TEST_BUFFER_SIZE-2)) {
// flog::warn("Overflow");
// }
}
cnd.notify_all();
_in->flush();

View File

@ -9,7 +9,7 @@ namespace dsp::convert {
StereoToMono(stream<stereo_t>* in) { base_type::init(in); }
static inline int process(int count, const stereo_t* in, float* out) {
inline int process(int count, const stereo_t* in, float* out) {
for (int i = 0; i < count; i++) {
out[i] = (in[i].l + in[i].r) / 2.0f;
}

View File

@ -3,8 +3,6 @@
#include "quadrature.h"
#include "../filter/fir.h"
#include "../taps/low_pass.h"
#include "../taps/high_pass.h"
#include "../taps/band_pass.h"
#include "../convert/mono_to_stereo.h"
namespace dsp::demod {
@ -19,26 +17,22 @@ namespace dsp::demod {
~FM() {
if (!base_type::_block_init) { return; }
base_type::stop();
dsp::taps::free(filterTaps);
dsp::taps::free(lpfTaps);
}
void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass, bool highPass) {
void init(dsp::stream<dsp::complex_t>* in, double samplerate, double bandwidth, bool lowPass) {
_samplerate = samplerate;
_bandwidth = bandwidth;
_lowPass = lowPass;
_highPass = highPass;
demod.init(NULL, bandwidth / 2.0, _samplerate);
loadDummyTaps();
fir.init(NULL, filterTaps);
// Initialize taps
updateFilter(lowPass, highPass);
lpfTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
lpf.init(NULL, lpfTaps);
if constexpr (std::is_same_v<T, float>) {
demod.out.free();
}
fir.out.free();
lpf.out.free();
base_type::init(in);
}
@ -49,7 +43,9 @@ namespace dsp::demod {
base_type::tempStop();
_samplerate = samplerate;
demod.setDeviation(_bandwidth / 2.0, _samplerate);
updateFilter(_lowPass, _highPass);
dsp::taps::free(lpfTaps);
lpfTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
lpf.setTaps(lpfTaps);
base_type::tempStart();
}
@ -58,20 +54,19 @@ namespace dsp::demod {
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
if (bandwidth == _bandwidth) { return; }
_bandwidth = bandwidth;
std::lock_guard<std::mutex> lck2(lpfMtx);
demod.setDeviation(_bandwidth / 2.0, _samplerate);
updateFilter(_lowPass, _highPass);
dsp::taps::free(lpfTaps);
lpfTaps = dsp::taps::lowPass(_bandwidth / 2, (_bandwidth / 2) * 0.1, _samplerate);
lpf.setTaps(lpfTaps);
}
void setLowPass(bool lowPass) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
updateFilter(lowPass, _highPass);
}
void setHighPass(bool highPass) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
updateFilter(_lowPass, highPass);
std::lock_guard<std::mutex> lck2(lpfMtx);
_lowPass = lowPass;
lpf.reset();
}
void reset() {
@ -79,23 +74,23 @@ namespace dsp::demod {
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop();
demod.reset();
fir.reset();
lpf.reset();
base_type::tempStart();
}
inline int process(int count, dsp::complex_t* in, T* out) {
if constexpr (std::is_same_v<T, float>) {
demod.process(count, in, out);
if (filtering) {
std::lock_guard<std::mutex> lck(filterMtx);
fir.process(count, out, out);
if (_lowPass) {
std::lock_guard<std::mutex> lck(lpfMtx);
lpf.process(count, out, out);
}
}
if constexpr (std::is_same_v<T, stereo_t>) {
demod.process(count, in, demod.out.writeBuf);
if (filtering) {
std::lock_guard<std::mutex> lck(filterMtx);
fir.process(count, demod.out.writeBuf, demod.out.writeBuf);
if (_lowPass) {
std::lock_guard<std::mutex> lck(lpfMtx);
lpf.process(count, demod.out.writeBuf, demod.out.writeBuf);
}
convert::MonoToStereo::process(count, demod.out.writeBuf, out);
}
@ -114,50 +109,13 @@ namespace dsp::demod {
}
private:
void updateFilter(bool lowPass, bool highPass) {
std::lock_guard<std::mutex> lck(filterMtx);
// Update values
_lowPass = lowPass;
_highPass = highPass;
filtering = (lowPass || highPass);
// Free filter taps
dsp::taps::free(filterTaps);
// Generate filter depending on low and high pass settings
if (_lowPass && _highPass) {
filterTaps = dsp::taps::bandPass<float>(300.0, _bandwidth / 2.0, 100.0, _samplerate);
}
else if (_highPass) {
filterTaps = dsp::taps::highPass(300.0, 100.0, _samplerate);
}
else if (_lowPass) {
filterTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
}
else {
loadDummyTaps();
}
// Set filter to use new taps
fir.setTaps(filterTaps);
fir.reset();
}
void loadDummyTaps() {
float dummyTap = 1.0f;
filterTaps = dsp::taps::fromArray<float>(1, &dummyTap);
}
double _samplerate;
double _bandwidth;
bool _lowPass;
bool _highPass;
bool filtering;
Quadrature demod;
tap<float> filterTaps;
filter::FIR<float, float> fir;
std::mutex filterMtx;
tap<float> lpfTaps;
filter::FIR<float, float> lpf;
std::mutex lpfMtx;
};
}

View File

@ -2,6 +2,5 @@
#include "../multirate/rrc_interpolator.h"
namespace dsp::mod {
// TODO: Check if resample before RRC is better than using the RRC taps as a filter (bandwidth probably not correct for alias-free resampling)
typedef multirate::RRCInterpolator<complex_t> PSK;
}

View File

@ -5,7 +5,7 @@
#include "../math/hz_to_rads.h"
namespace dsp::mod {
class Quadrature : public Processor<float, complex_t> {
class Quadrature : Processor<float, complex_t> {
using base_type = Processor<float, complex_t>;
public:
Quadrature() {}

View File

@ -83,6 +83,8 @@ namespace dsp::multirate {
int interp = OutSR / gcd;
int decim = InSR / gcd;
flog::warn("interp: {0}, decim: {1}", interp, decim);
// Configure resampler
double tapSamplerate = _symbolrate * (double)interp;
rrcTaps = taps::rootRaisedCosine<float>(_rrcTapCount * interp, _rrcBeta, _symbolrate, tapSamplerate);

View File

@ -1,183 +0,0 @@
#pragma once
#include "../processor.h"
#include "../window/nuttall.h"
#include <fftw3.h>
#include "../convert/stereo_to_mono.h"
namespace dsp::noise_reduction {
class Audio : public Processor<stereo_t, stereo_t> {
using base_type = Processor<stereo_t, stereo_t>;
public:
Audio() {}
Audio(stream<stereo_t>* in, int bins) { init(in, bins); }
~Audio() {
if (!base_type::_block_init) { return; }
base_type::stop();
destroyBuffers();
}
void init(stream<stereo_t>* in, int bins) {
_bins = bins;
complexBins = (bins / 2) + 1;
normFactor = 1.0f / (float)_bins;
initBuffers();
base_type::init(in);
}
void setBins(int bins) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop();
_bins = bins;
complexBins = (bins / 2) + 1;
normFactor = 1.0f / (float)_bins;
destroyBuffers();
initBuffers();
base_type::tempStart();
}
void setLevel(float level) {
_level = powf(10.0f, level * 0.1f);
}
void reset() {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop();
buffer::clear(buffer, _bins - 1);
buffer::clear(backFFTIn, _bins);
buffer::clear(noisePrint, _bins);
base_type::tempStart();
}
int process(int count, const stereo_t* in, stereo_t* out) {
// Write new input data to buffer
convert::StereoToMono::process(count, in, bufferStart);
// Iterate the FFT
for (int i = 0; i < count; i++) {
// Apply windows
volk_32f_x2_multiply_32f(forwFFTIn, &buffer[i], fftWin, _bins);
// Do forward FFT
fftwf_execute(forwardPlan);
// Get bin amplitude and square to get power
volk_32fc_magnitude_32f(ampBuf, (lv_32fc_t*)forwFFTOut, complexBins);
// Update noise print using a running average
volk_32f_s32f_multiply_32f(scaledAmps, ampBuf, alpha, complexBins);
volk_32f_s32f_multiply_32f(noisePrint, noisePrint, beta, complexBins);
volk_32f_x2_add_32f(noisePrint, noisePrint, scaledAmps, complexBins);
// Clamp amplitudes
volk_32f_x2_max_32f(ampBuf, ampBuf, noisePrint, complexBins);
// Compute Wiener (funny) filter
volk_32f_x2_subtract_32f(scaledAmps, ampBuf, noisePrint, complexBins);
volk_32f_x2_divide_32f(scaledAmps, scaledAmps, ampBuf, complexBins);
// Apply wiener filter to bins
volk_32fc_32f_multiply_32fc((lv_32fc_t*)backFFTIn, (lv_32fc_t*)forwFFTOut, scaledAmps, complexBins);
// Do reverse FFT and get first element
fftwf_execute(backwardPlan);
out[i].l = backFFTOut[_bins / 2];
out[i].r = backFFTOut[_bins / 2];
}
// Correct amplitude
volk_32f_s32f_multiply_32f((float*)out, (float*)out, normFactor, count*2);
// Move buffer buffer
memmove(buffer, &buffer[count], (_bins - 1) * sizeof(float));
return count;
}
int run() {
int count = base_type::_in->read();
if (count < 0) { return -1; }
process(count, base_type::_in->readBuf, base_type::out.writeBuf);
// Swap if some data was generated
base_type::_in->flush();
if (!base_type::out.swap(count)) { return -1; }
return count;
}
protected:
void initBuffers() {
// Allocate FFT buffers
forwFFTIn = (float*)fftwf_malloc(_bins * sizeof(float));
forwFFTOut = (complex_t*)fftwf_malloc(_bins * sizeof(complex_t));
backFFTIn = (complex_t*)fftwf_malloc(_bins * sizeof(complex_t));
backFFTOut = (float*)fftwf_malloc(_bins * sizeof(float));
// Allocate and clear delay buffer
buffer = buffer::alloc<float>(STREAM_BUFFER_SIZE + 64000);
bufferStart = &buffer[_bins - 1];
buffer::clear(buffer, _bins - 1);
// Clear backward FFT input
buffer::clear(backFFTIn, _bins);
// Allocate amplitude buffer
ampBuf = buffer::alloc<float>(_bins);
scaledAmps = buffer::alloc<float>(_bins);
noisePrint = buffer::alloc<float>(_bins);
buffer::clear(noisePrint, _bins);
// Allocate and generate Window
fftWin = buffer::alloc<float>(_bins);
for (int i = 0; i < _bins; i++) { fftWin[i] = window::nuttall(i, _bins - 1); }
// Plan FFTs
forwardPlan = fftwf_plan_dft_r2c_1d(_bins, forwFFTIn, (fftwf_complex*)forwFFTOut, FFTW_ESTIMATE);
backwardPlan = fftwf_plan_dft_c2r_1d(_bins, (fftwf_complex*)backFFTIn, backFFTOut, FFTW_ESTIMATE);
}
void destroyBuffers() {
fftwf_destroy_plan(forwardPlan);
fftwf_destroy_plan(backwardPlan);
fftwf_free(forwFFTIn);
fftwf_free(forwFFTOut);
fftwf_free(backFFTIn);
fftwf_free(backFFTOut);
buffer::free(buffer);
buffer::free(ampBuf);
buffer::free(scaledAmps);
buffer::free(noisePrint);
buffer::free(fftWin);
}
float _level = 0.0f;
float* forwFFTIn;
complex_t* forwFFTOut;
complex_t* backFFTIn;
float* backFFTOut;
fftwf_plan forwardPlan;
fftwf_plan backwardPlan;
float* buffer;
float* bufferStart;
float* fftWin;
float* ampBuf;
float* scaledAmps;
float* noisePrint;
int _bins;
int complexBins;
float normFactor = 1.0f;
float alpha = 0.0001f;
float beta = 0.9999f;
};
}

View File

@ -37,17 +37,21 @@ namespace dsp::noise_reduction {
inline int process(int count, complex_t* in, complex_t* out) {
for (int i = 0; i < count; i++) {
// Get signal amplitude and pass value if null
// Get signal amplitude
float inAmp = in[i].amplitude();
if (!inAmp) {
out[i] = in[i];
// Update average amplitude
float gain = 1.0f;
if (inAmp != 0.0f) {
amp = (amp * _invRate) + (inAmp * _rate);
float excess = inAmp / amp;
if (excess > _level) {
gain = 1.0f / excess;
}
}
// Update running average of amplitude
amp = (_rate*inAmp) + (_invRate*amp);
// Null out if spike (Note: ideally, it should try to guess the real data)
out[i] = (inAmp > _level*amp) ? complex_t{0.0f,0.0f} : in[i];
// Scale output by gain
out[i] = in[i] * gain;
}
return count;
}

View File

@ -15,7 +15,7 @@ namespace dsp::taps {
if (oddTapCount && !(count % 2)) { count++; }
return windowedSinc<T>(count, (bandStop - bandStart) / 2.0, sampleRate, [=](double n, double N) {
if constexpr (std::is_same_v<T, float>) {
return 2.0f * cosf(offsetOmega * (float)n) * window::nuttall(n, N);
return cosf(offsetOmega * (float)n) * window::nuttall(n, N);
}
if constexpr (std::is_same_v<T, complex_t>) {
// The offset is negative to flip the taps. Complex bandpass are asymetric

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

@ -169,7 +169,7 @@ namespace flog {
fprintf(outStream, "] %s\n", out.c_str());
#elif defined(__ANDROID__)
// Print format string
__android_log_print(TYPE_PRIORITIES[type], FLOG_ANDROID_TAG, COLOR_WHITE "[%02d/%02d/%02d %02d:%02d:%02d.%03d] [%s%s" COLOR_WHITE "] %s\n",
__android_log_buf_print(LOG_ID_DEFAULT, TYPE_PRIORITIES[type], FLOG_ANDROID_TAG, COLOR_WHITE "[%02d/%02d/%02d %02d:%02d:%02d.%03d] [%s%s" COLOR_WHITE "] %s\n",
nowc->tm_mday, nowc->tm_mon + 1, nowc->tm_year + 1900, nowc->tm_hour, nowc->tm_min, nowc->tm_sec, 0, TYPE_COLORS[type], TYPE_STR[type], out.c_str());
#else
// Print format string

View File

@ -288,7 +288,6 @@ namespace net {
// Save data
for (auto iface = addresses; iface; iface = iface->ifa_next) {
if (!iface->ifa_addr || !iface->ifa_netmask) { continue; }
if (iface->ifa_addr->sa_family != AF_INET) { continue; }
InterfaceInfo info;
info.address = ntohl(*(uint32_t*)&iface->ifa_addr->sa_data[2]);

View File

@ -1,7 +1,6 @@
#include <utils/networking.h>
#include <assert.h>
#include <utils/flog.h>
#include <stdexcept>
namespace net {

View File

@ -8,7 +8,7 @@ class OptionList {
public:
OptionList() { updateText(); }
void define(const K& key, const std::string& name, const T& value) {
void define(K key, std::string name, T value) {
if (keyExists(key)) { throw std::runtime_error("Key already exists"); }
if (nameExists(name)) { throw std::runtime_error("Name already exists"); }
if (valueExists(value)) { throw std::runtime_error("Value already exists"); }
@ -18,27 +18,27 @@ public:
updateText();
}
void define(const std::string& name, const T& value) {
void define(std::string name, T value) {
define(name, name, value);
}
void undefine(int id) {
void undefined(int id) {
keys.erase(keys.begin() + id);
names.erase(names.begin() + id);
values.erase(values.begin() + id);
updateText();
}
void undefineKey(const K& key) {
undefine(keyId(key));
void undefineKey(K key) {
undefined(keyId(key));
}
void undefineName(const std::string& name) {
undefine(nameId(name));
void undefineName(std::string name) {
undefined(nameId(name));
}
void undefineValue(const T& value) {
undefine(valueId(value));
void undefineValue(T value) {
undefined(valueId(value));
}
void clear() {
@ -48,61 +48,61 @@ public:
updateText();
}
int size() const {
int size() {
return keys.size();
}
bool empty() const {
bool empty() {
return keys.empty();
}
bool keyExists(const K& key) const {
bool keyExists(K key) {
if (std::find(keys.begin(), keys.end(), key) != keys.end()) { return true; }
return false;
}
bool nameExists(const std::string& name) const {
bool nameExists(std::string name) {
if (std::find(names.begin(), names.end(), name) != names.end()) { return true; }
return false;
}
bool valueExists(const T& value) const {
bool valueExists(T value) {
if (std::find(values.begin(), values.end(), value) != values.end()) { return true; }
return false;
}
int keyId(const K& key) const {
int keyId(K key) {
auto it = std::find(keys.begin(), keys.end(), key);
if (it == keys.end()) { throw std::runtime_error("Key doesn't exists"); }
return std::distance(keys.begin(), it);
}
int nameId(const std::string& name) const {
int nameId(std::string name) {
auto it = std::find(names.begin(), names.end(), name);
if (it == names.end()) { throw std::runtime_error("Name doesn't exists"); }
return std::distance(names.begin(), it);
}
int valueId(const T& value) const {
int valueId(T value) {
auto it = std::find(values.begin(), values.end(), value);
if (it == values.end()) { throw std::runtime_error("Value doesn't exists"); }
return std::distance(values.begin(), it);
}
inline const K& key(int id) const {
K key(int id) {
return keys[id];
}
inline const std::string& name(int id) const {
std::string name(int id) {
return names[id];
}
inline const T& value(int id) const {
T value(int id) {
return values[id];
}
inline const T& operator[](int& id) const {
return values[id];
T operator[](int& id) {
return value(id);
}
const char* txt = NULL;

View File

@ -214,10 +214,10 @@ private:
if (ImGui::Checkbox(CONCAT("Show Reference Lines##m17_showlines_", _this->name), &_this->showLines)) {
if (_this->showLines) {
_this->diag.lines.push_back(-1.0);
_this->diag.lines.push_back(-1.0/3.0);
_this->diag.lines.push_back(1.0/3.0);
_this->diag.lines.push_back(1.0);
_this->diag.lines.push_back(-0.75f);
_this->diag.lines.push_back(-0.25f);
_this->diag.lines.push_back(0.25f);
_this->diag.lines.push_back(0.75f);
}
else {
_this->diag.lines.clear();

View File

@ -45,7 +45,6 @@ namespace demod {
virtual int getDefaultDeemphasisMode() = 0;
virtual bool getFMIFNRAllowed() = 0;
virtual bool getNBAllowed() = 0;
virtual bool getAFNRAllowed() = 0;
virtual dsp::stream<dsp::stereo_t>* getOutput() = 0;
};
}

View File

@ -86,7 +86,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return false; }
bool getNBAllowed() { return false; }
bool getAFNRAllowed() { return false; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
private:

View File

@ -92,7 +92,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return false; }
bool getNBAllowed() { return false; }
bool getAFNRAllowed() { return false; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
private:

View File

@ -79,7 +79,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return false; }
bool getNBAllowed() { return true; }
bool getAFNRAllowed() { return false; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
private:

View File

@ -79,7 +79,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return false; }
bool getNBAllowed() { return true; }
bool getAFNRAllowed() { return false; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
private:

View File

@ -19,17 +19,15 @@ namespace demod {
// Load config
_config->acquire();
bool modified = false;
if (config->conf[name][getName()].contains("lowPass")) {
_lowPass = config->conf[name][getName()]["lowPass"];
}
if (config->conf[name][getName()].contains("highPass")) {
_highPass = config->conf[name][getName()]["highPass"];
}
_config->release();
_config->release(modified);
// Define structure
demod.init(input, getIFSampleRate(), bandwidth, _lowPass, _highPass);
demod.init(input, getIFSampleRate(), bandwidth, _lowPass);
}
void start() { demod.start(); }
@ -43,12 +41,6 @@ namespace demod {
_config->conf[name][getName()]["lowPass"] = _lowPass;
_config->release(true);
}
if (ImGui::Checkbox(("High Pass##_radio_wfm_highpass_" + name).c_str(), &_highPass)) {
demod.setHighPass(_highPass);
_config->acquire();
_config->conf[name][getName()]["highPass"] = _highPass;
_config->release(true);
}
}
void setBandwidth(double bandwidth) {
@ -75,7 +67,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return true; }
bool getNBAllowed() { return false; }
bool getAFNRAllowed() { return false; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
private:
@ -84,7 +75,6 @@ namespace demod {
ConfigManager* _config = NULL;
bool _lowPass = true;
bool _highPass = false;
std::string name;
};

View File

@ -59,7 +59,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return false; }
bool getNBAllowed() { return true; }
bool getAFNRAllowed() { return false; }
dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; }
private:

View File

@ -80,7 +80,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_NONE; }
bool getFMIFNRAllowed() { return false; }
bool getNBAllowed() { return true; }
bool getAFNRAllowed() { return true; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
private:

View File

@ -130,7 +130,6 @@ namespace demod {
int getDefaultDeemphasisMode() { return DEEMP_MODE_50US; }
bool getFMIFNRAllowed() { return true; }
bool getNBAllowed() { return false; }
bool getAFNRAllowed() { return false; }
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
// ============= DEDICATED FUNCTIONS =============

View File

@ -9,7 +9,6 @@
#include <dsp/noise_reduction/noise_blanker.h>
#include <dsp/noise_reduction/fm_if.h>
#include <dsp/noise_reduction/squelch.h>
#include <dsp/noise_reduction/audio.h>
#include <dsp/multirate/rational_resampler.h>
#include <dsp/filter/deephasis.h>
#include <core.h>
@ -84,11 +83,9 @@ public:
resamp.init(NULL, 250000.0, 48000.0);
deemp.init(NULL, 50e-6, 48000.0);
afNR.init(NULL, 1024);
afChain.addBlock(&resamp, true);
afChain.addBlock(&deemp, false);
afChain.addBlock(&afNR, false);
// Initialize the sink
srChangeHandler.ctx = this;
@ -250,12 +247,6 @@ private:
if (!_this->nbEnabled && _this->enabled) { style::endDisabled(); }
}
// Noise reduction
if (_this->afNRAllowed) {
if (ImGui::Checkbox(("Audio Noise Reduction##_radio_afnr_ena_" + _this->name).c_str(), &_this->afNREnabled)) {
_this->setAFNREnabled(_this->afNREnabled);
}
}
// Squelch
if (ImGui::Checkbox(("Squelch##_radio_sqelch_ena_" + _this->name).c_str(), &_this->squelchEnabled)) {
@ -379,8 +370,6 @@ private:
fmIFPresetId = ifnrPresets.valueId(IFNR_PRESET_VOICE);
nbAllowed = selectedDemod->getNBAllowed();
nbEnabled = false;
afNRAllowed = selectedDemod->getAFNRAllowed();
afNREnabled = false;
nbLevel = 0.0f;
double ifSamplerate = selectedDemod->getIFSampleRate();
config.acquire();
@ -422,9 +411,6 @@ private:
if (config.conf[name][selectedDemod->getName()].contains("noiseBlankerLevel")) {
nbLevel = config.conf[name][selectedDemod->getName()]["noiseBlankerLevel"];
}
if (config.conf[name][selectedDemod->getName()].contains("audioNoiseReductionEnabled")) {
nbEnabled = config.conf[name][selectedDemod->getName()]["audioNoiseReductionEnabled"];
}
config.release();
// Configure VFO
@ -460,10 +446,7 @@ private:
afChain.enableBlock(&resamp, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
// Configure deemphasis
setDeemphasisMode(deempAllowed ? deempModes[deempId] : DEEMP_MODE_NONE);
// Configure AF NR
setAFNREnabled(afNRAllowed && afNREnabled);
setDeemphasisMode(deempModes[deempId]);
}
else {
// Disable everything if post processing is disabled
@ -525,17 +508,6 @@ private:
config.release(true);
}
void setAFNREnabled(bool enable) {
afNREnabled = enable;
if (!postProcEnabled || !selectedDemod) { return; }
afChain.setBlockEnabled(&afNR, afNREnabled, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
// Save config
config.acquire();
config.conf[name][selectedDemod->getName()]["audioNoiseReductionEnabled"] = nbEnabled;
config.release(true);
}
void setNBEnabled(bool enable) {
nbEnabled = enable;
if (!selectedDemod) { return; }
@ -688,7 +660,6 @@ private:
dsp::chain<dsp::stereo_t> afChain;
dsp::multirate::RationalResampler<dsp::stereo_t> resamp;
dsp::filter::Deemphasis<dsp::stereo_t> deemp;
dsp::noise_reduction::Audio afNR;
SinkManager::Stream stream;
@ -712,9 +683,6 @@ private:
int deempId = 0;
bool deempAllowed;
bool afNREnabled = false;
bool afNRAllowed;
bool FMIFNRAllowed;
bool FMIFNREnabled = false;
int fmIFPresetId;

View File

@ -23,8 +23,7 @@
#include <utils/wav.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
#define SILENCE_LVL 10e-6
#define SILENCE_LVL 10e-20
SDRPP_MOD_INFO{
/* Name: */ "recorder",
@ -316,11 +315,11 @@ private:
}
if (_this->recording) { style::endDisabled(); }
if (ImGui::Checkbox(CONCAT("Ignore silence##_recorder_ignore_silence_", _this->name), &_this->ignoreSilence)) {
config.acquire();
config.conf[_this->name]["ignoreSilence"] = _this->ignoreSilence;
config.release(true);
}
// if (ImGui::Checkbox(CONCAT("Ignore silence##_recorder_ignore_silence_", _this->name), &_this->ignoreSilence)) {
// config.acquire();
// config.conf[_this->name]["ignoreSilence"] = _this->ignoreSilence;
// config.release(true);
// }
}
// Record button
@ -339,13 +338,7 @@ private:
uint64_t seconds = _this->writer.getSamplesWritten() / _this->samplerate;
time_t diff = seconds;
tm* dtm = gmtime(&diff);
if (_this->ignoreSilence && _this->ignoringSilence) {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "Paused %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec);
}
else {
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec);
}
ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "Recording %02d:%02d:%02d", dtm->tm_hour, dtm->tm_min, dtm->tm_sec);
}
}
@ -487,31 +480,13 @@ private:
static void stereoHandler(dsp::stereo_t* data, int count, void* ctx) {
RecorderModule* _this = (RecorderModule*)ctx;
if (_this->ignoreSilence) {
float absMax = 0.0f;
float* _data = (float*)data;
int _count = count * 2;
for (int i = 0; i < _count; i++) {
float val = fabsf(_data[i]);
if (val > absMax) { absMax = val; }
}
_this->ignoringSilence = (absMax < SILENCE_LVL);
if (_this->ignoringSilence) { return; }
}
// TODO: Ignore silence
_this->writer.write((float*)data, count);
}
static void monoHandler(float* data, int count, void* ctx) {
RecorderModule* _this = (RecorderModule*)ctx;
if (_this->ignoreSilence) {
float absMax = 0.0f;
for (int i = 0; i < count; i++) {
float val = fabsf(data[i]);
if (val > absMax) { absMax = val; }
}
_this->ignoringSilence = (absMax < SILENCE_LVL);
if (_this->ignoringSilence) { return; }
}
// TODO: Ignore silence
_this->writer.write(data, count);
}
@ -554,7 +529,6 @@ private:
dsp::stereo_t audioLvl = { -100.0f, -100.0f };
bool recording = false;
bool ignoringSilence = false;
wav::Writer writer;
std::recursive_mutex recMtx;
dsp::stream<dsp::complex_t>* basebandStream;

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

@ -429,34 +429,25 @@ I will soon publish a contributing.md listing the code style to use.
* Dale L Puckett (K0HYD)
* [Daniele D'Agnelli](https://linkedin.com/in/dagnelli)
* D. Jones
* Dexruus
* [EB3FRN](https://www.eb3frn.net/)
* Eric Johnson
* Ernest Murphy (NH7L)
* Flinger Films
* [Frank Werner (HB9FXQ)](https://twitter.com/HB9FXQ)
* gringogrigio
* Jeff Moe
* Joe Cupano
* KD1SQ
* Kezza
* Krys Kamieniecki
* Lee Donaghy
* Lee KD1SQ
* .lozenge. (Hank Hill)
* Martin Herren (HB9FXX)
* ON4MU
* [Passion-Radio.com](https://passion-radio.com/)
* Paul Maine
* Peter Betz
* [Scanner School](https://scannerschool.com/)
* Scott Palmer
* [SignalsEverywhere](https://signalseverywhere.com/)
* Syne Ardwin (WI9SYN)
* [W4IPA](https://twitter.com/W4IPAstroke5)
* William Arcand (W1WRA)
* [Yves Rougy](https://www.twitch.tv/yorzian)
* [Zipper](https://github.com/reppiZ)
* [Zipper](github.com/reppiZ)
## Contributors

View File

@ -1,274 +0,0 @@
{
"name": "Belgium",
"country_name": "Belgium",
"country_code": "BE",
"author_name": "Bastien Cabay - ON4BCY",
"author_url": "https://qrz.com/db/ON4BCY",
"bands": [
{
"name": "2200m - Amateur",
"type": "amateur",
"start": 135700,
"end": 137800
},
{
"name": "630m - Amateur",
"type": "amateur",
"start": 472000,
"end": 479000
},
{
"name": "600m - Amateur",
"type": "amateur",
"start": 501000,
"end": 504000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 526500,
"end": 1606500
},
{
"name": "160m - Amateur",
"type": "amateur",
"start": 1810000,
"end": 2000000
},
{
"name": "80m - Amateur",
"type": "amateur",
"start": 3500000,
"end": 3800000
},
{
"name": "60m - Amateur",
"type": "amateur",
"start": 5351500,
"end": 5366500
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 5950000,
"end": 6200000
},
{
"name": "40m - Amateur",
"type": "amateur",
"start": 7000000,
"end": 7200000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 7200000,
"end": 7300000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 9500000,
"end": 9900000
},
{
"name": "30m - Amateur",
"type": "amateur",
"start": 10100000,
"end": 10150000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 11650000,
"end": 12050000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 13600000,
"end": 13800000
},
{
"name": "20m - Amateur",
"type": "amateur",
"start": 14000000,
"end": 14350000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 15100000,
"end": 15600000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 17550000,
"end": 17900000
},
{
"name": "17m - Amateur",
"type": "amateur",
"start": 18068000,
"end": 18168000
},
{
"name": "15m - Amateur",
"type": "amateur",
"start": 21000000,
"end": 21450000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 21450000,
"end": 21850000
},
{
"name": "12m - Amateur",
"type": "amateur",
"start": 24890000,
"end": 24990000
},
{
"name": "AM Broadcast",
"type": "broadcast",
"start": 25670000,
"end": 26100000
},
{
"name": "11m - Citizen Band",
"type": "amateur",
"start": 26960000,
"end": 27410000
},
{
"name": "10m - Amateur",
"type": "amateur",
"start": 28000000,
"end": 29700000
},
{
"name": "6m - Amateur",
"type": "amateur",
"start": 50000000,
"end": 52000000
},
{
"name": "4m - Amateur",
"type": "amateur",
"start": 69945000,
"end": 69955000
},
{
"name": "4m - Amateur",
"type": "amateur",
"start": 70190000,
"end": 70412500
},
{
"name": "FM Broadcast",
"type": "broadcast",
"start": 87500000,
"end": 108000000
},
{
"name": "Space Exploration / Meteorology Sat. / S-PCS",
"type": "satellite",
"start": 137000000,
"end": 138000000
},
{
"name": "2m - Amateur",
"type": "amateur",
"start": 144000000,
"end": 146000000
},
{
"name": "T-DAB Broadcast",
"type": "broadcast",
"start": 174000000,
"end": 223000000
},
{
"name": "70cm - Amateur",
"type": "amateur",
"start": 430000000,
"end": 440000000
},
{
"name": "PMR446",
"type": "amateur",
"start": 446000000,
"end": 446200000
},
{
"name": "DVB-T - Broadcast",
"type": "broadcast",
"start": 470000000,
"end": 790000000
},
{
"name": "23cm - Amateur",
"type": "amateur",
"start": 1240000000,
"end": 1300000000
},
{
"name": "13cm - Amateur",
"type": "amateur",
"start": 2300000000,
"end": 2450000000
},
{
"name": "6cm - Amateur",
"type": "amateur",
"start": 5650000000,
"end": 5850000000
},
{
"name": "3cm - Amateur",
"type": "amateur",
"start": 10000000000,
"end": 10500000000
},
{
"name": "1.25cm - Amateur",
"type": "amateur",
"start": 24000000000,
"end": 24250000000
},
{
"name": "6mm - Amateur",
"type": "amateur",
"start": 47000000000,
"end": 47200000000
},
{
"name": "4mm - Amateur",
"type": "amateur",
"start": 75500000000,
"end": 81000000000
},
{
"name": "2.5mm - Amateur",
"type": "amateur",
"start": 122250000000,
"end": 123000000000
},
{
"name": "2mm - Amateur",
"type": "amateur",
"start": 142000000000,
"end": 149000000000
},
{
"name": "1mm - Amateur",
"type": "amateur",
"start": 241000000000,
"end": 250000000000
}
]
}

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;

View File

@ -25,7 +25,7 @@ ConfigManager config;
struct DeviceInfo {
RtAudio::DeviceInfo info;
int id;
bool operator==(const struct DeviceInfo& other) const {
bool operator==(const struct DeviceInfo& other) {
return other.id == id;
}
};

View File

@ -205,7 +205,6 @@ namespace hermes {
}
std::vector<Info> discover() {
// TODO: Maybe try to instead detect on each interface as a work around for 0.0.0.0 not receiving anything?
auto sock = net::openudp("0.0.0.0", 1024);
// Build discovery packet

View File

@ -39,7 +39,7 @@ namespace hermes {
uint8_t gatewareVerMin;
BoardID boardId;
bool operator==(const Info& b) const {
bool operator==(const Info& b) {
return !memcmp(mac, b.mac, 6);
}
};

View File

@ -319,7 +319,6 @@ private:
static void start(void* ctx) {
LimeSDRSourceModule* _this = (LimeSDRSourceModule*)ctx;
if (_this->running) { return; }
if (_this->selectedDevName.empty()) { return; }
// Open device
_this->openDev = NULL;
@ -330,10 +329,7 @@ private:
if (err) {
LMS_Close(_this->openDev);
LMS_Open(&_this->openDev, _this->devList[_this->devId], NULL);
if (err = LMS_Init(_this->openDev)) {
flog::error("Failed to re-initialize device ({})", err);
return;
}
LMS_Init(_this->openDev);
}
flog::warn("Channel count: {0}", LMS_GetNumChannels(_this->openDev, false));
@ -550,4 +546,4 @@ MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) {
MOD_EXPORT void _END_() {
config.disableAutoSave();
config.save();
}
}

View File

@ -108,12 +108,12 @@ namespace rfspace {
}
break;
case RFSPACE_DEV_ID_NET_SDR:
case RFSPACE_DEV_ID_SDR_IP:
default:
for (int n = 80000000 / (4 * 25); n >= 32000; n /= 2) {
sr.push_back(n);
}
break;
default:
break;
}
return sr;

View File

@ -503,7 +503,7 @@ private:
float refStep = 0.5;
struct SRCombo {
bool operator==(const SRCombo& b) const {
bool operator==(const SRCombo& b) {
return baseId == b.baseId && decimId == b.decimId;
}