1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-16 05:11:49 -05:00

NFM: RF, AF bandwidth and FM deviation independent setting. Implements #691

This commit is contained in:
f4exb 2020-11-08 02:04:17 +01:00
parent c5eb6a2eeb
commit fa68a8ff2b
11 changed files with 480 additions and 183 deletions

View File

@ -107,20 +107,46 @@ void NFMDemodGUI::on_deltaFrequency_changed(qint64 value)
applySettings(); applySettings();
} }
void NFMDemodGUI::on_rfBW_currentIndexChanged(int index) void NFMDemodGUI::on_channelSpacing_currentIndexChanged(int index)
{ {
qDebug() << "NFMDemodGUI::on_rfBW_currentIndexChanged" << index;
//ui->rfBWText->setText(QString("%1 k").arg(m_rfBW[value] / 1000.0));
m_channelMarker.setBandwidth(NFMDemodSettings::getRFBW(index));
m_settings.m_rfBandwidth = NFMDemodSettings::getRFBW(index); m_settings.m_rfBandwidth = NFMDemodSettings::getRFBW(index);
m_settings.m_fmDeviation = NFMDemodSettings::getFMDev(index); m_settings.m_afBandwidth = NFMDemodSettings::getAFBW(index);
m_settings.m_fmDeviation = 2.0 * NFMDemodSettings::getFMDev(index);
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
ui->rfBW->blockSignals(true);
ui->afBW->blockSignals(true);
ui->fmDev->blockSignals(true);
ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1));
ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0);
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->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 2000.0, 0, 'f', 1));
ui->fmDev->setValue(m_settings.m_fmDeviation / 200.0);
ui->rfBW->blockSignals(false);
ui->afBW->blockSignals(false);
ui->fmDev->blockSignals(false);
applySettings();
}
void NFMDemodGUI::on_rfBW_valueChanged(int value)
{
ui->rfBWText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1));
m_settings.m_rfBandwidth = value * 100.0;
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
applySettings(); applySettings();
} }
void NFMDemodGUI::on_afBW_valueChanged(int value) void NFMDemodGUI::on_afBW_valueChanged(int value)
{ {
ui->afBWText->setText(QString("%1 k").arg(value)); ui->afBWText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1));
m_settings.m_afBandwidth = value * 1000.0; m_settings.m_afBandwidth = value * 100.0;
applySettings();
}
void NFMDemodGUI::on_fmDev_valueChanged(int value)
{
ui->fmDevText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1));
m_settings.m_fmDeviation = value * 200.0;
applySettings(); applySettings();
} }
@ -281,12 +307,14 @@ NFMDemodGUI::NFMDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseban
blockApplySettings(true); blockApplySettings(true);
ui->rfBW->clear(); ui->channelSpacing->clear();
for (int i = 0; i < NFMDemodSettings::m_nbRfBW; i++) { for (int i = 0; i < NFMDemodSettings::m_nbChannelSpacings; i++) {
ui->rfBW->addItem(QString("%1").arg(NFMDemodSettings::getRFBW(i) / 1000.0, 0, 'f', 2)); ui->channelSpacing->addItem(QString("%1").arg(NFMDemodSettings::getChannelSpacing(i) / 1000.0, 0, 'f', 2));
} }
ui->channelSpacing->setCurrentIndex(NFMDemodSettings::getChannelSpacingIndex(12500));
int ctcss_nbTones; int ctcss_nbTones;
const Real *ctcss_tones = m_nfmDemod->getCtcssToneSet(ctcss_nbTones); const Real *ctcss_tones = m_nfmDemod->getCtcssToneSet(ctcss_nbTones);
@ -363,10 +391,14 @@ void NFMDemodGUI::displaySettings()
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
ui->rfBW->setCurrentIndex(NFMDemodSettings::getRFBWIndex(m_settings.m_rfBandwidth)); ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1));
ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0);
ui->afBWText->setText(QString("%1 k").arg(m_settings.m_afBandwidth / 1000.0)); ui->afBWText->setText(QString("%1k").arg(m_settings.m_afBandwidth / 1000.0, 0, 'f', 1));
ui->afBW->setValue(m_settings.m_afBandwidth / 1000.0); ui->afBW->setValue(m_settings.m_afBandwidth / 100.0);
ui->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 2000.0, 0, 'f', 1));
ui->fmDev->setValue(m_settings.m_fmDeviation / 200.0);
ui->volumeText->setText(QString("%1").arg(m_settings.m_volume*100.0, 0, 'f', 0)); ui->volumeText->setText(QString("%1").arg(m_settings.m_volume*100.0, 0, 'f', 0));
ui->volume->setValue(m_settings.m_volume * 100.0); ui->volume->setValue(m_settings.m_volume * 100.0);

View File

@ -64,8 +64,10 @@ private:
private slots: private slots:
void on_deltaFrequency_changed(qint64 value); void on_deltaFrequency_changed(qint64 value);
void on_rfBW_currentIndexChanged(int index); void on_channelSpacing_currentIndexChanged(int index);
void on_rfBW_valueChanged(int value);
void on_afBW_valueChanged(int value); void on_afBW_valueChanged(int value);
void on_fmDev_valueChanged(int value);
void on_volume_valueChanged(int value); void on_volume_valueChanged(int value);
void on_squelchGate_valueChanged(int value); void on_squelchGate_valueChanged(int value);
void on_deltaSquelch_toggled(bool checked); void on_deltaSquelch_toggled(bool checked);

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>302</width> <width>302</width>
<height>178</height> <height>182</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -204,14 +204,14 @@
<item> <item>
<layout class="QHBoxLayout" name="rfBWLayout"> <layout class="QHBoxLayout" name="rfBWLayout">
<item> <item>
<widget class="QLabel" name="rfBWLabel"> <widget class="QLabel" name="channelSpacingLabel">
<property name="text"> <property name="text">
<string>RFBW</string> <string>CS</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="rfBW"> <widget class="QComboBox" name="channelSpacing">
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>60</width> <width>60</width>
@ -219,23 +219,58 @@
</size> </size>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>RF demodulator bandwidth (kHz)</string> <string>Apply channel spacing settings</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="rfBWUnitText"> <widget class="QLabel" name="rfLabel">
<property name="text">
<string>RF</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="rfBW">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>10</width> <width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>RF bandwidth (kHz)</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>400</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>110</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rfBWText">
<property name="minimumSize">
<size>
<width>35</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="text"> <property name="text">
<string>k</string> <string>20.0k</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -252,23 +287,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item> <item>
<widget class="QLabel" name="afLabel"> <widget class="QLabel" name="afLabel">
<property name="text"> <property name="text">
<string>AFBW</string> <string>AF</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -287,13 +309,13 @@
<number>1</number> <number>1</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>20</number> <number>200</number>
</property> </property>
<property name="pageStep"> <property name="pageStep">
<number>1</number> <number>1</number>
</property> </property>
<property name="value"> <property name="value">
<number>3</number> <number>30</number>
</property> </property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@ -304,12 +326,12 @@
<widget class="QLabel" name="afBWText"> <widget class="QLabel" name="afBWText">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>25</width> <width>35</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="text"> <property name="text">
<string>3 k</string> <string>20.0k</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@ -321,79 +343,60 @@
<item> <item>
<layout class="QHBoxLayout" name="squelchLayout"> <layout class="QHBoxLayout" name="squelchLayout">
<item> <item>
<widget class="QLabel" name="volumeLabel"> <widget class="QLabel" name="fmDevLabel">
<property name="text"> <property name="text">
<string>Vol</string> <string>FMd</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QDial" name="volume"> <widget class="QSlider" name="fmDev">
<property name="maximumSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="toolTip">
<string>Sound volume (%)</string>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="volumeText">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>0</width> <width>0</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="maximumSize"> <property name="toolTip">
<string>FM peak deviation (kHz)</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>25</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="fmDevText">
<property name="minimumSize">
<size> <size>
<width>30</width> <width>35</width>
<height>16777215</height> <height>0</height>
</size> </size>
</property> </property>
<property name="toolTip">
<string>Sound volume (%)</string>
</property>
<property name="text"> <property name="text">
<string>100</string> <string>20.0k</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item> <item>
<widget class="QLabel" name="squelchLabel"> <widget class="QLabel" name="squelchLabel">
<property name="text"> <property name="text">
<string>Sq</string> <string> Sq</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -565,17 +568,61 @@
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="ctcssSpacer"> <widget class="QLabel" name="volumeLabel">
<property name="orientation"> <property name="text">
<enum>Qt::Horizontal</enum> <string>Vol</string>
</property> </property>
<property name="sizeHint" stdset="0"> </widget>
</item>
<item>
<widget class="QDial" name="volume">
<property name="maximumSize">
<size> <size>
<width>40</width> <width>24</width>
<height>20</height> <height>24</height>
</size> </size>
</property> </property>
</spacer> <property name="toolTip">
<string>Sound volume (%)</string>
</property>
<property name="maximum">
<number>200</number>
</property>
<property name="singleStep">
<number>1</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>100</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="volumeText">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Sound volume (%)</string>
</property>
<property name="text">
<string>100</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="ButtonSwitch" name="highPassFilter"> <widget class="ButtonSwitch" name="highPassFilter">

View File

@ -23,15 +23,23 @@
#include "nfmdemodsettings.h" #include "nfmdemodsettings.h"
// fixed |Carson (3k) |Carson (6k) // Standard channel spacings (kHz) using Carson rule
// | 11F3 16F3 | // beta based ............ 11F3 16F3 (36F9)
const int NFMDemodSettings::m_rfBW[] = { // 5 6.25 7.5 8.33 12.5 25 40 Spacing
5000, 6250, 8330, 11000, 16000, 20000, 25000, 40000 // 0.43 0.43 0.43 0.43 0.83 1.67 1.0 Beta
const int NFMDemodSettings::m_channelSpacings[] = {
5000, 6250, 7500, 8333, 12500, 25000, 40000
}; };
const int NFMDemodSettings::m_fmDev[] = { const int NFMDemodSettings::m_rfBW[] = { // RF bandwidth (Hz)
2500, 2500, 3330, 5000, 10000, 14000, 19000, 28000 4800, 6000, 7200, 8000, 11000, 16000, 36000
}; };
const int NFMDemodSettings::m_nbRfBW = 8; const int NFMDemodSettings::m_afBW[] = { // audio bandwidth (Hz)
1700, 2100, 2500, 2800, 3000, 3000, 9000
};
const int NFMDemodSettings::m_fmDev[] = { // peak deviation (Hz) - full is double
731, 903, 1075, 1204, 2500, 5000, 9000
};
const int NFMDemodSettings::m_nbChannelSpacings = 7;
NFMDemodSettings::NFMDemodSettings() : NFMDemodSettings::NFMDemodSettings() :
m_channelMarker(0) m_channelMarker(0)
@ -44,7 +52,7 @@ void NFMDemodSettings::resetToDefaults()
m_inputFrequencyOffset = 0; m_inputFrequencyOffset = 0;
m_rfBandwidth = 12500; m_rfBandwidth = 12500;
m_afBandwidth = 3000; m_afBandwidth = 3000;
m_fmDeviation = 2000; m_fmDeviation = 5000;
m_squelchGate = 5; // 10s of ms at 48000 Hz sample rate. Corresponds to 2400 for AGC attack m_squelchGate = 5; // 10s of ms at 48000 Hz sample rate. Corresponds to 2400 for AGC attack
m_deltaSquelch = false; m_deltaSquelch = false;
m_squelch = -30.0; m_squelch = -30.0;
@ -68,8 +76,8 @@ QByteArray NFMDemodSettings::serialize() const
{ {
SimpleSerializer s(1); SimpleSerializer s(1);
s.writeS32(1, m_inputFrequencyOffset); s.writeS32(1, m_inputFrequencyOffset);
s.writeS32(2, getRFBWIndex(m_rfBandwidth)); s.writeReal(2, m_rfBandwidth);
s.writeS32(3, m_afBandwidth/1000.0); s.writeReal(3, m_afBandwidth);
s.writeS32(4, m_volume*10.0); s.writeS32(4, m_volume*10.0);
s.writeS32(5, static_cast<int>(m_squelch)); s.writeS32(5, static_cast<int>(m_squelch));
s.writeBool(6, m_highPass); s.writeBool(6, m_highPass);
@ -92,6 +100,7 @@ QByteArray NFMDemodSettings::serialize() const
s.writeU32(19, m_reverseAPIDeviceIndex); s.writeU32(19, m_reverseAPIDeviceIndex);
s.writeU32(20, m_reverseAPIChannelIndex); s.writeU32(20, m_reverseAPIChannelIndex);
s.writeS32(21, m_streamIndex); s.writeS32(21, m_streamIndex);
s.writeReal(22, m_fmDeviation);
return s.final(); return s.final();
} }
@ -120,11 +129,8 @@ bool NFMDemodSettings::deserialize(const QByteArray& data)
d.readS32(1, &tmp, 0); d.readS32(1, &tmp, 0);
m_inputFrequencyOffset = tmp; m_inputFrequencyOffset = tmp;
d.readS32(2, &tmp, 4); d.readReal(2, &m_rfBandwidth, 12500.0);
m_rfBandwidth = getRFBW(tmp); d.readReal(3, &m_afBandwidth, 3000.0);
m_fmDeviation = getFMDev(tmp);
d.readS32(3, &tmp, 3);
m_afBandwidth = tmp * 1000.0;
d.readS32(4, &tmp, 20); d.readS32(4, &tmp, 20);
m_volume = tmp / 10.0; m_volume = tmp / 10.0;
d.readS32(5, &tmp, -30); d.readS32(5, &tmp, -30);
@ -153,6 +159,7 @@ bool NFMDemodSettings::deserialize(const QByteArray& data)
d.readU32(20, &utmp, 0); d.readU32(20, &utmp, 0);
m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp;
d.readS32(21, &m_streamIndex, 0); d.readS32(21, &m_streamIndex, 0);
d.readReal(22, &m_fmDeviation, 5000.0);
return true; return true;
} }
@ -163,37 +170,94 @@ bool NFMDemodSettings::deserialize(const QByteArray& data)
} }
} }
int NFMDemodSettings::getChannelSpacing(int index)
{
if (index < 0) {
return m_channelSpacings[0];
} else if (index < m_nbChannelSpacings) {
return m_channelSpacings[index];
} else {
return m_channelSpacings[m_nbChannelSpacings-1];
}
}
int NFMDemodSettings::getChannelSpacingIndex(int channelSpacing)
{
for (int i = 0; i < m_nbChannelSpacings; i++)
{
if (channelSpacing <= m_channelSpacings[i]) {
return i;
}
}
return m_nbChannelSpacings-1;
}
int NFMDemodSettings::getRFBW(int index) int NFMDemodSettings::getRFBW(int index)
{ {
if (index < 0) { if (index < 0) {
return m_rfBW[0]; return m_rfBW[0];
} else if (index < m_nbRfBW) { } else if (index < m_nbChannelSpacings) {
return m_rfBW[index]; return m_rfBW[index];
} else { } else {
return m_rfBW[m_nbRfBW-1]; return m_rfBW[m_nbChannelSpacings-1];
} }
} }
int NFMDemodSettings::getRFBWIndex(int rfbw)
{
for (int i = 0; i < m_nbChannelSpacings; i++)
{
if (rfbw <= m_rfBW[i]) {
return i;
}
}
return m_nbChannelSpacings-1;
}
int NFMDemodSettings::getAFBW(int index)
{
if (index < 0) {
return m_afBW[0];
} else if (index < m_nbChannelSpacings) {
return m_afBW[index];
} else {
return m_afBW[m_nbChannelSpacings-1];
}
}
int NFMDemodSettings::getAFBWIndex(int afbw)
{
for (int i = 0; i < m_nbChannelSpacings; i++)
{
if (afbw <= m_afBW[i]) {
return i;
}
}
return m_nbChannelSpacings-1;
}
int NFMDemodSettings::getFMDev(int index) int NFMDemodSettings::getFMDev(int index)
{ {
if (index < 0) { if (index < 0) {
return m_fmDev[0]; return m_fmDev[0];
} else if (index < m_nbRfBW) { } else if (index < m_nbChannelSpacings) {
return m_fmDev[index]; return m_fmDev[index];
} else { } else {
return m_fmDev[m_nbRfBW-1]; return m_fmDev[m_nbChannelSpacings-1];
} }
} }
int NFMDemodSettings::getRFBWIndex(int rfbw) int NFMDemodSettings::getFMDevIndex(int fmDev)
{ {
for (int i = 0; i < m_nbRfBW; i++) for (int i = 0; i < m_nbChannelSpacings; i++)
{ {
if (rfbw <= m_rfBW[i]) if (fmDev <= m_rfBW[i]) {
{
return i; return i;
} }
} }
return m_nbRfBW-1; return m_nbChannelSpacings-1;
} }

View File

@ -24,14 +24,16 @@ class Serializable;
struct NFMDemodSettings struct NFMDemodSettings
{ {
static const int m_nbRfBW; static const int m_nbChannelSpacings;
static const int m_channelSpacings[];
static const int m_rfBW[]; static const int m_rfBW[];
static const int m_afBW[];
static const int m_fmDev[]; static const int m_fmDev[];
int32_t m_inputFrequencyOffset; int32_t m_inputFrequencyOffset;
Real m_rfBandwidth; Real m_rfBandwidth;
Real m_afBandwidth; Real m_afBandwidth;
int m_fmDeviation; Real m_fmDeviation;
int m_squelchGate; int m_squelchGate;
bool m_deltaSquelch; bool m_deltaSquelch;
Real m_squelch; //!< deci-Bels Real m_squelch; //!< deci-Bels
@ -58,9 +60,14 @@ struct NFMDemodSettings
QByteArray serialize() const; QByteArray serialize() const;
bool deserialize(const QByteArray& data); bool deserialize(const QByteArray& data);
static int getChannelSpacing(int index);
static int getChannelSpacingIndex(int channelSpacing);
static int getRFBW(int index); static int getRFBW(int index);
static int getFMDev(int index);
static int getRFBWIndex(int rfbw); static int getRFBWIndex(int rfbw);
static int getAFBW(int index);
static int getAFBWIndex(int rfbw);
static int getFMDev(int index);
static int getFMDevIndex(int fmDev);
}; };

View File

@ -236,7 +236,7 @@ void NFMDemodSink::applyChannelSettings(int channelSampleRate, int channelFreque
if ((channelSampleRate != m_channelSampleRate) || force) if ((channelSampleRate != m_channelSampleRate) || force)
{ {
m_interpolator.create(16, channelSampleRate, m_settings.m_fmDeviation); m_interpolator.create(16, channelSampleRate, m_settings.m_rfBandwidth / 2.2);
m_interpolatorDistance = Real(channelSampleRate) / Real(m_audioSampleRate); m_interpolatorDistance = Real(channelSampleRate) / Real(m_audioSampleRate);
m_interpolatorDistanceRemain = m_interpolatorDistance; m_interpolatorDistanceRemain = m_interpolatorDistance;
@ -269,7 +269,7 @@ void NFMDemodSink::applySettings(const NFMDemodSettings& settings, bool force)
if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) if ((settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force)
{ {
m_interpolator.create(16, m_channelSampleRate, settings.m_fmDeviation); m_interpolator.create(16, m_channelSampleRate, settings.m_rfBandwidth / 2.2);
m_interpolatorDistance = Real(m_channelSampleRate) / Real(m_audioSampleRate); m_interpolatorDistance = Real(m_channelSampleRate) / Real(m_audioSampleRate);
m_interpolatorDistanceRemain = m_interpolatorDistance; m_interpolatorDistanceRemain = m_interpolatorDistance;
} }

View File

@ -137,24 +137,46 @@ void NFMModGUI::on_deltaFrequency_changed(qint64 value)
applySettings(); applySettings();
} }
void NFMModGUI::on_rfBW_currentIndexChanged(int index) void NFMModGUI::on_channelSpacing_currentIndexChanged(int index)
{ {
m_channelMarker.setBandwidth(NFMModSettings::getRFBW(index));
m_settings.m_rfBandwidth = NFMModSettings::getRFBW(index); m_settings.m_rfBandwidth = NFMModSettings::getRFBW(index);
m_settings.m_afBandwidth = NFMModSettings::getAFBW(index);
m_settings.m_fmDeviation = 2.0 * NFMModSettings::getFMDev(index);
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
ui->rfBW->blockSignals(true);
ui->afBW->blockSignals(true);
ui->fmDev->blockSignals(true);
ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1));
ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0);
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->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 2000.0, 0, 'f', 1));
ui->fmDev->setValue(m_settings.m_fmDeviation / 200.0);
ui->rfBW->blockSignals(false);
ui->afBW->blockSignals(false);
ui->fmDev->blockSignals(false);
applySettings();
}
void NFMModGUI::on_rfBW_valueChanged(int value)
{
ui->rfBWText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1));
m_settings.m_rfBandwidth = value * 100.0;
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
applySettings(); applySettings();
} }
void NFMModGUI::on_afBW_valueChanged(int value) void NFMModGUI::on_afBW_valueChanged(int value)
{ {
ui->afBWText->setText(QString("%1k").arg(value)); ui->afBWText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1));
m_settings.m_afBandwidth = value * 1000.0; m_settings.m_afBandwidth = value * 100.0;
applySettings(); applySettings();
} }
void NFMModGUI::on_fmDev_valueChanged(int value) void NFMModGUI::on_fmDev_valueChanged(int value)
{ {
ui->fmDevText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1)); ui->fmDevText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1));
m_settings.m_fmDeviation = value * 100.0; m_settings.m_fmDeviation = value * 200.0;
applySettings(); applySettings();
} }
@ -352,15 +374,15 @@ NFMModGUI::NFMModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSam
ui->setupUi(this); ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose, true); setAttribute(Qt::WA_DeleteOnClose, true);
blockApplySettings(true); ui->channelSpacing->blockSignals(true);
ui->channelSpacing->clear();
ui->rfBW->clear(); for (int i = 0; i < NFMModSettings::m_nbChannelSpacings; i++) {
for (int i = 0; i < NFMModSettings::m_nbRfBW; i++) { ui->channelSpacing->addItem(QString("%1").arg(NFMModSettings::getChannelSpacing(i) / 1000.0, 0, 'f', 2));
ui->rfBW->addItem(QString("%1").arg(NFMModSettings::getRFBW(i) / 1000.0, 0, 'f', 2));
} }
ui->rfBW->setCurrentIndex(6);
blockApplySettings(false); ui->channelSpacing->setCurrentIndex(NFMModSettings::getChannelSpacingIndex(25000));
ui->channelSpacing->blockSignals(false);
connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); connect(this, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
@ -439,7 +461,6 @@ void NFMModGUI::displaySettings()
m_channelMarker.blockSignals(true); m_channelMarker.blockSignals(true);
m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset);
m_channelMarker.setTitle(m_settings.m_title); m_channelMarker.setTitle(m_settings.m_title);
m_channelMarker.setBandwidth(m_settings.m_rfBandwidth);
m_channelMarker.blockSignals(false); m_channelMarker.blockSignals(false);
m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only
@ -451,13 +472,14 @@ void NFMModGUI::displaySettings()
ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency());
ui->rfBW->setCurrentIndex(NFMModSettings::getRFBWIndex(m_settings.m_rfBandwidth)); ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1));
ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0);
ui->afBWText->setText(QString("%1k").arg(m_settings.m_afBandwidth / 1000.0)); ui->afBWText->setText(QString("%1k").arg(m_settings.m_afBandwidth / 1000.0, 0, 'f', 1));
ui->afBW->setValue(m_settings.m_afBandwidth / 1000.0); ui->afBW->setValue(m_settings.m_afBandwidth / 100.0);
ui->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 1000.0, 0, 'f', 1)); ui->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 2000.0, 0, 'f', 1));
ui->fmDev->setValue(m_settings.m_fmDeviation / 100.0); ui->fmDev->setValue(m_settings.m_fmDeviation / 200.0);
ui->volumeText->setText(QString("%1").arg(m_settings.m_volumeFactor, 0, 'f', 1)); ui->volumeText->setText(QString("%1").arg(m_settings.m_volumeFactor, 0, 'f', 1));
ui->volume->setValue(m_settings.m_volumeFactor * 10.0); ui->volume->setValue(m_settings.m_volumeFactor * 10.0);

View File

@ -89,7 +89,8 @@ private slots:
void handleSourceMessages(); void handleSourceMessages();
void on_deltaFrequency_changed(qint64 value); void on_deltaFrequency_changed(qint64 value);
void on_rfBW_currentIndexChanged(int index); void on_channelSpacing_currentIndexChanged(int index);
void on_rfBW_valueChanged(int value);
void on_afBW_valueChanged(int value); void on_afBW_valueChanged(int value);
void on_fmDev_valueChanged(int value); void on_fmDev_valueChanged(int value);
void on_toneFrequency_valueChanged(int value); void on_toneFrequency_valueChanged(int value);

View File

@ -186,35 +186,22 @@
<item> <item>
<layout class="QHBoxLayout" name="rfBandwidthLayout"> <layout class="QHBoxLayout" name="rfBandwidthLayout">
<item> <item>
<widget class="QLabel" name="rfBwLabel"> <widget class="QLabel" name="channelSpacingLabel">
<property name="text"> <property name="text">
<string>RFBW</string> <string>CS</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QComboBox" name="rfBW"> <widget class="QComboBox" name="channelSpacing">
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>60</width> <width>60</width>
<height>16777215</height> <height>16777215</height>
</size> </size>
</property> </property>
</widget> <property name="toolTip">
</item> <string>Apply channel spacing settings</string>
<item>
<widget class="QLabel" name="rfBWUnit">
<property name="minimumSize">
<size>
<width>10</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>k</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -235,19 +222,19 @@
<item> <item>
<widget class="QSlider" name="afBW"> <widget class="QSlider" name="afBW">
<property name="toolTip"> <property name="toolTip">
<string>Audio bandwidth</string> <string>Audio bandwidth (kHz)</string>
</property> </property>
<property name="minimum"> <property name="minimum">
<number>1</number> <number>1</number>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>20</number> <number>200</number>
</property> </property>
<property name="pageStep"> <property name="pageStep">
<number>1</number> <number>1</number>
</property> </property>
<property name="value"> <property name="value">
<number>3</number> <number>30</number>
</property> </property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@ -258,12 +245,12 @@
<widget class="QLabel" name="afBWText"> <widget class="QLabel" name="afBWText">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>25</width> <width>35</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="text"> <property name="text">
<string>3k</string> <string>20.0k</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
@ -274,6 +261,51 @@
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="fmDeviationLayout"> <layout class="QHBoxLayout" name="fmDeviationLayout">
<item>
<widget class="QLabel" name="rfBWLabel">
<property name="text">
<string>RFBW</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="rfBW">
<property name="toolTip">
<string>RF bandwidth (kHz)</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>480</number>
</property>
<property name="pageStep">
<number>1</number>
</property>
<property name="value">
<number>160</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="rfBWText">
<property name="minimumSize">
<size>
<width>35</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>25.0k</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item> <item>
<widget class="QLabel" name="fmDevLabel"> <widget class="QLabel" name="fmDevLabel">
<property name="text"> <property name="text">
@ -296,10 +328,10 @@
</size> </size>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Modulation percentage</string> <string>FM peak deviation (kHz)</string>
</property> </property>
<property name="maximum"> <property name="maximum">
<number>300</number> <number>200</number>
</property> </property>
<property name="pageStep"> <property name="pageStep">
<number>1</number> <number>1</number>
@ -316,7 +348,7 @@
<widget class="QLabel" name="fmDevText"> <widget class="QLabel" name="fmDevText">
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>30</width> <width>35</width>
<height>0</height> <height>0</height>
</size> </size>
</property> </property>

View File

@ -24,10 +24,23 @@
#include "settings/serializable.h" #include "settings/serializable.h"
#include "nfmmodsettings.h" #include "nfmmodsettings.h"
const int NFMModSettings::m_rfBW[] = { // Standard channel spacings (kHz) using Carson rule
3000, 4000, 5000, 6250, 8330, 10000, 12500, 15000, 20000, 25000, 40000 // beta based ............ 11F3 16F3 (36F9)
// 5 6.25 7.5 8.33 12.5 25 40 Spacing
// 0.43 0.43 0.43 0.43 0.83 1.67 1.0 Beta
const int NFMModSettings::m_channelSpacings[] = {
5000, 6250, 7500, 8333, 12500, 25000, 40000
}; };
const int NFMModSettings::m_nbRfBW = 11; const int NFMModSettings::m_rfBW[] = { // RF bandwidth (Hz)
4800, 6000, 7200, 8000, 11000, 16000, 36000
};
const int NFMModSettings::m_afBW[] = { // audio bandwidth (Hz)
1700, 2100, 2500, 2800, 3000, 3000, 9000
};
const int NFMModSettings::m_fmDev[] = { // peak deviation (Hz) - full is double
731, 903, 1075, 1204, 2500, 5000, 9000
};
const int NFMModSettings::m_nbChannelSpacings = 7;
NFMModSettings::NFMModSettings() : NFMModSettings::NFMModSettings() :
@ -41,8 +54,8 @@ void NFMModSettings::resetToDefaults()
{ {
m_afBandwidth = 3000; m_afBandwidth = 3000;
m_inputFrequencyOffset = 0; m_inputFrequencyOffset = 0;
m_rfBandwidth = 12500.0f; m_rfBandwidth = 16000.0f;
m_fmDeviation = 3000.0f; m_fmDeviation = 10000.0f; //!< full deviation
m_toneFrequency = 1000.0f; m_toneFrequency = 1000.0f;
m_volumeFactor = 1.0f; m_volumeFactor = 1.0f;
m_channelMute = false; m_channelMute = false;
@ -124,7 +137,7 @@ bool NFMModSettings::deserialize(const QByteArray& data)
m_inputFrequencyOffset = tmp; m_inputFrequencyOffset = tmp;
d.readReal(2, &m_rfBandwidth, 12500.0); d.readReal(2, &m_rfBandwidth, 12500.0);
d.readReal(3, &m_afBandwidth, 1000.0); d.readReal(3, &m_afBandwidth, 1000.0);
d.readReal(4, &m_fmDeviation, 3000.0); d.readReal(4, &m_fmDeviation, 10000.0);
d.readU32(5, &m_rgbColor); d.readU32(5, &m_rgbColor);
d.readReal(6, &m_toneFrequency, 1000.0); d.readReal(6, &m_toneFrequency, 1000.0);
d.readReal(7, &m_volumeFactor, 1.0); d.readReal(7, &m_volumeFactor, 1.0);
@ -184,28 +197,96 @@ bool NFMModSettings::deserialize(const QByteArray& data)
} }
} }
int NFMModSettings::getChannelSpacing(int index)
{
if (index < 0) {
return m_channelSpacings[0];
} else if (index < m_nbChannelSpacings) {
return m_channelSpacings[index];
} else {
return m_channelSpacings[m_nbChannelSpacings-1];
}
}
int NFMModSettings::getChannelSpacingIndex(int channelSpacing)
{
for (int i = 0; i < m_nbChannelSpacings; i++)
{
if (channelSpacing <= m_channelSpacings[i]) {
return i;
}
}
return m_nbChannelSpacings-1;
}
int NFMModSettings::getRFBW(int index) int NFMModSettings::getRFBW(int index)
{ {
if (index < 0) { if (index < 0) {
return m_rfBW[0]; return m_rfBW[0];
} else if (index < m_nbRfBW) { } else if (index < m_nbChannelSpacings) {
return m_rfBW[index]; return m_rfBW[index];
} else { } else {
return m_rfBW[m_nbRfBW-1]; return m_rfBW[m_nbChannelSpacings-1];
} }
} }
int NFMModSettings::getRFBWIndex(int rfbw) int NFMModSettings::getRFBWIndex(int rfbw)
{ {
for (int i = 0; i < m_nbRfBW; i++) for (int i = 0; i < m_nbChannelSpacings; i++)
{ {
if (rfbw <= m_rfBW[i]) if (rfbw <= m_rfBW[i]) {
{
return i; return i;
} }
} }
return m_nbRfBW-1; return m_nbChannelSpacings-1;
}
int NFMModSettings::getAFBW(int index)
{
if (index < 0) {
return m_afBW[0];
} else if (index < m_nbChannelSpacings) {
return m_afBW[index];
} else {
return m_afBW[m_nbChannelSpacings-1];
}
}
int NFMModSettings::getAFBWIndex(int afbw)
{
for (int i = 0; i < m_nbChannelSpacings; i++)
{
if (afbw <= m_afBW[i]) {
return i;
}
}
return m_nbChannelSpacings-1;
}
int NFMModSettings::getFMDev(int index)
{
if (index < 0) {
return m_fmDev[0];
} else if (index < m_nbChannelSpacings) {
return m_fmDev[index];
} else {
return m_fmDev[m_nbChannelSpacings-1];
}
}
int NFMModSettings::getFMDevIndex(int fmDev)
{
for (int i = 0; i < m_nbChannelSpacings; i++)
{
if (fmDev <= m_fmDev[i]) {
return i;
}
}
return m_nbChannelSpacings-1;
} }
int NFMModSettings::getNbCTCSSFreq() int NFMModSettings::getNbCTCSSFreq()

View File

@ -35,8 +35,11 @@ struct NFMModSettings
NFMModInputCWTone NFMModInputCWTone
} NFMModInputAF; } NFMModInputAF;
static const int m_nbRfBW; static const int m_nbChannelSpacings;
static const int m_channelSpacings[];
static const int m_rfBW[]; static const int m_rfBW[];
static const int m_afBW[];
static const int m_fmDev[];
qint64 m_inputFrequencyOffset; qint64 m_inputFrequencyOffset;
Real m_rfBandwidth; Real m_rfBandwidth;
@ -76,8 +79,14 @@ struct NFMModSettings
const CWKeyerSettings& getCWKeyerSettings() const { return m_cwKeyerSettings; } const CWKeyerSettings& getCWKeyerSettings() const { return m_cwKeyerSettings; }
void setCWKeyerSettings(const CWKeyerSettings& cwKeyerSettings) { m_cwKeyerSettings = cwKeyerSettings; } void setCWKeyerSettings(const CWKeyerSettings& cwKeyerSettings) { m_cwKeyerSettings = cwKeyerSettings; }
static int getChannelSpacing(int index);
static int getChannelSpacingIndex(int channelSpacing);
static int getRFBW(int index); static int getRFBW(int index);
static int getRFBWIndex(int rfbw); static int getRFBWIndex(int rfbw);
static int getAFBW(int index);
static int getAFBWIndex(int afbw);
static int getFMDev(int index);
static int getFMDevIndex(int fmDev);
static int getNbCTCSSFreq(); static int getNbCTCSSFreq();
static float getCTCSSFreq(int index); static float getCTCSSFreq(int index);
static int getCTCSSFreqIndex(float ctcssFreq); static int getCTCSSFreqIndex(float ctcssFreq);