mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2025-01-24 00:34:44 +01:00
Added a noise blanker for SSB and DSB
This commit is contained in:
parent
241632288e
commit
15010cff01
@ -267,7 +267,7 @@ namespace dsp {
|
|||||||
public:
|
public:
|
||||||
NoiseBlanker() {}
|
NoiseBlanker() {}
|
||||||
|
|
||||||
NoiseBlanker(stream<complex_t>* in, float attack, float decay, float threshold, float level, float sampleRate) { init(in, attack, decay, threshold, level, sampleRate); }
|
NoiseBlanker(stream<complex_t>* in, float level) { init(in, level); }
|
||||||
|
|
||||||
~NoiseBlanker() {
|
~NoiseBlanker() {
|
||||||
if (!generic_block<NoiseBlanker>::_block_init) { return; }
|
if (!generic_block<NoiseBlanker>::_block_init) { return; }
|
||||||
@ -275,16 +275,9 @@ namespace dsp {
|
|||||||
volk_free(ampBuf);
|
volk_free(ampBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(stream<complex_t>* in, float attack, float decay, float threshold, float level, float sampleRate) {
|
void init(stream<complex_t>* in, float level) {
|
||||||
_in = in;
|
_in = in;
|
||||||
_attack = attack;
|
_level = powf(10.0f, level / 10.0f);;
|
||||||
_decay = decay;
|
|
||||||
_threshold = powf(10.0f, threshold / 10.0f);
|
|
||||||
_level = level;
|
|
||||||
_sampleRate = sampleRate;
|
|
||||||
|
|
||||||
_inv_attack = 1.0f - _attack;
|
|
||||||
_inv_decay = 1.0f - _decay;
|
|
||||||
|
|
||||||
ampBuf = (float*)volk_malloc(STREAM_BUFFER_SIZE*sizeof(float), volk_get_alignment());
|
ampBuf = (float*)volk_malloc(STREAM_BUFFER_SIZE*sizeof(float), volk_get_alignment());
|
||||||
|
|
||||||
@ -293,28 +286,8 @@ namespace dsp {
|
|||||||
generic_block<NoiseBlanker>::_block_init = true;
|
generic_block<NoiseBlanker>::_block_init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAttack(float attack) {
|
|
||||||
_attack = attack;
|
|
||||||
_inv_attack = 1.0f - _attack;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDecay(float decay) {
|
|
||||||
_decay = decay;
|
|
||||||
_inv_decay = 1.0f - _decay;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setThreshold(float threshold) {
|
|
||||||
_threshold = powf(10.0f, threshold / 10.0f);
|
|
||||||
spdlog::warn("Threshold {0}", _threshold);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setLevel(float level) {
|
void setLevel(float level) {
|
||||||
_level = level;
|
_level = powf(10.0f, level / 10.0f);
|
||||||
}
|
|
||||||
|
|
||||||
void setSampleRate(float sampleRate) {
|
|
||||||
_sampleRate = sampleRate;
|
|
||||||
// TODO: Change parameters if the algo needs it
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setInput(stream<complex_t>* in) {
|
void setInput(stream<complex_t>* in) {
|
||||||
@ -334,33 +307,13 @@ namespace dsp {
|
|||||||
// Get amplitudes
|
// Get amplitudes
|
||||||
volk_32fc_magnitude_32f(ampBuf, (lv_32fc_t*)_in->readBuf, count);
|
volk_32fc_magnitude_32f(ampBuf, (lv_32fc_t*)_in->readBuf, count);
|
||||||
|
|
||||||
// Apply filtering and threshold
|
// Hard limit the amplitude
|
||||||
float val;
|
complex_t inVal;
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
// Filter using attack/threshold methode
|
inVal = _in->readBuf[i];
|
||||||
val = ampBuf[i];
|
out.writeBuf[i] = (ampBuf[i] > _level) ? inVal * (_level / inVal.amplitude()) : inVal;
|
||||||
if (val > lastValue) {
|
|
||||||
lastValue = (_inv_attack*lastValue) + (_attack*val);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
lastValue = (_inv_decay*lastValue) + (_decay*val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply threshold and invert
|
|
||||||
if (lastValue > _threshold) {
|
|
||||||
ampBuf[i] = _threshold / (lastValue * _level);
|
|
||||||
if (ampBuf[i] == 0) {
|
|
||||||
spdlog::warn("WTF???");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ampBuf[i] = 1.0f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multiply
|
|
||||||
volk_32fc_32f_multiply_32fc((lv_32fc_t*)out.writeBuf, (lv_32fc_t*)_in->readBuf, ampBuf, count);
|
|
||||||
|
|
||||||
_in->flush();
|
_in->flush();
|
||||||
if (!out.swap(count)) { return -1; }
|
if (!out.swap(count)) { return -1; }
|
||||||
return count;
|
return count;
|
||||||
@ -371,15 +324,7 @@ namespace dsp {
|
|||||||
private:
|
private:
|
||||||
float* ampBuf;
|
float* ampBuf;
|
||||||
|
|
||||||
float _attack;
|
|
||||||
float _decay;
|
|
||||||
float _inv_attack;
|
|
||||||
float _inv_decay;
|
|
||||||
float _threshold;
|
|
||||||
float _level;
|
float _level;
|
||||||
float _sampleRate;
|
|
||||||
|
|
||||||
float lastValue = 0.0f;
|
|
||||||
|
|
||||||
stream<complex_t>* _in;
|
stream<complex_t>* _in;
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ namespace demod {
|
|||||||
virtual int getDefaultDeemphasisMode() = 0;
|
virtual int getDefaultDeemphasisMode() = 0;
|
||||||
virtual double getAFBandwidth(double bandwidth) = 0;
|
virtual double getAFBandwidth(double bandwidth) = 0;
|
||||||
virtual bool getFMIFNRAllowed() = 0;
|
virtual bool getFMIFNRAllowed() = 0;
|
||||||
|
virtual bool getNBAllowed() = 0;
|
||||||
|
|
||||||
virtual bool getDynamicAFBandwidth() = 0;
|
virtual bool getDynamicAFBandwidth() = 0;
|
||||||
virtual dsp::stream<dsp::stereo_t>* getOutput() = 0;
|
virtual dsp::stream<dsp::stereo_t>* getOutput() = 0;
|
||||||
|
@ -68,6 +68,7 @@ namespace demod {
|
|||||||
double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; }
|
double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; }
|
||||||
bool getDynamicAFBandwidth() { return true; }
|
bool getDynamicAFBandwidth() { return true; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
|
bool getNBAllowed() { return false; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -69,6 +69,7 @@ namespace demod {
|
|||||||
double getAFBandwidth(double bandwidth) { return (bandwidth / 2.0) + 1000.0; }
|
double getAFBandwidth(double bandwidth) { return (bandwidth / 2.0) + 1000.0; }
|
||||||
bool getDynamicAFBandwidth() { return true; }
|
bool getDynamicAFBandwidth() { return true; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
|
bool getNBAllowed() { return false; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -70,6 +70,7 @@ namespace demod {
|
|||||||
double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; }
|
double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; }
|
||||||
bool getDynamicAFBandwidth() { return true; }
|
bool getDynamicAFBandwidth() { return true; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
|
bool getNBAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -70,6 +70,7 @@ namespace demod {
|
|||||||
double getAFBandwidth(double bandwidth) { return bandwidth; }
|
double getAFBandwidth(double bandwidth) { return bandwidth; }
|
||||||
bool getDynamicAFBandwidth() { return true; }
|
bool getDynamicAFBandwidth() { return true; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
|
bool getNBAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -62,6 +62,7 @@ namespace demod {
|
|||||||
double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; }
|
double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; }
|
||||||
bool getDynamicAFBandwidth() { return true; }
|
bool getDynamicAFBandwidth() { return true; }
|
||||||
bool getFMIFNRAllowed() { return true; }
|
bool getFMIFNRAllowed() { return true; }
|
||||||
|
bool getNBAllowed() { return false; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -63,6 +63,7 @@ namespace demod {
|
|||||||
double getAFBandwidth(double bandwidth) { return bandwidth; }
|
double getAFBandwidth(double bandwidth) { return bandwidth; }
|
||||||
bool getDynamicAFBandwidth() { return false; }
|
bool getDynamicAFBandwidth() { return false; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
|
bool getNBAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -70,6 +70,7 @@ namespace demod {
|
|||||||
double getAFBandwidth(double bandwidth) { return bandwidth; }
|
double getAFBandwidth(double bandwidth) { return bandwidth; }
|
||||||
bool getDynamicAFBandwidth() { return true; }
|
bool getDynamicAFBandwidth() { return true; }
|
||||||
bool getFMIFNRAllowed() { return false; }
|
bool getFMIFNRAllowed() { return false; }
|
||||||
|
bool getNBAllowed() { return true; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -86,6 +86,7 @@ namespace demod {
|
|||||||
double getAFBandwidth(double bandwidth) { return 16000.0; }
|
double getAFBandwidth(double bandwidth) { return 16000.0; }
|
||||||
bool getDynamicAFBandwidth() { return false; }
|
bool getDynamicAFBandwidth() { return false; }
|
||||||
bool getFMIFNRAllowed() { return true; }
|
bool getFMIFNRAllowed() { return true; }
|
||||||
|
bool getNBAllowed() { return false; }
|
||||||
dsp::stream<dsp::stereo_t>* getOutput() { return stereo ? demodStereo.out : &demod.out; }
|
dsp::stream<dsp::stereo_t>* getOutput() { return stereo ? demodStereo.out : &demod.out; }
|
||||||
|
|
||||||
// ============= DEDICATED FUNCTIONS =============
|
// ============= DEDICATED FUNCTIONS =============
|
||||||
|
@ -62,10 +62,12 @@ public:
|
|||||||
fmnr.block.init(NULL, 32);
|
fmnr.block.init(NULL, 32);
|
||||||
notch.block.init(NULL, 0.5, 0, 250000); // TODO: The rate has to depend on IF sample rate so the width is always the same
|
notch.block.init(NULL, 0.5, 0, 250000); // TODO: The rate has to depend on IF sample rate so the width is always the same
|
||||||
squelch.block.init(NULL, MIN_SQUELCH);
|
squelch.block.init(NULL, MIN_SQUELCH);
|
||||||
|
nb.block.init(NULL, -100.0f);
|
||||||
|
|
||||||
ifChain.add(¬ch);
|
ifChain.add(¬ch);
|
||||||
ifChain.add(&squelch);
|
ifChain.add(&squelch);
|
||||||
ifChain.add(&fmnr);
|
ifChain.add(&fmnr);
|
||||||
|
ifChain.add(&nb);
|
||||||
|
|
||||||
// Load configuration for and enabled all demodulators
|
// Load configuration for and enabled all demodulators
|
||||||
EventHandler<dsp::stream<dsp::stereo_t>*> _demodOutputChangeHandler;
|
EventHandler<dsp::stream<dsp::stereo_t>*> _demodOutputChangeHandler;
|
||||||
@ -258,6 +260,21 @@ private:
|
|||||||
}
|
}
|
||||||
if (!_this->squelchEnabled && _this->enabled) { style::endDisabled(); }
|
if (!_this->squelchEnabled && _this->enabled) { style::endDisabled(); }
|
||||||
|
|
||||||
|
// Noise blanker
|
||||||
|
if (_this->nbAllowed) {
|
||||||
|
if (ImGui::Checkbox(("Noise Blanker##_radio_nb_ena_" + _this->name).c_str(), &_this->nbEnabled)) {
|
||||||
|
_this->setNoiseBlankerEnabled(_this->nbEnabled);
|
||||||
|
}
|
||||||
|
if (!_this->nbEnabled && _this->enabled) { style::beginDisabled(); }
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
|
||||||
|
if (ImGui::SliderFloat(("##_radio_nb_lvl_" + _this->name).c_str(), &_this->nbLevel, 0.0f, -100.0f, "%.3fdB")) {
|
||||||
|
_this->setNoiseBlankerLevel(_this->nbLevel);
|
||||||
|
}
|
||||||
|
if (!_this->nbEnabled && _this->enabled) { style::endDisabled(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// // Notch filter
|
// // Notch filter
|
||||||
// if (ImGui::Checkbox("Notch##_radio_notch_ena_", &_this->notchEnabled)) {
|
// if (ImGui::Checkbox("Notch##_radio_notch_ena_", &_this->notchEnabled)) {
|
||||||
// _this->ifChain.setState(&_this->notch, _this->notchEnabled);
|
// _this->ifChain.setState(&_this->notch, _this->notchEnabled);
|
||||||
@ -324,6 +341,10 @@ private:
|
|||||||
postProcEnabled = selectedDemod->getPostProcEnabled();
|
postProcEnabled = selectedDemod->getPostProcEnabled();
|
||||||
FMIFNRAllowed = selectedDemod->getFMIFNRAllowed();
|
FMIFNRAllowed = selectedDemod->getFMIFNRAllowed();
|
||||||
FMIFNREnabled = false;
|
FMIFNREnabled = false;
|
||||||
|
nbAllowed = selectedDemod->getNBAllowed();
|
||||||
|
nbEnabled = false;
|
||||||
|
nbLevel = 0.0f;
|
||||||
|
config.acquire();
|
||||||
if (config.conf[name][selectedDemod->getName()].contains("bandwidth")) {
|
if (config.conf[name][selectedDemod->getName()].contains("bandwidth")) {
|
||||||
bandwidth = config.conf[name][selectedDemod->getName()]["bandwidth"];
|
bandwidth = config.conf[name][selectedDemod->getName()]["bandwidth"];
|
||||||
bandwidth = std::clamp<double>(bandwidth, minBandwidth, maxBandwidth);
|
bandwidth = std::clamp<double>(bandwidth, minBandwidth, maxBandwidth);
|
||||||
@ -343,6 +364,16 @@ private:
|
|||||||
if (config.conf[name][selectedDemod->getName()].contains("FMIFNREnabled")) {
|
if (config.conf[name][selectedDemod->getName()].contains("FMIFNREnabled")) {
|
||||||
FMIFNREnabled = config.conf[name][selectedDemod->getName()]["FMIFNREnabled"];
|
FMIFNREnabled = config.conf[name][selectedDemod->getName()]["FMIFNREnabled"];
|
||||||
}
|
}
|
||||||
|
if (config.conf[name][selectedDemod->getName()].contains("noiseBlankerEnabled")) {
|
||||||
|
nbEnabled = config.conf[name][selectedDemod->getName()]["noiseBlankerEnabled"];
|
||||||
|
}
|
||||||
|
if (config.conf[name][selectedDemod->getName()].contains("noiseBlankerEnabled")) {
|
||||||
|
nbEnabled = config.conf[name][selectedDemod->getName()]["noiseBlankerEnabled"];
|
||||||
|
}
|
||||||
|
if (config.conf[name][selectedDemod->getName()].contains("noiseBlankerLevel")) {
|
||||||
|
nbLevel = config.conf[name][selectedDemod->getName()]["noiseBlankerLevel"];
|
||||||
|
}
|
||||||
|
config.release();
|
||||||
deempMode = std::clamp<int>(deempMode, 0, _DEEMP_MODE_COUNT-1);
|
deempMode = std::clamp<int>(deempMode, 0, _DEEMP_MODE_COUNT-1);
|
||||||
|
|
||||||
// Configure VFO
|
// Configure VFO
|
||||||
@ -363,6 +394,10 @@ private:
|
|||||||
squelch.block.setLevel(squelchLevel);
|
squelch.block.setLevel(squelchLevel);
|
||||||
setSquelchEnabled(squelchEnabled);
|
setSquelchEnabled(squelchEnabled);
|
||||||
|
|
||||||
|
// Configure noise blanker
|
||||||
|
nb.block.setLevel(nbLevel);
|
||||||
|
setNoiseBlankerEnabled(nbEnabled);
|
||||||
|
|
||||||
// Configure AF chain
|
// Configure AF chain
|
||||||
if (postProcEnabled) {
|
if (postProcEnabled) {
|
||||||
// Configure resampler
|
// Configure resampler
|
||||||
@ -483,6 +518,28 @@ private:
|
|||||||
config.release(true);
|
config.release(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setNoiseBlankerEnabled(bool enabled) {
|
||||||
|
nbEnabled = enabled;
|
||||||
|
if (!selectedDemod) { return; }
|
||||||
|
ifChain.setState(&nb, nbEnabled);
|
||||||
|
|
||||||
|
// Save config
|
||||||
|
config.acquire();
|
||||||
|
config.conf[name][selectedDemod->getName()]["noiseBlankerEnabled"] = nbEnabled;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setNoiseBlankerLevel(float level) {
|
||||||
|
nbLevel = level;
|
||||||
|
if (!selectedDemod) { return; }
|
||||||
|
nb.block.setLevel(nbLevel);
|
||||||
|
|
||||||
|
// Save config
|
||||||
|
config.acquire();
|
||||||
|
config.conf[name][selectedDemod->getName()]["noiseBlankerLevel"] = nbLevel;
|
||||||
|
config.release(true);
|
||||||
|
}
|
||||||
|
|
||||||
static void vfoUserChangedBandwidthHandler(double newBw, void* ctx) {
|
static void vfoUserChangedBandwidthHandler(double newBw, void* ctx) {
|
||||||
RadioModule* _this = (RadioModule*)ctx;
|
RadioModule* _this = (RadioModule*)ctx;
|
||||||
_this->setBandwidth(newBw);
|
_this->setBandwidth(newBw);
|
||||||
@ -568,6 +625,7 @@ private:
|
|||||||
dsp::ChainLink<dsp::FMIFNoiseReduction, dsp::complex_t> fmnr;
|
dsp::ChainLink<dsp::FMIFNoiseReduction, dsp::complex_t> fmnr;
|
||||||
dsp::ChainLink<dsp::NotchFilter, dsp::complex_t> notch;
|
dsp::ChainLink<dsp::NotchFilter, dsp::complex_t> notch;
|
||||||
dsp::ChainLink<dsp::Squelch, dsp::complex_t> squelch;
|
dsp::ChainLink<dsp::Squelch, dsp::complex_t> squelch;
|
||||||
|
dsp::ChainLink<dsp::NoiseBlanker, dsp::complex_t> nb;
|
||||||
|
|
||||||
// Audio chain
|
// Audio chain
|
||||||
dsp::stream<dsp::stereo_t> dummyAudioStream;
|
dsp::stream<dsp::stereo_t> dummyAudioStream;
|
||||||
@ -587,12 +645,15 @@ private:
|
|||||||
float bandwidth;
|
float bandwidth;
|
||||||
bool bandwidthLocked;
|
bool bandwidthLocked;
|
||||||
int snapInterval;
|
int snapInterval;
|
||||||
|
int selectedDemodID = 1;
|
||||||
|
bool postProcEnabled;
|
||||||
|
|
||||||
bool squelchEnabled = false;
|
bool squelchEnabled = false;
|
||||||
float squelchLevel;
|
float squelchLevel;
|
||||||
int selectedDemodID = 1;
|
|
||||||
int deempMode = DEEMP_MODE_NONE;
|
int deempMode = DEEMP_MODE_NONE;
|
||||||
bool deempAllowed;
|
bool deempAllowed;
|
||||||
bool postProcEnabled;
|
|
||||||
bool FMIFNRAllowed;
|
bool FMIFNRAllowed;
|
||||||
bool FMIFNREnabled = false;
|
bool FMIFNREnabled = false;
|
||||||
|
|
||||||
@ -600,6 +661,10 @@ private:
|
|||||||
float notchPos = 0;
|
float notchPos = 0;
|
||||||
float notchWidth = 500;
|
float notchWidth = 500;
|
||||||
|
|
||||||
|
bool nbAllowed;
|
||||||
|
bool nbEnabled;
|
||||||
|
float nbLevel = -100.0f;
|
||||||
|
|
||||||
const double MIN_SQUELCH = -100.0;
|
const double MIN_SQUELCH = -100.0;
|
||||||
const double MAX_SQUELCH = 0.0;
|
const double MAX_SQUELCH = 0.0;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user