mirror of https://github.com/f4exb/sdrangel.git
M17: implemented APRS
This commit is contained in:
parent
b69275949a
commit
3cf3938757
|
@ -45,6 +45,7 @@
|
|||
|
||||
MESSAGE_CLASS_DEFINITION(M17Demod::MsgConfigureM17Demod, Message)
|
||||
MESSAGE_CLASS_DEFINITION(M17Demod::MsgReportSMS, Message)
|
||||
MESSAGE_CLASS_DEFINITION(M17Demod::MsgReportAPRS, Message)
|
||||
|
||||
const char* const M17Demod::m_channelIdURI = "sdrangel.channel.m17demod";
|
||||
const char* const M17Demod::m_channelId = "M17Demod";
|
||||
|
@ -185,6 +186,27 @@ bool M17Demod::handleMessage(const Message& cmd)
|
|||
|
||||
return true;
|
||||
}
|
||||
else if (MsgReportAPRS::match(cmd))
|
||||
{
|
||||
MsgReportAPRS& report = (MsgReportAPRS&) cmd;
|
||||
// Forward to GUI if any
|
||||
if (getMessageQueueToGUI()) {
|
||||
getMessageQueueToGUI()->push(new MsgReportAPRS(report));
|
||||
}
|
||||
|
||||
// Forward to APRS and other packet features
|
||||
QList<ObjectPipe*> packetsPipes;
|
||||
MainCore::instance()->getMessagePipes().getMessagePipes(this, "packets", packetsPipes);
|
||||
|
||||
for (const auto& pipe : packetsPipes)
|
||||
{
|
||||
MessageQueue *messageQueue = qobject_cast<MessageQueue*>(pipe->m_element);
|
||||
MainCore::MsgPacket *msg = MainCore::MsgPacket::create(this, report.getPacket(), QDateTime::currentDateTime());
|
||||
messageQueue->push(msg);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -85,6 +85,67 @@ public:
|
|||
{ }
|
||||
};
|
||||
|
||||
class MsgReportAPRS : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const QString& getSource() const { return m_source; }
|
||||
const QString& getDest() const { return m_dest; }
|
||||
const QString& getFrom() const { return m_from; }
|
||||
const QString& getTo() const { return m_to; }
|
||||
const QString& getVia() const { return m_via; }
|
||||
const QString& getType() const { return m_type; }
|
||||
const QString& getPID() const { return m_pid; }
|
||||
const QString& getData() const { return m_data; }
|
||||
QByteArray& getPacket() { return m_packet; }
|
||||
|
||||
static MsgReportAPRS* create(
|
||||
const QString& source,
|
||||
const QString& dest,
|
||||
const QString& from,
|
||||
const QString& to,
|
||||
const QString& via,
|
||||
const QString& type,
|
||||
const QString& pid,
|
||||
const QString& data
|
||||
)
|
||||
{
|
||||
return new MsgReportAPRS(source, dest, from, to, via, type, pid, data);
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_source;
|
||||
QString m_dest;
|
||||
QString m_from;
|
||||
QString m_to;
|
||||
QString m_via;
|
||||
QString m_type;
|
||||
QString m_pid;
|
||||
QString m_data;
|
||||
QByteArray m_packet;
|
||||
|
||||
MsgReportAPRS(
|
||||
const QString& source,
|
||||
const QString& dest,
|
||||
const QString& from,
|
||||
const QString& to,
|
||||
const QString& via,
|
||||
const QString& type,
|
||||
const QString& pid,
|
||||
const QString& data
|
||||
) :
|
||||
Message(),
|
||||
m_source(source),
|
||||
m_dest(dest),
|
||||
m_from(from),
|
||||
m_to(to),
|
||||
m_via(via),
|
||||
m_type(type),
|
||||
m_pid(pid),
|
||||
m_data(data)
|
||||
{ }
|
||||
};
|
||||
|
||||
M17Demod(DeviceAPI *deviceAPI);
|
||||
virtual ~M17Demod();
|
||||
virtual void destroy() { delete this; }
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <QMainWindow>
|
||||
#include <QDebug>
|
||||
#include <QScrollBar>
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
|
||||
#include <complex>
|
||||
|
||||
|
@ -30,6 +32,7 @@
|
|||
#include "plugin/pluginapi.h"
|
||||
#include "util/simpleserializer.h"
|
||||
#include "util/db.h"
|
||||
#include "util/csv.h"
|
||||
#include "gui/basicchannelsettingsdialog.h"
|
||||
#include "gui/devicestreamselectiondialog.h"
|
||||
#include "gui/crightclickenabler.h"
|
||||
|
@ -131,6 +134,43 @@ bool M17DemodGUI::handleMessage(const Message& message)
|
|||
|
||||
return true;
|
||||
}
|
||||
else if (M17Demod::MsgReportAPRS::match(message))
|
||||
{
|
||||
const M17Demod::MsgReportAPRS& report = (M17Demod::MsgReportAPRS&) message;
|
||||
// Is scroll bar at bottom
|
||||
QScrollBar *sb = ui->aprsPackets->verticalScrollBar();
|
||||
bool scrollToBottom = sb->value() == sb->maximum();
|
||||
|
||||
ui->aprsPackets->setSortingEnabled(false);
|
||||
int row = ui->aprsPackets->rowCount();
|
||||
ui->aprsPackets->setRowCount(row + 1);
|
||||
|
||||
QTableWidgetItem *fromItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *toItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *viaItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *typeItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *pidItem = new QTableWidgetItem();
|
||||
QTableWidgetItem *dataASCIIItem = new QTableWidgetItem();
|
||||
ui->aprsPackets->setItem(row, 0, fromItem);
|
||||
ui->aprsPackets->setItem(row, 1, toItem);
|
||||
ui->aprsPackets->setItem(row, 2, viaItem);
|
||||
ui->aprsPackets->setItem(row, 3, typeItem);
|
||||
ui->aprsPackets->setItem(row, 4, pidItem);
|
||||
ui->aprsPackets->setItem(row, 5, dataASCIIItem);
|
||||
fromItem->setText(report.getFrom());
|
||||
toItem->setText(report.getTo());
|
||||
viaItem->setText(report.getVia());
|
||||
typeItem->setText(report.getType());
|
||||
pidItem->setText(report.getPID());
|
||||
dataASCIIItem->setText(report.getData());
|
||||
ui->aprsPackets->setSortingEnabled(true);
|
||||
|
||||
if (scrollToBottom) {
|
||||
ui->aprsPackets->scrollToBottom();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
|
@ -239,6 +279,11 @@ void M17DemodGUI::on_highPassFilter_toggled(bool checked)
|
|||
applySettings();
|
||||
}
|
||||
|
||||
void M17DemodGUI::on_aprsClearTable_clicked()
|
||||
{
|
||||
ui->aprsPackets->setRowCount(0);
|
||||
}
|
||||
|
||||
void M17DemodGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
||||
{
|
||||
(void) widget;
|
||||
|
@ -655,6 +700,7 @@ void M17DemodGUI::makeUIConnections()
|
|||
QObject::connect(ui->highPassFilter, &ButtonSwitch::toggled, this, &M17DemodGUI::on_highPassFilter_toggled);
|
||||
QObject::connect(ui->audioMute, &QToolButton::toggled, this, &M17DemodGUI::on_audioMute_toggled);
|
||||
QObject::connect(ui->viewStatusLog, &QPushButton::clicked, this, &M17DemodGUI::on_viewStatusLog_clicked);
|
||||
QObject::connect(ui->aprsClearTable, &QPushButton::clicked, this, &M17DemodGUI::on_aprsClearTable_clicked);
|
||||
}
|
||||
|
||||
void M17DemodGUI::updateAbsoluteCenterFrequency()
|
||||
|
|
|
@ -111,6 +111,7 @@ private:
|
|||
void makeUIConnections();
|
||||
void updateAbsoluteCenterFrequency();
|
||||
QString getStatus(int status, bool streamElsePacket, int packetProtocol);
|
||||
void packetReceived(QByteArray packet);
|
||||
|
||||
void leaveEvent(QEvent*);
|
||||
void enterEvent(QEvent*);
|
||||
|
@ -129,6 +130,7 @@ private slots:
|
|||
void on_squelch_valueChanged(int value);
|
||||
void on_highPassFilter_toggled(bool checked);
|
||||
void on_audioMute_toggled(bool checked);
|
||||
void on_aprsClearTable_clicked();
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDialogCalled(const QPoint& p);
|
||||
void on_viewStatusLog_clicked();
|
||||
|
|
|
@ -732,7 +732,7 @@
|
|||
<enum>QTabWidget::East</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="settingsTab">
|
||||
<property name="toolTip">
|
||||
|
@ -1549,6 +1549,90 @@
|
|||
<attribute name="toolTip">
|
||||
<string>APRS</string>
|
||||
</attribute>
|
||||
<widget class="QTableWidget" name="aprsPackets">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>24</y>
|
||||
<width>480</width>
|
||||
<height>186</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Received packets</string>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>From</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Source callsign/address</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>To</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Destination callsign/address</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Via</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Repeater addresses</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>AX.25 frame type</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>PID</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Layer 3 protocol ID</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Data (ASCII)</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Packet data as ASCII</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
<widget class="QPushButton" name="aprsClearTable">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>24</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Clear packets from table</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../sdrgui/resources/res.qrc">
|
||||
<normaloff>:/bin.png</normaloff>:/bin.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <QDebug>
|
||||
|
||||
#include "audio/audiofifo.h"
|
||||
#include "util/ax25.h"
|
||||
|
||||
#include "m17/ax25_frame.h"
|
||||
#include "m17demod.h"
|
||||
|
@ -148,10 +149,6 @@ void M17DemodProcessor::diagnostic_callback(
|
|||
oss << buffer;
|
||||
qDebug() << "M17DemodProcessor::diagnostic_callback: " << oss.str().c_str();
|
||||
}
|
||||
|
||||
if (status == 0) { // unlocked
|
||||
m_this->resetInfo();
|
||||
}
|
||||
}
|
||||
|
||||
bool M17DemodProcessor::decode_lich(mobilinkd::M17FrameDecoder::lich_buffer_t const& lich)
|
||||
|
@ -361,9 +358,9 @@ bool M17DemodProcessor::decode_packet(mobilinkd::M17FrameDecoder::packet_buffer_
|
|||
oss << *it;
|
||||
}
|
||||
|
||||
qDebug() << "M17DemodProcessor::decode_packet: "
|
||||
<< " From:" << getSrcCall()
|
||||
<< " To:" << getDestcCall()
|
||||
qDebug() << "M17DemodProcessor::decode SMS_packet: "
|
||||
<< " Src:" << getSrcCall()
|
||||
<< " Dest:" << getDestcCall()
|
||||
<< " SMS:" << oss.str().c_str();
|
||||
|
||||
if (m_demodInputMessageQueue)
|
||||
|
@ -376,6 +373,40 @@ bool M17DemodProcessor::decode_packet(mobilinkd::M17FrameDecoder::packet_buffer_
|
|||
m_demodInputMessageQueue->push(msg);
|
||||
}
|
||||
}
|
||||
else if (m_stdPacketProtocol == StdPacketAPRS)
|
||||
{
|
||||
AX25Packet ax25;
|
||||
QByteArray packet = QByteArray(reinterpret_cast<const char*>(&m_currentPacket[1]), m_currentPacket.size()-3);
|
||||
|
||||
if (ax25.decode(packet))
|
||||
{
|
||||
qDebug() << "M17DemodProcessor::decode APRS_packet: "
|
||||
<< " Src:" << getSrcCall()
|
||||
<< " Dest:" << getDestcCall()
|
||||
<< " From:" << ax25.m_from
|
||||
<< " To: " << ax25.m_to
|
||||
<< " Via: " << ax25.m_via
|
||||
<< " Type: " << ax25.m_type
|
||||
<< " PID: " << ax25.m_pid
|
||||
<< " Data: " << ax25.m_dataASCII;
|
||||
|
||||
if (m_demodInputMessageQueue)
|
||||
{
|
||||
M17Demod::MsgReportAPRS *msg = M17Demod::MsgReportAPRS::create(
|
||||
getSrcCall(),
|
||||
getDestcCall(),
|
||||
ax25.m_from,
|
||||
ax25.m_to,
|
||||
ax25.m_via,
|
||||
ax25.m_type,
|
||||
ax25.m_pid,
|
||||
ax25.m_dataASCII
|
||||
);
|
||||
msg->getPacket() = packet;
|
||||
m_demodInputMessageQueue->push(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ struct M17DemodSettings
|
|||
int m_traceStroke; // [0..255]
|
||||
int m_traceDecay; // [0..255]
|
||||
QString m_audioDeviceName;
|
||||
QString m_aprsLogFilename;
|
||||
bool m_aprsLogEnabled;
|
||||
int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx).
|
||||
bool m_useReverseAPI;
|
||||
QString m_reverseAPIAddress;
|
||||
|
|
|
@ -174,7 +174,10 @@ void M17DemodSink::feed(const SampleVector::const_iterator& begin, const SampleV
|
|||
|
||||
if (m_squelchWasOpen)
|
||||
{
|
||||
m_m17DemodProcessor.resetInfo();
|
||||
if (m_m17DemodProcessor.getStreamElsePacket()) { // if packet kepp last values
|
||||
m_m17DemodProcessor.resetInfo();
|
||||
}
|
||||
|
||||
m_m17DemodProcessor.setDCDOff(); // indicate loss of carrier
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ set(modm17_SOURCES
|
|||
m17modprocessor.cpp
|
||||
m17modfifo.cpp
|
||||
m17moddecimator.cpp
|
||||
m17modax25.cpp
|
||||
m17modplugin.cpp
|
||||
m17modsettings.cpp
|
||||
m17modwebapiadapter.cpp
|
||||
|
@ -19,6 +20,7 @@ set(modm17_HEADERS
|
|||
m17modprocessor.h
|
||||
m17modfifo.h
|
||||
m17moddecimator.h
|
||||
m17modax25.h
|
||||
m17modplugin.h
|
||||
m17modsettings.h
|
||||
m17modwebapiadapter.h
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// 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 <QString>
|
||||
#include <array>
|
||||
#include "util/crc.h"
|
||||
#include "m17modax25.h"
|
||||
|
||||
M17ModAX25::M17ModAX25() :
|
||||
m_ax25Control(3),
|
||||
m_ax25PID(AX25_NO_L3)
|
||||
{}
|
||||
|
||||
M17ModAX25::~M17ModAX25()
|
||||
{}
|
||||
|
||||
QByteArray M17ModAX25::makePacket(const QString& callsign, const QString& to, const QString& via, const QString& data)
|
||||
{
|
||||
std::array<uint8_t, AX25_MAX_BYTES> packet;
|
||||
uint8_t *crc_start;
|
||||
uint8_t *p;
|
||||
crc16x25 crc;
|
||||
uint16_t crcValue;
|
||||
int len;
|
||||
int packet_length;
|
||||
|
||||
// Create AX.25 packet
|
||||
p = packet.data();
|
||||
// Unique preamble flag
|
||||
// *p++ = AX25_FLAG;
|
||||
crc_start = p;
|
||||
// Dest
|
||||
p = ax25_address(p, to, 0xe0);
|
||||
// From
|
||||
p = ax25_address(p, callsign, 0x60);
|
||||
// Via
|
||||
p = ax25_address(p, via, 0x61);
|
||||
// Control
|
||||
*p++ = m_ax25Control;
|
||||
// PID
|
||||
*p++ = m_ax25PID;
|
||||
// Data
|
||||
len = data.length();
|
||||
memcpy(p, data.toUtf8(), len);
|
||||
p += len;
|
||||
// CRC (do not include flags)
|
||||
crc.calculate(crc_start, p-crc_start);
|
||||
crcValue = crc.get();
|
||||
*p++ = crcValue & 0xff;
|
||||
*p++ = (crcValue >> 8);
|
||||
// Unique postamble flag
|
||||
// *p++ = AX25_FLAG;
|
||||
|
||||
packet_length = p-&packet[0];
|
||||
|
||||
return QByteArray(reinterpret_cast<const char*>(packet.data()), packet_length);
|
||||
}
|
||||
|
||||
uint8_t *M17ModAX25::ax25_address(uint8_t *p, QString address, uint8_t crrl)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
QByteArray b;
|
||||
uint8_t ssid = 0;
|
||||
bool hyphenSeen = false;
|
||||
|
||||
len = address.length();
|
||||
b = address.toUtf8();
|
||||
ssid = 0;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
if ((i < len) && !hyphenSeen)
|
||||
{
|
||||
if (b[i] == '-')
|
||||
{
|
||||
ax25_ssid(b, i, len, ssid);
|
||||
hyphenSeen = true;
|
||||
*p++ = ' ' << 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*p++ = b[i] << 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*p++ = ' ' << 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (b[i] == '-') {
|
||||
ax25_ssid(b, i, len, ssid);
|
||||
}
|
||||
|
||||
*p++ = crrl | (ssid << 1);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
bool M17ModAX25::ax25_ssid(QByteArray& b, int i, int len, uint8_t& ssid)
|
||||
{
|
||||
if (b[i] == '-')
|
||||
{
|
||||
if (len > i + 1)
|
||||
{
|
||||
ssid = b[i+1] - '0';
|
||||
|
||||
if ((len > i + 2) && isdigit(b[i+2])) {
|
||||
ssid = (ssid*10) + (b[i+2] - '0');
|
||||
}
|
||||
|
||||
if (ssid >= 16)
|
||||
{
|
||||
qDebug("M17ModAX25::ax25_ssid: ax25_address: SSID greater than 15 not supported");
|
||||
ssid = ssid & 0xf;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug("M17ModAX25::ax25_ssid: ax25_address: SSID number missing");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) 2022 Edouard Griffiths, F4EXB //
|
||||
// //
|
||||
// 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 PLUGINS_CHANNELTX_MODM17_M17MODAX25_H_
|
||||
#define PLUGINS_CHANNELTX_MODM17_M17MODAX25_H_
|
||||
|
||||
#include <QByteArray>
|
||||
#include <cstdint>
|
||||
|
||||
class QString;
|
||||
|
||||
class M17ModAX25
|
||||
{
|
||||
public:
|
||||
M17ModAX25();
|
||||
~M17ModAX25();
|
||||
void setAX25Control(int ax25Control) { m_ax25Control = ax25Control; }
|
||||
void setAX25PID(int ax25PID) { m_ax25PID = ax25PID; }
|
||||
QByteArray makePacket(const QString& callsign, const QString& to, const QString& via, const QString& data);
|
||||
|
||||
static const int AX25_MAX_FLAGS = 1024;
|
||||
static const int AX25_MAX_BYTES = (2*AX25_MAX_FLAGS+1+28+2+256+2+1);
|
||||
static const int AX25_MAX_BITS = (AX25_MAX_BYTES*2);
|
||||
static const uint8_t AX25_FLAG = 0x7e;
|
||||
static const uint8_t AX25_NO_L3 = 0xf0;
|
||||
|
||||
private:
|
||||
int m_ax25Control;
|
||||
int m_ax25PID;
|
||||
|
||||
static uint8_t *ax25_address(uint8_t *p, QString address, uint8_t crrl);
|
||||
static bool ax25_ssid(QByteArray& b, int i, int len, uint8_t& ssid);
|
||||
};
|
||||
|
||||
#endif // PLUGINS_CHANNELTX_MODM17_M17MODAX25_H_
|
||||
|
|
@ -347,6 +347,36 @@ void M17ModGUI::on_smsText_editingFinished()
|
|||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_aprsFromText_editingFinished()
|
||||
{
|
||||
m_settings.m_aprsCallsign = ui->aprsFromText->text();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_aprsTo_currentTextChanged(const QString &text)
|
||||
{
|
||||
m_settings.m_aprsTo = text;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_aprsVia_currentTextChanged(const QString &text)
|
||||
{
|
||||
m_settings.m_aprsVia = text;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_aprsData_editingFinished()
|
||||
{
|
||||
m_settings.m_aprsData = ui->aprsData->text();
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::on_aprsInsertPosition_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_aprsInsertPosition = checked;
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void M17ModGUI::configureFileName()
|
||||
{
|
||||
qDebug() << "M17ModGUI::configureFileName: " << m_fileName.toStdString().c_str();
|
||||
|
@ -556,6 +586,7 @@ void M17ModGUI::displaySettings()
|
|||
ui->aprsData->setText(m_settings.m_aprsData);
|
||||
ui->aprsTo->lineEdit()->setText(m_settings.m_aprsTo);
|
||||
ui->aprsVia->lineEdit()->setText(m_settings.m_aprsVia);
|
||||
ui->aprsInsertPosition->setChecked(m_settings.m_aprsInsertPosition);
|
||||
|
||||
getRollupContents()->restoreState(m_rollupState);
|
||||
updateAbsoluteCenterFrequency();
|
||||
|
@ -764,7 +795,13 @@ void M17ModGUI::makeUIConnections()
|
|||
QObject::connect(ui->sendPacket, &QPushButton::clicked, this, &M17ModGUI::on_sendPacket_clicked);
|
||||
QObject::connect(ui->loopPacket, &ButtonSwitch::toggled, this, &M17ModGUI::on_loopPacket_toggled);
|
||||
QObject::connect(ui->loopPacketInterval, &QDial::valueChanged, this, &M17ModGUI::on_loopPacketInterval_valueChanged);
|
||||
QObject::connect(ui->packetDataWidget, &QTabWidget::currentChanged, this, &M17ModGUI::on_packetDataWidget_currentChanged);
|
||||
QObject::connect(ui->smsText, &CustomTextEdit::editingFinished, this, &M17ModGUI::on_smsText_editingFinished);
|
||||
QObject::connect(ui->aprsFromText, &QLineEdit::editingFinished, this, &M17ModGUI::on_aprsFromText_editingFinished);
|
||||
QObject::connect(ui->aprsTo, &QComboBox::currentTextChanged, this, &M17ModGUI::on_aprsTo_currentTextChanged);
|
||||
QObject::connect(ui->aprsVia, &QComboBox::currentTextChanged, this, &M17ModGUI::on_aprsVia_currentTextChanged);
|
||||
QObject::connect(ui->aprsData, &QLineEdit::editingFinished, this, &M17ModGUI::on_aprsData_editingFinished);
|
||||
QObject::connect(ui->aprsInsertPosition, &ButtonSwitch::toggled, this, &M17ModGUI::on_aprsInsertPosition_toggled);
|
||||
QObject::connect(ui->source, &QLineEdit::editingFinished, this, &M17ModGUI::on_source_editingFinished);
|
||||
QObject::connect(ui->destination, &QLineEdit::editingFinished, this, &M17ModGUI::on_destination_editingFinished);
|
||||
QObject::connect(ui->can, QOverload<int>::of(&QSpinBox::valueChanged), this, &M17ModGUI::on_can_valueChanged);
|
||||
|
|
|
@ -142,8 +142,15 @@ private slots:
|
|||
void on_destination_editingFinished();
|
||||
void on_insertPosition_toggled(bool checked);
|
||||
void on_can_valueChanged(int value);
|
||||
|
||||
void on_smsText_editingFinished();
|
||||
|
||||
void on_aprsFromText_editingFinished();
|
||||
void on_aprsTo_currentTextChanged(const QString &text);
|
||||
void on_aprsVia_currentTextChanged(const QString &text);
|
||||
void on_aprsData_editingFinished();
|
||||
void on_aprsInsertPosition_toggled(bool checked);
|
||||
|
||||
void onWidgetRolled(QWidget* widget, bool rollDown);
|
||||
void onMenuDialogCalled(const QPoint& p);
|
||||
|
||||
|
|
|
@ -18,10 +18,13 @@
|
|||
#include <codec2/codec2.h>
|
||||
|
||||
#include "m17/M17Modulator.h"
|
||||
#include "maincore.h"
|
||||
|
||||
#include "m17modax25.h"
|
||||
#include "m17modprocessor.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(M17ModProcessor::MsgSendSMS, Message)
|
||||
MESSAGE_CLASS_DEFINITION(M17ModProcessor::MsgSendAPRS, Message)
|
||||
MESSAGE_CLASS_DEFINITION(M17ModProcessor::MsgSendAudioFrame, Message)
|
||||
MESSAGE_CLASS_DEFINITION(M17ModProcessor::MsgStartAudio, Message)
|
||||
MESSAGE_CLASS_DEFINITION(M17ModProcessor::MsgStopAudio, Message)
|
||||
|
@ -57,6 +60,24 @@ bool M17ModProcessor::handleMessage(const Message& cmd)
|
|||
// test(notif.getSourceCall(), notif.getDestCall());
|
||||
return true;
|
||||
}
|
||||
else if (MsgSendAPRS::match(cmd))
|
||||
{
|
||||
const MsgSendAPRS& notif = (const MsgSendAPRS&) cmd;
|
||||
M17ModAX25 modAX25;
|
||||
QString strData;
|
||||
|
||||
if (notif.getInsertPosition()) {
|
||||
strData += "!" + formatAPRSPosition();
|
||||
} else {
|
||||
strData = notif.getData();
|
||||
}
|
||||
|
||||
QByteArray packetBytes = modAX25.makePacket(notif.getCall(), notif.getTo(), notif.getVia(), strData);
|
||||
packetBytes.prepend(0x02); // APRS standard type
|
||||
packetBytes.truncate(798); // Maximum packet size is 798 payload + 2 bytes CRC = 800 bytes (32*25)
|
||||
processPacket(notif.getSourceCall(), notif.getDestCall(), notif.getCAN(), packetBytes);
|
||||
return true;
|
||||
}
|
||||
else if (MsgSendAudioFrame::match(cmd))
|
||||
{
|
||||
MsgSendAudioFrame& notif = (MsgSendAudioFrame&) cmd;
|
||||
|
@ -248,3 +269,47 @@ void M17ModProcessor::output_baseband(std::array<uint8_t, 2> sync_word, const st
|
|||
std::array<int16_t, 1920> baseband = m_m17Modulator.symbols_to_baseband(temp); // 1920 48 kS/s int16_t samples
|
||||
m_basebandFifo.write(baseband.data(), 1920);
|
||||
}
|
||||
|
||||
QString M17ModProcessor::formatAPRSPosition()
|
||||
{
|
||||
float latitude = MainCore::instance()->getSettings().getLatitude();
|
||||
float longitude = MainCore::instance()->getSettings().getLongitude();
|
||||
|
||||
int latDeg, latMin, latFrac, latNorth;
|
||||
int longDeg, longMin, longFrac, longEast;
|
||||
|
||||
// Convert decimal latitude to degrees, min and hundreths of a minute
|
||||
latNorth = latitude >= 0.0f;
|
||||
latitude = abs(latitude);
|
||||
latDeg = (int) latitude;
|
||||
latitude -= (float) latDeg;
|
||||
latitude *= 60.0f;
|
||||
latMin = (int) latitude;
|
||||
latitude -= (float) latMin;
|
||||
latitude *= 100.0f;
|
||||
latFrac = round(latitude);
|
||||
|
||||
// Convert decimal longitude
|
||||
longEast = longitude >= 0.0f;
|
||||
longitude = abs(longitude);
|
||||
longDeg = (int) longitude;
|
||||
longitude -= (float) longDeg;
|
||||
longitude *= 60.0f;
|
||||
longMin = (int) longitude;
|
||||
longitude -= (float) longMin;
|
||||
longitude *= 100.0f;
|
||||
longFrac = round(longitude);
|
||||
|
||||
// Insert position with house symbol (-) in to data field
|
||||
QString latStr = QString("%1%2.%3%4")
|
||||
.arg(latDeg, 2, 10, QChar('0'))
|
||||
.arg(latMin, 2, 10, QChar('0'))
|
||||
.arg(latFrac, 2, 10, QChar('0'))
|
||||
.arg(latNorth ? 'N' : 'S');
|
||||
QString longStr = QString("%1%2.%3%4")
|
||||
.arg(longDeg, 3, 10, QChar('0'))
|
||||
.arg(longMin, 2, 10, QChar('0'))
|
||||
.arg(longFrac, 2, 10, QChar('0'))
|
||||
.arg(longEast ? 'E' : 'W');
|
||||
return QString("%1/%2-").arg(latStr).arg(longStr);
|
||||
}
|
||||
|
|
|
@ -60,6 +60,65 @@ public:
|
|||
{ }
|
||||
};
|
||||
|
||||
class MsgSendAPRS : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
const QString& getSourceCall() const { return m_sourceCall; }
|
||||
const QString& getDestCall() const { return m_destCall; }
|
||||
uint8_t getCAN() const { return m_can; }
|
||||
const QString& getCall() const { return m_call; }
|
||||
const QString& getTo() const { return m_to; }
|
||||
const QString& getVia() const { return m_via; }
|
||||
const QString& getData() const { return m_data; }
|
||||
bool getInsertPosition() const { return m_insertPosition; }
|
||||
|
||||
static MsgSendAPRS* create(
|
||||
const QString& sourceCall,
|
||||
const QString& destCall,
|
||||
uint8_t can,
|
||||
const QString& call,
|
||||
const QString& to,
|
||||
const QString& via,
|
||||
const QString& data,
|
||||
bool insertPosition
|
||||
)
|
||||
{
|
||||
return new MsgSendAPRS(sourceCall, destCall, can, call, to, via, data, insertPosition);
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_sourceCall;
|
||||
QString m_destCall;
|
||||
uint8_t m_can;
|
||||
QString m_call;
|
||||
QString m_to;
|
||||
QString m_via;
|
||||
QString m_data;
|
||||
bool m_insertPosition;
|
||||
|
||||
MsgSendAPRS(
|
||||
const QString& sourceCall,
|
||||
const QString& destCall,
|
||||
uint8_t can,
|
||||
const QString& call,
|
||||
const QString& to,
|
||||
const QString& via,
|
||||
const QString& data,
|
||||
bool insertPosition
|
||||
) :
|
||||
Message(),
|
||||
m_sourceCall(sourceCall),
|
||||
m_destCall(destCall),
|
||||
m_can(can),
|
||||
m_call(call),
|
||||
m_to(to),
|
||||
m_via(via),
|
||||
m_data(data),
|
||||
m_insertPosition(insertPosition)
|
||||
{ }
|
||||
};
|
||||
|
||||
class MsgSendAudioFrame : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
|
@ -154,6 +213,7 @@ private:
|
|||
void send_preamble();
|
||||
void send_eot();
|
||||
void output_baseband(std::array<uint8_t, 2> sync_word, const std::array<int8_t, 368>& frame);
|
||||
QString formatAPRSPosition();
|
||||
|
||||
private slots:
|
||||
void handleInputMessages();
|
||||
|
|
|
@ -577,4 +577,18 @@ void M17ModSource::sendPacket()
|
|||
);
|
||||
m_processor->getInputMessageQueue()->push(msg);
|
||||
}
|
||||
else if (m_settings.m_packetType == M17ModSettings::PacketType::PacketAPRS)
|
||||
{
|
||||
M17ModProcessor::MsgSendAPRS *msg = M17ModProcessor::MsgSendAPRS::create(
|
||||
m_settings.m_sourceCall,
|
||||
m_settings.m_destCall,
|
||||
m_settings.m_can,
|
||||
m_settings.m_aprsCallsign,
|
||||
m_settings.m_aprsTo,
|
||||
m_settings.m_aprsVia,
|
||||
m_settings.m_aprsData,
|
||||
m_settings.m_aprsInsertPosition
|
||||
);
|
||||
m_processor->getInputMessageQueue()->push(msg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,12 +25,14 @@
|
|||
|
||||
const QStringList APRSSettings::m_pipeTypes = {
|
||||
QStringLiteral("PacketDemod"),
|
||||
QStringLiteral("ChirpChatDemod")
|
||||
QStringLiteral("ChirpChatDemod"),
|
||||
QStringLiteral("M17Demod")
|
||||
};
|
||||
|
||||
const QStringList APRSSettings::m_pipeURIs = {
|
||||
QStringLiteral("sdrangel.channel.packetdemod"),
|
||||
QStringLiteral("sdrangel.channel.chirpchatdemod")
|
||||
QStringLiteral("sdrangel.channel.chirpchatdemod"),
|
||||
QStringLiteral("sdrangel.channel.m17demod")
|
||||
};
|
||||
|
||||
const QStringList APRSSettings::m_altitudeUnitNames = {
|
||||
|
|
Loading…
Reference in New Issue