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") file(GLOB_RECURSE SRC "src/*.cpp" "src/*.c")
add_definitions(-DSDRPP_IS_CORE) add_definitions(-DSDRPP_IS_CORE)
add_definitions(-DFLOG_ANDROID_TAG="SDR++")
if (MSVC) if (MSVC)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif () endif ()

View File

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

View File

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

View File

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

View File

@ -3,8 +3,6 @@
#include "quadrature.h" #include "quadrature.h"
#include "../filter/fir.h" #include "../filter/fir.h"
#include "../taps/low_pass.h" #include "../taps/low_pass.h"
#include "../taps/high_pass.h"
#include "../taps/band_pass.h"
#include "../convert/mono_to_stereo.h" #include "../convert/mono_to_stereo.h"
namespace dsp::demod { namespace dsp::demod {
@ -19,26 +17,22 @@ namespace dsp::demod {
~FM() { ~FM() {
if (!base_type::_block_init) { return; } if (!base_type::_block_init) { return; }
base_type::stop(); 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; _samplerate = samplerate;
_bandwidth = bandwidth; _bandwidth = bandwidth;
_lowPass = lowPass; _lowPass = lowPass;
_highPass = highPass;
demod.init(NULL, bandwidth / 2.0, _samplerate); demod.init(NULL, bandwidth / 2.0, _samplerate);
loadDummyTaps(); lpfTaps = dsp::taps::lowPass(_bandwidth / 2.0, (_bandwidth / 2.0) * 0.1, _samplerate);
fir.init(NULL, filterTaps); lpf.init(NULL, lpfTaps);
// Initialize taps
updateFilter(lowPass, highPass);
if constexpr (std::is_same_v<T, float>) { if constexpr (std::is_same_v<T, float>) {
demod.out.free(); demod.out.free();
} }
fir.out.free(); lpf.out.free();
base_type::init(in); base_type::init(in);
} }
@ -49,7 +43,9 @@ namespace dsp::demod {
base_type::tempStop(); base_type::tempStop();
_samplerate = samplerate; _samplerate = samplerate;
demod.setDeviation(_bandwidth / 2.0, _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(); base_type::tempStart();
} }
@ -58,20 +54,19 @@ namespace dsp::demod {
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
if (bandwidth == _bandwidth) { return; } if (bandwidth == _bandwidth) { return; }
_bandwidth = bandwidth; _bandwidth = bandwidth;
std::lock_guard<std::mutex> lck2(lpfMtx);
demod.setDeviation(_bandwidth / 2.0, _samplerate); 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) { void setLowPass(bool lowPass) {
assert(base_type::_block_init); assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); 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 setHighPass(bool highPass) {
assert(base_type::_block_init);
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
updateFilter(_lowPass, highPass);
} }
void reset() { void reset() {
@ -79,23 +74,23 @@ namespace dsp::demod {
std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx); std::lock_guard<std::recursive_mutex> lck(base_type::ctrlMtx);
base_type::tempStop(); base_type::tempStop();
demod.reset(); demod.reset();
fir.reset(); lpf.reset();
base_type::tempStart(); base_type::tempStart();
} }
inline int process(int count, dsp::complex_t* in, T* out) { inline int process(int count, dsp::complex_t* in, T* out) {
if constexpr (std::is_same_v<T, float>) { if constexpr (std::is_same_v<T, float>) {
demod.process(count, in, out); demod.process(count, in, out);
if (filtering) { if (_lowPass) {
std::lock_guard<std::mutex> lck(filterMtx); std::lock_guard<std::mutex> lck(lpfMtx);
fir.process(count, out, out); lpf.process(count, out, out);
} }
} }
if constexpr (std::is_same_v<T, stereo_t>) { if constexpr (std::is_same_v<T, stereo_t>) {
demod.process(count, in, demod.out.writeBuf); demod.process(count, in, demod.out.writeBuf);
if (filtering) { if (_lowPass) {
std::lock_guard<std::mutex> lck(filterMtx); std::lock_guard<std::mutex> lck(lpfMtx);
fir.process(count, demod.out.writeBuf, demod.out.writeBuf); lpf.process(count, demod.out.writeBuf, demod.out.writeBuf);
} }
convert::MonoToStereo::process(count, demod.out.writeBuf, out); convert::MonoToStereo::process(count, demod.out.writeBuf, out);
} }
@ -114,50 +109,13 @@ namespace dsp::demod {
} }
private: 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 _samplerate;
double _bandwidth; double _bandwidth;
bool _lowPass; bool _lowPass;
bool _highPass;
bool filtering;
Quadrature demod; Quadrature demod;
tap<float> filterTaps; tap<float> lpfTaps;
filter::FIR<float, float> fir; filter::FIR<float, float> lpf;
std::mutex filterMtx; std::mutex lpfMtx;
}; };
} }

View File

@ -2,6 +2,5 @@
#include "../multirate/rrc_interpolator.h" #include "../multirate/rrc_interpolator.h"
namespace dsp::mod { 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; typedef multirate::RRCInterpolator<complex_t> PSK;
} }

View File

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

View File

@ -83,6 +83,8 @@ namespace dsp::multirate {
int interp = OutSR / gcd; int interp = OutSR / gcd;
int decim = InSR / gcd; int decim = InSR / gcd;
flog::warn("interp: {0}, decim: {1}", interp, decim);
// Configure resampler // Configure resampler
double tapSamplerate = _symbolrate * (double)interp; double tapSamplerate = _symbolrate * (double)interp;
rrcTaps = taps::rootRaisedCosine<float>(_rrcTapCount * interp, _rrcBeta, _symbolrate, tapSamplerate); 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) { inline int process(int count, complex_t* in, complex_t* out) {
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
// Get signal amplitude and pass value if null // Get signal amplitude
float inAmp = in[i].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 // Scale output by gain
amp = (_rate*inAmp) + (_invRate*amp); out[i] = in[i] * gain;
// 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];
} }
return count; return count;
} }

View File

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

View File

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

View File

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

View File

@ -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);
} }

View File

@ -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;
}; };

View File

@ -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;
}; };

View File

@ -169,7 +169,7 @@ namespace flog {
fprintf(outStream, "] %s\n", out.c_str()); fprintf(outStream, "] %s\n", out.c_str());
#elif defined(__ANDROID__) #elif defined(__ANDROID__)
// Print format string // 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()); 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 #else
// Print format string // Print format string

View File

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

View File

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

View File

@ -8,7 +8,7 @@ class OptionList {
public: public:
OptionList() { updateText(); } 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 (keyExists(key)) { throw std::runtime_error("Key already exists"); }
if (nameExists(name)) { throw std::runtime_error("Name already exists"); } if (nameExists(name)) { throw std::runtime_error("Name already exists"); }
if (valueExists(value)) { throw std::runtime_error("Value already exists"); } if (valueExists(value)) { throw std::runtime_error("Value already exists"); }
@ -18,27 +18,27 @@ public:
updateText(); updateText();
} }
void define(const std::string& name, const T& value) { void define(std::string name, T value) {
define(name, name, value); define(name, name, value);
} }
void undefine(int id) { void undefined(int id) {
keys.erase(keys.begin() + id); keys.erase(keys.begin() + id);
names.erase(names.begin() + id); names.erase(names.begin() + id);
values.erase(values.begin() + id); values.erase(values.begin() + id);
updateText(); updateText();
} }
void undefineKey(const K& key) { void undefineKey(K key) {
undefine(keyId(key)); undefined(keyId(key));
} }
void undefineName(const std::string& name) { void undefineName(std::string name) {
undefine(nameId(name)); undefined(nameId(name));
} }
void undefineValue(const T& value) { void undefineValue(T value) {
undefine(valueId(value)); undefined(valueId(value));
} }
void clear() { void clear() {
@ -48,61 +48,61 @@ public:
updateText(); updateText();
} }
int size() const { int size() {
return keys.size(); return keys.size();
} }
bool empty() const { bool empty() {
return keys.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; } if (std::find(keys.begin(), keys.end(), key) != keys.end()) { return true; }
return false; 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; } if (std::find(names.begin(), names.end(), name) != names.end()) { return true; }
return false; return false;
} }
bool valueExists(const T& value) const { bool valueExists(T value) {
if (std::find(values.begin(), values.end(), value) != values.end()) { return true; } if (std::find(values.begin(), values.end(), value) != values.end()) { return true; }
return false; return false;
} }
int keyId(const K& key) const { int keyId(K key) {
auto it = std::find(keys.begin(), keys.end(), key); auto it = std::find(keys.begin(), keys.end(), key);
if (it == keys.end()) { throw std::runtime_error("Key doesn't exists"); } if (it == keys.end()) { throw std::runtime_error("Key doesn't exists"); }
return std::distance(keys.begin(), it); 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); auto it = std::find(names.begin(), names.end(), name);
if (it == names.end()) { throw std::runtime_error("Name doesn't exists"); } if (it == names.end()) { throw std::runtime_error("Name doesn't exists"); }
return std::distance(names.begin(), it); 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); auto it = std::find(values.begin(), values.end(), value);
if (it == values.end()) { throw std::runtime_error("Value doesn't exists"); } if (it == values.end()) { throw std::runtime_error("Value doesn't exists"); }
return std::distance(values.begin(), it); return std::distance(values.begin(), it);
} }
inline const K& key(int id) const { K key(int id) {
return keys[id]; return keys[id];
} }
inline const std::string& name(int id) const { std::string name(int id) {
return names[id]; return names[id];
} }
inline const T& value(int id) const { T value(int id) {
return values[id]; return values[id];
} }
inline const T& operator[](int& id) const { T operator[](int& id) {
return values[id]; return value(id);
} }
const char* txt = NULL; 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 (ImGui::Checkbox(CONCAT("Show Reference Lines##m17_showlines_", _this->name), &_this->showLines)) {
if (_this->showLines) { if (_this->showLines) {
_this->diag.lines.push_back(-1.0); _this->diag.lines.push_back(-0.75f);
_this->diag.lines.push_back(-1.0/3.0); _this->diag.lines.push_back(-0.25f);
_this->diag.lines.push_back(1.0/3.0); _this->diag.lines.push_back(0.25f);
_this->diag.lines.push_back(1.0); _this->diag.lines.push_back(0.75f);
} }
else { else {
_this->diag.lines.clear(); _this->diag.lines.clear();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -205,7 +205,6 @@ namespace hermes {
} }
std::vector<Info> discover() { 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); auto sock = net::openudp("0.0.0.0", 1024);
// Build discovery packet // Build discovery packet

View File

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

View File

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

View File

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

View File

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