diff --git a/doc/img/PacketMod_plugin.png b/doc/img/PacketMod_plugin.png index 72899037b..db6e2f4dd 100644 Binary files a/doc/img/PacketMod_plugin.png and b/doc/img/PacketMod_plugin.png differ diff --git a/plugins/channeltx/modpacket/CMakeLists.txt b/plugins/channeltx/modpacket/CMakeLists.txt index 69a4297c4..27e5a3182 100644 --- a/plugins/channeltx/modpacket/CMakeLists.txt +++ b/plugins/channeltx/modpacket/CMakeLists.txt @@ -27,6 +27,8 @@ if(NOT SERVER_MODE) ${modpacket_SOURCES} packetmodgui.cpp packetmodgui.ui + packetmodbpfdialog.cpp + packetmodbpfdialog.ui packetmodrepeatdialog.cpp packetmodrepeatdialog.ui packetmodtxsettingsdialog.cpp @@ -35,6 +37,7 @@ if(NOT SERVER_MODE) set(modpacket_HEADERS ${modpacket_HEADERS} packetmodgui.h + packetmodbpfdialog.h packetmodrepeatdialog.h packetmodtxsettingsdialog.h ) diff --git a/plugins/channeltx/modpacket/packetmod.cpp b/plugins/channeltx/modpacket/packetmod.cpp index 30d59284c..a3e1b88b7 100644 --- a/plugins/channeltx/modpacket/packetmod.cpp +++ b/plugins/channeltx/modpacket/packetmod.cpp @@ -154,6 +154,9 @@ void PacketMod::applySettings(const PacketModSettings& settings, bool force) << " m_preEmphasis: " << settings.m_preEmphasis << " m_preEmphasisTau: " << settings.m_preEmphasisTau << " m_preEmphasisHighFreq: " << settings.m_preEmphasisHighFreq + << " m_bpf: " << settings.m_bpf + << " m_bpfLowCutoff: " << settings.m_bpfLowCutoff + << " m_bpfHighCutoff: " << settings.m_bpfHighCutoff << " m_useReverseAPI: " << settings.m_useReverseAPI << " m_reverseAPIAddress: " << settings.m_reverseAPIAddress << " m_reverseAPIAddress: " << settings.m_reverseAPIPort @@ -215,6 +218,18 @@ void PacketMod::applySettings(const PacketModSettings& settings, bool force) reverseAPIKeys.append("preEmphasisHighFreq"); } + if((settings.m_bpf != m_settings.m_bpf) || force) { + reverseAPIKeys.append("bpf"); + } + + if((settings.m_bpfLowCutoff != m_settings.m_bpfLowCutoff) || force) { + reverseAPIKeys.append("bpfLowCutoff"); + } + + if((settings.m_bpfHighCutoff != m_settings.m_bpfHighCutoff) || force) { + reverseAPIKeys.append("bpfHighCutoff"); + } + if (m_settings.m_streamIndex != settings.m_streamIndex) { if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only @@ -309,6 +324,9 @@ void PacketMod::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("inputFrequencyOffset")) { settings.m_inputFrequencyOffset = response.getPacketModSettings()->getInputFrequencyOffset(); } + if (channelSettingsKeys.contains("mode")) { + settings.setMode(*response.getPacketModSettings()->getMode()); + } if (channelSettingsKeys.contains("rfBandwidth")) { settings.m_rfBandwidth = response.getPacketModSettings()->getRfBandwidth(); } @@ -345,6 +363,15 @@ void PacketMod::webapiUpdateChannelSettings( if (channelSettingsKeys.contains("preEmphasisHighFreq")) { settings.m_preEmphasisHighFreq = response.getPacketModSettings()->getPreEmphasisHighFreq(); } + if (channelSettingsKeys.contains("bpf")) { + settings.m_bpf = response.getPacketModSettings()->getBpf() != 0; + } + if (channelSettingsKeys.contains("bpfLowCutoff")) { + settings.m_bpfLowCutoff = response.getPacketModSettings()->getBpfLowCutoff(); + } + if (channelSettingsKeys.contains("bpfHighCutoff")) { + settings.m_bpfHighCutoff = response.getPacketModSettings()->getBpfHighCutoff(); + } if (channelSettingsKeys.contains("rgbColor")) { settings.m_rgbColor = response.getPacketModSettings()->getRgbColor(); } @@ -415,6 +442,7 @@ int PacketMod::webapiActionsPost( void PacketMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const PacketModSettings& settings) { response.getPacketModSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + response.getPacketModSettings()->setMode(new QString(settings.getMode())); response.getPacketModSettings()->setRfBandwidth(settings.m_rfBandwidth); response.getPacketModSettings()->setFmDeviation(settings.m_fmDeviation); response.getPacketModSettings()->setGain(settings.m_gain); @@ -427,6 +455,9 @@ void PacketMod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& res response.getPacketModSettings()->setPreEmphasis(settings.m_preEmphasis ? 1 : 0); response.getPacketModSettings()->setPreEmphasisTau(settings.m_preEmphasisTau); response.getPacketModSettings()->setPreEmphasisHighFreq(settings.m_preEmphasisHighFreq); + response.getPacketModSettings()->setBpf(settings.m_bpf ? 1 : 0); + response.getPacketModSettings()->setBpfLowCutoff(settings.m_bpfLowCutoff); + response.getPacketModSettings()->setBpfHighCutoff(settings.m_bpfHighCutoff); response.getPacketModSettings()->setRgbColor(settings.m_rgbColor); if (response.getPacketModSettings()->getTitle()) { @@ -505,6 +536,15 @@ void PacketMod::webapiReverseSendSettings(QList& channelSettingsKeys, c if (channelSettingsKeys.contains("preEmphasisHighFreq") || force) { swgPacketModSettings->setPreEmphasisHighFreq(settings.m_preEmphasisHighFreq); } + if (channelSettingsKeys.contains("bpf") || force) { + swgPacketModSettings->setBpf(settings.m_preEmphasis); + } + if (channelSettingsKeys.contains("bpfLowCutoff") || force) { + swgPacketModSettings->setBpfLowCutoff(settings.m_bpfLowCutoff); + } + if (channelSettingsKeys.contains("bpfHighCutoff") || force) { + swgPacketModSettings->setBpfHighCutoff(settings.m_bpfHighCutoff); + } if (channelSettingsKeys.contains("rgbColor") || force) { swgPacketModSettings->setRgbColor(settings.m_rgbColor); } diff --git a/plugins/channeltx/modpacket/packetmodbpfdialog.cpp b/plugins/channeltx/modpacket/packetmodbpfdialog.cpp new file mode 100644 index 000000000..c318d0658 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodbpfdialog.cpp @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#define _USE_MATH_DEFINES +#include +#include "packetmodbpfdialog.h" +#include "ui_packetmodbpfdialog.h" + +PacketModBPFDialog::PacketModBPFDialog(float lowFreq, float highFreq, int taps, QWidget* parent) : + QDialog(parent), + ui(new Ui::PacketModBPFDialog) +{ + ui->setupUi(this); + ui->lowFreq->setValue(lowFreq); + ui->highFreq->setValue(highFreq); + ui->taps->setValue(taps); +} + +PacketModBPFDialog::~PacketModBPFDialog() +{ + delete ui; +} + +void PacketModBPFDialog::accept() +{ + m_lowFreq = ui->lowFreq->value(); + m_highFreq = ui->highFreq->value(); + m_taps = ui->taps->value(); + + QDialog::accept(); +} diff --git a/plugins/channeltx/modpacket/packetmodbpfdialog.h b/plugins/channeltx/modpacket/packetmodbpfdialog.h new file mode 100644 index 000000000..7746228b3 --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodbpfdialog.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_PACKETMODBPFDIALOG_H +#define INCLUDE_PACKETMODBPFDIALOG_H + +#include + +namespace Ui { + class PacketModBPFDialog; +} + +class PacketModBPFDialog : public QDialog { + Q_OBJECT + +public: + explicit PacketModBPFDialog(float lowFreq, float highFreq, int taps, QWidget* parent = 0); + ~PacketModBPFDialog(); + + float m_lowFreq; + float m_highFreq; + int m_taps; + +private slots: + void accept(); + +private: + Ui::PacketModBPFDialog* ui; +}; + +#endif // INCLUDE_PACKETMODBPFDIALOG_H diff --git a/plugins/channeltx/modpacket/packetmodbpfdialog.ui b/plugins/channeltx/modpacket/packetmodbpfdialog.ui new file mode 100644 index 000000000..9c864668d --- /dev/null +++ b/plugins/channeltx/modpacket/packetmodbpfdialog.ui @@ -0,0 +1,146 @@ + + + PacketModBPFDialog + + + + 0 + 0 + 351 + 152 + + + + + Liberation Sans + 9 + + + + Bandpass Filter Settings + + + + + + + + + Low frequency corner (Hz) + + + + + + + Low frequency corner. + + + 10000.000000000000000 + + + 1000.000000000000000 + + + + + + + High frequency corner (Hz) + + + + + + + High frequency corner. + + + 1000000.000000000000000 + + + 1000.000000000000000 + + + + + + + Number of taps in the filter. More taps gives sharper roll off. Must be odd. + + + 5 + + + 100001 + + + 301 + + + + + + + + + + Filter taps + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + lowFreq + highFreq + + + + + buttonBox + accepted() + PacketModBPFDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PacketModBPFDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/plugins/channeltx/modpacket/packetmodgui.cpp b/plugins/channeltx/modpacket/packetmodgui.cpp index ed7edce42..2ed69fb3e 100644 --- a/plugins/channeltx/modpacket/packetmodgui.cpp +++ b/plugins/channeltx/modpacket/packetmodgui.cpp @@ -39,6 +39,7 @@ #include "packetmodgui.h" #include "packetmodrepeatdialog.h" #include "packetmodtxsettingsdialog.h" +#include "packetmodbpfdialog.h" PacketModGUI* PacketModGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSource *channelTx) @@ -140,6 +141,28 @@ void PacketModGUI::on_deltaFrequency_changed(qint64 value) applySettings(); } +void PacketModGUI::on_mode_currentIndexChanged(int value) +{ + QString mode = ui->mode->currentText(); + + // If m_doApplySettings is set, we are here from a call to displaySettings, + // so we only want to display the current settings, not update them + // as though a user had selected a new mode + if (m_doApplySettings) + m_settings.setMode(mode); + + ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1)); + ui->fmDevText->setText(QString("%1k").arg(m_settings.m_fmDeviation / 1000.0, 0, 'f', 1)); + ui->fmDev->setValue(m_settings.m_fmDeviation / 100.0); + ui->glSpectrum->setCenterFrequency(m_settings.m_spectrumRate/4); + ui->glSpectrum->setSampleRate(m_settings.m_spectrumRate/2); + applySettings(); + + // Remove custom mode when deselected, as we no longer know how to set it + if (value < 2) + ui->mode->removeItem(2); +} + void PacketModGUI::on_rfBW_valueChanged(int value) { float bw = value * 100.0f; @@ -254,6 +277,12 @@ void PacketModGUI::on_preEmphasis_toggled(bool checked) applySettings(); } +void PacketModGUI::on_bpf_toggled(bool checked) +{ + m_settings.m_bpf = checked; + applySettings(); +} + void PacketModGUI::preEmphasisSelect() { FMPreemphasisDialog dialog(m_settings.m_preEmphasisTau, m_settings.m_preEmphasisHighFreq); @@ -265,6 +294,18 @@ void PacketModGUI::preEmphasisSelect() } } +void PacketModGUI::bpfSelect() +{ + PacketModBPFDialog dialog(m_settings.m_bpfLowCutoff, m_settings.m_bpfHighCutoff, m_settings.m_bpfTaps); + if (dialog.exec() == QDialog::Accepted) + { + m_settings.m_bpfLowCutoff = dialog.m_lowFreq; + m_settings.m_bpfHighCutoff = dialog.m_highFreq; + m_settings.m_bpfTaps = dialog.m_taps; + applySettings(); + } +} + void PacketModGUI::repeatSelect() { PacketModRepeatDialog dialog(m_settings.m_repeatDelay, m_settings.m_repeatCount); @@ -280,7 +321,10 @@ void PacketModGUI::txSettingsSelect() { PacketModTXSettingsDialog dialog(m_settings.m_rampUpBits, m_settings.m_rampDownBits, m_settings.m_rampRange, m_settings.m_modulateWhileRamping, + m_settings.m_modulation, m_settings.m_baud, m_settings.m_markFrequency, m_settings.m_spaceFrequency, + m_settings.m_pulseShaping, m_settings.m_beta, m_settings.m_symbolSpan, + m_settings.m_scramble, m_settings.m_polynomial, m_settings.m_ax25PreFlags, m_settings.m_ax25PostFlags, m_settings.m_ax25Control, m_settings.m_ax25PID, m_settings.m_lpfTaps, @@ -292,8 +336,15 @@ void PacketModGUI::txSettingsSelect() m_settings.m_rampDownBits = dialog.m_rampDownBits; m_settings.m_rampRange = dialog.m_rampRange; m_settings.m_modulateWhileRamping = dialog.m_modulateWhileRamping; + m_settings.m_modulation = static_cast(dialog.m_modulation); + m_settings.m_baud = dialog.m_baud; m_settings.m_markFrequency = dialog.m_markFrequency; m_settings.m_spaceFrequency = dialog.m_spaceFrequency; + m_settings.m_pulseShaping = dialog.m_pulseShaping; + m_settings.m_beta = dialog.m_beta; + m_settings.m_symbolSpan = dialog.m_symbolSpan; + m_settings.m_scramble = dialog.m_scramble; + m_settings.m_polynomial = dialog.m_polynomial; m_settings.m_ax25PreFlags = dialog.m_ax25PreFlags; m_settings.m_ax25PostFlags = dialog.m_ax25PostFlags; m_settings.m_ax25Control = dialog.m_ax25Control; @@ -302,6 +353,7 @@ void PacketModGUI::txSettingsSelect() m_settings.m_bbNoise = dialog.m_bbNoise; m_settings.m_rfNoise = dialog.m_rfNoise; m_settings.m_writeToFile = dialog.m_writeToFile; + displaySettings(); applySettings(); } } @@ -399,6 +451,9 @@ PacketModGUI::PacketModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb CRightClickEnabler *preempRightClickEnabler = new CRightClickEnabler(ui->preEmphasis); connect(preempRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(preEmphasisSelect())); + CRightClickEnabler *bpfRightClickEnabler = new CRightClickEnabler(ui->bpf); + connect(bpfRightClickEnabler, SIGNAL(rightClick(const QPoint &)), this, SLOT(bpfSelect())); + ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999); @@ -479,6 +534,18 @@ void PacketModGUI::displaySettings() blockApplySettings(true); ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); + if ((m_settings.m_baud == 1200) && (m_settings.m_modulation == PacketModSettings::AFSK)) + ui->mode->setCurrentIndex(0); + else if ((m_settings.m_baud == 9600) && (m_settings.m_modulation == PacketModSettings::FSK)) + ui->mode->setCurrentIndex(1); + else + { + ui->mode->removeItem(2); + ui->mode->addItem(m_settings.getMode()); + ui->mode->setCurrentIndex(2); + } + ui->glSpectrum->setCenterFrequency(m_settings.m_spectrumRate/4); + ui->glSpectrum->setSampleRate(m_settings.m_spectrumRate/2); ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1)); ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0); diff --git a/plugins/channeltx/modpacket/packetmodgui.h b/plugins/channeltx/modpacket/packetmodgui.h index 0c0cd4813..bab47c8a8 100644 --- a/plugins/channeltx/modpacket/packetmodgui.h +++ b/plugins/channeltx/modpacket/packetmodgui.h @@ -88,6 +88,7 @@ private slots: void handleSourceMessages(); void on_deltaFrequency_changed(qint64 value); + void on_mode_currentIndexChanged(int value); void on_rfBW_valueChanged(int index); void on_fmDev_valueChanged(int value); void on_gain_valueChanged(int value); @@ -101,7 +102,9 @@ private slots: void on_packet_returnPressed(); void on_repeat_toggled(bool checked); void on_preEmphasis_toggled(bool checked); + void on_bpf_toggled(bool checked); void preEmphasisSelect(); + void bpfSelect(); void repeatSelect(); void txSettingsSelect(); diff --git a/plugins/channeltx/modpacket/packetmodgui.ui b/plugins/channeltx/modpacket/packetmodgui.ui index 6ae31b79b..8506f45e0 100644 --- a/plugins/channeltx/modpacket/packetmodgui.ui +++ b/plugins/channeltx/modpacket/packetmodgui.ui @@ -210,6 +210,11 @@ 1200 AFSK + + + 9600 FSK + + @@ -472,6 +477,20 @@ + + + + Baseband bandpass filter. Right click for additional settings. + + + ... + + + + :/filter_bandpass.png:/filter_bandpass.png + + + diff --git a/plugins/channeltx/modpacket/packetmodsettings.cpp b/plugins/channeltx/modpacket/packetmodsettings.cpp index 68f0ae8dc..8f0f00faa 100644 --- a/plugins/channeltx/modpacket/packetmodsettings.cpp +++ b/plugins/channeltx/modpacket/packetmodsettings.cpp @@ -33,7 +33,7 @@ void PacketModSettings::resetToDefaults() { m_inputFrequencyOffset = 0; m_baud = 1200; - m_rfBandwidth = 10000.0f; + m_rfBandwidth = 12500.0f; m_fmDeviation = 2500.0f; m_gain = -2.0f; // To avoid overflow, which results in out-of-band RF m_channelMute = false; @@ -47,7 +47,7 @@ void PacketModSettings::resetToDefaults() m_markFrequency = 2200; m_spaceFrequency = 1200; m_ax25PreFlags = 5; - m_ax25PostFlags = 2; + m_ax25PostFlags = 4; // Extra seemingly needed for 9600. m_ax25Control = 3; m_ax25PID = 0xf0; m_preEmphasis = false; @@ -70,6 +70,56 @@ void PacketModSettings::resetToDefaults() m_reverseAPIPort = 8888; m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; + m_bpf = false; + m_bpfLowCutoff = m_spaceFrequency - 400.0f; + m_bpfHighCutoff = m_markFrequency + 400.0f; + m_bpfTaps = 301; + m_scramble = false; + m_polynomial = 0x10800; + m_pulseShaping = true; + m_beta = 0.5f; + m_symbolSpan = 6; +} + +bool PacketModSettings::setMode(QString mode) +{ + int baud; + bool valid; + + // First part of mode string should give baud rate + baud = mode.split(" ")[0].toInt(&valid); + if (!valid) + return false; + + if (mode.endsWith("AFSK")) + { + // UK channels - https://rsgb.org/main/blog/news/gb2rs/headlines/2015/12/04/check-your-aprs-deviation/ + m_baud = baud; + m_scramble = false; + m_rfBandwidth = 12500.0f; + m_fmDeviation = 2500.0f; + m_spectrumRate = 8000; + m_modulation = PacketModSettings::AFSK; + } + else if (mode.endsWith("FSK")) + { + // G3RUH - http://www.jrmiller.demon.co.uk/products/figs/man9k6.pdf + m_baud = baud; + m_scramble = true; + m_rfBandwidth = 20000.0f; + m_fmDeviation = 3000.0f; + m_spectrumRate = 24000; + m_bpf = false; + m_modulation = PacketModSettings::FSK; + } + else + return false; + return true; +} + +QString PacketModSettings::getMode() const +{ + return QString("%1 %2").arg(m_baud).arg(m_modulation == PacketModSettings::AFSK ? "AFSK" : "FSK"); } QByteArray PacketModSettings::serialize() const @@ -119,6 +169,18 @@ QByteArray PacketModSettings::serialize() const s.writeU32(38, m_reverseAPIDeviceIndex); s.writeU32(39, m_reverseAPIChannelIndex); + s.writeBool(40, m_bpf); + s.writeReal(41, m_bpfLowCutoff); + s.writeReal(42, m_bpfHighCutoff); + s.writeS32(43, m_bpfTaps); + s.writeBool(44, m_scramble); + s.writeS32(45, m_polynomial); + s.writeBool(46, m_pulseShaping); + s.writeReal(47, m_beta); + s.writeS32(48, m_symbolSpan); + s.writeS32(49, m_spectrumRate); + s.writeS32(50, m_modulation); + return s.final(); } @@ -155,7 +217,7 @@ bool PacketModSettings::deserialize(const QByteArray& data) d.readS32(14, &m_markFrequency, 5); d.readS32(15, &m_spaceFrequency, 5); d.readS32(16, &m_ax25PreFlags, 5); - d.readS32(17, &m_ax25PostFlags, 2); + d.readS32(17, &m_ax25PostFlags, 4); d.readS32(18, &m_ax25Control, 3); d.readS32(19, &m_ax25PID, 0xf0); d.readBool(20, &m_preEmphasis, false); @@ -193,6 +255,18 @@ bool PacketModSettings::deserialize(const QByteArray& data) d.readU32(39, &utmp, 0); m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + d.readBool(40, &m_bpf, false); + d.readReal(41, &m_bpfLowCutoff, 1200.0 - 400.0f); + d.readReal(42, &m_bpfHighCutoff, 2200.0 + 400.0f); + d.readS32(43, &m_bpfTaps, 301); + d.readBool(44, &m_scramble, m_baud == 9600); + d.readS32(45, &m_polynomial, 0x10800); + d.readBool(46, &m_pulseShaping, true); + d.readReal(47, &m_beta, 0.5f); + d.readS32(48, &m_symbolSpan, 6); + d.readS32(49, &m_spectrumRate, m_baud == 1200 ? 8000 : 24000); + d.readS32(50, (qint32 *)&m_modulation, m_baud == 1200 ? PacketModSettings::AFSK : PacketModSettings::FSK); + return true; } else diff --git a/plugins/channeltx/modpacket/packetmodsettings.h b/plugins/channeltx/modpacket/packetmodsettings.h index 2514bd2c0..a7db6ee62 100644 --- a/plugins/channeltx/modpacket/packetmodsettings.h +++ b/plugins/channeltx/modpacket/packetmodsettings.h @@ -30,6 +30,7 @@ struct PacketModSettings static const int infinitePackets = -1; qint64 m_inputFrequencyOffset; + enum Modulation {AFSK, FSK} m_modulation; int m_baud; Real m_rfBandwidth; Real m_fmDeviation; @@ -49,8 +50,8 @@ struct PacketModSettings int m_ax25Control; int m_ax25PID; bool m_preEmphasis; - float m_preEmphasisTau; - float m_preEmphasisHighFreq; + Real m_preEmphasisTau; + Real m_preEmphasisHighFreq; int m_lpfTaps; bool m_bbNoise; bool m_rfNoise; @@ -69,12 +70,23 @@ struct PacketModSettings uint16_t m_reverseAPIPort; uint16_t m_reverseAPIDeviceIndex; uint16_t m_reverseAPIChannelIndex; + bool m_bpf; + Real m_bpfLowCutoff; + Real m_bpfHighCutoff; + int m_bpfTaps; + bool m_scramble; + int m_polynomial; + bool m_pulseShaping; + float m_beta; + int m_symbolSpan; PacketModSettings(); void resetToDefaults(); void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } QByteArray serialize() const; bool deserialize(const QByteArray& data); + bool setMode(QString mode); + QString getMode() const; }; #endif /* PLUGINS_CHANNELTX_MODPACKET_PACKETMODSETTINGS_H */ diff --git a/plugins/channeltx/modpacket/packetmodsource.cpp b/plugins/channeltx/modpacket/packetmodsource.cpp index 2e0d9f43b..d849e4ec0 100644 --- a/plugins/channeltx/modpacket/packetmodsource.cpp +++ b/plugins/channeltx/modpacket/packetmodsource.cpp @@ -25,11 +25,12 @@ PacketModSource::PacketModSource() : m_channelSampleRate(48000), + m_spectrumRate(0), m_preemphasisFilter(48000, FMPREEMPHASIS_TAU_US), m_channelFrequencyOffset(0), m_magsq(0.0), m_audioPhase(0.0f), - m_fmPhase(0.0f), + m_fmPhase(0.0), m_levelCalcCount(0), m_peakLevel(0.0f), m_levelSum(0.0f), @@ -37,9 +38,13 @@ PacketModSource::PacketModSource() : m_byteIdx(0), m_bitIdx(0), m_last5Bits(0), - m_state(idle) + m_state(idle), + m_scrambler(0x10800, 0x0) { m_lowpass.create(301, m_channelSampleRate, 22000.0 / 2.0); + qDebug() << "PacketModSource::PacketModSource creating BPF : " << m_channelSampleRate; + m_bandpass.create(301, m_channelSampleRate, 800.0, 2600.0); + m_pulseShape.create(0.5, 6, m_channelSampleRate/9600); applySettings(m_settings, true); applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); } @@ -111,9 +116,6 @@ void PacketModSource::sampleToSpectrum(Real sample) void PacketModSource::modulateSample() { Real audioMod; - Real theta; - Real f_delta; - Real linearGain; Real linearRampGain; Real emphasis; @@ -133,14 +135,19 @@ void PacketModSource::modulateSample() } else { - // Bell 202 AFSK + if (m_sampleIdx == 0) { if (bitsValid()) { // NRZI encoding - encode 0 as change of freq, 1 no change if (getBit() == 0) - m_f = m_f == m_settings.m_markFrequency ? m_settings.m_spaceFrequency : m_settings.m_markFrequency; + m_nrziBit = m_nrziBit == 1 ? 0 : 1; + // Scramble to ensure lots of transitions + if (m_settings.m_scramble) + m_scrambledBit = m_scrambler.scramble(m_nrziBit); + else + m_scrambledBit = m_nrziBit; } // Should we start ramping down power? if ((m_bitCount < m_settings.m_rampDownBits) || ((m_bitCount == 0) && !m_settings.m_rampDownBits)) @@ -151,17 +158,40 @@ void PacketModSource::modulateSample() } } m_sampleIdx++; - if (m_sampleIdx > m_samplesPerSymbol) + if (m_sampleIdx >= m_samplesPerSymbol) m_sampleIdx = 0; if (!m_settings.m_bbNoise) - audioMod = sin(m_audioPhase); + { + if (m_settings.m_modulation == PacketModSettings::AFSK) + { + // Bell 202 AFSK + audioMod = sin(m_audioPhase); + if ((m_state == tx) || m_settings.m_modulateWhileRamping) + m_audioPhase += (M_PI * 2.0f * (m_scrambledBit ? m_settings.m_markFrequency : m_settings.m_spaceFrequency)) / (m_channelSampleRate); + if (m_audioPhase > M_PI) + m_audioPhase -= (2.0f * M_PI); + } + else + { + // FSK + if (m_settings.m_pulseShaping) + { + if ((m_sampleIdx == 1) && (m_state != ramp_down)) + audioMod = m_pulseShape.filter(m_scrambledBit ? 1.0f : -1.0f); + else + audioMod = m_pulseShape.filter(0.0f); + } + else + audioMod = m_scrambledBit ? 1.0f : -1.0f; + } + } else audioMod = (Real)rand()/((Real)RAND_MAX)-0.5; // Noise to test filter frequency response - if ((m_state == tx) || m_settings.m_modulateWhileRamping) - m_audioPhase += (M_PI * 2.0f * m_f) / (m_channelSampleRate); - if (m_audioPhase > M_PI) - m_audioPhase -= (2.0f * M_PI); + + // Baseband bandpass filter + if (m_settings.m_bpf) + audioMod = m_bandpass.filter(audioMod); // Preemphasis filter if (m_settings.m_preEmphasis) @@ -174,25 +204,25 @@ void PacketModSource::modulateSample() sampleToSpectrum(audioMod); // FM - m_fmPhase += audioMod; + m_fmPhase += m_phaseSensitivity * audioMod; + // Keep phase in range -pi,pi if (m_fmPhase > M_PI) - m_fmPhase -= (2.0f * M_PI); - f_delta = m_settings.m_fmDeviation / m_channelSampleRate; - theta = 2.0f * M_PI * f_delta * m_fmPhase; + m_fmPhase -= 2.0f * M_PI; + else if (m_fmPhase < -M_PI) + m_fmPhase += 2.0f * M_PI; linearRampGain = powf(10.0f, m_pow/20.0f); - linearGain = powf(10.0f, m_settings.m_gain/20.0f); if (!m_settings.m_rfNoise) { - m_modSample.real(linearGain * linearRampGain * cos(theta)); - m_modSample.imag(linearGain * linearRampGain * sin(theta)); + m_modSample.real(m_linearGain * linearRampGain * cos(m_fmPhase)); + m_modSample.imag(m_linearGain * linearRampGain * sin(m_fmPhase)); } else { // Noise to test filter frequency response - m_modSample.real(linearGain * ((Real)rand()/((Real)RAND_MAX)-0.5f)); - m_modSample.imag(linearGain * ((Real)rand()/((Real)RAND_MAX)-0.5f)); + m_modSample.real(m_linearGain * ((Real)rand()/((Real)RAND_MAX)-0.5f)); + m_modSample.imag(m_linearGain * ((Real)rand()/((Real)RAND_MAX)-0.5f)); } // Apply low pass filter to limit RF BW @@ -262,6 +292,7 @@ void PacketModSource::calculateLevel(Real& sample) void PacketModSource::applySettings(const PacketModSettings& settings, bool force) { + // Only recreate filters if settings have changed if ((settings.m_lpfTaps != m_settings.m_lpfTaps) || (settings.m_rfBandwidth != m_settings.m_rfBandwidth) || force) { qDebug() << "PacketModSource::applySettings: Creating new lpf with taps " << settings.m_lpfTaps << " rfBW " << settings.m_rfBandwidth; @@ -272,8 +303,40 @@ void PacketModSource::applySettings(const PacketModSettings& settings, bool forc qDebug() << "PacketModSource::applySettings: Creating new preemphasis filter with tau " << settings.m_preEmphasisTau << " highFreq " << settings.m_preEmphasisHighFreq << " sampleRate " << m_channelSampleRate; m_preemphasisFilter.configure(m_channelSampleRate, settings.m_preEmphasisTau, settings.m_preEmphasisHighFreq); } + if ((settings.m_bpfLowCutoff != m_settings.m_bpfLowCutoff) || (settings.m_bpfHighCutoff != m_settings.m_bpfHighCutoff) + || (settings.m_bpfTaps != m_settings.m_bpfTaps)|| force) + { + qDebug() << "PacketModSource::applySettings: Recreating bandpass filter: " + << " m_bpfTaps: " << settings.m_bpfTaps + << " m_channelSampleRate:" << m_channelSampleRate + << " m_bpfLowCutoff: " << settings.m_bpfLowCutoff + << " m_bpfHighCutoff: " << settings.m_bpfHighCutoff; + m_bandpass.create(settings.m_bpfTaps, m_channelSampleRate, settings.m_bpfLowCutoff, settings.m_bpfHighCutoff); + } + if ((settings.m_beta != m_settings.m_beta) || (settings.m_symbolSpan != m_settings.m_symbolSpan) || (settings.m_baud != m_settings.m_baud) || force) + { + qDebug() << "PacketModSource::applySettings: Recreating pulse shaping filter: " + << " beta: " << settings.m_beta + << " symbolSpan: " << settings.m_symbolSpan + << " channelSampleRate:" << m_channelSampleRate + << " baud:" << settings.m_baud; + m_pulseShape.create(settings.m_beta, m_settings.m_symbolSpan, m_channelSampleRate/settings.m_baud); + } + if ((settings.m_polynomial != m_settings.m_polynomial) || force) + m_scrambler.setPolynomial(settings.m_polynomial); + if ((settings.m_spectrumRate != m_settings.m_spectrumRate) || force) + { + m_interpolatorDistanceRemain = 0; + m_interpolatorConsumed = false; + m_interpolatorDistance = (Real) m_channelSampleRate / (Real) settings.m_spectrumRate; + m_interpolator.create(48, settings.m_spectrumRate, settings.m_spectrumRate / 2.2, 3.0); + } m_settings = settings; + + // Precalculate FM sensensity and linear gain to save doing it in the loop + m_phaseSensitivity = 2.0f * M_PI * m_settings.m_fmDeviation / (double)m_channelSampleRate; + m_linearGain = powf(10.0f, m_settings.m_gain/20.0f); } void PacketModSource::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) @@ -292,8 +355,25 @@ void PacketModSource::applyChannelSettings(int channelSampleRate, int channelFre if ((m_channelSampleRate != channelSampleRate) || force) { - m_preemphasisFilter.configure(channelSampleRate, m_settings.m_preEmphasisTau); + qDebug() << "PacketModSource::applyChannelSettings: Recreating filters"; m_lowpass.create(m_settings.m_lpfTaps, channelSampleRate, m_settings.m_rfBandwidth / 2.0); + qDebug() << "PacketModSource::applyChannelSettings: Recreating bandpass filter: " + << " bpfTaps: " << m_settings.m_bpfTaps + << " channelSampleRate:" << channelSampleRate + << " bpfLowCutoff: " << m_settings.m_bpfLowCutoff + << " bpfHighCutoff: " << m_settings.m_bpfHighCutoff; + m_bandpass.create(m_settings.m_bpfTaps, channelSampleRate, m_settings.m_bpfLowCutoff, m_settings.m_bpfHighCutoff); + m_preemphasisFilter.configure(channelSampleRate, m_settings.m_preEmphasisTau); + qDebug() << "PacketModSource::applyChannelSettings: Recreating pulse shaping filter: " + << " beta: " << m_settings.m_beta + << " symbolSpan: " << m_settings.m_symbolSpan + << " channelSampleRate:" << m_channelSampleRate + << " baud:" << m_settings.m_baud; + m_pulseShape.create(m_settings.m_beta, m_settings.m_symbolSpan, channelSampleRate/m_settings.m_baud); + } + + if ((m_channelSampleRate != channelSampleRate) || (m_spectrumRate != m_settings.m_spectrumRate) || force) + { m_interpolatorDistanceRemain = 0; m_interpolatorConsumed = false; m_interpolatorDistance = (Real) channelSampleRate / (Real) m_settings.m_spectrumRate; @@ -302,6 +382,11 @@ void PacketModSource::applyChannelSettings(int channelSampleRate, int channelFre m_channelSampleRate = channelSampleRate; m_channelFrequencyOffset = channelFrequencyOffset; + m_spectrumRate = m_settings.m_spectrumRate; + m_samplesPerSymbol = m_channelSampleRate / m_settings.m_baud; + qDebug() << "m_samplesPerSymbol: " << m_samplesPerSymbol << " (" << m_channelSampleRate << "/" << m_settings.m_baud << ")"; + // Precalculate FM sensensity to save doing it in the loop + m_phaseSensitivity = 2.0f * M_PI * m_settings.m_fmDeviation / (double)m_channelSampleRate; } static uint8_t *ax25_address(uint8_t *p, QString address, uint8_t crrl) @@ -395,7 +480,7 @@ void PacketModSource::initTX() m_byteIdx = 0; m_bitIdx = 0; m_bitCount = m_bitCountTotal; // Reset to allow retransmission - m_f = m_settings.m_spaceFrequency; + m_nrziBit = 0; if (m_settings.m_rampUpBits == 0) { m_state = tx; @@ -407,6 +492,7 @@ void PacketModSource::initTX() m_pow = -(Real)m_settings.m_rampRange; m_powRamp = m_settings.m_rampRange/(m_settings.m_rampUpBits * (Real)m_samplesPerSymbol); } + m_scrambler.init(); } void PacketModSource::addTXPacket(QString callsign, QString to, QString via, QString data) @@ -475,8 +561,10 @@ void PacketModSource::addTXPacket(QString callsign, QString to, QString via, QSt // single tone m_sampleIdx = 0; m_audioPhase = 0.0f; - m_fmPhase = 0.0f; + m_fmPhase = 0.0; if (m_settings.m_writeToFile) m_audioFile.open("packetmod.csv", std::ofstream::out); + else if (m_audioFile.is_open()) + m_audioFile.close(); } diff --git a/plugins/channeltx/modpacket/packetmodsource.h b/plugins/channeltx/modpacket/packetmodsource.h index d48cd36a0..6f23ab803 100644 --- a/plugins/channeltx/modpacket/packetmodsource.h +++ b/plugins/channeltx/modpacket/packetmodsource.h @@ -31,7 +31,10 @@ #include "dsp/interpolator.h" #include "dsp/lowpass.h" #include "dsp/bandpass.h" +#include "dsp/highpass.h" +#include "dsp/raisedcosine.h" #include "dsp/fmpreemphasis.h" +#include "util/lfsr.h" #include "util/movingaverage.h" #include "packetmodsettings.h" @@ -69,13 +72,20 @@ public: private: int m_channelSampleRate; int m_channelFrequencyOffset; + int m_spectrumRate; PacketModSettings m_settings; NCO m_carrierNco; Real m_audioPhase; - Real m_fmPhase; + double m_fmPhase; // Double gives cleaner spectrum than Real + double m_phaseSensitivity; + Real m_linearGain; Complex m_modSample; + int m_nrziBit; // Output of NRZI coder + int m_scrambledBit; // Output from scrambler to be pulse shaped + RaisedCosine m_pulseShape; // Pulse shaping filter + Bandpass m_bandpass; // Baseband bandpass filter for AFSK Lowpass m_lowpass; // Low pass filter to limit RF bandwidth FMPreemphasis m_preemphasisFilter; // FM preemphasis filter to amplify high frequencies @@ -97,7 +107,6 @@ private: static const int m_levelNbSamples = 480; // every 10ms assuming 48k Sa/s - int m_f; // Current frequency int m_sampleIdx; // Sample index in to symbol int m_samplesPerSymbol; // Number of samples per symbol Real m_pow; // In dB @@ -115,6 +124,8 @@ private: int m_bitCount; // Count of number of valid bits in m_bits int m_bitCountTotal; + LFSR m_scrambler; // Scrambler + std::ofstream m_audioFile; // For debug output of baseband waveform bool bitsValid(); // Are there and bits to transmit diff --git a/plugins/channeltx/modpacket/packetmodtxsettingsdialog.cpp b/plugins/channeltx/modpacket/packetmodtxsettingsdialog.cpp index d3a09193a..b4564e5c0 100644 --- a/plugins/channeltx/modpacket/packetmodtxsettingsdialog.cpp +++ b/plugins/channeltx/modpacket/packetmodtxsettingsdialog.cpp @@ -18,8 +18,10 @@ #include "packetmodtxsettingsdialog.h" PacketModTXSettingsDialog::PacketModTXSettingsDialog(int rampUpBits, int rampDownBits, - int rampRange, bool modulateWhileRamping, + int rampRange, bool modulateWhileRamping, int modulation, int baud, int markFrequency, int spaceFrequency, + bool pulseShaping, float beta, int symbolSpan, + bool scramble, int polynomial, int ax25PreFlags, int ax25PostFlags, int ax25Control, int ax25PID, int lpfTaps, bool bbNoise, bool rfNoise, bool writeToFile, @@ -32,8 +34,15 @@ PacketModTXSettingsDialog::PacketModTXSettingsDialog(int rampUpBits, int rampDow ui->rampDown->setValue(rampDownBits); ui->rampRange->setValue(rampRange); ui->modulateWhileRamping->setChecked(modulateWhileRamping); + ui->modulation->setCurrentIndex(modulation); + ui->baud->setValue(baud); ui->markFrequency->setValue(markFrequency); + ui->pulseShaping->setChecked(pulseShaping); + ui->beta->setValue(beta); + ui->symbolSpan->setValue(symbolSpan); ui->spaceFrequency->setValue(spaceFrequency); + ui->scramble->setChecked(scramble); + ui->polynomial->setValue(polynomial); ui->ax25PreFlags->setValue(ax25PreFlags); ui->ax25PostFlags->setValue(ax25PostFlags); ui->ax25Control->setValue(ax25Control); @@ -55,8 +64,15 @@ void PacketModTXSettingsDialog::accept() m_rampDownBits = ui->rampDown->value(); m_rampRange = ui->rampRange->value(); m_modulateWhileRamping = ui->modulateWhileRamping->isChecked(); + m_modulation = ui->modulation->currentIndex(); + m_baud = ui->baud->value(); m_markFrequency = ui->markFrequency->value(); m_spaceFrequency = ui->spaceFrequency->value(); + m_pulseShaping = ui->pulseShaping->isChecked(); + m_beta = ui->beta->value(); + m_symbolSpan = ui->symbolSpan->value(); + m_scramble = ui->scramble->isChecked(); + m_polynomial = ui->polynomial->value(); m_ax25PreFlags = ui->ax25PreFlags->value(); m_ax25PostFlags = ui->ax25PostFlags->value(); m_ax25Control = ui->ax25Control->value(); diff --git a/plugins/channeltx/modpacket/packetmodtxsettingsdialog.h b/plugins/channeltx/modpacket/packetmodtxsettingsdialog.h index 7889ba625..9ef800408 100644 --- a/plugins/channeltx/modpacket/packetmodtxsettingsdialog.h +++ b/plugins/channeltx/modpacket/packetmodtxsettingsdialog.h @@ -25,8 +25,10 @@ class PacketModTXSettingsDialog : public QDialog { public: explicit PacketModTXSettingsDialog(int rampUpBits, int rampDownBits, int rampRange, - bool modulateWhileRamping, + bool modulateWhileRamping, int modulation, int baud, int markFrequency, int spaceFrequency, + bool pulseShaping, float beta, int symbolSpan, + bool scramble, int polynomial, int ax25PreFlags, int ax25PostFlags, int ax25Control, int ax25PID, int lpfTaps, bool bbNoise, bool rfNoise, bool writeToFile, @@ -37,8 +39,15 @@ public: int m_rampDownBits; int m_rampRange; bool m_modulateWhileRamping; + int m_modulation; + int m_baud; int m_markFrequency; int m_spaceFrequency; + bool m_pulseShaping; + float m_beta; + int m_symbolSpan; + bool m_scramble; + int m_polynomial; int m_ax25PreFlags; int m_ax25PostFlags; int m_ax25Control; diff --git a/plugins/channeltx/modpacket/packetmodtxsettingsdialog.ui b/plugins/channeltx/modpacket/packetmodtxsettingsdialog.ui index f9a28efb7..592c8be52 100644 --- a/plugins/channeltx/modpacket/packetmodtxsettingsdialog.ui +++ b/plugins/channeltx/modpacket/packetmodtxsettingsdialog.ui @@ -7,7 +7,7 @@ 0 0 351 - 449 + 849 @@ -21,8 +21,335 @@ - + + + AX.25 Protocol Settings + + + + + + AX.25 preamble flags + + + + + + + Number of flags to be transmitted before the frame. This gives more time for a receiver to unmute. + + + 1 + + + 1024 + + + + + + + AX.25 postamble flags + + + + + + + Number of flags to be transmitted after the frame. + + + 1 + + + 1024 + + + + + + + AX.25 control + + + + + + + Value of control field in AX.25 frame. + + + 0x + + + 255 + + + 3 + + + 16 + + + + + + + AX.25 PID + + + + + + + Value of PID field in AX.25 frame. Use 0xf0 for no L3. + + + 0x + + + 255 + + + 240 + + + 16 + + + + + + + + + + Modulation + + + + + + Baud rate + + + + + + + Baud rate (symbols per second). + + + 100000 + + + 1200 + + + + + + + Modulaton type. + + + + AFSK + + + + + FSK + + + + + + + + Modulation + + + + + + + RF BW limit LPF taps + + + + + + + Number of taps in LPF for RF BW filter. + + + 10000 + + + + + + + + + + AFSK Modulation + + + + + + Frequency of tone to generate for a mark (1). + + + 24000 + + + 100 + + + + + + + Mark frequency (Hz) + + + + + + + Space frequency (Hz) + + + + + + + Frequency of tone to generate for a space (0). + + + 24000 + + + 100 + + + + + + + + + + FSK Modulation + + + + + + Enable raised cosine pulse shaping filter + + + Raised cosine pulse shaping + + + + + + + Roll-off of the filter + + + 1.000000000000000 + + + 0.250000000000000 + + + + + + + Number of symbols over which filter is applied + + + 1 + + + 20 + + + + + + + Filter rolloff (beta) + + + + + + + Filter symbol span + + + + + + + + + + Scrambing + + + + + Enabling scrambling. + + + Scramble + + + + + + + Polynomial + + + + + + + Polynomial of the scrambler. The +1 is implicit. + + + 0x + + + 2147483647 + + + 67584 + + + 16 + + + + + + + + + + Power Ramping + + @@ -68,7 +395,7 @@ - + Modulate during ramping. @@ -78,156 +405,26 @@ - - - - Mark frequency (Hz) - - - - - + + + + + + + Debug + + + + - Frequency of tone to generate for a mark (1). + Generate white noise as baseband signal. - - 24000 - - - 100 - - - - - - Space frequency (Hz) + Generate BB noise - - - - Frequency of tone to generate for a space (0). - - - 24000 - - - 100 - - - - - - - AX.25 preamble flags - - - - - - - Number of flags to be transmitted before the frame. This gives more time for a receiver to unmute. - - - 1 - - - 1024 - - - - - - - AX.25 postamble flags - - - - - - - Number of flags to be transmitted after the frame. - - - 1 - - - 1024 - - - - - - - AX.25 control - - - - - - - Value of control field in AX.25 frame. - - - 0x - - - 255 - - - 3 - - - 16 - - - - - - - AX.25 PID - - - - - - - Value of PID field in AX.25 frame. Use 0xf0 for no L3. - - - 0x - - - 255 - - - 240 - - - 16 - - - - - - - Lowpass taps - - - - - - - Number of taps in LPF for RF BW filter. - - - 10000 - - - - + Generate white noise as RF signal. @@ -237,7 +434,7 @@ - + @@ -253,16 +450,6 @@ - - - - Generate white noise as baseband signal. - - - Generate BB noise - - - @@ -278,22 +465,6 @@ - - rampUp - rampDown - rampRange - modulateWhileRamping - markFrequency - spaceFrequency - ax25PreFlags - ax25PostFlags - ax25Control - ax25PID - lpfTaps - bbNoise - rfNoise - writeToFile - diff --git a/plugins/channeltx/modpacket/readme.md b/plugins/channeltx/modpacket/readme.md index bc5b5c080..3b4dcbdd1 100644 --- a/plugins/channeltx/modpacket/readme.md +++ b/plugins/channeltx/modpacket/readme.md @@ -22,7 +22,7 @@ Use this button to toggle mute for this channel.

4: Modulation

-This specifies the baud rate and modulation that is used for the packet transmission. Currently 1200 baud AFSK is supported. +This specifies the baud rate and modulation that is used for the packet transmission. Currently 1200 baud AFSK and 9600 baud FSK are supported.

5: RF Bandwidth

@@ -50,29 +50,33 @@ Enter your amateur radio callsign and optionally a sub-station ID (SSID). E.g. M Check this button to enable a FM preemphasis filter, which amplifiers higher frequencies. Right click to open the dialog to adjust settings for the filter. -

11: Repeat

+

11: Bandpass Filter

+ +Check this button to enable a baseband bandpass filter. Right click to open the dialog to adjust settings for the filter. + +

12: Repeat

Check this button to repeated transmit a packet. Right click to open the dialog to adjust the delay between retransmission and number of times the packet should be repeated. -

12: Insertion position

+

13: Insertion position

Inserts position as APRS formatted latitude and longitude in to the current cursor position within the data field. Lattitude and longitude can be specified under Preferences > My position. -

13: To

+

14: To

Enter the destination for the packet. To send the packet to the APRS network, use APRS or APZ. -

14: Via

+

15: Via

Enter the routing for the packet. To have the packet repeated by digipeaters, use WIDE2-2. To have the packet repeated by the International Space Station (ISS), use ARISS. -

15: Data

+

16: Data

-The packet of data to send. To send an APRS status message, use the format >Status. APRS messages can be tracked on https://aprs.fi +The packet of data to send. To send an APRS status message, use the format >Status. The APRS specification can be found at: http://www.aprs.org/doc/APRS101.PDF. APRS messages can be tracked on https://aprs.fi -

16: TX

+

17: TX

-Transmits a packet based on the current values in callsign, to, via and data. +Transmits a packet containing the current values in callsign, to, via and data fields.

API

@@ -80,6 +84,6 @@ Full details of the API can be found in the Swagger documentation. Here is a qui curl -X POST "http://127.0.0.1:8091/sdrangel/deviceset/1/channel/0/actions" -d '{"channelType": "PacketMod", "direction": 1, "PacketModActions": { "tx": { "callsign": "MYCALL", "to": "APRS", "via": "WIDE2-2", "data": ">Using SDRangel API to transmit" }}}' -Or to set the frequency deviation: +Or to set the mode to 9600 FSK: - curl -X PATCH "http://127.0.0.1:8091/sdrangel/deviceset/1/channel/0/settings" -d '{"channelType": "PacketMod", "direction": 1, "PacketModSettings": {"fmDeviation": 5000}}' + curl -X PATCH "http://127.0.0.1:8091/sdrangel/deviceset/1/channel/0/settings" -d '{"channelType": "PacketMod", "direction": 1, "PacketModSettings": {"mode": "9600 FSK"}}' diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 1dc6811ee..c80c15a2a 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -147,6 +147,7 @@ set(sdrbase_SOURCES util/CRC64.cpp util/db.cpp util/fixedtraits.cpp + util/lfsr.cpp util/message.cpp util/messagequeue.cpp util/prettyprint.cpp @@ -265,6 +266,7 @@ set(sdrbase_HEADERS dsp/phaselock.h dsp/phaselockcomplex.h dsp/projector.h + dsp/raisedcosine.h dsp/recursivefilters.h dsp/samplemififo.h dsp/samplemofifo.h @@ -305,6 +307,7 @@ set(sdrbase_HEADERS util/fixedtraits.h util/incrementalarray.h util/incrementalvector.h + util/lfsr.h util/message.h util/messagequeue.h util/movingaverage.h diff --git a/sdrbase/dsp/raisedcosine.h b/sdrbase/dsp/raisedcosine.h new file mode 100644 index 000000000..0816a912f --- /dev/null +++ b/sdrbase/dsp/raisedcosine.h @@ -0,0 +1,136 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2015 Edouard Griffiths, F4EXB // +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_RAISEDCOSINE_H +#define INCLUDE_RAISEDCOSINE_H + +#define _USE_MATH_DEFINES +#include +#include "dsp/dsptypes.h" + +// Raised-cosine low-pass filter for pulse shaping, without intersymbol interference (ISI) +// https://en.wikipedia.org/wiki/Raised-cosine_filter +// This could be optimised in to a polyphase filter, as samplesPerSymbol-1 inputs +// to filter() should be zero, as the data is upsampled to the sample rate +template class RaisedCosine { +public: + RaisedCosine() : m_ptr(0) { } + + // beta - roll-off factor + // symbolSpan - number of symbols over which the filter is spread + // samplesPerSymbol - number of samples per symbol + void create(double beta, int symbolSpan, int samplesPerSymbol) + { + int nTaps = symbolSpan * samplesPerSymbol + 1; + int i; + + // check constraints + if(!(nTaps & 1)) { + qDebug("Raised cosine filter has to have an odd number of taps"); + nTaps++; + } + + // make room + m_samples.resize(nTaps); + for(int i = 0; i < nTaps; i++) + m_samples[i] = 0; + m_ptr = 0; + m_taps.resize(nTaps / 2 + 1); + + // calculate filter taps + for(i = 0; i < nTaps / 2 + 1; i++) + { + double t = (i - (nTaps / 2)) / (double)samplesPerSymbol; + double denominator = 1.0 - std::pow(2.0 * beta * t, 2.0); + double sinc; + + if (denominator != 0.0) + { + if (t == 0) + sinc = 1.0; + else + sinc = sin(M_PI*t)/(M_PI*t); + m_taps[i] = sinc * (cos(M_PI*beta*t) / denominator) / (double)samplesPerSymbol; + } + else + m_taps[i] = beta * sin(M_PI/(2.0*beta)) / (2.0*samplesPerSymbol); + } + + // normalize + double sum = 0; + for(i = 0; i < (int)m_taps.size() - 1; i++) + sum += std::pow(m_taps[i], 2.0) * 2; + sum += std::pow(m_taps[i], 2.0); + sum = std::sqrt(sum); + for(i = 0; i < (int)m_taps.size(); i++) + m_taps[i] /= sum; + } + + Type filter(Type sample) + { + Type acc = 0; + int a = m_ptr; + int b = a - 1; + int i, n_taps, size; + + m_samples[m_ptr] = sample; + size = m_samples.size(); // Valgrind optim (2) + + while (b < 0) + { + b += size; + } + + n_taps = m_taps.size() - 1; // Valgrind optim + + for (i = 0; i < n_taps; i++) + { + acc += (m_samples[a] + m_samples[b]) * m_taps[i]; + a++; + + while (a >= size) + { + a -= size; + } + + b--; + + while(b < 0) + { + b += size; + } + } + + acc += m_samples[a] * m_taps[i]; + m_ptr++; + + while(m_ptr >= size) + { + m_ptr -= size; + } + + return acc; + } + +private: + std::vector m_taps; + std::vector m_samples; + int m_ptr; +}; + +#endif // INCLUDE_RAISEDCOSINE_H diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 994b0eb4f..bbb0fb7bf 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -4932,6 +4932,10 @@ margin-bottom: 20px; "type" : "integer", "format" : "int64" }, + "mode" : { + "type" : "string", + "description" : "Transmission mode. \"1200 AFSK\" or \"9600 FSK\"." + }, "rfBandwidth" : { "type" : "number", "format" : "float" @@ -4974,6 +4978,17 @@ margin-bottom: 20px; "type" : "number", "format" : "float" }, + "bpf" : { + "type" : "integer" + }, + "bpfLowCutoff" : { + "type" : "number", + "format" : "float" + }, + "bpfHighCutoff" : { + "type" : "number", + "format" : "float" + }, "rgbColor" : { "type" : "integer" }, @@ -33454,7 +33469,7 @@ except ApiException as e:
- Generated 2020-09-18T15:59:26.503+02:00 + Generated 2020-09-23T09:56:01.490+02:00
diff --git a/sdrbase/util/lfsr.cpp b/sdrbase/util/lfsr.cpp new file mode 100644 index 000000000..459a43dac --- /dev/null +++ b/sdrbase/util/lfsr.cpp @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "lfsr.h" +#include "popcount.h" + +// Shift LFSR one bit +int LFSR::shift() +{ + int bit; + + bit = (popcount(m_sr & m_polynomial) & 1) ^ 1; + m_sr = (m_sr << 1) | bit; + return bit; +} + +// Scramble a single bit +int LFSR::scramble(int bit_in) +{ + int bit_out; + + bit_out = (popcount(m_sr & m_polynomial) & 1) ^ bit_in; + m_sr = (m_sr << 1) | bit_out; + return bit_out; +} + #include + +// Scramble data using LFSR - LSB first +void LFSR::scramble(uint8_t *data, int length) +{ + uint8_t byte_in, byte_out; + int bit_in, bit_out; + + for(int i = 0; i < length; i++) + { + byte_in = data[i]; + byte_out = 0; + for (int j = 0; j < 8; j++) + { + bit_in = (byte_in >> j) & 1; + bit_out = (popcount(m_sr & m_polynomial) & 1) ^ bit_in; + m_sr = (m_sr << 1) | bit_out; + byte_out = byte_out | (bit_out << j); + } + data[i] = byte_out; + } +} + +// Descramble data using LFSR - LSB first +void LFSR::descramble(uint8_t *data, int length) +{ + uint8_t byte_in, byte_out; + int bit_in, bit_out; + + for(int i = 0; i < length; i++) + { + byte_in = data[i]; + byte_out = 0; + for (int j = 0; j < 8; j++) + { + bit_in = (byte_in >> j) & 1; + bit_out = (popcount(m_sr & m_polynomial) & 1) ^ bit_in; + m_sr = (m_sr << 1) | bit_in; + byte_out = byte_out | (bit_out << j); + } + data[i] = byte_out; + } +} + +// XOR data with rand_bit of LFSR - LSB first +void LFSR::randomize(uint8_t *data, int length) +{ + uint8_t byte_in, byte_out; + int bit_in, bit_out, bit; + + for(int i = 0; i < length; i++) + { + byte_in = data[i]; + byte_out = 0; + for (int j = 0; j < 8; j++) + { + // XOR input bit with specified bit from SR + bit_in = (byte_in >> j) & 1; + bit_out = ((m_sr >> m_rand_bit) & 1) ^ bit_in; + byte_out = byte_out | (bit_out << j); + // Update LFSR + bit = popcount(m_sr & m_polynomial) & 1; + m_sr = (m_sr << 1) | bit; + } + data[i] = byte_out; + } +} diff --git a/sdrbase/util/lfsr.h b/sdrbase/util/lfsr.h new file mode 100644 index 000000000..b487fbbac --- /dev/null +++ b/sdrbase/util/lfsr.h @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_LFSR_H +#define INCLUDE_LFSR_H + +#include + +#include "export.h" + +// Linear feedback shift register that can be used for scrambling or generating +// PN (Pseudo Noise) random sequence. +class SDRBASE_API LFSR +{ +public: + // Create and initialise LFSR with specified number of bits, polynomial and + // initial state (which must be non-zero, unless a multiplicative scrambler). + // The +1 is implicit in the polynomial so x^1 + 1 should be passed as 0x01 + LFSR(uint32_t polynomial, uint32_t init_value = ~0U, int rand_bit = -1) : + m_rand_bit(rand_bit), + m_polynomial(polynomial), + m_init_value(init_value) + { + init(); + } + + // Initialise LFSR state + void init() + { + m_sr = m_init_value; + } + + // Shift the LFSR one bit and return output of XOR + int shift(); + + // Multiplicative scramble a single bit using LFSR + int scramble(int bit_in); + + // Multiplicative scramble of data using LFSR - LSB first + void scramble(uint8_t *data, int length); + // Descramble data using LFSR - LSB first + void descramble(uint8_t *data, int length); + + // XOR data with rand_bit from LFSR generating pseudo noise (PN) sequence - LSB first + void randomize(uint8_t *data, int length); + + // Get current shift-register value + uint32_t getSR() + { + return m_sr; + } + + // Set the polynomial + void setPolynomial(uint32_t polynomial) + { + m_polynomial = polynomial; + } + + // Get the polynomial + uint32_t getPolynomial() + { + return m_polynomial; + } + +private: + int m_rand_bit; // Which bit from the SR to use in randomize() + uint32_t m_polynomial; // Polynomial coefficients (+1 is implicit) + uint32_t m_init_value; // Value to initialise SR to when init() is called + uint32_t m_sr; // Shift register +}; + +// http://www.jrmiller.demon.co.uk/products/figs/man9k6.pdf +// In Matlab: comm.Scrambler(2, '1 + z^-12 + z^-17', 0) +// Call scramble() +class SDRBASE_API ScramblerG3RUG : public LFSR +{ +public: + ScramblerG3RUG() : LFSR(0x10800, 0x0) {} +}; + +// https://public.ccsds.org/Pubs/131x0b3e1.pdf +// x^8+x^7+x^5+x^3+1 +// In Matlab: comm.PNSequence('Polynomial', 'x^8+x^7+x^5+x^3+1', 'InitialConditions', [1 1 1 1 1 1 1 1]) +// Call randomize() +class SDRBASE_API RandomizeCCSDS : public LFSR +{ +public: + RandomizeCCSDS() : LFSR(0x95, 0xff, 7) {} +}; + +#endif diff --git a/sdrbase/util/popcount.h b/sdrbase/util/popcount.h new file mode 100644 index 000000000..cd7c5ac17 --- /dev/null +++ b/sdrbase/util/popcount.h @@ -0,0 +1,40 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Jon Beniston, M7RCE // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_POPCOUNT_H +#define INCLUDE_POPCOUNT_H + +// Population count - count number of bits +#if defined(__cplusplus) && (__cplusplus >= 202002L) +#include +#define popcount std::popcount +#elif defined (__GNUC__) +#define popcount __builtin_popcount +#elif defined(_MSC_VER) +#include +#define popcount __popcnt +#else +static int popcount(int in) +{ + int cnt = 0; + for(int i = 0; i < 32; i++) + cnt += (in >> i) & 1; + return cnt; +} +#endif + +#endif /* INCLUDE_POPCOUNT_H */ diff --git a/swagger/sdrangel/api/swagger/include/PacketMod.yaml b/swagger/sdrangel/api/swagger/include/PacketMod.yaml index 06e3236fe..99e4ce4c8 100644 --- a/swagger/sdrangel/api/swagger/include/PacketMod.yaml +++ b/swagger/sdrangel/api/swagger/include/PacketMod.yaml @@ -4,6 +4,9 @@ PacketModSettings: inputFrequencyOffset: type: integer format: int64 + mode: + description: Transmission mode. "1200 AFSK" or "9600 FSK". + type: string rfBandwidth: type: number format: float @@ -34,6 +37,14 @@ PacketModSettings: preEmphasisHighFreq: type: number format: float + bpf: + type: integer + bpfLowCutoff: + type: number + format: float + bpfHighCutoff: + type: number + format: float rgbColor: type: integer title: diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index 994b0eb4f..bbb0fb7bf 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -4932,6 +4932,10 @@ margin-bottom: 20px; "type" : "integer", "format" : "int64" }, + "mode" : { + "type" : "string", + "description" : "Transmission mode. \"1200 AFSK\" or \"9600 FSK\"." + }, "rfBandwidth" : { "type" : "number", "format" : "float" @@ -4974,6 +4978,17 @@ margin-bottom: 20px; "type" : "number", "format" : "float" }, + "bpf" : { + "type" : "integer" + }, + "bpfLowCutoff" : { + "type" : "number", + "format" : "float" + }, + "bpfHighCutoff" : { + "type" : "number", + "format" : "float" + }, "rgbColor" : { "type" : "integer" }, @@ -33454,7 +33469,7 @@ except ApiException as e:
- Generated 2020-09-18T15:59:26.503+02:00 + Generated 2020-09-23T09:56:01.490+02:00
diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.cpp index ea40165fe..51360b8d7 100644 --- a/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.cpp @@ -30,6 +30,8 @@ SWGPacketModSettings::SWGPacketModSettings(QString* json) { SWGPacketModSettings::SWGPacketModSettings() { input_frequency_offset = 0L; m_input_frequency_offset_isSet = false; + mode = nullptr; + m_mode_isSet = false; rf_bandwidth = 0.0f; m_rf_bandwidth_isSet = false; fm_deviation = 0.0f; @@ -54,6 +56,12 @@ SWGPacketModSettings::SWGPacketModSettings() { m_pre_emphasis_tau_isSet = false; pre_emphasis_high_freq = 0.0f; m_pre_emphasis_high_freq_isSet = false; + bpf = 0; + m_bpf_isSet = false; + bpf_low_cutoff = 0.0f; + m_bpf_low_cutoff_isSet = false; + bpf_high_cutoff = 0.0f; + m_bpf_high_cutoff_isSet = false; rgb_color = 0; m_rgb_color_isSet = false; title = nullptr; @@ -80,6 +88,8 @@ void SWGPacketModSettings::init() { input_frequency_offset = 0L; m_input_frequency_offset_isSet = false; + mode = new QString(""); + m_mode_isSet = false; rf_bandwidth = 0.0f; m_rf_bandwidth_isSet = false; fm_deviation = 0.0f; @@ -104,6 +114,12 @@ SWGPacketModSettings::init() { m_pre_emphasis_tau_isSet = false; pre_emphasis_high_freq = 0.0f; m_pre_emphasis_high_freq_isSet = false; + bpf = 0; + m_bpf_isSet = false; + bpf_low_cutoff = 0.0f; + m_bpf_low_cutoff_isSet = false; + bpf_high_cutoff = 0.0f; + m_bpf_high_cutoff_isSet = false; rgb_color = 0; m_rgb_color_isSet = false; title = new QString(""); @@ -125,6 +141,12 @@ SWGPacketModSettings::init() { void SWGPacketModSettings::cleanup() { + if(mode != nullptr) { + delete mode; + } + + + @@ -164,6 +186,8 @@ void SWGPacketModSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", ""); + ::SWGSDRangel::setValue(&mode, pJson["mode"], "QString", "QString"); + ::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", ""); ::SWGSDRangel::setValue(&fm_deviation, pJson["fmDeviation"], "float", ""); @@ -188,6 +212,12 @@ SWGPacketModSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&pre_emphasis_high_freq, pJson["preEmphasisHighFreq"], "float", ""); + ::SWGSDRangel::setValue(&bpf, pJson["bpf"], "qint32", ""); + + ::SWGSDRangel::setValue(&bpf_low_cutoff, pJson["bpfLowCutoff"], "float", ""); + + ::SWGSDRangel::setValue(&bpf_high_cutoff, pJson["bpfHighCutoff"], "float", ""); + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); @@ -223,6 +253,9 @@ SWGPacketModSettings::asJsonObject() { if(m_input_frequency_offset_isSet){ obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset)); } + if(mode != nullptr && *mode != QString("")){ + toJsonValue(QString("mode"), mode, obj, QString("QString")); + } if(m_rf_bandwidth_isSet){ obj->insert("rfBandwidth", QJsonValue(rf_bandwidth)); } @@ -259,6 +292,15 @@ SWGPacketModSettings::asJsonObject() { if(m_pre_emphasis_high_freq_isSet){ obj->insert("preEmphasisHighFreq", QJsonValue(pre_emphasis_high_freq)); } + if(m_bpf_isSet){ + obj->insert("bpf", QJsonValue(bpf)); + } + if(m_bpf_low_cutoff_isSet){ + obj->insert("bpfLowCutoff", QJsonValue(bpf_low_cutoff)); + } + if(m_bpf_high_cutoff_isSet){ + obj->insert("bpfHighCutoff", QJsonValue(bpf_high_cutoff)); + } if(m_rgb_color_isSet){ obj->insert("rgbColor", QJsonValue(rgb_color)); } @@ -297,6 +339,16 @@ SWGPacketModSettings::setInputFrequencyOffset(qint64 input_frequency_offset) { this->m_input_frequency_offset_isSet = true; } +QString* +SWGPacketModSettings::getMode() { + return mode; +} +void +SWGPacketModSettings::setMode(QString* mode) { + this->mode = mode; + this->m_mode_isSet = true; +} + float SWGPacketModSettings::getRfBandwidth() { return rf_bandwidth; @@ -417,6 +469,36 @@ SWGPacketModSettings::setPreEmphasisHighFreq(float pre_emphasis_high_freq) { this->m_pre_emphasis_high_freq_isSet = true; } +qint32 +SWGPacketModSettings::getBpf() { + return bpf; +} +void +SWGPacketModSettings::setBpf(qint32 bpf) { + this->bpf = bpf; + this->m_bpf_isSet = true; +} + +float +SWGPacketModSettings::getBpfLowCutoff() { + return bpf_low_cutoff; +} +void +SWGPacketModSettings::setBpfLowCutoff(float bpf_low_cutoff) { + this->bpf_low_cutoff = bpf_low_cutoff; + this->m_bpf_low_cutoff_isSet = true; +} + +float +SWGPacketModSettings::getBpfHighCutoff() { + return bpf_high_cutoff; +} +void +SWGPacketModSettings::setBpfHighCutoff(float bpf_high_cutoff) { + this->bpf_high_cutoff = bpf_high_cutoff; + this->m_bpf_high_cutoff_isSet = true; +} + qint32 SWGPacketModSettings::getRgbColor() { return rgb_color; @@ -505,6 +587,9 @@ SWGPacketModSettings::isSet(){ if(m_input_frequency_offset_isSet){ isObjectUpdated = true; break; } + if(mode && *mode != QString("")){ + isObjectUpdated = true; break; + } if(m_rf_bandwidth_isSet){ isObjectUpdated = true; break; } @@ -541,6 +626,15 @@ SWGPacketModSettings::isSet(){ if(m_pre_emphasis_high_freq_isSet){ isObjectUpdated = true; break; } + if(m_bpf_isSet){ + isObjectUpdated = true; break; + } + if(m_bpf_low_cutoff_isSet){ + isObjectUpdated = true; break; + } + if(m_bpf_high_cutoff_isSet){ + isObjectUpdated = true; break; + } if(m_rgb_color_isSet){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.h b/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.h index 15b1afae8..35d0fb34f 100644 --- a/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGPacketModSettings.h @@ -45,6 +45,9 @@ public: qint64 getInputFrequencyOffset(); void setInputFrequencyOffset(qint64 input_frequency_offset); + QString* getMode(); + void setMode(QString* mode); + float getRfBandwidth(); void setRfBandwidth(float rf_bandwidth); @@ -81,6 +84,15 @@ public: float getPreEmphasisHighFreq(); void setPreEmphasisHighFreq(float pre_emphasis_high_freq); + qint32 getBpf(); + void setBpf(qint32 bpf); + + float getBpfLowCutoff(); + void setBpfLowCutoff(float bpf_low_cutoff); + + float getBpfHighCutoff(); + void setBpfHighCutoff(float bpf_high_cutoff); + qint32 getRgbColor(); void setRgbColor(qint32 rgb_color); @@ -112,6 +124,9 @@ private: qint64 input_frequency_offset; bool m_input_frequency_offset_isSet; + QString* mode; + bool m_mode_isSet; + float rf_bandwidth; bool m_rf_bandwidth_isSet; @@ -148,6 +163,15 @@ private: float pre_emphasis_high_freq; bool m_pre_emphasis_high_freq_isSet; + qint32 bpf; + bool m_bpf_isSet; + + float bpf_low_cutoff; + bool m_bpf_low_cutoff_isSet; + + float bpf_high_cutoff; + bool m_bpf_high_cutoff_isSet; + qint32 rgb_color; bool m_rgb_color_isSet;