kopia lustrzana https://github.com/AlexandreRouma/SDRPlusPlus
Added a noise blanker for SSB and DSB
rodzic
241632288e
commit
15010cff01
|
@ -267,7 +267,7 @@ namespace dsp {
|
|||
public:
|
||||
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() {
|
||||
if (!generic_block<NoiseBlanker>::_block_init) { return; }
|
||||
|
@ -275,16 +275,9 @@ namespace dsp {
|
|||
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;
|
||||
_attack = attack;
|
||||
_decay = decay;
|
||||
_threshold = powf(10.0f, threshold / 10.0f);
|
||||
_level = level;
|
||||
_sampleRate = sampleRate;
|
||||
|
||||
_inv_attack = 1.0f - _attack;
|
||||
_inv_decay = 1.0f - _decay;
|
||||
_level = powf(10.0f, level / 10.0f);;
|
||||
|
||||
ampBuf = (float*)volk_malloc(STREAM_BUFFER_SIZE*sizeof(float), volk_get_alignment());
|
||||
|
||||
|
@ -293,28 +286,8 @@ namespace dsp {
|
|||
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) {
|
||||
_level = level;
|
||||
}
|
||||
|
||||
void setSampleRate(float sampleRate) {
|
||||
_sampleRate = sampleRate;
|
||||
// TODO: Change parameters if the algo needs it
|
||||
_level = powf(10.0f, level / 10.0f);
|
||||
}
|
||||
|
||||
void setInput(stream<complex_t>* in) {
|
||||
|
@ -334,33 +307,13 @@ namespace dsp {
|
|||
// Get amplitudes
|
||||
volk_32fc_magnitude_32f(ampBuf, (lv_32fc_t*)_in->readBuf, count);
|
||||
|
||||
// Apply filtering and threshold
|
||||
float val;
|
||||
// Hard limit the amplitude
|
||||
complex_t inVal;
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Filter using attack/threshold methode
|
||||
val = ampBuf[i];
|
||||
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;
|
||||
}
|
||||
inVal = _in->readBuf[i];
|
||||
out.writeBuf[i] = (ampBuf[i] > _level) ? inVal * (_level / inVal.amplitude()) : inVal;
|
||||
}
|
||||
|
||||
// Multiply
|
||||
volk_32fc_32f_multiply_32fc((lv_32fc_t*)out.writeBuf, (lv_32fc_t*)_in->readBuf, ampBuf, count);
|
||||
|
||||
_in->flush();
|
||||
if (!out.swap(count)) { return -1; }
|
||||
return count;
|
||||
|
@ -371,15 +324,7 @@ namespace dsp {
|
|||
private:
|
||||
float* ampBuf;
|
||||
|
||||
float _attack;
|
||||
float _decay;
|
||||
float _inv_attack;
|
||||
float _inv_decay;
|
||||
float _threshold;
|
||||
float _level;
|
||||
float _sampleRate;
|
||||
|
||||
float lastValue = 0.0f;
|
||||
|
||||
stream<complex_t>* _in;
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace demod {
|
|||
virtual int getDefaultDeemphasisMode() = 0;
|
||||
virtual double getAFBandwidth(double bandwidth) = 0;
|
||||
virtual bool getFMIFNRAllowed() = 0;
|
||||
virtual bool getNBAllowed() = 0;
|
||||
|
||||
virtual bool getDynamicAFBandwidth() = 0;
|
||||
virtual dsp::stream<dsp::stereo_t>* getOutput() = 0;
|
||||
|
|
|
@ -68,6 +68,7 @@ namespace demod {
|
|||
double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; }
|
||||
bool getDynamicAFBandwidth() { return true; }
|
||||
bool getFMIFNRAllowed() { return false; }
|
||||
bool getNBAllowed() { return false; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -69,6 +69,7 @@ namespace demod {
|
|||
double getAFBandwidth(double bandwidth) { return (bandwidth / 2.0) + 1000.0; }
|
||||
bool getDynamicAFBandwidth() { return true; }
|
||||
bool getFMIFNRAllowed() { return false; }
|
||||
bool getNBAllowed() { return false; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -70,6 +70,7 @@ namespace demod {
|
|||
double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; }
|
||||
bool getDynamicAFBandwidth() { return true; }
|
||||
bool getFMIFNRAllowed() { return false; }
|
||||
bool getNBAllowed() { return true; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -70,6 +70,7 @@ namespace demod {
|
|||
double getAFBandwidth(double bandwidth) { return bandwidth; }
|
||||
bool getDynamicAFBandwidth() { return true; }
|
||||
bool getFMIFNRAllowed() { return false; }
|
||||
bool getNBAllowed() { return true; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -62,6 +62,7 @@ namespace demod {
|
|||
double getAFBandwidth(double bandwidth) { return bandwidth / 2.0; }
|
||||
bool getDynamicAFBandwidth() { return true; }
|
||||
bool getFMIFNRAllowed() { return true; }
|
||||
bool getNBAllowed() { return false; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &demod.out; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -63,6 +63,7 @@ namespace demod {
|
|||
double getAFBandwidth(double bandwidth) { return bandwidth; }
|
||||
bool getDynamicAFBandwidth() { return false; }
|
||||
bool getFMIFNRAllowed() { return false; }
|
||||
bool getNBAllowed() { return true; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &c2s.out; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -70,6 +70,7 @@ namespace demod {
|
|||
double getAFBandwidth(double bandwidth) { return bandwidth; }
|
||||
bool getDynamicAFBandwidth() { return true; }
|
||||
bool getFMIFNRAllowed() { return false; }
|
||||
bool getNBAllowed() { return true; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return &m2s.out; }
|
||||
|
||||
private:
|
||||
|
|
|
@ -86,6 +86,7 @@ namespace demod {
|
|||
double getAFBandwidth(double bandwidth) { return 16000.0; }
|
||||
bool getDynamicAFBandwidth() { return false; }
|
||||
bool getFMIFNRAllowed() { return true; }
|
||||
bool getNBAllowed() { return false; }
|
||||
dsp::stream<dsp::stereo_t>* getOutput() { return stereo ? demodStereo.out : &demod.out; }
|
||||
|
||||
// ============= DEDICATED FUNCTIONS =============
|
||||
|
|
|
@ -62,10 +62,12 @@ public:
|
|||
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
|
||||
squelch.block.init(NULL, MIN_SQUELCH);
|
||||
nb.block.init(NULL, -100.0f);
|
||||
|
||||
ifChain.add(¬ch);
|
||||
ifChain.add(&squelch);
|
||||
ifChain.add(&fmnr);
|
||||
ifChain.add(&nb);
|
||||
|
||||
// Load configuration for and enabled all demodulators
|
||||
EventHandler<dsp::stream<dsp::stereo_t>*> _demodOutputChangeHandler;
|
||||
|
@ -258,6 +260,21 @@ private:
|
|||
}
|
||||
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
|
||||
// if (ImGui::Checkbox("Notch##_radio_notch_ena_", &_this->notchEnabled)) {
|
||||
// _this->ifChain.setState(&_this->notch, _this->notchEnabled);
|
||||
|
@ -324,6 +341,10 @@ private:
|
|||
postProcEnabled = selectedDemod->getPostProcEnabled();
|
||||
FMIFNRAllowed = selectedDemod->getFMIFNRAllowed();
|
||||
FMIFNREnabled = false;
|
||||
nbAllowed = selectedDemod->getNBAllowed();
|
||||
nbEnabled = false;
|
||||
nbLevel = 0.0f;
|
||||
config.acquire();
|
||||
if (config.conf[name][selectedDemod->getName()].contains("bandwidth")) {
|
||||
bandwidth = config.conf[name][selectedDemod->getName()]["bandwidth"];
|
||||
bandwidth = std::clamp<double>(bandwidth, minBandwidth, maxBandwidth);
|
||||
|
@ -343,6 +364,16 @@ private:
|
|||
if (config.conf[name][selectedDemod->getName()].contains("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);
|
||||
|
||||
// Configure VFO
|
||||
|
@ -363,6 +394,10 @@ private:
|
|||
squelch.block.setLevel(squelchLevel);
|
||||
setSquelchEnabled(squelchEnabled);
|
||||
|
||||
// Configure noise blanker
|
||||
nb.block.setLevel(nbLevel);
|
||||
setNoiseBlankerEnabled(nbEnabled);
|
||||
|
||||
// Configure AF chain
|
||||
if (postProcEnabled) {
|
||||
// Configure resampler
|
||||
|
@ -483,6 +518,28 @@ private:
|
|||
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) {
|
||||
RadioModule* _this = (RadioModule*)ctx;
|
||||
_this->setBandwidth(newBw);
|
||||
|
@ -568,6 +625,7 @@ private:
|
|||
dsp::ChainLink<dsp::FMIFNoiseReduction, dsp::complex_t> fmnr;
|
||||
dsp::ChainLink<dsp::NotchFilter, dsp::complex_t> notch;
|
||||
dsp::ChainLink<dsp::Squelch, dsp::complex_t> squelch;
|
||||
dsp::ChainLink<dsp::NoiseBlanker, dsp::complex_t> nb;
|
||||
|
||||
// Audio chain
|
||||
dsp::stream<dsp::stereo_t> dummyAudioStream;
|
||||
|
@ -587,12 +645,15 @@ private:
|
|||
float bandwidth;
|
||||
bool bandwidthLocked;
|
||||
int snapInterval;
|
||||
int selectedDemodID = 1;
|
||||
bool postProcEnabled;
|
||||
|
||||
bool squelchEnabled = false;
|
||||
float squelchLevel;
|
||||
int selectedDemodID = 1;
|
||||
|
||||
int deempMode = DEEMP_MODE_NONE;
|
||||
bool deempAllowed;
|
||||
bool postProcEnabled;
|
||||
|
||||
bool FMIFNRAllowed;
|
||||
bool FMIFNREnabled = false;
|
||||
|
||||
|
@ -600,6 +661,10 @@ private:
|
|||
float notchPos = 0;
|
||||
float notchWidth = 500;
|
||||
|
||||
bool nbAllowed;
|
||||
bool nbEnabled;
|
||||
float nbLevel = -100.0f;
|
||||
|
||||
const double MIN_SQUELCH = -100.0;
|
||||
const double MAX_SQUELCH = 0.0;
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue