mirror of
https://github.com/f4exb/sdrangel.git
synced 2025-02-03 09:44:01 -05:00
Add 9600 FSK modem with scrambler and raised-cosine pulse-shaping.
Add baseband BPF for AFSK.
This commit is contained in:
parent
b85c4a4f1a
commit
9543f3a117
Binary file not shown.
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 22 KiB |
@ -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
|
||||
)
|
||||
|
@ -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);
|
||||
}
|
||||
|
45
plugins/channeltx/modpacket/packetmodbpfdialog.cpp
Normal file
45
plugins/channeltx/modpacket/packetmodbpfdialog.cpp
Normal file
@ -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();
|
||||
}
|
45
plugins/channeltx/modpacket/packetmodbpfdialog.h
Normal file
45
plugins/channeltx/modpacket/packetmodbpfdialog.h
Normal file
@ -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
|
146
plugins/channeltx/modpacket/packetmodbpfdialog.ui
Normal file
146
plugins/channeltx/modpacket/packetmodbpfdialog.ui
Normal file
@ -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>
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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">
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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"}}'
|
||||
|
@ -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
|
||||
|
136
sdrbase/dsp/raisedcosine.h
Normal file
136
sdrbase/dsp/raisedcosine.h
Normal file
@ -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
|
@ -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>
|
||||
|
106
sdrbase/util/lfsr.cpp
Normal file
106
sdrbase/util/lfsr.cpp
Normal file
@ -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
Normal file
105
sdrbase/util/lfsr.h
Normal file
@ -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
|
40
sdrbase/util/popcount.h
Normal file
40
sdrbase/util/popcount.h
Normal file
@ -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 */
|
@ -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:
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user