diff --git a/doc/img/BFMDemod_plugin_settings.png b/doc/img/BFMDemod_plugin_settings.png index 3e0d833af..33d313690 100644 Binary files a/doc/img/BFMDemod_plugin_settings.png and b/doc/img/BFMDemod_plugin_settings.png differ diff --git a/doc/img/BFMDemod_plugin_settings.xcf b/doc/img/BFMDemod_plugin_settings.xcf index ff9b5c572..9b18cfe3a 100644 Binary files a/doc/img/BFMDemod_plugin_settings.xcf and b/doc/img/BFMDemod_plugin_settings.xcf differ diff --git a/plugins/channelrx/demodbfm/bfmdemod.cpp b/plugins/channelrx/demodbfm/bfmdemod.cpp index 85ce4710d..e4919239e 100644 --- a/plugins/channelrx/demodbfm/bfmdemod.cpp +++ b/plugins/channelrx/demodbfm/bfmdemod.cpp @@ -217,6 +217,7 @@ void BFMDemod::applySettings(const BFMDemodSettings& settings, bool force) << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset << " m_rfBandwidth: " << settings.m_rfBandwidth << " m_afBandwidth: " << settings.m_afBandwidth + << " m_deEmphasis: " << settings.getDeEmphasisTimeConstant() << " m_volume: " << settings.m_volume << " m_squelch: " << settings.m_squelch << " m_audioStereo: " << settings.m_audioStereo @@ -257,6 +258,9 @@ void BFMDemod::applySettings(const BFMDemodSettings& settings, bool force) if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { reverseAPIKeys.append("rfBandwidth"); } + if ((settings.m_deEmphasis != m_settings.m_deEmphasis) || force) { + reverseAPIKeys.append("deEmphasis"); + } if ((settings.m_squelch != m_settings.m_squelch) || force) { reverseAPIKeys.append("squelch"); } @@ -386,6 +390,13 @@ void BFMDemod::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("afBandwidth")) { settings.m_afBandwidth = response.getBfmDemodSettings()->getAfBandwidth(); } + if (channelSettingsKeys.contains("deEmphasis")) + { + int deEmphasis = response.getBfmDemodSettings()->getDeEmphasis(); + if (deEmphasis >= 0 && deEmphasis < 2) { + settings.m_deEmphasis = static_cast(deEmphasis); + } + } if (channelSettingsKeys.contains("volume")) { settings.m_volume = response.getBfmDemodSettings()->getVolume(); } @@ -461,6 +472,7 @@ void BFMDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& resp response.getBfmDemodSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); response.getBfmDemodSettings()->setRfBandwidth(settings.m_rfBandwidth); response.getBfmDemodSettings()->setAfBandwidth(settings.m_afBandwidth); + response.getBfmDemodSettings()->setDeEmphasis(static_cast(settings.m_deEmphasis)); response.getBfmDemodSettings()->setVolume(settings.m_volume); response.getBfmDemodSettings()->setSquelch(settings.m_squelch); response.getBfmDemodSettings()->setAudioMute(settings.m_audioMute ? 1 : 0); @@ -672,6 +684,9 @@ void BFMDemod::webapiFormatChannelSettings( if (channelSettingsKeys.contains("afBandwidth") || force) { swgBFMDemodSettings->setAfBandwidth(settings.m_afBandwidth); } + if (channelSettingsKeys.contains("deEmphasis") || force) { + swgBFMDemodSettings->setDeEmphasis(static_cast(settings.m_deEmphasis)); + } if (channelSettingsKeys.contains("volume") || force) { swgBFMDemodSettings->setVolume(settings.m_volume); } diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.cpp b/plugins/channelrx/demodbfm/bfmdemodgui.cpp index 449aca387..d490bbdb1 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodgui.cpp @@ -179,6 +179,12 @@ void BFMDemodGUI::on_afBW_valueChanged(int value) applySettings(); } +void BFMDemodGUI::on_deEmphasis_currentIndexChanged(int index) +{ + m_settings.m_deEmphasis = static_cast(index); + applySettings(); +} + void BFMDemodGUI::on_volume_valueChanged(int value) { ui->volumeText->setText(QString("%1").arg(value / 10.0, 0, 'f', 1)); @@ -886,6 +892,7 @@ void BFMDemodGUI::makeUIConnections() QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &BFMDemodGUI::on_deltaFrequency_changed); QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &BFMDemodGUI::on_rfBW_valueChanged); QObject::connect(ui->afBW, &QSlider::valueChanged, this, &BFMDemodGUI::on_afBW_valueChanged); + QObject::connect(ui->deEmphasis, QOverload::of(&QComboBox::currentIndexChanged), this, &BFMDemodGUI::on_deEmphasis_currentIndexChanged); QObject::connect(ui->volume, &QSlider::valueChanged, this, &BFMDemodGUI::on_volume_valueChanged); QObject::connect(ui->squelch, &QSlider::valueChanged, this, &BFMDemodGUI::on_squelch_valueChanged); QObject::connect(ui->audioMute, &QToolButton::toggled, this, &BFMDemodGUI::on_audioMute_toggled); diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.h b/plugins/channelrx/demodbfm/bfmdemodgui.h index c533002d0..f8b0fb2cd 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.h +++ b/plugins/channelrx/demodbfm/bfmdemodgui.h @@ -108,6 +108,7 @@ private slots: void on_deltaFrequency_changed(qint64 value); void on_rfBW_valueChanged(int value); void on_afBW_valueChanged(int value); + void on_deEmphasis_currentIndexChanged(int index); void on_volume_valueChanged(int value); void on_squelch_valueChanged(int value); void on_audioMute_toggled(bool mute); diff --git a/plugins/channelrx/demodbfm/bfmdemodgui.ui b/plugins/channelrx/demodbfm/bfmdemodgui.ui index f24134e9b..21c4b3835 100644 --- a/plugins/channelrx/demodbfm/bfmdemodgui.ui +++ b/plugins/channelrx/demodbfm/bfmdemodgui.ui @@ -418,6 +418,37 @@ + + + + Qt::Vertical + + + + + + + De-emph + + + + + + + De-emphasis time constant + + + + 50us + + + + + 75us + + + + diff --git a/plugins/channelrx/demodbfm/bfmdemodsettings.cpp b/plugins/channelrx/demodbfm/bfmdemodsettings.cpp index 97faab736..9b2f68fd5 100644 --- a/plugins/channelrx/demodbfm/bfmdemodsettings.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodsettings.cpp @@ -51,6 +51,7 @@ void BFMDemodSettings::resetToDefaults() m_lsbStereo = false; m_showPilot = false; m_rdsActive = false; + m_deEmphasis = DeEmphasis50us; m_rgbColor = QColor(80, 120, 228).rgb(); m_title = "Broadcast FM Demod"; m_audioDeviceName = AudioDeviceManager::m_defaultDeviceName; @@ -103,6 +104,7 @@ QByteArray BFMDemodSettings::serialize() const s.writeBlob(23, m_geometryBytes); s.writeBool(24, m_hidden); s.writeBool(25, m_audioMute); + s.writeS32(26, m_deEmphasis); return s.final(); } @@ -181,6 +183,8 @@ bool BFMDemodSettings::deserialize(const QByteArray& data) d.readBlob(23, &m_geometryBytes); d.readBool(24, &m_hidden, false); d.readBool(25, &m_audioMute, false); + d.readS32(26, &tmp, DeEmphasis50us); + m_deEmphasis = static_cast(tmp); return true; } diff --git a/plugins/channelrx/demodbfm/bfmdemodsettings.h b/plugins/channelrx/demodbfm/bfmdemodsettings.h index 15395dde8..a7ba275e4 100644 --- a/plugins/channelrx/demodbfm/bfmdemodsettings.h +++ b/plugins/channelrx/demodbfm/bfmdemodsettings.h @@ -27,6 +27,12 @@ class Serializable; struct BFMDemodSettings { + enum DeEmphasis + { + DeEmphasis50us, + DeEmphasis75us, + }; + qint64 m_inputFrequencyOffset; Real m_rfBandwidth; Real m_afBandwidth; @@ -37,6 +43,7 @@ struct BFMDemodSettings bool m_lsbStereo; bool m_showPilot; bool m_rdsActive; + DeEmphasis m_deEmphasis; quint32 m_rgbColor; QString m_title; QString m_audioDeviceName; @@ -76,6 +83,19 @@ struct BFMDemodSettings return (3*rfBW)/2; } } + + double getDeEmphasisTimeConstant() const + { + switch (m_deEmphasis) + { + case DeEmphasis50us: + return 50e-6; + case DeEmphasis75us: + return 75e-6; + default: + return 50e-6; + } + } }; diff --git a/plugins/channelrx/demodbfm/bfmdemodsink.cpp b/plugins/channelrx/demodbfm/bfmdemodsink.cpp index 7d04d0ceb..3c69bca92 100644 --- a/plugins/channelrx/demodbfm/bfmdemodsink.cpp +++ b/plugins/channelrx/demodbfm/bfmdemodsink.cpp @@ -30,7 +30,6 @@ #include "rdsparser.h" #include "bfmdemodsink.h" -const Real BFMDemodSink::default_deemphasis = 50.0; // 50 us const int BFMDemodSink::default_excursion = 750000; // +/- 75 kHz BFMDemodSink::BFMDemodSink() : @@ -41,8 +40,8 @@ BFMDemodSink::BFMDemodSink() : m_audioBufferFill(0), m_audioFifo(48000), m_pilotPLL(19000/384000, 50/384000, 0.01), - m_deemphasisFilterX(default_deemphasis * 48000 * 1.0e-6), - m_deemphasisFilterY(default_deemphasis * 48000 * 1.0e-6), + m_deemphasisFilterX(m_settings.getDeEmphasisTimeConstant() * 48000), + m_deemphasisFilterY(m_settings.getDeEmphasisTimeConstant() * 48000), m_fmExcursion(default_excursion) { m_magsq = 0.0f; @@ -67,8 +66,8 @@ BFMDemodSink::BFMDemodSink() : m_rfFilter = new fftfilt(-50000.0 / 384000.0, 50000.0 / 384000.0, filtFftLen); - m_deemphasisFilterX.configure(default_deemphasis * m_audioSampleRate * 1.0e-6); - m_deemphasisFilterY.configure(default_deemphasis * m_audioSampleRate * 1.0e-6); + m_deemphasisFilterX.configure(m_settings.getDeEmphasisTimeConstant() * m_audioSampleRate); + m_deemphasisFilterY.configure(m_settings.getDeEmphasisTimeConstant() * m_audioSampleRate); m_phaseDiscri.setFMScaling(384000/m_fmExcursion); m_audioBuffer.resize(1<<14); @@ -287,8 +286,8 @@ void BFMDemodSink::applyAudioSampleRate(int sampleRate) m_interpolatorStereoDistanceRemain = (Real) m_channelSampleRate / sampleRate; m_interpolatorStereoDistance = (Real) m_channelSampleRate / (Real) sampleRate; - m_deemphasisFilterX.configure(default_deemphasis * sampleRate * 1.0e-6); - m_deemphasisFilterY.configure(default_deemphasis * sampleRate * 1.0e-6); + m_deemphasisFilterX.configure(m_settings.getDeEmphasisTimeConstant() * sampleRate); + m_deemphasisFilterY.configure(m_settings.getDeEmphasisTimeConstant() * sampleRate); m_audioSampleRate = sampleRate; } @@ -337,6 +336,7 @@ void BFMDemodSink::applySettings(const BFMDemodSettings& settings, bool force) << " m_inputFrequencyOffset: " << settings.m_inputFrequencyOffset << " m_rfBandwidth: " << settings.m_rfBandwidth << " m_afBandwidth: " << settings.m_afBandwidth + << " m_deEmphasis: " << settings.getDeEmphasisTimeConstant() << " m_volume: " << settings.m_volume << " m_squelch: " << settings.m_squelch << " m_audioStereo: " << settings.m_audioStereo @@ -354,6 +354,12 @@ void BFMDemodSink::applySettings(const BFMDemodSettings& settings, bool force) applyAudioSampleRate(m_audioSampleRate); // re-apply audio sample rate to reconfigure interpolators } + if ((settings.getDeEmphasisTimeConstant() != m_settings.getDeEmphasisTimeConstant()) || force) + { + m_deemphasisFilterX.configure(settings.getDeEmphasisTimeConstant() * m_audioSampleRate); + m_deemphasisFilterY.configure(settings.getDeEmphasisTimeConstant() * m_audioSampleRate); + } + if ((settings.m_afBandwidth != m_settings.m_afBandwidth) || force) { m_interpolator.create(16, m_channelSampleRate, settings.m_afBandwidth); diff --git a/plugins/channelrx/demodbfm/bfmdemodsink.h b/plugins/channelrx/demodbfm/bfmdemodsink.h index 4855437c3..675ab31ff 100644 --- a/plugins/channelrx/demodbfm/bfmdemodsink.h +++ b/plugins/channelrx/demodbfm/bfmdemodsink.h @@ -155,7 +155,6 @@ private: LowPassFilterRC m_deemphasisFilterX; LowPassFilterRC m_deemphasisFilterY; - static const Real default_deemphasis; Real m_fmExcursion; static const int default_excursion; diff --git a/plugins/channelrx/demodbfm/readme.md b/plugins/channelrx/demodbfm/readme.md index 0c006ac7a..026e11ade 100644 --- a/plugins/channelrx/demodbfm/readme.md +++ b/plugins/channelrx/demodbfm/readme.md @@ -48,29 +48,36 @@ This is the power of the reconstructed stereo pilot signal. Use this button to activate or de-activate RDS decoding -

Audio mute

+

A.8: Audio mute

Use this button to mute or unmute audio -

A.8: Level meter in dB

+

A.9: Level meter in dB

- top bar (green): average value - bottom bar (blue green): instantaneous peak value - tip vertical bar (bright green): peak hold value -

A.9: RF Bandwidth

+

A.10: RF Bandwidth

This is the bandwidth in kHz of the channel signal before demodulation. It can be set in steps: 80, 100, 120, 140, 160, 180, 200, 220 and 250 kHz. Inspect the baseband spectrum (B) to adjust for best quality. -

A.10: AF bandwidth

+

A.11: AF bandwidth

This is the AF bandwidth in kHz. It can be varied continuously between 1 and 20 kHz in steps of 1 kHz. -

A.11: AF volume

+

A.12: De-emphasis

+ +Select the de-emphasis time constant: + + - **50us**: for Europe, Australia... + - **75us**: for North America, Japan... + +

A.13: AF volume

This is the relative AF volume from 0 to 10. -

A.12: Squelch

+

A.14: Squelch

Adjust squelch in dB. diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index f191e3b5a..0b61c7772 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -2948,6 +2948,10 @@ margin-bottom: 20px; "type" : "number", "format" : "float" }, + "deEmphasis" : { + "type" : "integer", + "description" : "De-emphasis time constant (us)\n * 0 - 50us (Europe, Australia)\n * 1 - 75us (US, Japan)\n" + }, "volume" : { "type" : "number", "format" : "float" @@ -59650,7 +59654,7 @@ except ApiException as e:
- Generated 2026-01-03T23:29:39.831+01:00 + Generated 2026-01-04T00:29:36.688+01:00
diff --git a/sdrbase/resources/webapi/doc/swagger/include/BFMDemod.yaml b/sdrbase/resources/webapi/doc/swagger/include/BFMDemod.yaml index 66b8af818..a0e503898 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/BFMDemod.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/BFMDemod.yaml @@ -10,6 +10,12 @@ BFMDemodSettings: afBandwidth: type: number format: float + deEmphasis: + type: integer + description: > + De-emphasis time constant (us) + * 0 - 50us (Europe, Australia) + * 1 - 75us (US, Japan) volume: type: number format: float diff --git a/swagger/sdrangel/api/swagger/include/BFMDemod.yaml b/swagger/sdrangel/api/swagger/include/BFMDemod.yaml index 5a163f411..5f91c1abd 100644 --- a/swagger/sdrangel/api/swagger/include/BFMDemod.yaml +++ b/swagger/sdrangel/api/swagger/include/BFMDemod.yaml @@ -10,6 +10,12 @@ BFMDemodSettings: afBandwidth: type: number format: float + deEmphasis: + type: integer + description: > + De-emphasis time constant (us) + * 0 - 50us (Europe, Australia) + * 1 - 75us (US, Japan) volume: type: number format: float diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index f191e3b5a..0b61c7772 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -2948,6 +2948,10 @@ margin-bottom: 20px; "type" : "number", "format" : "float" }, + "deEmphasis" : { + "type" : "integer", + "description" : "De-emphasis time constant (us)\n * 0 - 50us (Europe, Australia)\n * 1 - 75us (US, Japan)\n" + }, "volume" : { "type" : "number", "format" : "float" @@ -59650,7 +59654,7 @@ except ApiException as e:
- Generated 2026-01-03T23:29:39.831+01:00 + Generated 2026-01-04T00:29:36.688+01:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.cpp index c15ba7104..dc2cdf915 100644 --- a/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.cpp @@ -34,6 +34,8 @@ SWGBFMDemodSettings::SWGBFMDemodSettings() { m_rf_bandwidth_isSet = false; af_bandwidth = 0.0f; m_af_bandwidth_isSet = false; + de_emphasis = 0; + m_de_emphasis_isSet = false; volume = 0.0f; m_volume_isSet = false; squelch = 0.0f; @@ -86,6 +88,8 @@ SWGBFMDemodSettings::init() { m_rf_bandwidth_isSet = false; af_bandwidth = 0.0f; m_af_bandwidth_isSet = false; + de_emphasis = 0; + m_de_emphasis_isSet = false; volume = 0.0f; m_volume_isSet = false; squelch = 0.0f; @@ -139,6 +143,7 @@ SWGBFMDemodSettings::cleanup() { + if(title != nullptr) { delete title; } @@ -181,6 +186,8 @@ SWGBFMDemodSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&af_bandwidth, pJson["afBandwidth"], "float", ""); + ::SWGSDRangel::setValue(&de_emphasis, pJson["deEmphasis"], "qint32", ""); + ::SWGSDRangel::setValue(&volume, pJson["volume"], "float", ""); ::SWGSDRangel::setValue(&squelch, pJson["squelch"], "float", ""); @@ -244,6 +251,9 @@ SWGBFMDemodSettings::asJsonObject() { if(m_af_bandwidth_isSet){ obj->insert("afBandwidth", QJsonValue(af_bandwidth)); } + if(m_de_emphasis_isSet){ + obj->insert("deEmphasis", QJsonValue(de_emphasis)); + } if(m_volume_isSet){ obj->insert("volume", QJsonValue(volume)); } @@ -335,6 +345,16 @@ SWGBFMDemodSettings::setAfBandwidth(float af_bandwidth) { this->m_af_bandwidth_isSet = true; } +qint32 +SWGBFMDemodSettings::getDeEmphasis() { + return de_emphasis; +} +void +SWGBFMDemodSettings::setDeEmphasis(qint32 de_emphasis) { + this->de_emphasis = de_emphasis; + this->m_de_emphasis_isSet = true; +} + float SWGBFMDemodSettings::getVolume() { return volume; @@ -539,6 +559,9 @@ SWGBFMDemodSettings::isSet(){ if(m_af_bandwidth_isSet){ isObjectUpdated = true; break; } + if(m_de_emphasis_isSet){ + isObjectUpdated = true; break; + } if(m_volume_isSet){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.h index 36e5bc36c..ebefb13a8 100644 --- a/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGBFMDemodSettings.h @@ -54,6 +54,9 @@ public: float getAfBandwidth(); void setAfBandwidth(float af_bandwidth); + qint32 getDeEmphasis(); + void setDeEmphasis(qint32 de_emphasis); + float getVolume(); void setVolume(float volume); @@ -124,6 +127,9 @@ private: float af_bandwidth; bool m_af_bandwidth_isSet; + qint32 de_emphasis; + bool m_de_emphasis_isSet; + float volume; bool m_volume_isSet;