29 Commits

Author SHA1 Message Date
87da47f53d added new files 2023-03-27 01:50:48 +02:00
75050347de noise reduction work 2023-03-27 01:50:34 +02:00
a9f882e5b1 Fixed halfed volume with HPF on 2023-03-25 21:22:40 +01:00
3420808f3a added high-pass filter in NFM mode 2023-03-25 19:09:27 +01:00
d3d245992d Merge pull request #1021 from sergeyvfx/fix_start_crash
Fix crash in LimeSDR start when the device is disconnected
2023-03-23 23:20:49 +01:00
9a3414b847 Made the code more useful to the user
maybe just yeeting out of the start function with no indication to the user is a bad idea?
2023-03-23 23:19:05 +01:00
90c26f8c1b Fix crash in LimeSDR start when the device is disconnected
This fixes crash when a device which was enumerated on the
application startup gets disconnected prior to the attempt
to start the radio.

The check is done after the existing work around for the
LimeSuite bug. The idea is to give it the best possible
chance for the radio to start.

Possibly, the check needs needs to happen in the second
LMS_Open() but ideally this needs to be verified against
the original workaround scenario.
2023-03-23 22:36:44 +01:00
ec4dc6cc9e fix 2023-03-22 20:47:05 +01:00
93b28d1495 M17 decoder fix 2023-03-22 18:38:15 +01:00
84291deaf6 Merge branch 'master' of https://github.com/AlexandreRouma/SDRPlusPlus 2023-03-22 18:20:38 +01:00
21e0696917 readme update 2023-03-22 18:20:34 +01:00
19247ef4f2 Merge pull request #1013 from sergeyvfx/fix_empty_limesdr_crash
Fix crash starting LimeSDR source without device
2023-03-14 23:00:13 +01:00
1e5601e773 Better check
Need to check if a device is actually selected, not just if there is one to select
2023-03-14 22:17:47 +01:00
ae1fd87f02 Fix crash starting LimeSDR source without device
This change fixes crash when a LimeSDR source is started without
an actual device selected.
2023-03-14 21:37:22 +01:00
ab2aee316c Merge pull request #1012 from Bastiti/patch-1
Belgian bandplan
2023-03-14 11:33:44 +01:00
109374277e Belgian bandplan
Added the amateur radio band as specified in the 2019
https://www.ibpt.be/file/cc73d96153bbd5448a56f19d925d05b1379c7f21/821514ac30e14d85dfdbe6747d1b82af9fd4da4c/2019-05-24_ram-decision.pdf

Broadcast band added from BIPT/IBPT Bandplanning.

According to Belgian laws, you're not allowed to receive anything execpt Ham Radio, CB, PMR446 or Broadcasting.
2023-03-14 09:08:43 +01:00
692436f6e4 Merge pull request #1010 from hb9fxq/patch-3
Update readme.md
2023-03-13 18:13:17 +01:00
eccb715d0c Update readme.md 2023-03-13 17:55:44 +01:00
f6f074e0c7 added missing patrons 2023-03-13 17:29:37 +01:00
50a77a7e60 Added a PR template 2023-03-11 13:52:37 +01:00
a4f3c92a03 added missing include to legacy network lib 2023-03-10 17:59:13 +01:00
37920b6476 adjustments 2023-03-10 00:45:56 +01:00
314b8bf72d Const correctness fix for optionlist 2023-03-09 15:14:33 +01:00
5f0858bab2 Removed logging from the DSP lib 2023-03-09 08:56:48 +01:00
007761a027 made it a bit cleaner 2023-03-08 19:49:23 +01:00
801f1be6b2 Hopefully a fix for the rfspace SDR-IP 2023-03-08 19:46:14 +01:00
9cc793e328 bugfix 2023-03-08 19:39:15 +01:00
4283cacae6 fixed build for volk 1.x 2023-03-02 15:19:16 +01:00
6f9dacdd53 Added back 'ignore silence' option in the recorder 2023-03-02 14:47:02 +01:00
36 changed files with 694 additions and 95 deletions

7
.github/pull_request_template.md vendored Normal file
View File

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

View File

@ -2,5 +2,6 @@
#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 : Processor<float, complex_t> {
class Quadrature : public Processor<float, complex_t> {
using base_type = Processor<float, complex_t>;
public:
Quadrature() {}

View File

@ -83,8 +83,6 @@ 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

@ -0,0 +1,183 @@
#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,21 +37,17 @@ 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
// Get signal amplitude and pass value if null
float inAmp = in[i].amplitude();
// 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;
}
if (!inAmp) {
out[i] = in[i];
}
// Scale output by gain
out[i] = in[i] * gain;
// 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];
}
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 cosf(offsetOmega * (float)n) * window::nuttall(n, N);
return 2.0f * 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

@ -169,7 +169,7 @@ namespace flog {
fprintf(outStream, "] %s\n", out.c_str());
#elif defined(__ANDROID__)
// Print format string
__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",
__android_log_print(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,6 +288,7 @@ 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,6 +1,7 @@
#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(K key, std::string name, T value) {
void define(const K& key, const std::string& name, const 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(std::string name, T value) {
void define(const std::string& name, const T& value) {
define(name, name, value);
}
void undefined(int id) {
void undefine(int id) {
keys.erase(keys.begin() + id);
names.erase(names.begin() + id);
values.erase(values.begin() + id);
updateText();
}
void undefineKey(K key) {
undefined(keyId(key));
void undefineKey(const K& key) {
undefine(keyId(key));
}
void undefineName(std::string name) {
undefined(nameId(name));
void undefineName(const std::string& name) {
undefine(nameId(name));
}
void undefineValue(T value) {
undefined(valueId(value));
void undefineValue(const T& value) {
undefine(valueId(value));
}
void clear() {
@ -48,61 +48,61 @@ public:
updateText();
}
int size() {
int size() const {
return keys.size();
}
bool empty() {
bool empty() const {
return keys.empty();
}
bool keyExists(K key) {
bool keyExists(const K& key) const {
if (std::find(keys.begin(), keys.end(), key) != keys.end()) { return true; }
return false;
}
bool nameExists(std::string name) {
bool nameExists(const std::string& name) const {
if (std::find(names.begin(), names.end(), name) != names.end()) { return true; }
return false;
}
bool valueExists(T value) {
bool valueExists(const T& value) const {
if (std::find(values.begin(), values.end(), value) != values.end()) { return true; }
return false;
}
int keyId(K key) {
int keyId(const K& key) const {
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(std::string name) {
int nameId(const std::string& name) const {
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(T value) {
int valueId(const T& value) const {
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);
}
K key(int id) {
inline const K& key(int id) const {
return keys[id];
}
std::string name(int id) {
inline const std::string& name(int id) const {
return names[id];
}
T value(int id) {
inline const T& value(int id) const {
return values[id];
}
T operator[](int& id) {
return value(id);
inline const T& operator[](int& id) const {
return values[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(-0.75f);
_this->diag.lines.push_back(-0.25f);
_this->diag.lines.push_back(0.25f);
_this->diag.lines.push_back(0.75f);
_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);
}
else {
_this->diag.lines.clear();

View File

@ -45,6 +45,7 @@ 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,6 +86,7 @@ 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,6 +92,7 @@ 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,6 +79,7 @@ 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,6 +79,7 @@ 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,15 +19,17 @@ namespace demod {
// Load config
_config->acquire();
bool modified = false;
if (config->conf[name][getName()].contains("lowPass")) {
_lowPass = config->conf[name][getName()]["lowPass"];
}
_config->release(modified);
if (config->conf[name][getName()].contains("highPass")) {
_highPass = config->conf[name][getName()]["highPass"];
}
_config->release();
// Define structure
demod.init(input, getIFSampleRate(), bandwidth, _lowPass);
demod.init(input, getIFSampleRate(), bandwidth, _lowPass, _highPass);
}
void start() { demod.start(); }
@ -41,6 +43,12 @@ 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) {
@ -67,6 +75,7 @@ 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:
@ -75,6 +84,7 @@ namespace demod {
ConfigManager* _config = NULL;
bool _lowPass = true;
bool _highPass = false;
std::string name;
};

View File

@ -59,6 +59,7 @@ 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,6 +80,7 @@ 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,6 +130,7 @@ 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,6 +9,7 @@
#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>
@ -83,9 +84,11 @@ 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;
@ -247,6 +250,12 @@ 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)) {
@ -370,6 +379,8 @@ 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();
@ -411,6 +422,9 @@ 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
@ -446,7 +460,10 @@ private:
afChain.enableBlock(&resamp, [=](dsp::stream<dsp::stereo_t>* out){ stream.setInput(out); });
// Configure deemphasis
setDeemphasisMode(deempModes[deempId]);
setDeemphasisMode(deempAllowed ? deempModes[deempId] : DEEMP_MODE_NONE);
// Configure AF NR
setAFNREnabled(afNRAllowed && afNREnabled);
}
else {
// Disable everything if post processing is disabled
@ -508,6 +525,17 @@ 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; }
@ -660,6 +688,7 @@ 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;
@ -683,6 +712,9 @@ private:
int deempId = 0;
bool deempAllowed;
bool afNREnabled = false;
bool afNRAllowed;
bool FMIFNRAllowed;
bool FMIFNREnabled = false;
int fmIFPresetId;

View File

@ -23,7 +23,8 @@
#include <utils/wav.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str())
#define SILENCE_LVL 10e-20
#define SILENCE_LVL 10e-6
SDRPP_MOD_INFO{
/* Name: */ "recorder",
@ -315,11 +316,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
@ -338,7 +339,13 @@ private:
uint64_t seconds = _this->writer.getSamplesWritten() / _this->samplerate;
time_t diff = seconds;
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);
}
}
}
@ -480,13 +487,31 @@ private:
static void stereoHandler(dsp::stereo_t* data, int count, void* ctx) {
RecorderModule* _this = (RecorderModule*)ctx;
// TODO: Ignore silence
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; }
}
_this->writer.write((float*)data, count);
}
static void monoHandler(float* data, int count, void* ctx) {
RecorderModule* _this = (RecorderModule*)ctx;
// TODO: Ignore silence
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; }
}
_this->writer.write(data, count);
}
@ -529,6 +554,7 @@ 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

@ -429,25 +429,34 @@ 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)
* [Zipper](github.com/reppiZ)
* William Arcand (W1WRA)
* [Yves Rougy](https://www.twitch.tv/yorzian)
* [Zipper](https://github.com/reppiZ)
## Contributors

View File

@ -0,0 +1,274 @@
{
"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 @@ ConfigManager config;
struct DeviceInfo {
RtAudio::DeviceInfo info;
int id;
bool operator==(const struct DeviceInfo& other) {
bool operator==(const struct DeviceInfo& other) const {
return other.id == id;
}
};

View File

@ -205,6 +205,7 @@ 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) {
bool operator==(const Info& b) const {
return !memcmp(mac, b.mac, 6);
}
};

View File

@ -319,6 +319,7 @@ private:
static void start(void* ctx) {
LimeSDRSourceModule* _this = (LimeSDRSourceModule*)ctx;
if (_this->running) { return; }
if (_this->selectedDevName.empty()) { return; }
// Open device
_this->openDev = NULL;
@ -329,7 +330,10 @@ private:
if (err) {
LMS_Close(_this->openDev);
LMS_Open(&_this->openDev, _this->devList[_this->devId], NULL);
LMS_Init(_this->openDev);
if (err = LMS_Init(_this->openDev)) {
flog::error("Failed to re-initialize device ({})", err);
return;
}
}
flog::warn("Channel count: {0}", LMS_GetNumChannels(_this->openDev, false));
@ -546,4 +550,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) {
bool operator==(const SRCombo& b) const {
return baseId == b.baseId && decimId == b.decimId;
}