Add 9600 FSK modem with scrambler and raised-cosine pulse-shaping.

Add baseband BPF for AFSK.
pull/647/head
Jon Beniston 2020-09-23 13:42:29 +01:00 zatwierdzone przez f4exb
rodzic b85c4a4f1a
commit 9543f3a117
27 zmienionych plików z 1523 dodań i 221 usunięć

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 17 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 22 KiB

Wyświetl plik

@ -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
)

Wyświetl plik

@ -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<QString>& 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);
}

Wyświetl plik

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#define _USE_MATH_DEFINES
#include <cmath>
#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();
}

Wyświetl plik

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_PACKETMODBPFDIALOG_H
#define INCLUDE_PACKETMODBPFDIALOG_H
#include <QDialog>
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

Wyświetl plik

@ -0,0 +1,146 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PacketModBPFDialog</class>
<widget class="QDialog" name="PacketModBPFDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>351</width>
<height>152</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>Bandpass Filter Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="lowFreqLabel">
<property name="text">
<string>Low frequency corner (Hz)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="lowFreq">
<property name="toolTip">
<string>Low frequency corner.</string>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="singleStep">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="highFreqLabel">
<property name="text">
<string>High frequency corner (Hz)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="highFreq">
<property name="toolTip">
<string>High frequency corner.</string>
</property>
<property name="maximum">
<double>1000000.000000000000000</double>
</property>
<property name="singleStep">
<double>1000.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="taps">
<property name="toolTip">
<string>Number of taps in the filter. More taps gives sharper roll off. Must be odd.</string>
</property>
<property name="minimum">
<number>5</number>
</property>
<property name="maximum">
<number>100001</number>
</property>
<property name="value">
<number>301</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="tapsLabel">
<property name="toolTip">
<string/>
</property>
<property name="text">
<string>Filter taps</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>lowFreq</tabstop>
<tabstop>highFreq</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>PacketModBPFDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>PacketModBPFDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

Wyświetl plik

@ -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<PacketModSettings::Modulation>(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);

Wyświetl plik

@ -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();

Wyświetl plik

@ -210,6 +210,11 @@
<string>1200 AFSK</string>
</property>
</item>
<item>
<property name="text">
<string>9600 FSK</string>
</property>
</item>
</widget>
</item>
<item>
@ -472,6 +477,20 @@
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="bpf">
<property name="toolTip">
<string>Baseband bandpass filter. Right click for additional settings.</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/filter_bandpass.png</normaloff>:/filter_bandpass.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="repeat">
<property name="toolTip">

Wyświetl plik

@ -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

Wyświetl plik

@ -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 */

Wyświetl plik

@ -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();
}

Wyświetl plik

@ -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<Real> m_pulseShape; // Pulse shaping filter
Bandpass<Real> m_bandpass; // Baseband bandpass filter for AFSK
Lowpass<Complex> 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

Wyświetl plik

@ -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();

Wyświetl plik

@ -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;

Wyświetl plik

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>351</width>
<height>449</height>
<height>849</height>
</rect>
</property>
<property name="font">
@ -21,8 +21,335 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<widget class="QGroupBox" name="ax25Group">
<property name="title">
<string>AX.25 Protocol Settings</string>
</property>
<layout class="QFormLayout" name="formLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="ax25PreFlagsLabel">
<property name="text">
<string>AX.25 preamble flags</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="ax25PreFlags">
<property name="toolTip">
<string>Number of flags to be transmitted before the frame. This gives more time for a receiver to unmute.</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1024</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="ax25PostFlagsLabel">
<property name="text">
<string>AX.25 postamble flags</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="ax25PostFlags">
<property name="toolTip">
<string>Number of flags to be transmitted after the frame.</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1024</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="ax25ControlLabel">
<property name="text">
<string>AX.25 control</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="ax25Control">
<property name="toolTip">
<string>Value of control field in AX.25 frame.</string>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>3</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="ax25PIDLabel">
<property name="text">
<string>AX.25 PID</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="ax25PID">
<property name="toolTip">
<string>Value of PID field in AX.25 frame. Use 0xf0 for no L3.</string>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>240</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="baudGroup">
<property name="title">
<string>Modulation</string>
</property>
<layout class="QFormLayout" name="formLayout_8">
<item row="1" column="0">
<widget class="QLabel" name="baudLabel">
<property name="text">
<string>Baud rate</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="baud">
<property name="toolTip">
<string>Baud rate (symbols per second).</string>
</property>
<property name="maximum">
<number>100000</number>
</property>
<property name="value">
<number>1200</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="modulation">
<property name="toolTip">
<string>Modulaton type.</string>
</property>
<item>
<property name="text">
<string>AFSK</string>
</property>
</item>
<item>
<property name="text">
<string>FSK</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="modulationLabel">
<property name="text">
<string>Modulation</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lpfTapsLabel">
<property name="text">
<string>RF BW limit LPF taps</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="lpfTaps">
<property name="toolTip">
<string>Number of taps in LPF for RF BW filter.</string>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="afskGroup">
<property name="title">
<string>AFSK Modulation</string>
</property>
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="1">
<widget class="QSpinBox" name="markFrequency">
<property name="toolTip">
<string>Frequency of tone to generate for a mark (1).</string>
</property>
<property name="maximum">
<number>24000</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="markFrequencyLabel">
<property name="text">
<string>Mark frequency (Hz)</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="spaceFrequencyLabel">
<property name="text">
<string>Space frequency (Hz)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="spaceFrequency">
<property name="toolTip">
<string>Frequency of tone to generate for a space (0).</string>
</property>
<property name="maximum">
<number>24000</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="fskGroup">
<property name="title">
<string>FSK Modulation</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QCheckBox" name="pulseShaping">
<property name="toolTip">
<string>Enable raised cosine pulse shaping filter</string>
</property>
<property name="text">
<string>Raised cosine pulse shaping</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="beta">
<property name="toolTip">
<string>Roll-off of the filter</string>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.250000000000000</double>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="symbolSpan">
<property name="toolTip">
<string>Number of symbols over which filter is applied</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>20</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="betaLabel">
<property name="text">
<string>Filter rolloff (beta)</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="symbolSpanLabel">
<property name="text">
<string>Filter symbol span</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="scramblingGroup">
<property name="title">
<string>Scrambing</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="scramble">
<property name="toolTip">
<string>Enabling scrambling.</string>
</property>
<property name="text">
<string>Scramble</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="polynomialLabel">
<property name="text">
<string>Polynomial</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="polynomial">
<property name="toolTip">
<string>Polynomial of the scrambler. The +1 is implicit.</string>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>2147483647</number>
</property>
<property name="value">
<number>67584</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="powerRampingGroup">
<property name="title">
<string>Power Ramping</string>
</property>
<layout class="QFormLayout" name="formLayout_6">
<item row="0" column="0">
<widget class="QLabel" name="rampUpLabel">
<property name="text">
@ -68,7 +395,7 @@
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="3" column="0">
<widget class="QCheckBox" name="modulateWhileRamping">
<property name="toolTip">
<string>Modulate during ramping.</string>
@ -78,156 +405,26 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="markFrequencyLabel">
<property name="text">
<string>Mark frequency (Hz)</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="markFrequency">
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="debugGroup">
<property name="title">
<string>Debug</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QCheckBox" name="bbNoise">
<property name="toolTip">
<string>Frequency of tone to generate for a mark (1).</string>
<string>Generate white noise as baseband signal.</string>
</property>
<property name="maximum">
<number>24000</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="spaceFrequencyLabel">
<property name="text">
<string>Space frequency (Hz)</string>
<string>Generate BB noise</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="spaceFrequency">
<property name="toolTip">
<string>Frequency of tone to generate for a space (0).</string>
</property>
<property name="maximum">
<number>24000</number>
</property>
<property name="singleStep">
<number>100</number>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="ax25PreFlagsLabel">
<property name="text">
<string>AX.25 preamble flags</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="ax25PreFlags">
<property name="toolTip">
<string>Number of flags to be transmitted before the frame. This gives more time for a receiver to unmute.</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1024</number>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="ax25PostFlagsLabel">
<property name="text">
<string>AX.25 postamble flags</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QSpinBox" name="ax25PostFlags">
<property name="toolTip">
<string>Number of flags to be transmitted after the frame.</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1024</number>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="ax25ControlLabel">
<property name="text">
<string>AX.25 control</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QSpinBox" name="ax25Control">
<property name="toolTip">
<string>Value of control field in AX.25 frame.</string>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>3</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="ax25PIDLabel">
<property name="text">
<string>AX.25 PID</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QSpinBox" name="ax25PID">
<property name="toolTip">
<string>Value of PID field in AX.25 frame. Use 0xf0 for no L3.</string>
</property>
<property name="prefix">
<string>0x</string>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>240</number>
</property>
<property name="displayIntegerBase">
<number>16</number>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="lpfTapsLabel">
<property name="text">
<string>Lowpass taps</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QSpinBox" name="lpfTaps">
<property name="toolTip">
<string>Number of taps in LPF for RF BW filter.</string>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item row="12" column="0">
<item row="1" column="0">
<widget class="QCheckBox" name="rfNoise">
<property name="toolTip">
<string>Generate white noise as RF signal.</string>
@ -237,7 +434,7 @@
</property>
</widget>
</item>
<item row="13" column="0">
<item row="2" column="0">
<widget class="QCheckBox" name="writeToFile">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
@ -253,16 +450,6 @@
</property>
</widget>
</item>
<item row="11" column="0">
<widget class="QCheckBox" name="bbNoise">
<property name="toolTip">
<string>Generate white noise as baseband signal.</string>
</property>
<property name="text">
<string>Generate BB noise</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -278,22 +465,6 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>rampUp</tabstop>
<tabstop>rampDown</tabstop>
<tabstop>rampRange</tabstop>
<tabstop>modulateWhileRamping</tabstop>
<tabstop>markFrequency</tabstop>
<tabstop>spaceFrequency</tabstop>
<tabstop>ax25PreFlags</tabstop>
<tabstop>ax25PostFlags</tabstop>
<tabstop>ax25Control</tabstop>
<tabstop>ax25PID</tabstop>
<tabstop>lpfTaps</tabstop>
<tabstop>bbNoise</tabstop>
<tabstop>rfNoise</tabstop>
<tabstop>writeToFile</tabstop>
</tabstops>
<resources/>
<connections>
<connection>

Wyświetl plik

@ -22,7 +22,7 @@ Use this button to toggle mute for this channel.
<h3>4: Modulation</h3>
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.
<h3>5: RF Bandwidth</h3>
@ -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.
<h3>11: Repeat</h3>
<h3>11: Bandpass Filter</h3>
Check this button to enable a baseband bandpass filter. Right click to open the dialog to adjust settings for the filter.
<h3>12: Repeat</h3>
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.
<h3>12: Insertion position</h3>
<h3>13: Insertion position</h3>
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.
<h3>13: To</h3>
<h3>14: To</h3>
Enter the destination for the packet. To send the packet to the APRS network, use APRS or APZ.
<h3>14: Via</h3>
<h3>15: Via</h3>
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.
<h3>15: Data</h3>
<h3>16: Data</h3>
The packet of data to send. To send an APRS status message, use the format <tt>>Status</tt>. APRS messages can be tracked on https://aprs.fi
The packet of data to send. To send an APRS status message, use the format <tt>>Status</tt>. The APRS specification can be found at: http://www.aprs.org/doc/APRS101.PDF. APRS messages can be tracked on https://aprs.fi
<h3>16: TX</h3>
<h3>17: TX</h3>
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.
<h2>API</h2>
@ -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"}}'

Wyświetl plik

@ -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

Wyświetl plik

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_RAISEDCOSINE_H
#define INCLUDE_RAISEDCOSINE_H
#define _USE_MATH_DEFINES
#include <math.h>
#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 Type> 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<Real> m_taps;
std::vector<Type> m_samples;
int m_ptr;
};
#endif // INCLUDE_RAISEDCOSINE_H

Wyświetl plik

@ -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:
</div>
<div id="generator">
<div class="content">
Generated 2020-09-18T15:59:26.503+02:00
Generated 2020-09-23T09:56:01.490+02:00
</div>
</div>
</div>

Wyświetl plik

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#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 <stdio.h>
// 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;
}
}

105
sdrbase/util/lfsr.h 100644
Wyświetl plik

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_LFSR_H
#define INCLUDE_LFSR_H
#include <stdint.h>
#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

Wyświetl plik

@ -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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_POPCOUNT_H
#define INCLUDE_POPCOUNT_H
// Population count - count number of bits
#if defined(__cplusplus) && (__cplusplus >= 202002L)
#include <bit>
#define popcount std::popcount
#elif defined (__GNUC__)
#define popcount __builtin_popcount
#elif defined(_MSC_VER)
#include <intrin.h>
#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 */

Wyświetl plik

@ -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:

Wyświetl plik

@ -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:
</div>
<div id="generator">
<div class="content">
Generated 2020-09-18T15:59:26.503+02:00
Generated 2020-09-23T09:56:01.490+02:00
</div>
</div>
</div>

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;