1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-26 01:39:05 -05:00

NFM mod: make pre-emphasis and CTCSS highpass filter optional so that digital modulation from file is possible

This commit is contained in:
f4exb 2022-06-04 08:47:10 +02:00
parent c341b5f6f9
commit d4c73ce194
6 changed files with 76 additions and 6 deletions

View File

@ -202,6 +202,12 @@ void NFMModGUI::on_afBW_valueChanged(int value)
applySettings(); applySettings();
} }
void NFMModGUI::on_preEmphasis_toggled(bool checked)
{
m_settings.m_preEmphasisOn = checked;
applySettings();
}
void NFMModGUI::on_fmDev_valueChanged(int value) void NFMModGUI::on_fmDev_valueChanged(int value)
{ {
ui->fmDevText->setText(QString("%1%2k").arg(QChar(0xB1, 0x00)).arg(value / 10.0, 0, 'f', 1)); ui->fmDevText->setText(QString("%1%2k").arg(QChar(0xB1, 0x00)).arg(value / 10.0, 0, 'f', 1));
@ -352,6 +358,12 @@ void NFMModGUI::on_dcsPositive_toggled(bool checked)
applySettings(); applySettings();
} }
void NFMModGUI::on_bpf_toggled(bool checked)
{
m_settings.m_bpfOn = checked;
applySettings();
}
void NFMModGUI::configureFileName() void NFMModGUI::configureFileName()
{ {
qDebug() << "FileSourceGui::configureFileName: " << m_fileName.toStdString().c_str(); qDebug() << "FileSourceGui::configureFileName: " << m_fileName.toStdString().c_str();
@ -546,6 +558,7 @@ void NFMModGUI::displaySettings()
ui->afBWText->setText(QString("%1k").arg(m_settings.m_afBandwidth / 1000.0, 0, 'f', 1)); ui->afBWText->setText(QString("%1k").arg(m_settings.m_afBandwidth / 1000.0, 0, 'f', 1));
ui->afBW->setValue(m_settings.m_afBandwidth / 100.0); ui->afBW->setValue(m_settings.m_afBandwidth / 100.0);
ui->preEmphasis->setChecked(m_settings.m_preEmphasisOn);
ui->fmDevText->setText(QString("%1%2k").arg(QChar(0xB1, 0x00)).arg(m_settings.m_fmDeviation / 2000.0, 0, 'f', 1)); ui->fmDevText->setText(QString("%1%2k").arg(QChar(0xB1, 0x00)).arg(m_settings.m_fmDeviation / 2000.0, 0, 'f', 1));
ui->fmDev->setValue(m_settings.m_fmDeviation / 200.0); ui->fmDev->setValue(m_settings.m_fmDeviation / 200.0);
@ -567,6 +580,7 @@ void NFMModGUI::displaySettings()
ui->dcsOn->setEnabled(!m_settings.m_ctcssOn); ui->dcsOn->setEnabled(!m_settings.m_ctcssOn);
ui->dcsCode->setText(tr("%1").arg(m_settings.m_dcsCode, 3, 8, QLatin1Char('0'))); ui->dcsCode->setText(tr("%1").arg(m_settings.m_dcsCode, 3, 8, QLatin1Char('0')));
ui->dcsPositive->setChecked(m_settings.m_dcsPositive); ui->dcsPositive->setChecked(m_settings.m_dcsPositive);
ui->bpf->setChecked(m_settings.m_bpfOn);
ui->channelMute->setChecked(m_settings.m_channelMute); ui->channelMute->setChecked(m_settings.m_channelMute);
ui->playLoop->setChecked(m_settings.m_playLoop); ui->playLoop->setChecked(m_settings.m_playLoop);
@ -707,6 +721,7 @@ void NFMModGUI::makeUIConnections()
QObject::connect(ui->channelSpacingApply, &QPushButton::clicked, this, &NFMModGUI::on_channelSpacingApply_clicked); QObject::connect(ui->channelSpacingApply, &QPushButton::clicked, this, &NFMModGUI::on_channelSpacingApply_clicked);
QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &NFMModGUI::on_rfBW_valueChanged); QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &NFMModGUI::on_rfBW_valueChanged);
QObject::connect(ui->afBW, &QSlider::valueChanged, this, &NFMModGUI::on_afBW_valueChanged); QObject::connect(ui->afBW, &QSlider::valueChanged, this, &NFMModGUI::on_afBW_valueChanged);
QObject::connect(ui->preEmphasis, &QCheckBox::toggled, this, &NFMModGUI::on_preEmphasis_toggled);
QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &NFMModGUI::on_fmDev_valueChanged); QObject::connect(ui->fmDev, &QSlider::valueChanged, this, &NFMModGUI::on_fmDev_valueChanged);
QObject::connect(ui->toneFrequency, &QDial::valueChanged, this, &NFMModGUI::on_toneFrequency_valueChanged); QObject::connect(ui->toneFrequency, &QDial::valueChanged, this, &NFMModGUI::on_toneFrequency_valueChanged);
QObject::connect(ui->volume, &QDial::valueChanged, this, &NFMModGUI::on_volume_valueChanged); QObject::connect(ui->volume, &QDial::valueChanged, this, &NFMModGUI::on_volume_valueChanged);
@ -723,6 +738,7 @@ void NFMModGUI::makeUIConnections()
QObject::connect(ui->dcsOn, &QCheckBox::toggled, this, &NFMModGUI::on_dcsOn_toggled); QObject::connect(ui->dcsOn, &QCheckBox::toggled, this, &NFMModGUI::on_dcsOn_toggled);
QObject::connect(ui->dcsCode, &QLineEdit::editingFinished, this, &NFMModGUI::on_dcsCode_editingFinished); QObject::connect(ui->dcsCode, &QLineEdit::editingFinished, this, &NFMModGUI::on_dcsCode_editingFinished);
QObject::connect(ui->dcsPositive, &QCheckBox::toggled, this, &NFMModGUI::on_dcsPositive_toggled); QObject::connect(ui->dcsPositive, &QCheckBox::toggled, this, &NFMModGUI::on_dcsPositive_toggled);
QObject::connect(ui->bpf, &QCheckBox::toggled, this, &NFMModGUI::on_bpf_toggled);
QObject::connect(ui->feedbackEnable, &QToolButton::toggled, this, &NFMModGUI::on_feedbackEnable_toggled); QObject::connect(ui->feedbackEnable, &QToolButton::toggled, this, &NFMModGUI::on_feedbackEnable_toggled);
QObject::connect(ui->feedbackVolume, &QDial::valueChanged, this, &NFMModGUI::on_feedbackVolume_valueChanged); QObject::connect(ui->feedbackVolume, &QDial::valueChanged, this, &NFMModGUI::on_feedbackVolume_valueChanged);
} }

View File

@ -114,6 +114,7 @@ private slots:
void on_channelSpacingApply_clicked(); void on_channelSpacingApply_clicked();
void on_rfBW_valueChanged(int value); void on_rfBW_valueChanged(int value);
void on_afBW_valueChanged(int value); void on_afBW_valueChanged(int value);
void on_preEmphasis_toggled(bool checked);
void on_fmDev_valueChanged(int value); void on_fmDev_valueChanged(int value);
void on_toneFrequency_valueChanged(int value); void on_toneFrequency_valueChanged(int value);
void on_volume_valueChanged(int value); void on_volume_valueChanged(int value);
@ -132,6 +133,7 @@ private slots:
void on_dcsOn_toggled(bool checked); void on_dcsOn_toggled(bool checked);
void on_dcsCode_editingFinished(); void on_dcsCode_editingFinished();
void on_dcsPositive_toggled(bool checked); void on_dcsPositive_toggled(bool checked);
void on_bpf_toggled(bool checked);
void on_feedbackEnable_toggled(bool checked); void on_feedbackEnable_toggled(bool checked);
void on_feedbackVolume_valueChanged(int value); void on_feedbackVolume_valueChanged(int value);

View File

@ -279,6 +279,42 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="ButtonSwitch" name="preEmphasis">
<property name="minimumSize">
<size>
<width>24</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Pre-emphasis (120us) on/off</string>
</property>
<property name="text">
<string>P</string>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="bpf">
<property name="maximumSize">
<size>
<width>24</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Audio high pass filter for CTCSS on/off</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/filter_highpass.png</normaloff>:/filter_highpass.png</iconset>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>

View File

@ -66,6 +66,8 @@ void NFMModSettings::resetToDefaults()
m_dcsOn = false; m_dcsOn = false;
m_dcsCode = 0023; m_dcsCode = 0023;
m_dcsPositive = false; m_dcsPositive = false;
m_preEmphasisOn = true;
m_bpfOn = true;
m_rgbColor = QColor(255, 0, 0).rgb(); m_rgbColor = QColor(255, 0, 0).rgb();
m_title = "NFM Modulator"; m_title = "NFM Modulator";
m_modAFInput = NFMModInputAF::NFMModInputNone; m_modAFInput = NFMModInputAF::NFMModInputNone;
@ -98,15 +100,16 @@ QByteArray NFMModSettings::serialize() const
if (m_cwKeyerGUI) { if (m_cwKeyerGUI) {
s.writeBlob(8, m_cwKeyerGUI->serialize()); s.writeBlob(8, m_cwKeyerGUI->serialize());
} else { // standalone operation with presets } else { // standalone operation with presets
s.writeBlob(6, m_cwKeyerSettings.serialize()); s.writeBlob(8, m_cwKeyerSettings.serialize());
} }
s.writeBool(9, m_ctcssOn);
s.writeS32(10, m_ctcssIndex);
if (m_channelMarker) { if (m_channelMarker) {
s.writeBlob(11, m_channelMarker->serialize()); s.writeBlob(11, m_channelMarker->serialize());
} }
s.writeBool(9, m_ctcssOn);
s.writeS32(10, m_ctcssIndex);
s.writeString(12, m_title); s.writeString(12, m_title);
s.writeS32(13, (int) m_modAFInput); s.writeS32(13, (int) m_modAFInput);
s.writeString(14, m_audioDeviceName); s.writeString(14, m_audioDeviceName);
@ -130,6 +133,8 @@ QByteArray NFMModSettings::serialize() const
s.writeS32(28, m_workspaceIndex); s.writeS32(28, m_workspaceIndex);
s.writeBlob(29, m_geometryBytes); s.writeBlob(29, m_geometryBytes);
s.writeBool(30, m_hidden); s.writeBool(30, m_hidden);
s.writeBool(31, m_preEmphasisOn);
s.writeBool(32, m_bpfOn);
return s.final(); return s.final();
} }
@ -217,6 +222,8 @@ bool NFMModSettings::deserialize(const QByteArray& data)
d.readS32(28, &m_workspaceIndex, 0); d.readS32(28, &m_workspaceIndex, 0);
d.readBlob(29, &m_geometryBytes); d.readBlob(29, &m_geometryBytes);
d.readBool(30, &m_hidden, false); d.readBool(30, &m_hidden, false);
d.readBool(31, &m_preEmphasisOn, true);
d.readBool(32, &m_bpfOn, true);
return true; return true;
} }

View File

@ -54,6 +54,8 @@ struct NFMModSettings
bool m_dcsOn; bool m_dcsOn;
int m_dcsCode; int m_dcsCode;
bool m_dcsPositive; bool m_dcsPositive;
bool m_preEmphasisOn;
bool m_bpfOn;
quint32 m_rgbColor; quint32 m_rgbColor;
QString m_title; QString m_title;
NFMModInputAF m_modAFInput; NFMModInputAF m_modAFInput;

View File

@ -144,7 +144,12 @@ void NFMModSource::modulateSample()
Real t0, t1, t; Real t0, t1, t;
pullAF(t0); pullAF(t0);
m_preemphasisFilter.process(t0, t);
if (m_settings.m_preEmphasisOn) {
m_preemphasisFilter.process(t0, t);
} else {
t = t0;
}
if (m_settings.m_feedbackAudioEnable) { if (m_settings.m_feedbackAudioEnable) {
pushFeedback(t * m_settings.m_feedbackVolumeFactor * 16384.0f); pushFeedback(t * m_settings.m_feedbackVolumeFactor * 16384.0f);
@ -156,8 +161,10 @@ void NFMModSource::modulateSample()
t1 = (0.85f * m_bandpass.filter(t) + 0.15f * 0.625f * m_ctcssNco.next()) * 1.2f; t1 = (0.85f * m_bandpass.filter(t) + 0.15f * 0.625f * m_ctcssNco.next()) * 1.2f;
} else if (m_settings.m_dcsOn) { } else if (m_settings.m_dcsOn) {
t1 = (0.9f * m_bandpass.filter(t) + 0.1f * 0.625f * m_dcsMod.next()) * 1.2f; t1 = (0.9f * m_bandpass.filter(t) + 0.1f * 0.625f * m_dcsMod.next()) * 1.2f;
} else { } else if (m_settings.m_bpfOn) {
t1 = m_bandpass.filter(t) * 1.2f; t1 = m_bandpass.filter(t) * 1.2f;
} else {
t1 = m_lowpass.filter(t) * 1.2f;
} }
m_modPhasor += (m_settings.m_fmDeviation / (float) m_audioSampleRate) * t1; m_modPhasor += (m_settings.m_fmDeviation / (float) m_audioSampleRate) * t1;
@ -350,7 +357,7 @@ void NFMModSource::applyAudioSampleRate(int sampleRate)
m_interpolatorConsumed = false; m_interpolatorConsumed = false;
m_interpolatorDistance = (Real) sampleRate / (Real) m_channelSampleRate; m_interpolatorDistance = (Real) sampleRate / (Real) m_channelSampleRate;
m_interpolator.create(48, sampleRate, m_settings.m_rfBandwidth / 2.2, 3.0); m_interpolator.create(48, sampleRate, m_settings.m_rfBandwidth / 2.2, 3.0);
m_lowpass.create(301, sampleRate, 250.0); m_lowpass.create(301, sampleRate, m_settings.m_afBandwidth);
m_bandpass.create(301, sampleRate, 300.0, m_settings.m_afBandwidth); m_bandpass.create(301, sampleRate, 300.0, m_settings.m_afBandwidth);
m_toneNco.setFreq(m_settings.m_toneFrequency, sampleRate); m_toneNco.setFreq(m_settings.m_toneFrequency, sampleRate);
m_ctcssNco.setFreq(NFMModSettings::getCTCSSFreq(m_settings.m_ctcssIndex), sampleRate); m_ctcssNco.setFreq(NFMModSettings::getCTCSSFreq(m_settings.m_ctcssIndex), sampleRate);