mirror of
https://github.com/g4klx/MMDVMHost.git
synced 2026-02-14 11:34:15 +01:00
Merge branch 'master' into mqtt
This commit is contained in:
commit
d085ca8b0f
405
AX25Control.cpp
405
AX25Control.cpp
|
|
@ -1,405 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2023,2025 Jonathan Naylor, G4KLX
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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 for more details.
|
||||
*/
|
||||
|
||||
#include "AX25Control.h"
|
||||
#include "AX25Defines.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
#if defined(USE_AX25)
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
|
||||
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
|
||||
|
||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
CAX25Control::CAX25Control(CAX25Network* network, bool trace, CRSSIInterpolator* rssiMapper) :
|
||||
m_network(network),
|
||||
m_trace(trace),
|
||||
m_rssiMapper(rssiMapper),
|
||||
m_enabled(true)
|
||||
{
|
||||
assert(rssiMapper != nullptr);
|
||||
}
|
||||
|
||||
CAX25Control::~CAX25Control()
|
||||
{
|
||||
}
|
||||
|
||||
bool CAX25Control::writeModem(unsigned char *data, unsigned int len)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
if (!m_enabled)
|
||||
return false;
|
||||
|
||||
bool hasRSSI = data[0U] == TAG_RSSI;
|
||||
|
||||
unsigned int offset = hasRSSI ? 3U : 1U;
|
||||
|
||||
int rssi = 0;
|
||||
if (hasRSSI) {
|
||||
uint16_t raw = 0U;
|
||||
raw |= (data[1U] << 8) & 0xFF00U;
|
||||
raw |= (data[2U] << 0) & 0x00FFU;
|
||||
|
||||
// Convert the raw RSSI to dBm
|
||||
rssi = m_rssiMapper->interpolate(raw);
|
||||
if (rssi != 0)
|
||||
LogDebug("AX.25, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
|
||||
}
|
||||
|
||||
if (m_trace)
|
||||
decode(data + offset, len - offset);
|
||||
|
||||
decodeJSON("rf", data + offset, len - offset, rssi);
|
||||
|
||||
CUtils::dump(1U, "AX.25 received packet", data, len);
|
||||
|
||||
if (m_network == nullptr)
|
||||
return true;
|
||||
|
||||
return m_network->write(data + offset, len - offset);
|
||||
}
|
||||
|
||||
unsigned int CAX25Control::readModem(unsigned char* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
if (m_network == nullptr)
|
||||
return 0U;
|
||||
|
||||
if (!m_enabled)
|
||||
return 0U;
|
||||
|
||||
unsigned int length = m_network->read(data, 500U);
|
||||
|
||||
decodeJSON("network", data, length);
|
||||
|
||||
if (length > 0U)
|
||||
CUtils::dump(1U, "AX.25 transmitted packet", data, length);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void CAX25Control::enable(bool enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
}
|
||||
|
||||
void CAX25Control::decode(const unsigned char* data, unsigned int length)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
assert(length >= 15U);
|
||||
|
||||
std::string text;
|
||||
|
||||
bool more = decodeAddress(data + 7U, text);
|
||||
|
||||
text += '>';
|
||||
|
||||
decodeAddress(data + 0U, text);
|
||||
|
||||
unsigned int n = 14U;
|
||||
while (more && n < length) {
|
||||
text += ',';
|
||||
more = decodeAddress(data + n, text, true);
|
||||
n += 7U;
|
||||
}
|
||||
|
||||
text += ' ';
|
||||
|
||||
if ((data[n] & 0x01U) == 0x00U) {
|
||||
// I frame
|
||||
char t[20U];
|
||||
::sprintf(t, "<I S%u R%u>", (data[n] >> 1) & 0x07U, (data[n] >> 5) & 0x07U);
|
||||
text += t;
|
||||
} else {
|
||||
if ((data[n] & 0x02U) == 0x00U) {
|
||||
// S frame
|
||||
char t[20U];
|
||||
switch (data[n] & 0x0FU) {
|
||||
case 0x01U:
|
||||
::sprintf(t, "<RR R%u>", (data[n] >> 5) & 0x07U);
|
||||
break;
|
||||
case 0x05U:
|
||||
::sprintf(t, "<RNR R%u>", (data[n] >> 5) & 0x07U);
|
||||
break;
|
||||
case 0x09U:
|
||||
::sprintf(t, "<REJ R%u>", (data[n] >> 5) & 0x07U);
|
||||
break;
|
||||
case 0x0DU:
|
||||
::sprintf(t, "<SREJ R%u>", (data[n] >> 5) & 0x07U);
|
||||
break;
|
||||
default:
|
||||
::sprintf(t, "<Unknown R%u>", (data[n] >> 5) & 0x07U);
|
||||
break;
|
||||
}
|
||||
|
||||
text += t;
|
||||
LogMessage("AX.25, %s", text.c_str());
|
||||
return;
|
||||
} else {
|
||||
// U frame
|
||||
switch (data[n] & 0xEFU) {
|
||||
case 0x6FU:
|
||||
text += "<SABME>";
|
||||
break;
|
||||
case 0x2FU:
|
||||
text += "<SABM>";
|
||||
break;
|
||||
case 0x43U:
|
||||
text += "<DISC>";
|
||||
break;
|
||||
case 0x0FU:
|
||||
text += "<DM>";
|
||||
break;
|
||||
case 0x63U:
|
||||
text += "<UA>";
|
||||
break;
|
||||
case 0x87U:
|
||||
text += "<FRMR>";
|
||||
break;
|
||||
case 0x03U:
|
||||
text += "<UI>";
|
||||
break;
|
||||
case 0xAFU:
|
||||
text += "<XID>";
|
||||
break;
|
||||
case 0xE3U:
|
||||
text += "<TEST>";
|
||||
break;
|
||||
default:
|
||||
text += "<Unknown>";
|
||||
break;
|
||||
}
|
||||
|
||||
if ((data[n] & 0xEFU) != 0x03U) {
|
||||
LogMessage("AX.25, %s", text.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n += 2U;
|
||||
|
||||
LogMessage("AX.25, %s %.*s", text.c_str(), length - n, data + n);
|
||||
}
|
||||
|
||||
void CAX25Control::decodeJSON(const char* source, const unsigned char* data, unsigned int length, int rssi)
|
||||
{
|
||||
assert(source != nullptr);
|
||||
assert(data != nullptr);
|
||||
assert(length >= 15U);
|
||||
|
||||
nlohmann::json json;
|
||||
|
||||
json["timestamp"] = CUtils::createTimestamp();
|
||||
json["source"] = source;
|
||||
|
||||
std::string text;
|
||||
|
||||
bool isDigi;
|
||||
bool more = decodeAddressJSON(data + 7U, text, isDigi);
|
||||
json["source_cs"] = text;
|
||||
|
||||
decodeAddressJSON(data + 0U, text, isDigi);
|
||||
json["destination_cs"] = text;
|
||||
|
||||
if (rssi != 0)
|
||||
json["rssi"] = rssi;
|
||||
|
||||
unsigned int n = 14U;
|
||||
|
||||
if (more) {
|
||||
while (more && n < length) {
|
||||
nlohmann::json digi;
|
||||
|
||||
more = decodeAddressJSON(data + n, text, isDigi);
|
||||
n += 7U;
|
||||
|
||||
digi["callsign"] = text;
|
||||
digi["repeated"] = isDigi;
|
||||
|
||||
json["digipeaters"] = digi;
|
||||
}
|
||||
}
|
||||
|
||||
if ((data[n] & 0x01U) == 0x00U) {
|
||||
// I frame
|
||||
char t[20U];
|
||||
::sprintf(t, "I S%u R%u", (data[n] >> 1) & 0x07U, (data[n] >> 5) & 0x07U);
|
||||
json["type"] = t;
|
||||
} else {
|
||||
if ((data[n] & 0x02U) == 0x00U) {
|
||||
// S frame
|
||||
char t[20U];
|
||||
switch (data[n] & 0x0FU) {
|
||||
case 0x01U:
|
||||
::sprintf(t, "RR R%u", (data[n] >> 5) & 0x07U);
|
||||
break;
|
||||
case 0x05U:
|
||||
::sprintf(t, "RNR R%u", (data[n] >> 5) & 0x07U);
|
||||
break;
|
||||
case 0x09U:
|
||||
::sprintf(t, "REJ R%u", (data[n] >> 5) & 0x07U);
|
||||
break;
|
||||
case 0x0DU:
|
||||
::sprintf(t, "SREJ R%u", (data[n] >> 5) & 0x07U);
|
||||
break;
|
||||
default:
|
||||
::sprintf(t, "Unknown R%u", (data[n] >> 5) & 0x07U);
|
||||
break;
|
||||
}
|
||||
|
||||
json["type"] = t;
|
||||
WriteJSON("AX.25", json);
|
||||
return;
|
||||
} else {
|
||||
// U frame
|
||||
switch (data[n] & 0xEFU) {
|
||||
case 0x6FU:
|
||||
text = "SABME";
|
||||
break;
|
||||
case 0x2FU:
|
||||
text = "SABM";
|
||||
break;
|
||||
case 0x43U:
|
||||
text = "DISC";
|
||||
break;
|
||||
case 0x0FU:
|
||||
text = "DM";
|
||||
break;
|
||||
case 0x63U:
|
||||
text = "UA";
|
||||
break;
|
||||
case 0x87U:
|
||||
text = "FRMR";
|
||||
break;
|
||||
case 0x03U:
|
||||
text = "UI";
|
||||
break;
|
||||
case 0xAFU:
|
||||
text = "XID";
|
||||
break;
|
||||
case 0xE3U:
|
||||
text = "TEST";
|
||||
break;
|
||||
default:
|
||||
text = "Unknown>";
|
||||
break;
|
||||
}
|
||||
|
||||
json["type"] = text;
|
||||
|
||||
if ((data[n] & 0xEFU) != 0x03U) {
|
||||
WriteJSON("AX.25", json);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n++;
|
||||
|
||||
char buffer[5U];
|
||||
::sprintf(buffer, "%02X", data[n]);
|
||||
json["pid"] = buffer;
|
||||
|
||||
n++;
|
||||
|
||||
text.clear();
|
||||
|
||||
for (unsigned int i = 0U; i < (length - n); i++) {
|
||||
char buffer[5U];
|
||||
::sprintf(buffer, "%02X ", data[n + i]);
|
||||
text += buffer;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0U; i < (length - n); i++) {
|
||||
char c = data[n + i];
|
||||
|
||||
if (::isprint(c) || c == ' ')
|
||||
text += c;
|
||||
else
|
||||
text += '.';
|
||||
}
|
||||
|
||||
json["data"] = text;
|
||||
|
||||
WriteJSON("AX.25", json);
|
||||
}
|
||||
|
||||
bool CAX25Control::decodeAddress(const unsigned char* data, std::string& text, bool isDigi) const
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
for (unsigned int i = 0U; i < 6U; i++) {
|
||||
char c = data[i] >> 1;
|
||||
if (c != ' ')
|
||||
text += c;
|
||||
}
|
||||
|
||||
unsigned char ssid = (data[6U] >> 1) & 0x0FU;
|
||||
if (ssid > 0U) {
|
||||
text += '-';
|
||||
if (ssid >= 10U) {
|
||||
text += '1';
|
||||
text += '0' + ssid - 10U;
|
||||
} else {
|
||||
text += '0' + ssid;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDigi) {
|
||||
if ((data[6U] & 0x80U) == 0x80U)
|
||||
text += '*';
|
||||
}
|
||||
|
||||
return (data[6U] & 0x01U) == 0x00U;
|
||||
}
|
||||
|
||||
bool CAX25Control::decodeAddressJSON(const unsigned char* data, std::string& text, bool& isDigi) const
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
text.clear();
|
||||
|
||||
for (unsigned int i = 0U; i < 6U; i++) {
|
||||
char c = data[i] >> 1;
|
||||
if (c != ' ')
|
||||
text += c;
|
||||
}
|
||||
|
||||
unsigned char ssid = (data[6U] >> 1) & 0x0FU;
|
||||
if (ssid > 0U) {
|
||||
text += '-';
|
||||
if (ssid >= 10U) {
|
||||
text += '1';
|
||||
text += '0' + ssid - 10U;
|
||||
} else {
|
||||
text += '0' + ssid;
|
||||
}
|
||||
}
|
||||
|
||||
isDigi = (data[6U] & 0x80U) == 0x80U;
|
||||
|
||||
return (data[6U] & 0x01U) == 0x00U;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2023 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(AX25Control_H)
|
||||
#define AX25Control_H
|
||||
|
||||
#include "RSSIInterpolator.h"
|
||||
#include "AX25Network.h"
|
||||
#include "Defines.h"
|
||||
|
||||
#if defined(USE_AX25)
|
||||
|
||||
#include <string>
|
||||
|
||||
class CAX25Control {
|
||||
public:
|
||||
CAX25Control(CAX25Network* network, bool trace, CRSSIInterpolator* rssiMapper);
|
||||
~CAX25Control();
|
||||
|
||||
bool writeModem(unsigned char* data, unsigned int len);
|
||||
|
||||
unsigned int readModem(unsigned char* data);
|
||||
|
||||
void enable(bool enabled);
|
||||
|
||||
private:
|
||||
CAX25Network* m_network;
|
||||
bool m_trace;
|
||||
CRSSIInterpolator* m_rssiMapper;
|
||||
bool m_enabled;
|
||||
|
||||
void decode(const unsigned char* data, unsigned int length);
|
||||
void decodeJSON(const char* source, const unsigned char* data, unsigned int length, int rssi = 0);
|
||||
bool decodeAddress(const unsigned char* data, std::string& text, bool isDigi = false) const;
|
||||
bool decodeAddressJSON(const unsigned char* data, std::string& text, bool& isDigi) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(AX25Defines_H)
|
||||
#define AX25Defines_H
|
||||
|
||||
const unsigned int AX25_CALLSIGN_TEXT_LENGTH = 6U;
|
||||
const unsigned int AX25_SSID_LENGTH = 1U;
|
||||
const unsigned int AX25_CALLSIGN_LENGTH = 7U;
|
||||
|
||||
const unsigned int AX25_MAX_DIGIPEATERS = 6U;
|
||||
|
||||
const unsigned char AX25_PID_NOL3 = 0xF0U;
|
||||
|
||||
const unsigned int AX25_MAX_FRAME_LENGTH_BYTES = 330U; // Callsign (7) + Callsign (7) + 8 Digipeaters (56) +
|
||||
// Control (1) + PID (1) + Data (256) + Checksum (2)
|
||||
const unsigned char AX25_KISS_DATA = 0x00U;
|
||||
|
||||
const unsigned char AX25_FEND = 0xC0U;
|
||||
const unsigned char AX25_FESC = 0xDBU;
|
||||
const unsigned char AX25_TFEND = 0xDCU;
|
||||
const unsigned char AX25_TFESC = 0xDDU;
|
||||
|
||||
#endif
|
||||
185
AX25Network.cpp
185
AX25Network.cpp
|
|
@ -1,185 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2023,2025 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "AX25Network.h"
|
||||
#include "AX25Defines.h"
|
||||
#include "MQTTConnection.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
#if defined(USE_AX25)
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
// In Log.cpp
|
||||
extern CMQTTConnection* m_mqtt;
|
||||
|
||||
CAX25Network::CAX25Network(bool debug) :
|
||||
m_buffer(1000U, "AX.25 buffer"),
|
||||
m_mutex(),
|
||||
m_debug(debug),
|
||||
m_enabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
CAX25Network::~CAX25Network()
|
||||
{
|
||||
}
|
||||
|
||||
bool CAX25Network::open()
|
||||
{
|
||||
LogMessage("Opening AX.25 network connection");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CAX25Network::write(const unsigned char* data, unsigned int length)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
assert(length > 0U);
|
||||
assert(m_mqtt != nullptr);
|
||||
|
||||
if (!m_enabled)
|
||||
return true;
|
||||
|
||||
unsigned char txData[500U];
|
||||
unsigned int txLength = 0U;
|
||||
|
||||
txData[txLength++] = AX25_FEND;
|
||||
txData[txLength++] = AX25_KISS_DATA;
|
||||
|
||||
for (unsigned int i = 0U; i < length; i++) {
|
||||
unsigned char c = data[i];
|
||||
|
||||
switch (c) {
|
||||
case AX25_FEND:
|
||||
txData[txLength++] = AX25_FESC;
|
||||
txData[txLength++] = AX25_TFEND;
|
||||
break;
|
||||
case AX25_FESC:
|
||||
txData[txLength++] = AX25_FESC;
|
||||
txData[txLength++] = AX25_TFESC;
|
||||
break;
|
||||
default:
|
||||
txData[txLength++] = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
txData[txLength++] = AX25_FEND;
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "AX.25 network KISS packet sent", txData, txLength);
|
||||
|
||||
m_mqtt->publish("ax25-out", txData, txLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int CAX25Network::read(unsigned char* data, unsigned int length)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
assert(length > 0U);
|
||||
|
||||
if (m_buffer.isEmpty())
|
||||
return 0U;
|
||||
|
||||
unsigned char rxData[500U];
|
||||
unsigned int rxLength = 0U;
|
||||
|
||||
m_mutex.lock();
|
||||
|
||||
m_buffer.getData((unsigned char*)&rxLength, sizeof(unsigned int));
|
||||
m_buffer.getData(rxData, rxLength);
|
||||
|
||||
m_mutex.unlock();
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "AX.25 network KISS packet received", rxData, rxLength);
|
||||
|
||||
if (rxData[0U] != AX25_FEND) {
|
||||
LogWarning("Missing FEND at start of a KISS frame - 0x%02X", rxData[0U]);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
if (rxData[1U] != AX25_KISS_DATA) {
|
||||
LogWarning("Invalid KISS type byte received - 0x%02X", rxData[1U]);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
bool complete = false;
|
||||
|
||||
length = 0U;
|
||||
unsigned char lastChar = 0x00U;
|
||||
for (unsigned int i = 2U; i < rxLength; i++) {
|
||||
unsigned char c = rxData[i];
|
||||
|
||||
if (c == AX25_FEND) {
|
||||
complete = true;
|
||||
break;
|
||||
} else if (c == AX25_TFEND && lastChar == AX25_FESC) {
|
||||
data[length++] = AX25_FEND;
|
||||
} else if (c == AX25_TFESC && lastChar == AX25_FESC) {
|
||||
data[length++] = AX25_FESC;
|
||||
} else {
|
||||
data[length++] = c;
|
||||
}
|
||||
|
||||
lastChar = c;
|
||||
}
|
||||
|
||||
if (!complete)
|
||||
return 0U;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
void CAX25Network::setData(const unsigned char* data, unsigned int length)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
assert(length > 0U);
|
||||
|
||||
m_mutex.lock();
|
||||
|
||||
m_buffer.addData((unsigned char*)&length, sizeof(unsigned int));
|
||||
m_buffer.addData(data, length);
|
||||
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
void CAX25Network::reset()
|
||||
{
|
||||
}
|
||||
|
||||
void CAX25Network::close()
|
||||
{
|
||||
LogMessage("Closing AX.25 network connection");
|
||||
}
|
||||
|
||||
void CAX25Network::enable(bool enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
|
||||
if (enabled != m_enabled)
|
||||
m_buffer.clear();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2021,2023 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef AX25Network_H
|
||||
#define AX25Network_H
|
||||
|
||||
#include "Defines.h"
|
||||
|
||||
#if defined(USE_AX25)
|
||||
|
||||
#include "RingBuffer.h"
|
||||
#include "Mutex.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
class CAX25Network {
|
||||
public:
|
||||
CAX25Network(bool debug);
|
||||
~CAX25Network();
|
||||
|
||||
bool open();
|
||||
|
||||
void enable(bool enabled);
|
||||
|
||||
bool write(const unsigned char* data, unsigned int length);
|
||||
|
||||
unsigned int read(unsigned char* data, unsigned int length);
|
||||
|
||||
void setData(const unsigned char* data, unsigned int length);
|
||||
|
||||
void reset();
|
||||
|
||||
void close();
|
||||
|
||||
private:
|
||||
CRingBuffer<unsigned char> m_buffer;
|
||||
CMutex m_mutex;
|
||||
bool m_debug;
|
||||
bool m_enabled;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
12
Conf.cpp
12
Conf.cpp
|
|
@ -164,10 +164,8 @@ m_modemDMRTXLevel(50.0F),
|
|||
m_modemYSFTXLevel(50.0F),
|
||||
m_modemP25TXLevel(50.0F),
|
||||
m_modemNXDNTXLevel(50.0F),
|
||||
m_modemM17TXLevel(50.0F),
|
||||
m_modemPOCSAGTXLevel(50.0F),
|
||||
m_modemFMTXLevel(50.0F),
|
||||
m_modemAX25TXLevel(50.0F),
|
||||
m_modemRSSIMappingFile(),
|
||||
m_modemUseCOSAsLockout(false),
|
||||
m_modemTrace(false),
|
||||
|
|
@ -572,10 +570,10 @@ bool CConf::read()
|
|||
else if (::strcmp(key, "Duplex") == 0)
|
||||
m_duplex = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "ModeHang") == 0)
|
||||
m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = m_nxdnNetworkModeHang = m_m17NetworkModeHang = m_fmNetworkModeHang =
|
||||
m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = m_nxdnModeHang = m_m17ModeHang = m_fmModeHang = (unsigned int)::atoi(value);
|
||||
m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = m_nxdnNetworkModeHang = m_fmNetworkModeHang =
|
||||
m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = m_nxdnModeHang = m_fmModeHang = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "RFModeHang") == 0)
|
||||
m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = m_nxdnModeHang = m_m17ModeHang = m_fmModeHang = (unsigned int)::atoi(value);
|
||||
m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = m_nxdnModeHang = m_fmModeHang = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "NetModeHang") == 0)
|
||||
m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = m_nxdnNetworkModeHang = m_m17NetworkModeHang = m_fmNetworkModeHang = (unsigned int)::atoi(value);
|
||||
else if (::strcmp(key, "Daemon") == 0)
|
||||
|
|
@ -688,7 +686,7 @@ bool CConf::read()
|
|||
else if (::strcmp(key, "RXLevel") == 0)
|
||||
m_modemRXLevel = float(::atof(value));
|
||||
else if (::strcmp(key, "TXLevel") == 0)
|
||||
m_modemAX25TXLevel = m_modemFMTXLevel = m_modemCWIdTXLevel = m_modemDStarTXLevel = m_modemDMRTXLevel = m_modemYSFTXLevel = m_modemP25TXLevel = m_modemNXDNTXLevel = m_modemM17TXLevel = m_modemPOCSAGTXLevel = float(::atof(value));
|
||||
m_modemFMTXLevel = m_modemCWIdTXLevel = m_modemDStarTXLevel = m_modemDMRTXLevel = m_modemYSFTXLevel = m_modemP25TXLevel = m_modemNXDNTXLevel = m_modemPOCSAGTXLevel = float(::atof(value));
|
||||
else if (::strcmp(key, "CWIdTXLevel") == 0)
|
||||
m_modemCWIdTXLevel = float(::atof(value));
|
||||
#if defined(USE_DSTAR)
|
||||
|
|
@ -1139,6 +1137,8 @@ bool CConf::read()
|
|||
} else if (section == SECTION::NXDN_NETWORK) {
|
||||
if (::strcmp(key, "Enable") == 0)
|
||||
m_nxdnNetworkEnabled = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Protocol") == 0)
|
||||
m_nxdnNetworkProtocol = value;
|
||||
else if (::strcmp(key, "LocalAddress") == 0)
|
||||
m_nxdnLocalAddress = value;
|
||||
else if (::strcmp(key, "LocalPort") == 0)
|
||||
|
|
|
|||
82
Conf.h
82
Conf.h
|
|
@ -117,17 +117,11 @@ public:
|
|||
#if defined(USE_NXDN)
|
||||
float getModemNXDNTXLevel() const;
|
||||
#endif
|
||||
#if defined(USE_M17)
|
||||
float getModemM17TXLevel() const;
|
||||
#endif
|
||||
#if defined(USE_POCSAG)
|
||||
float getModemPOCSAGTXLevel() const;
|
||||
#endif
|
||||
#if defined(USE_FM)
|
||||
float getModemFMTXLevel() const;
|
||||
#endif
|
||||
#if defined(USE_AX25)
|
||||
float getModemAX25TXLevel() const;
|
||||
#endif
|
||||
std::string getModemRSSIMappingFile() const;
|
||||
bool getModemUseCOSAsLockout() const;
|
||||
|
|
@ -212,32 +206,12 @@ public:
|
|||
unsigned int getNXDNModeHang() const;
|
||||
#endif
|
||||
|
||||
#if defined(USE_M17)
|
||||
// The M17 section
|
||||
bool getM17Enabled() const;
|
||||
unsigned int getM17CAN() const;
|
||||
bool getM17SelfOnly() const;
|
||||
bool getM17AllowEncryption() const;
|
||||
unsigned int getM17TXHang() const;
|
||||
unsigned int getM17ModeHang() const;
|
||||
#endif
|
||||
|
||||
#if defined(USE_POCSAG)
|
||||
// The POCSAG section
|
||||
bool getPOCSAGEnabled() const;
|
||||
unsigned int getPOCSAGFrequency() const;
|
||||
#endif
|
||||
|
||||
#if defined(USE_AX25)
|
||||
// The AX.25 section
|
||||
bool getAX25Enabled() const;
|
||||
unsigned int getAX25TXDelay() const;
|
||||
int getAX25RXTwist() const;
|
||||
unsigned int getAX25SlotTime() const;
|
||||
unsigned int getAX25PPersist() const;
|
||||
bool getAX25Trace() const;
|
||||
#endif
|
||||
|
||||
#if defined(USE_FM)
|
||||
// The FM Section
|
||||
bool getFMEnabled() const;
|
||||
|
|
@ -337,17 +311,6 @@ public:
|
|||
bool getNXDNNetworkDebug() const;
|
||||
#endif
|
||||
|
||||
#if defined(USE_M17)
|
||||
// The M17 Network section
|
||||
bool getM17NetworkEnabled() const;
|
||||
std::string getM17GatewayAddress() const;
|
||||
unsigned short getM17GatewayPort() const;
|
||||
std::string getM17LocalAddress() const;
|
||||
unsigned short getM17LocalPort() const;
|
||||
unsigned int getM17NetworkModeHang() const;
|
||||
bool getM17NetworkDebug() const;
|
||||
#endif
|
||||
|
||||
#if defined(USE_POCSAG)
|
||||
// The POCSAG Network section
|
||||
bool getPOCSAGNetworkEnabled() const;
|
||||
|
|
@ -374,12 +337,6 @@ public:
|
|||
bool getFMNetworkDebug() const;
|
||||
#endif
|
||||
|
||||
#if defined(USE_AX25)
|
||||
// The AX.25 Network section
|
||||
bool getAX25NetworkEnabled() const;
|
||||
bool getAX25NetworkDebug() const;
|
||||
#endif
|
||||
|
||||
// The Lock File section
|
||||
bool getLockFileEnabled() const;
|
||||
std::string getLockFileName() const;
|
||||
|
|
@ -458,10 +415,8 @@ private:
|
|||
float m_modemYSFTXLevel;
|
||||
float m_modemP25TXLevel;
|
||||
float m_modemNXDNTXLevel;
|
||||
float m_modemM17TXLevel;
|
||||
float m_modemPOCSAGTXLevel;
|
||||
float m_modemFMTXLevel;
|
||||
float m_modemAX25TXLevel;
|
||||
std::string m_modemRSSIMappingFile;
|
||||
bool m_modemUseCOSAsLockout;
|
||||
bool m_modemTrace;
|
||||
|
|
@ -545,15 +500,6 @@ private:
|
|||
#endif
|
||||
unsigned int m_nxdnModeHang;
|
||||
|
||||
#if defined(USE_M17)
|
||||
bool m_m17Enabled;
|
||||
unsigned int m_m17CAN;
|
||||
bool m_m17SelfOnly;
|
||||
bool m_m17AllowEncryption;
|
||||
unsigned int m_m17TXHang;
|
||||
#endif
|
||||
unsigned int m_m17ModeHang;
|
||||
|
||||
#if defined(USE_POCSAG)
|
||||
bool m_pocsagEnabled;
|
||||
#endif
|
||||
|
|
@ -602,17 +548,6 @@ private:
|
|||
#endif
|
||||
unsigned int m_fmModeHang;
|
||||
|
||||
#if defined(USE_AX25)
|
||||
bool m_ax25Enabled;
|
||||
#endif
|
||||
unsigned int m_ax25TXDelay;
|
||||
#if defined(USE_AX25)
|
||||
int m_ax25RXTwist;
|
||||
unsigned int m_ax25SlotTime;
|
||||
unsigned int m_ax25PPersist;
|
||||
bool m_ax25Trace;
|
||||
#endif
|
||||
|
||||
#if defined(USE_DSTAR)
|
||||
bool m_dstarNetworkEnabled;
|
||||
std::string m_dstarGatewayAddress;
|
||||
|
|
@ -675,18 +610,6 @@ private:
|
|||
bool m_nxdnNetworkDebug;
|
||||
#endif
|
||||
|
||||
#if defined(USE_M17)
|
||||
bool m_m17NetworkEnabled;
|
||||
std::string m_m17GatewayAddress;
|
||||
unsigned short m_m17GatewayPort;
|
||||
std::string m_m17LocalAddress;
|
||||
unsigned short m_m17LocalPort;
|
||||
#endif
|
||||
unsigned int m_m17NetworkModeHang;
|
||||
#if defined(USE_M17)
|
||||
bool m_m17NetworkDebug;
|
||||
#endif
|
||||
|
||||
#if defined(USE_POCSAG)
|
||||
bool m_pocsagNetworkEnabled;
|
||||
std::string m_pocsagGatewayAddress;
|
||||
|
|
@ -713,11 +636,6 @@ private:
|
|||
bool m_fmNetworkDebug;
|
||||
#endif
|
||||
|
||||
#if defined(USE_AX25)
|
||||
bool m_ax25NetworkEnabled;
|
||||
bool m_ax25NetworkDebug;
|
||||
#endif
|
||||
|
||||
bool m_lockFileEnabled;
|
||||
std::string m_lockFileName;
|
||||
|
||||
|
|
|
|||
27
DMRSlot.cpp
27
DMRSlot.cpp
|
|
@ -1903,6 +1903,9 @@ void CDMRSlot::clock()
|
|||
}
|
||||
}
|
||||
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
if ((m_netState == RPT_NET_STATE::AUDIO) || (m_netState == RPT_NET_STATE::DATA)) {
|
||||
m_networkWatchdog.clock(ms);
|
||||
|
||||
|
|
@ -2225,6 +2228,19 @@ void CDMRSlot::enable(bool enabled)
|
|||
m_queue.clear();
|
||||
|
||||
// Reset the RF section
|
||||
switch (m_rfState) {
|
||||
case RPT_RF_STATE::LISTENING:
|
||||
case RPT_RF_STATE::REJECTED:
|
||||
case RPT_RF_STATE::INVALID:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (m_rfTimeoutTimer.isRunning()) {
|
||||
if (!m_rfTimeout)
|
||||
LogMessage("DMR Slot %u, RF user has timed out", m_slotNo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_rfState = RPT_RF_STATE::LISTENING;
|
||||
|
||||
m_rfTimeoutTimer.stop();
|
||||
|
|
@ -2244,6 +2260,17 @@ void CDMRSlot::enable(bool enabled)
|
|||
m_rfLC = nullptr;
|
||||
|
||||
// Reset the networking section
|
||||
switch(m_netState) {
|
||||
case RPT_NET_STATE::IDLE:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (m_netTimeoutTimer.isRunning()) {
|
||||
if (!m_netTimeout)
|
||||
LogMessage("DMR Slot %u, network user has timed out", m_slotNo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_netState = RPT_NET_STATE::IDLE;
|
||||
|
||||
m_lastFrameValid = false;
|
||||
|
|
|
|||
|
|
@ -831,6 +831,9 @@ void CDStarControl::clock()
|
|||
if (m_network != nullptr)
|
||||
writeNetwork();
|
||||
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
m_ackTimer.clock(ms);
|
||||
if (m_ackTimer.isRunning() && m_ackTimer.hasExpired()) {
|
||||
sendAck();
|
||||
|
|
@ -1289,11 +1292,33 @@ void CDStarControl::enable(bool enabled)
|
|||
m_queue.clear();
|
||||
|
||||
// Reset the RF section
|
||||
switch (m_rfState) {
|
||||
case RPT_RF_STATE::LISTENING:
|
||||
case RPT_RF_STATE::REJECTED:
|
||||
case RPT_RF_STATE::INVALID:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (m_rfTimeoutTimer.isRunning()) {
|
||||
LogMessage("D-Star, RF user has timed out");
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_rfState = RPT_RF_STATE::LISTENING;
|
||||
|
||||
m_rfTimeoutTimer.stop();
|
||||
|
||||
// Reset the networking section
|
||||
switch(m_netState) {
|
||||
case RPT_NET_STATE::IDLE:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (m_netTimeoutTimer.isRunning()) {
|
||||
LogMessage("D-Star, network user has timed out");
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_netState = RPT_NET_STATE::IDLE;
|
||||
|
||||
m_lastFrameValid = false;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ const unsigned char MODE_YSF = 3U;
|
|||
const unsigned char MODE_P25 = 4U;
|
||||
const unsigned char MODE_NXDN = 5U;
|
||||
const unsigned char MODE_POCSAG = 6U;
|
||||
const unsigned char MODE_M17 = 7U;
|
||||
|
||||
const unsigned char MODE_FM = 10U;
|
||||
|
||||
|
|
|
|||
89
M17CRC.cpp
89
M17CRC.cpp
|
|
@ -1,89 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2021,2023,2025 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "M17CRC.h"
|
||||
|
||||
#if defined(USE_M17)
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
||||
const uint16_t CRC16_TABLE[] = {0x0000U, 0x5935U, 0xB26AU, 0xEB5FU, 0x3DE1U, 0x64D4U, 0x8F8BU, 0xD6BEU, 0x7BC2U, 0x22F7U, 0xC9A8U,
|
||||
0x909DU, 0x4623U, 0x1F16U, 0xF449U, 0xAD7CU, 0xF784U, 0xAEB1U, 0x45EEU, 0x1CDBU, 0xCA65U, 0x9350U,
|
||||
0x780FU, 0x213AU, 0x8C46U, 0xD573U, 0x3E2CU, 0x6719U, 0xB1A7U, 0xE892U, 0x03CDU, 0x5AF8U, 0xB63DU,
|
||||
0xEF08U, 0x0457U, 0x5D62U, 0x8BDCU, 0xD2E9U, 0x39B6U, 0x6083U, 0xCDFFU, 0x94CAU, 0x7F95U, 0x26A0U,
|
||||
0xF01EU, 0xA92BU, 0x4274U, 0x1B41U, 0x41B9U, 0x188CU, 0xF3D3U, 0xAAE6U, 0x7C58U, 0x256DU, 0xCE32U,
|
||||
0x9707U, 0x3A7BU, 0x634EU, 0x8811U, 0xD124U, 0x079AU, 0x5EAFU, 0xB5F0U, 0xECC5U, 0x354FU, 0x6C7AU,
|
||||
0x8725U, 0xDE10U, 0x08AEU, 0x519BU, 0xBAC4U, 0xE3F1U, 0x4E8DU, 0x17B8U, 0xFCE7U, 0xA5D2U, 0x736CU,
|
||||
0x2A59U, 0xC106U, 0x9833U, 0xC2CBU, 0x9BFEU, 0x70A1U, 0x2994U, 0xFF2AU, 0xA61FU, 0x4D40U, 0x1475U,
|
||||
0xB909U, 0xE03CU, 0x0B63U, 0x5256U, 0x84E8U, 0xDDDDU, 0x3682U, 0x6FB7U, 0x8372U, 0xDA47U, 0x3118U,
|
||||
0x682DU, 0xBE93U, 0xE7A6U, 0x0CF9U, 0x55CCU, 0xF8B0U, 0xA185U, 0x4ADAU, 0x13EFU, 0xC551U, 0x9C64U,
|
||||
0x773BU, 0x2E0EU, 0x74F6U, 0x2DC3U, 0xC69CU, 0x9FA9U, 0x4917U, 0x1022U, 0xFB7DU, 0xA248U, 0x0F34U,
|
||||
0x5601U, 0xBD5EU, 0xE46BU, 0x32D5U, 0x6BE0U, 0x80BFU, 0xD98AU, 0x6A9EU, 0x33ABU, 0xD8F4U, 0x81C1U,
|
||||
0x577FU, 0x0E4AU, 0xE515U, 0xBC20U, 0x115CU, 0x4869U, 0xA336U, 0xFA03U, 0x2CBDU, 0x7588U, 0x9ED7U,
|
||||
0xC7E2U, 0x9D1AU, 0xC42FU, 0x2F70U, 0x7645U, 0xA0FBU, 0xF9CEU, 0x1291U, 0x4BA4U, 0xE6D8U, 0xBFEDU,
|
||||
0x54B2U, 0x0D87U, 0xDB39U, 0x820CU, 0x6953U, 0x3066U, 0xDCA3U, 0x8596U, 0x6EC9U, 0x37FCU, 0xE142U,
|
||||
0xB877U, 0x5328U, 0x0A1DU, 0xA761U, 0xFE54U, 0x150BU, 0x4C3EU, 0x9A80U, 0xC3B5U, 0x28EAU, 0x71DFU,
|
||||
0x2B27U, 0x7212U, 0x994DU, 0xC078U, 0x16C6U, 0x4FF3U, 0xA4ACU, 0xFD99U, 0x50E5U, 0x09D0U, 0xE28FU,
|
||||
0xBBBAU, 0x6D04U, 0x3431U, 0xDF6EU, 0x865BU, 0x5FD1U, 0x06E4U, 0xEDBBU, 0xB48EU, 0x6230U, 0x3B05U,
|
||||
0xD05AU, 0x896FU, 0x2413U, 0x7D26U, 0x9679U, 0xCF4CU, 0x19F2U, 0x40C7U, 0xAB98U, 0xF2ADU, 0xA855U,
|
||||
0xF160U, 0x1A3FU, 0x430AU, 0x95B4U, 0xCC81U, 0x27DEU, 0x7EEBU, 0xD397U, 0x8AA2U, 0x61FDU, 0x38C8U,
|
||||
0xEE76U, 0xB743U, 0x5C1CU, 0x0529U, 0xE9ECU, 0xB0D9U, 0x5B86U, 0x02B3U, 0xD40DU, 0x8D38U, 0x6667U,
|
||||
0x3F52U, 0x922EU, 0xCB1BU, 0x2044U, 0x7971U, 0xAFCFU, 0xF6FAU, 0x1DA5U, 0x4490U, 0x1E68U, 0x475DU,
|
||||
0xAC02U, 0xF537U, 0x2389U, 0x7ABCU, 0x91E3U, 0xC8D6U, 0x65AAU, 0x3C9FU, 0xD7C0U, 0x8EF5U, 0x584BU,
|
||||
0x017EU, 0xEA21U, 0xB314U};
|
||||
|
||||
bool CM17CRC::checkCRC16(const unsigned char* in, unsigned int nBytes)
|
||||
{
|
||||
assert(in != nullptr);
|
||||
assert(nBytes > 2U);
|
||||
|
||||
uint16_t crc = createCRC16(in, nBytes - 2U);
|
||||
|
||||
uint8_t temp[2U];
|
||||
temp[0U] = (crc >> 8) & 0xFFU;
|
||||
temp[1U] = (crc >> 0) & 0xFFU;
|
||||
|
||||
return temp[0U] == in[nBytes - 2U] && temp[1U] == in[nBytes - 1U];
|
||||
}
|
||||
|
||||
void CM17CRC::encodeCRC16(unsigned char* in, unsigned int nBytes)
|
||||
{
|
||||
assert(in != nullptr);
|
||||
assert(nBytes > 2U);
|
||||
|
||||
uint16_t crc = createCRC16(in, nBytes - 2U);
|
||||
|
||||
in[nBytes - 2U] = (crc >> 8) & 0xFFU;
|
||||
in[nBytes - 1U] = (crc >> 0) & 0xFFU;
|
||||
}
|
||||
|
||||
uint16_t CM17CRC::createCRC16(const unsigned char* in, unsigned int nBytes)
|
||||
{
|
||||
assert(in != nullptr);
|
||||
|
||||
uint16_t crc = 0xFFFFU;
|
||||
|
||||
for (unsigned int i = 0U; i < nBytes; i++)
|
||||
crc = (crc << 8) ^ CRC16_TABLE[((crc >> 8) ^ uint16_t(in[i])) & 0x00FFU];
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
41
M17CRC.h
41
M17CRC.h
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2021,2023 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(M17CRC_H)
|
||||
#define M17CRC_H
|
||||
|
||||
#include "Defines.h"
|
||||
|
||||
#if defined(USE_M17)
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class CM17CRC
|
||||
{
|
||||
public:
|
||||
static bool checkCRC16(const unsigned char* in, unsigned int nBytes);
|
||||
static void encodeCRC16(unsigned char* in, unsigned int nBytes);
|
||||
|
||||
private:
|
||||
static uint16_t createCRC16(const unsigned char* in, unsigned int nBytes);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
1171
M17Control.cpp
1171
M17Control.cpp
File diff suppressed because it is too large
Load diff
136
M17Control.h
136
M17Control.h
|
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020-2023 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(M17Control_H)
|
||||
#define M17Control_H
|
||||
|
||||
#include "RSSIInterpolator.h"
|
||||
#include "M17Network.h"
|
||||
#include "M17Defines.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "StopWatch.h"
|
||||
#include "Defines.h"
|
||||
#include "M17LSF.h"
|
||||
#include "Timer.h"
|
||||
#include "Modem.h"
|
||||
|
||||
#if defined(USE_M17)
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
class CM17Control {
|
||||
public:
|
||||
CM17Control(const std::string& callsign, unsigned int can, bool selfOnly, bool allowEncryption, CM17Network* network, unsigned int timeout, bool duplex, CRSSIInterpolator* rssiMapper);
|
||||
~CM17Control();
|
||||
|
||||
bool writeModem(unsigned char* data, unsigned int len);
|
||||
|
||||
unsigned int readModem(unsigned char* data);
|
||||
|
||||
void clock(unsigned int ms);
|
||||
|
||||
bool isBusy() const;
|
||||
|
||||
void enable(bool enabled);
|
||||
|
||||
private:
|
||||
std::string m_callsign;
|
||||
unsigned int m_can;
|
||||
bool m_selfOnly;
|
||||
bool m_allowEncryption;
|
||||
CM17Network* m_network;
|
||||
bool m_duplex;
|
||||
CRingBuffer<unsigned char> m_queue;
|
||||
std::string m_source;
|
||||
std::string m_dest;
|
||||
RPT_RF_STATE m_rfState;
|
||||
RPT_NET_STATE m_netState;
|
||||
CTimer m_rfTimeoutTimer;
|
||||
CTimer m_netTimeoutTimer;
|
||||
CTimer m_networkWatchdog;
|
||||
CStopWatch m_elapsed;
|
||||
unsigned int m_rfFrames;
|
||||
unsigned int m_netFrames;
|
||||
unsigned int m_rfErrs;
|
||||
unsigned int m_rfBits;
|
||||
unsigned int m_rfLSFCount;
|
||||
CM17LSF m_rfCurrentRFLSF;
|
||||
CM17LSF m_rfCurrentNetLSF;
|
||||
CM17LSF m_rfCollectingLSF;
|
||||
CM17LSF m_rfCollectedLSF;
|
||||
unsigned int m_rfLSFn;
|
||||
CM17LSF m_netLSF;
|
||||
unsigned int m_netLSFn;
|
||||
unsigned char m_rfTextBits;
|
||||
unsigned char m_netTextBits;
|
||||
char* m_rfText;
|
||||
char* m_netText;
|
||||
CRSSIInterpolator* m_rssiMapper;
|
||||
int m_rssi;
|
||||
int m_maxRSSI;
|
||||
int m_minRSSI;
|
||||
int m_aveRSSI;
|
||||
unsigned int m_rssiCountTotal;
|
||||
int m_rssiAccum;
|
||||
unsigned int m_rssiCount;
|
||||
unsigned int m_bitsCount;
|
||||
unsigned int m_bitErrsAccum;
|
||||
bool m_enabled;
|
||||
|
||||
bool processRFHeader(bool lateEntry);
|
||||
|
||||
void writeQueueRF(const unsigned char* data);
|
||||
|
||||
void writeQueueNet(const unsigned char* data);
|
||||
|
||||
void writeNetwork();
|
||||
|
||||
void interleaver(const unsigned char* in, unsigned char* out) const;
|
||||
void decorrelator(const unsigned char* in, unsigned char* out) const;
|
||||
|
||||
bool checkCallsign(const std::string& source) const;
|
||||
|
||||
void createRFLSF(bool addCallsign);
|
||||
|
||||
void writeEndRF();
|
||||
void writeEndNet();
|
||||
|
||||
void writeJSONRSSI();
|
||||
void writeJSONBER();
|
||||
void writeJSONText(const char* text);
|
||||
|
||||
void writeJSONRF(const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest);
|
||||
void writeJSONRF(const char* action, float duration, float ber);
|
||||
void writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI);
|
||||
|
||||
void writeJSONNet(const char* action, RPT_NET_STATE state, const std::string& source, const std::string& dest);
|
||||
void writeJSONNet(const char* action, float duration);
|
||||
|
||||
void writeJSONRF(nlohmann::json& json, const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest);
|
||||
void writeJSONRF(nlohmann::json& json, const char* action);
|
||||
void writeJSONRF(nlohmann::json& json, const char* action, float duration, float ber);
|
||||
void writeJSONNet(nlohmann::json& json, const char* action);
|
||||
void writeJSONNet(nlohmann::json& json, const char* action, RPT_NET_STATE state, const std::string& source, const std::string& dest);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,288 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2021,2023,2025 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "M17Convolution.h"
|
||||
|
||||
#if defined(USE_M17)
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
const unsigned int PUNCTURE_LIST_LINK_SETUP_COUNT = 60U;
|
||||
|
||||
const unsigned int PUNCTURE_LIST_LINK_SETUP[] = {
|
||||
2U, 6U, 10U, 14U, 18U, 22U, 26U, 30U, 34U, 38U, 42U, 46U, 50U, 54U, 58U, 63U, 67U, 71U, 75U, 79U, 83U,
|
||||
87U, 91U, 95U, 99U, 103U, 107U, 111U, 115U, 119U, 124U, 128U, 132U, 136U, 140U, 144U, 148U, 152U, 156U, 160U, 164U, 168U,
|
||||
172U, 176U, 180U, 185U, 189U, 193U, 197U, 201U, 205U, 209U, 213U, 217U, 221U, 225U, 229U, 233U, 237U, 241U, 246U, 250U, 254U,
|
||||
258U, 262U, 266U, 270U, 274U, 278U, 282U, 286U, 290U, 294U, 298U, 302U, 307U, 311U, 315U, 319U, 323U, 327U, 331U, 335U, 339U,
|
||||
343U, 347U, 351U, 355U, 359U, 363U, 368U, 372U, 376U, 380U, 384U, 388U, 392U, 396U, 400U, 404U, 408U, 412U, 416U, 420U, 424U,
|
||||
429U, 433U, 437U, 441U, 445U, 449U, 453U, 457U, 461U, 465U, 469U, 473U, 477U, 481U, 485U};
|
||||
|
||||
const unsigned int PUNCTURE_LIST_DATA_COUNT = 12U;
|
||||
|
||||
const unsigned int PUNCTURE_LIST_DATA[] = {
|
||||
11U, 23U, 35U, 47U, 59U, 71U, 83U, 95U, 107U, 119U, 131U, 143U, 155U, 167U, 179U, 191U, 203U, 215U, 227U, 239U, 251U,
|
||||
263U, 275U, 287U};
|
||||
|
||||
const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U};
|
||||
|
||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
const uint8_t BRANCH_TABLE1[] = {0U, 0U, 0U, 0U, 2U, 2U, 2U, 2U};
|
||||
const uint8_t BRANCH_TABLE2[] = {0U, 2U, 2U, 0U, 0U, 2U, 2U, 0U};
|
||||
|
||||
const unsigned int NUM_OF_STATES_D2 = 8U;
|
||||
const unsigned int NUM_OF_STATES = 16U;
|
||||
const uint32_t M = 4U;
|
||||
const unsigned int K = 5U;
|
||||
|
||||
CM17Convolution::CM17Convolution() :
|
||||
m_metrics1(nullptr),
|
||||
m_metrics2(nullptr),
|
||||
m_oldMetrics(nullptr),
|
||||
m_newMetrics(nullptr),
|
||||
m_decisions(nullptr),
|
||||
m_dp(nullptr)
|
||||
{
|
||||
m_metrics1 = new uint16_t[20U];
|
||||
m_metrics2 = new uint16_t[20U];
|
||||
m_decisions = new uint64_t[300U];
|
||||
}
|
||||
|
||||
CM17Convolution::~CM17Convolution()
|
||||
{
|
||||
delete[] m_metrics1;
|
||||
delete[] m_metrics2;
|
||||
delete[] m_decisions;
|
||||
}
|
||||
|
||||
void CM17Convolution::encodeLinkSetup(const unsigned char* in, unsigned char* out) const
|
||||
{
|
||||
assert(in != nullptr);
|
||||
assert(out != nullptr);
|
||||
|
||||
unsigned char temp1[31U];
|
||||
::memset(temp1, 0x00U, 31U);
|
||||
::memcpy(temp1, in, 30U);
|
||||
|
||||
unsigned char temp2[61U];
|
||||
encode(temp1, temp2, 244U);
|
||||
|
||||
unsigned int n = 0U;
|
||||
unsigned int index = 0U;
|
||||
for (unsigned int i = 0U; i < 488U; i++) {
|
||||
if (i != PUNCTURE_LIST_LINK_SETUP[index]) {
|
||||
bool b = READ_BIT1(temp2, i);
|
||||
WRITE_BIT1(out, n, b);
|
||||
n++;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CM17Convolution::encodeData(const unsigned char* in, unsigned char* out) const
|
||||
{
|
||||
assert(in != nullptr);
|
||||
assert(out != nullptr);
|
||||
|
||||
unsigned char temp1[19U];
|
||||
::memset(temp1, 0x00U, 19U);
|
||||
::memcpy(temp1, in, 18U);
|
||||
|
||||
unsigned char temp2[37U];
|
||||
encode(temp1, temp2, 148U);
|
||||
|
||||
unsigned int n = 0U;
|
||||
unsigned int index = 0U;
|
||||
for (unsigned int i = 0U; i < 296U; i++) {
|
||||
if (i != PUNCTURE_LIST_DATA[index]) {
|
||||
bool b = READ_BIT1(temp2, i);
|
||||
WRITE_BIT1(out, n, b);
|
||||
n++;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int CM17Convolution::decodeLinkSetup(const unsigned char* in, unsigned char* out)
|
||||
{
|
||||
assert(in != nullptr);
|
||||
assert(out != nullptr);
|
||||
|
||||
uint8_t temp[500U];
|
||||
::memset(temp, 0x00U, 500U);
|
||||
|
||||
unsigned int n = 0U;
|
||||
unsigned int index = 0U;
|
||||
for (unsigned int i = 0U; i < 368U; i++) {
|
||||
if (n == PUNCTURE_LIST_LINK_SETUP[index]) {
|
||||
temp[n++] = 1U;
|
||||
index++;
|
||||
}
|
||||
|
||||
bool b = READ_BIT1(in, i);
|
||||
temp[n++] = b ? 2U : 0U;
|
||||
}
|
||||
|
||||
start();
|
||||
|
||||
n = 0U;
|
||||
for (unsigned int i = 0U; i < 244U; i++) {
|
||||
uint8_t s0 = temp[n++];
|
||||
uint8_t s1 = temp[n++];
|
||||
|
||||
decode(s0, s1);
|
||||
}
|
||||
|
||||
return chainback(out, 240U) - PUNCTURE_LIST_LINK_SETUP_COUNT;
|
||||
}
|
||||
|
||||
unsigned int CM17Convolution::decodeData(const unsigned char* in, unsigned char* out)
|
||||
{
|
||||
assert(in != nullptr);
|
||||
assert(out != nullptr);
|
||||
|
||||
uint8_t temp[300U];
|
||||
::memset(temp, 0x00U, 300U);
|
||||
|
||||
unsigned int n = 0U;
|
||||
unsigned int index = 0U;
|
||||
for (unsigned int i = 0U; i < 272U; i++) {
|
||||
if (n == PUNCTURE_LIST_DATA[index]) {
|
||||
temp[n++] = 1U;
|
||||
index++;
|
||||
}
|
||||
|
||||
bool b = READ_BIT1(in, i);
|
||||
temp[n++] = b ? 2U : 0U;
|
||||
}
|
||||
|
||||
start();
|
||||
|
||||
n = 0U;
|
||||
for (unsigned int i = 0U; i < 148U; i++) {
|
||||
uint8_t s0 = temp[n++];
|
||||
uint8_t s1 = temp[n++];
|
||||
|
||||
decode(s0, s1);
|
||||
}
|
||||
|
||||
return chainback(out, 144U) - PUNCTURE_LIST_DATA_COUNT;
|
||||
}
|
||||
|
||||
void CM17Convolution::start()
|
||||
{
|
||||
::memset(m_metrics1, 0x00U, NUM_OF_STATES * sizeof(uint16_t));
|
||||
::memset(m_metrics2, 0x00U, NUM_OF_STATES * sizeof(uint16_t));
|
||||
|
||||
m_oldMetrics = m_metrics1;
|
||||
m_newMetrics = m_metrics2;
|
||||
m_dp = m_decisions;
|
||||
}
|
||||
|
||||
void CM17Convolution::decode(uint8_t s0, uint8_t s1)
|
||||
{
|
||||
*m_dp = 0U;
|
||||
|
||||
for (uint8_t i = 0U; i < NUM_OF_STATES_D2; i++) {
|
||||
uint8_t j = i * 2U;
|
||||
|
||||
uint16_t metric = std::abs(BRANCH_TABLE1[i] - s0) + std::abs(BRANCH_TABLE2[i] - s1);
|
||||
|
||||
uint16_t m0 = m_oldMetrics[i] + metric;
|
||||
uint16_t m1 = m_oldMetrics[i + NUM_OF_STATES_D2] + (M - metric);
|
||||
uint8_t decision0 = (m0 >= m1) ? 1U : 0U;
|
||||
m_newMetrics[j + 0U] = decision0 != 0U ? m1 : m0;
|
||||
|
||||
m0 = m_oldMetrics[i] + (M - metric);
|
||||
m1 = m_oldMetrics[i + NUM_OF_STATES_D2] + metric;
|
||||
uint8_t decision1 = (m0 >= m1) ? 1U : 0U;
|
||||
m_newMetrics[j + 1U] = decision1 != 0U ? m1 : m0;
|
||||
|
||||
*m_dp |= (uint64_t(decision1) << (j + 1U)) | (uint64_t(decision0) << (j + 0U));
|
||||
}
|
||||
|
||||
++m_dp;
|
||||
|
||||
assert((m_dp - m_decisions) <= 300);
|
||||
|
||||
uint16_t* tmp = m_oldMetrics;
|
||||
m_oldMetrics = m_newMetrics;
|
||||
m_newMetrics = tmp;
|
||||
}
|
||||
|
||||
unsigned int CM17Convolution::chainback(unsigned char* out, unsigned int nBits)
|
||||
{
|
||||
assert(out != nullptr);
|
||||
|
||||
uint32_t state = 0U;
|
||||
|
||||
while (nBits-- > 0) {
|
||||
--m_dp;
|
||||
|
||||
uint32_t i = state >> (9 - K);
|
||||
uint8_t bit = uint8_t(*m_dp >> i) & 1;
|
||||
state = (bit << 7) | (state >> 1);
|
||||
|
||||
WRITE_BIT1(out, nBits, bit != 0U);
|
||||
}
|
||||
|
||||
unsigned int minCost = m_oldMetrics[0];
|
||||
|
||||
for (unsigned int i = 0U; i < NUM_OF_STATES; i++) {
|
||||
if (m_oldMetrics[i] < minCost)
|
||||
minCost = m_oldMetrics[i];
|
||||
}
|
||||
|
||||
return minCost / (M >> 1);
|
||||
}
|
||||
|
||||
void CM17Convolution::encode(const unsigned char* in, unsigned char* out, unsigned int nBits) const
|
||||
{
|
||||
assert(in != nullptr);
|
||||
assert(out != nullptr);
|
||||
assert(nBits > 0U);
|
||||
|
||||
uint8_t d1 = 0U, d2 = 0U, d3 = 0U, d4 = 0U;
|
||||
uint32_t k = 0U;
|
||||
for (unsigned int i = 0U; i < nBits; i++) {
|
||||
uint8_t d = READ_BIT1(in, i) ? 1U : 0U;
|
||||
|
||||
uint8_t g1 = (d + d3 + d4) & 1;
|
||||
uint8_t g2 = (d + d1 + d2 + d4) & 1;
|
||||
|
||||
d4 = d3;
|
||||
d3 = d2;
|
||||
d2 = d1;
|
||||
d1 = d;
|
||||
|
||||
WRITE_BIT1(out, k, g1 != 0U);
|
||||
k++;
|
||||
|
||||
WRITE_BIT1(out, k, g2 != 0U);
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2021,2023 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(M17Convolution_H)
|
||||
#define M17Convolution_H
|
||||
|
||||
#include "Defines.h"
|
||||
|
||||
#if defined(USE_M17)
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class CM17Convolution {
|
||||
public:
|
||||
CM17Convolution();
|
||||
~CM17Convolution();
|
||||
|
||||
unsigned int decodeLinkSetup(const unsigned char* in, unsigned char* out);
|
||||
unsigned int decodeData(const unsigned char* in, unsigned char* out);
|
||||
|
||||
void encodeLinkSetup(const unsigned char* in, unsigned char* out) const;
|
||||
void encodeData(const unsigned char* in, unsigned char* out) const;
|
||||
|
||||
private:
|
||||
uint16_t* m_metrics1;
|
||||
uint16_t* m_metrics2;
|
||||
uint16_t* m_oldMetrics;
|
||||
uint16_t* m_newMetrics;
|
||||
uint64_t* m_decisions;
|
||||
uint64_t* m_dp;
|
||||
|
||||
void start();
|
||||
void decode(uint8_t s0, uint8_t s1);
|
||||
|
||||
unsigned int chainback(unsigned char* out, unsigned int nBits);
|
||||
|
||||
void encode(const unsigned char* in, unsigned char* out, unsigned int nBits) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
80
M17Defines.h
80
M17Defines.h
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2021,2025 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(M17DEFINES_H)
|
||||
#define M17DEFINES_H
|
||||
|
||||
const unsigned int M17_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate
|
||||
|
||||
const unsigned int M17_FRAME_LENGTH_BITS = 384U;
|
||||
const unsigned int M17_FRAME_LENGTH_BYTES = M17_FRAME_LENGTH_BITS / 8U;
|
||||
|
||||
const unsigned char M17_LINK_SETUP_SYNC_BYTES[] = {0x55U, 0xF7U};
|
||||
const unsigned char M17_STREAM_SYNC_BYTES[] = {0xFFU, 0x5DU};
|
||||
const unsigned char M17_EOT_SYNC_BYTES[] = {0x55U, 0x5DU};
|
||||
|
||||
const unsigned int M17_SYNC_LENGTH_BITS = 16U;
|
||||
const unsigned int M17_SYNC_LENGTH_BYTES = M17_SYNC_LENGTH_BITS / 8U;
|
||||
|
||||
const unsigned int M17_LSF_LENGTH_BITS = 240U;
|
||||
const unsigned int M17_LSF_LENGTH_BYTES = M17_LSF_LENGTH_BITS / 8U;
|
||||
|
||||
const unsigned int M17_LSF_FRAGMENT_LENGTH_BITS = M17_LSF_LENGTH_BITS / 6U;
|
||||
const unsigned int M17_LSF_FRAGMENT_LENGTH_BYTES = M17_LSF_FRAGMENT_LENGTH_BITS / 8U;
|
||||
|
||||
const unsigned int M17_LICH_FRAGMENT_LENGTH_BITS = M17_LSF_FRAGMENT_LENGTH_BITS + 8U;
|
||||
const unsigned int M17_LICH_FRAGMENT_LENGTH_BYTES = M17_LICH_FRAGMENT_LENGTH_BITS / 8U;
|
||||
|
||||
const unsigned int M17_LSF_FRAGMENT_FEC_LENGTH_BITS = M17_LSF_FRAGMENT_LENGTH_BITS * 2U;
|
||||
const unsigned int M17_LSF_FRAGMENT_FEC_LENGTH_BYTES = M17_LSF_FRAGMENT_FEC_LENGTH_BITS / 8U;
|
||||
|
||||
const unsigned int M17_LICH_FRAGMENT_FEC_LENGTH_BITS = M17_LICH_FRAGMENT_LENGTH_BITS * 2U;
|
||||
const unsigned int M17_LICH_FRAGMENT_FEC_LENGTH_BYTES = M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 8U;
|
||||
|
||||
const unsigned int M17_PAYLOAD_LENGTH_BITS = 128U;
|
||||
const unsigned int M17_PAYLOAD_LENGTH_BYTES = M17_PAYLOAD_LENGTH_BITS / 8U;
|
||||
|
||||
const unsigned char M17_NULL_NONCE[] = {0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
|
||||
const unsigned int M17_META_LENGTH_BITS = 112U;
|
||||
const unsigned int M17_META_LENGTH_BYTES = M17_META_LENGTH_BITS / 8U;
|
||||
|
||||
const unsigned int M17_FN_LENGTH_BITS = 16U;
|
||||
const unsigned int M17_FN_LENGTH_BYTES = M17_FN_LENGTH_BITS / 8U;
|
||||
|
||||
const unsigned int M17_CRC_LENGTH_BITS = 16U;
|
||||
const unsigned int M17_CRC_LENGTH_BYTES = M17_CRC_LENGTH_BITS / 8U;
|
||||
|
||||
const unsigned char M17_3200_SILENCE[] = {0x01U, 0x00U, 0x09U, 0x43U, 0x9CU, 0xE4U, 0x21U, 0x08U};
|
||||
const unsigned char M17_1600_SILENCE[] = {0x0CU, 0x41U, 0x09U, 0x03U, 0x0CU, 0x41U, 0x09U, 0x03U};
|
||||
|
||||
const unsigned char M17_PACKET_TYPE = 0U;
|
||||
const unsigned char M17_STREAM_TYPE = 1U;
|
||||
|
||||
const unsigned char M17_DATA_TYPE_DATA = 0x01U;
|
||||
const unsigned char M17_DATA_TYPE_VOICE = 0x02U;
|
||||
const unsigned char M17_DATA_TYPE_VOICE_DATA = 0x03U;
|
||||
|
||||
const unsigned char M17_ENCRYPTION_TYPE_NONE = 0x00U;
|
||||
const unsigned char M17_ENCRYPTION_TYPE_AES = 0x01U;
|
||||
const unsigned char M17_ENCRYPTION_TYPE_SCRAMBLE = 0x02U;
|
||||
|
||||
const unsigned char M17_ENCRYPTION_SUB_TYPE_TEXT = 0x00U;
|
||||
const unsigned char M17_ENCRYPTION_SUB_TYPE_GPS = 0x01U;
|
||||
const unsigned char M17_ENCRYPTION_SUB_TYPE_CALLSIGNS = 0x02U;
|
||||
|
||||
#endif
|
||||
225
M17LSF.cpp
225
M17LSF.cpp
|
|
@ -1,225 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2021,2023,2025 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "M17LSF.h"
|
||||
#include "M17Utils.h"
|
||||
#include "M17Defines.h"
|
||||
#include "M17CRC.h"
|
||||
|
||||
#if defined(USE_M17)
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
CM17LSF::CM17LSF(const CM17LSF& lsf) :
|
||||
m_lsf(nullptr),
|
||||
m_valid(lsf.m_valid)
|
||||
{
|
||||
m_lsf = new unsigned char[M17_LSF_LENGTH_BYTES];
|
||||
|
||||
::memcpy(m_lsf, lsf.m_lsf, M17_LSF_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
CM17LSF::CM17LSF() :
|
||||
m_lsf(nullptr),
|
||||
m_valid(false)
|
||||
{
|
||||
m_lsf = new unsigned char[M17_LSF_LENGTH_BYTES];
|
||||
|
||||
::memset(m_lsf, 0x00U, M17_LSF_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
CM17LSF::~CM17LSF()
|
||||
{
|
||||
delete[] m_lsf;
|
||||
}
|
||||
|
||||
void CM17LSF::getNetwork(unsigned char* data) const
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
::memcpy(data, m_lsf, M17_LSF_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
void CM17LSF::setNetwork(const unsigned char* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
::memcpy(m_lsf, data, M17_LSF_LENGTH_BYTES);
|
||||
|
||||
m_valid = true;
|
||||
}
|
||||
|
||||
std::string CM17LSF::getSource() const
|
||||
{
|
||||
std::string callsign;
|
||||
CM17Utils::decodeCallsign(m_lsf + 6U, callsign);
|
||||
|
||||
return callsign;
|
||||
}
|
||||
|
||||
void CM17LSF::setSource(const std::string& callsign)
|
||||
{
|
||||
CM17Utils::encodeCallsign(callsign, m_lsf + 6U);
|
||||
}
|
||||
|
||||
std::string CM17LSF::getDest() const
|
||||
{
|
||||
std::string callsign;
|
||||
CM17Utils::decodeCallsign(m_lsf + 0U, callsign);
|
||||
|
||||
return callsign;
|
||||
}
|
||||
|
||||
void CM17LSF::setDest(const std::string& callsign)
|
||||
{
|
||||
CM17Utils::encodeCallsign(callsign, m_lsf + 0U);
|
||||
}
|
||||
|
||||
unsigned char CM17LSF::getPacketStream() const
|
||||
{
|
||||
return m_lsf[13U] & 0x01U;
|
||||
}
|
||||
|
||||
void CM17LSF::setPacketStream(unsigned char ps)
|
||||
{
|
||||
m_lsf[13U] &= 0xF7U;
|
||||
m_lsf[13U] |= ps & 0x01U;
|
||||
}
|
||||
|
||||
unsigned char CM17LSF::getDataType() const
|
||||
{
|
||||
return (m_lsf[13U] >> 1) & 0x03U;
|
||||
}
|
||||
|
||||
void CM17LSF::setDataType(unsigned char type)
|
||||
{
|
||||
m_lsf[13U] &= 0xF9U;
|
||||
m_lsf[13U] |= (type << 1) & 0x06U;
|
||||
}
|
||||
|
||||
unsigned char CM17LSF::getEncryptionType() const
|
||||
{
|
||||
return (m_lsf[13U] >> 3) & 0x03U;
|
||||
}
|
||||
|
||||
void CM17LSF::setEncryptionType(unsigned char type)
|
||||
{
|
||||
m_lsf[13U] &= 0xE7U;
|
||||
m_lsf[13U] |= (type << 3) & 0x18U;
|
||||
}
|
||||
|
||||
unsigned char CM17LSF::getEncryptionSubType() const
|
||||
{
|
||||
return (m_lsf[13U] >> 5) & 0x03U;
|
||||
}
|
||||
|
||||
void CM17LSF::setEncryptionSubType(unsigned char type)
|
||||
{
|
||||
m_lsf[13U] &= 0x9FU;
|
||||
m_lsf[13U] |= (type << 5) & 0x60U;
|
||||
}
|
||||
|
||||
unsigned char CM17LSF::getCAN() const
|
||||
{
|
||||
return ((m_lsf[12U] << 1) & 0x0EU) | ((m_lsf[13U] >> 7) & 0x01U);
|
||||
}
|
||||
|
||||
void CM17LSF::setCAN(unsigned char can)
|
||||
{
|
||||
m_lsf[13U] &= 0x7FU;
|
||||
m_lsf[13U] |= (can << 7) & 0x80U;
|
||||
|
||||
m_lsf[12U] &= 0xF8U;
|
||||
m_lsf[12U] |= (can >> 1) & 0x07U;
|
||||
}
|
||||
|
||||
void CM17LSF::getMeta(unsigned char* data) const
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
::memcpy(data, m_lsf + 14U, M17_META_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
void CM17LSF::setMeta(const unsigned char* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
::memcpy(m_lsf + 14U, data, M17_META_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
void CM17LSF::reset()
|
||||
{
|
||||
::memset(m_lsf, 0x00U, M17_LSF_LENGTH_BYTES);
|
||||
|
||||
m_valid = false;
|
||||
}
|
||||
|
||||
bool CM17LSF::isValid() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
void CM17LSF::getLinkSetup(unsigned char* data) const
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
::memcpy(data, m_lsf, M17_LSF_LENGTH_BYTES);
|
||||
|
||||
CM17CRC::encodeCRC16(data, M17_LSF_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
void CM17LSF::setLinkSetup(const unsigned char* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
::memcpy(m_lsf, data, M17_LSF_LENGTH_BYTES);
|
||||
|
||||
m_valid = CM17CRC::checkCRC16(m_lsf, M17_LSF_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
void CM17LSF::getFragment(unsigned char* data, unsigned int n) const
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
CM17CRC::encodeCRC16(m_lsf, M17_LSF_LENGTH_BYTES);
|
||||
|
||||
::memcpy(data, m_lsf + (n * M17_LSF_FRAGMENT_LENGTH_BYTES), M17_LSF_FRAGMENT_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
void CM17LSF::setFragment(const unsigned char* data, unsigned int n)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
::memcpy(m_lsf + (n * M17_LSF_FRAGMENT_LENGTH_BYTES), data, M17_LSF_FRAGMENT_LENGTH_BYTES);
|
||||
|
||||
m_valid = CM17CRC::checkCRC16(m_lsf, M17_LSF_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
CM17LSF& CM17LSF::operator=(const CM17LSF& lsf)
|
||||
{
|
||||
if (&lsf != this) {
|
||||
::memcpy(m_lsf, lsf.m_lsf, M17_LSF_LENGTH_BYTES);
|
||||
m_valid = lsf.m_valid;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
80
M17LSF.h
80
M17LSF.h
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2021,2023 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(M17LSF_H)
|
||||
#define M17LSF_H
|
||||
|
||||
#include "Defines.h"
|
||||
|
||||
#if defined(USE_M17)
|
||||
|
||||
#include <string>
|
||||
|
||||
class CM17LSF {
|
||||
public:
|
||||
CM17LSF(const CM17LSF& lsf);
|
||||
CM17LSF();
|
||||
~CM17LSF();
|
||||
|
||||
void getNetwork(unsigned char* data) const;
|
||||
void setNetwork(const unsigned char* data);
|
||||
|
||||
std::string getSource() const;
|
||||
void setSource(const std::string& callsign);
|
||||
|
||||
std::string getDest() const;
|
||||
void setDest(const std::string& callsign);
|
||||
|
||||
unsigned char getPacketStream() const;
|
||||
void setPacketStream(unsigned char ps);
|
||||
|
||||
unsigned char getDataType() const;
|
||||
void setDataType(unsigned char type);
|
||||
|
||||
unsigned char getEncryptionType() const;
|
||||
void setEncryptionType(unsigned char type);
|
||||
|
||||
unsigned char getEncryptionSubType() const;
|
||||
void setEncryptionSubType(unsigned char type);
|
||||
|
||||
unsigned char getCAN() const;
|
||||
void setCAN(unsigned char can);
|
||||
|
||||
void getMeta(unsigned char* data) const;
|
||||
void setMeta(const unsigned char* data);
|
||||
|
||||
void reset();
|
||||
bool isValid() const;
|
||||
|
||||
void getLinkSetup(unsigned char* data) const;
|
||||
void setLinkSetup(const unsigned char* data);
|
||||
|
||||
void getFragment(unsigned char* data, unsigned int n) const;
|
||||
void setFragment(const unsigned char* data, unsigned int n);
|
||||
|
||||
CM17LSF& operator=(const CM17LSF& lsf);
|
||||
|
||||
private:
|
||||
unsigned char* m_lsf;
|
||||
bool m_valid;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
221
M17Network.cpp
221
M17Network.cpp
|
|
@ -1,221 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2021,2023,2025 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "M17Network.h"
|
||||
#include "M17Defines.h"
|
||||
#include "M17Utils.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
|
||||
#if defined(USE_M17)
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
const unsigned int BUFFER_LENGTH = 200U;
|
||||
|
||||
CM17Network::CM17Network(const std::string& localAddress, unsigned short localPort, const std::string& gatewayAddress, unsigned short gatewayPort, bool debug) :
|
||||
m_socket(localAddress, localPort),
|
||||
m_addr(),
|
||||
m_addrLen(0U),
|
||||
m_debug(debug),
|
||||
m_enabled(false),
|
||||
m_outId(0U),
|
||||
m_inId(0U),
|
||||
m_buffer(1000U, "M17 Network"),
|
||||
m_random(),
|
||||
m_timer(1000U, 5U)
|
||||
{
|
||||
if (CUDPSocket::lookup(gatewayAddress, gatewayPort, m_addr, m_addrLen) != 0) {
|
||||
m_addrLen = 0U;
|
||||
return;
|
||||
}
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 mt(rd());
|
||||
m_random = mt;
|
||||
}
|
||||
|
||||
CM17Network::~CM17Network()
|
||||
{
|
||||
}
|
||||
|
||||
bool CM17Network::open()
|
||||
{
|
||||
if (m_addrLen == 0U) {
|
||||
LogError("M17, unable to resolve the gateway address");
|
||||
return false;
|
||||
}
|
||||
|
||||
LogMessage("Opening M17 network connection");
|
||||
|
||||
bool ret = m_socket.open(m_addr);
|
||||
|
||||
if (ret) {
|
||||
m_timer.start();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool CM17Network::write(const unsigned char* data)
|
||||
{
|
||||
if (m_addrLen == 0U)
|
||||
return false;
|
||||
|
||||
assert(data != nullptr);
|
||||
|
||||
unsigned char buffer[100U];
|
||||
|
||||
buffer[0U] = 'M';
|
||||
buffer[1U] = '1';
|
||||
buffer[2U] = '7';
|
||||
buffer[3U] = ' ';
|
||||
|
||||
// Create a random id for this transmission if needed
|
||||
if (m_outId == 0U) {
|
||||
std::uniform_int_distribution<uint16_t> dist(0x0001, 0xFFFE);
|
||||
m_outId = dist(m_random);
|
||||
}
|
||||
|
||||
buffer[4U] = m_outId / 256U; // Unique session id
|
||||
buffer[5U] = m_outId % 256U;
|
||||
|
||||
::memcpy(buffer + 6U, data, 46U);
|
||||
|
||||
// Dummy CRC
|
||||
buffer[52U] = 0x00U;
|
||||
buffer[53U] = 0x00U;
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "M17 Network Transmitted", buffer, 54U);
|
||||
|
||||
return m_socket.write(buffer, 54U, m_addr, m_addrLen);
|
||||
}
|
||||
|
||||
void CM17Network::clock(unsigned int ms)
|
||||
{
|
||||
m_timer.clock(ms);
|
||||
if (m_timer.isRunning() && m_timer.hasExpired()) {
|
||||
sendPing();
|
||||
m_timer.start();
|
||||
}
|
||||
|
||||
unsigned char buffer[BUFFER_LENGTH];
|
||||
|
||||
sockaddr_storage address;
|
||||
unsigned int addrLen;
|
||||
int length = m_socket.read(buffer, BUFFER_LENGTH, address, addrLen);
|
||||
if (length <= 0)
|
||||
return;
|
||||
|
||||
if (!CUDPSocket::match(m_addr, address)) {
|
||||
LogMessage("M17, packet received from an invalid source");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "M17 Network Received", buffer, length);
|
||||
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
if (::memcmp(buffer + 0U, "PING", 4U) == 0)
|
||||
return;
|
||||
|
||||
if (::memcmp(buffer + 0U, "M17 ", 4U) != 0) {
|
||||
CUtils::dump(2U, "M17, received unknown packet", buffer, length);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t id = (buffer[4U] << 8) + (buffer[5U] << 0);
|
||||
if (m_inId == 0U) {
|
||||
m_inId = id;
|
||||
} else {
|
||||
if (id != m_inId)
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char c = length - 6U;
|
||||
m_buffer.addData(&c, 1U);
|
||||
|
||||
m_buffer.addData(buffer + 6U, length - 6U);
|
||||
}
|
||||
|
||||
bool CM17Network::read(unsigned char* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
if (m_buffer.isEmpty())
|
||||
return false;
|
||||
|
||||
unsigned char c = 0U;
|
||||
m_buffer.getData(&c, 1U);
|
||||
|
||||
m_buffer.getData(data, c);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CM17Network::close()
|
||||
{
|
||||
m_socket.close();
|
||||
|
||||
LogMessage("Closing M17 network connection");
|
||||
}
|
||||
|
||||
void CM17Network::reset()
|
||||
{
|
||||
m_outId = 0U;
|
||||
m_inId = 0U;
|
||||
}
|
||||
|
||||
void CM17Network::enable(bool enabled)
|
||||
{
|
||||
if (enabled && !m_enabled)
|
||||
reset();
|
||||
else if (!enabled && m_enabled)
|
||||
m_buffer.clear();
|
||||
|
||||
m_enabled = enabled;
|
||||
}
|
||||
|
||||
bool CM17Network::isConnected() const
|
||||
{
|
||||
return (m_addrLen != 0);
|
||||
}
|
||||
|
||||
void CM17Network::sendPing()
|
||||
{
|
||||
unsigned char buffer[5U];
|
||||
|
||||
buffer[0U] = 'P';
|
||||
buffer[1U] = 'I';
|
||||
buffer[2U] = 'N';
|
||||
buffer[3U] = 'G';
|
||||
|
||||
if (m_debug)
|
||||
CUtils::dump(1U, "M17 Network Transmitted", buffer, 4U);
|
||||
|
||||
m_socket.write(buffer, 4U, m_addr, m_addrLen);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
72
M17Network.h
72
M17Network.h
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2021,2023 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef M17Network_H
|
||||
#define M17Network_H
|
||||
|
||||
#include "M17Defines.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "UDPSocket.h"
|
||||
#include "Defines.h"
|
||||
#include "Timer.h"
|
||||
|
||||
#if defined(USE_M17)
|
||||
|
||||
#include <random>
|
||||
#include <cstdint>
|
||||
|
||||
class CM17Network {
|
||||
public:
|
||||
CM17Network(const std::string& localAddress, unsigned short localPort, const std::string& gatewayAddress, unsigned short gatewayPort, bool debug);
|
||||
~CM17Network();
|
||||
|
||||
bool open();
|
||||
|
||||
void enable(bool enabled);
|
||||
|
||||
bool write(const unsigned char* data);
|
||||
|
||||
bool read(unsigned char* data);
|
||||
|
||||
void reset();
|
||||
|
||||
void close();
|
||||
|
||||
void clock(unsigned int ms);
|
||||
|
||||
bool isConnected() const;
|
||||
|
||||
private:
|
||||
CUDPSocket m_socket;
|
||||
sockaddr_storage m_addr;
|
||||
unsigned int m_addrLen;
|
||||
bool m_debug;
|
||||
bool m_enabled;
|
||||
uint16_t m_outId;
|
||||
uint16_t m_inId;
|
||||
CRingBuffer<unsigned char> m_buffer;
|
||||
std::mt19937 m_random;
|
||||
CTimer m_timer;
|
||||
|
||||
void sendPing();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
240
M17Utils.cpp
240
M17Utils.cpp
|
|
@ -1,240 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2021,2023,2024,2025 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "M17Utils.h"
|
||||
#include "M17Defines.h"
|
||||
|
||||
#if defined(USE_M17)
|
||||
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
||||
const std::string M17_CHARS = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.";
|
||||
|
||||
const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
|
||||
|
||||
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
|
||||
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
|
||||
|
||||
void CM17Utils::encodeCallsign(const std::string& callsign, unsigned char* encoded)
|
||||
{
|
||||
assert(encoded != nullptr);
|
||||
|
||||
if (callsign == "ALL" || callsign == "ALL ") {
|
||||
encoded[0U] = 0xFFU;
|
||||
encoded[1U] = 0xFFU;
|
||||
encoded[2U] = 0xFFU;
|
||||
encoded[3U] = 0xFFU;
|
||||
encoded[4U] = 0xFFU;
|
||||
encoded[5U] = 0xFFU;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int len = (unsigned int)callsign.size();
|
||||
if (len > 9U)
|
||||
len = 9U;
|
||||
|
||||
uint64_t enc = 0ULL;
|
||||
for (int i = len - 1; i >= 0; i--) {
|
||||
if ((i == 0) && (callsign[i] == '#')) {
|
||||
enc += 262144000000000ULL;
|
||||
} else {
|
||||
size_t pos = M17_CHARS.find(callsign[i]);
|
||||
if (pos == std::string::npos)
|
||||
pos = 0ULL;
|
||||
|
||||
enc *= 40ULL;
|
||||
enc += pos;
|
||||
}
|
||||
}
|
||||
|
||||
encoded[0U] = (enc >> 40) & 0xFFU;
|
||||
encoded[1U] = (enc >> 32) & 0xFFU;
|
||||
encoded[2U] = (enc >> 24) & 0xFFU;
|
||||
encoded[3U] = (enc >> 16) & 0xFFU;
|
||||
encoded[4U] = (enc >> 8) & 0xFFU;
|
||||
encoded[5U] = (enc >> 0) & 0xFFU;
|
||||
}
|
||||
|
||||
void CM17Utils::decodeCallsign(const unsigned char* encoded, std::string& callsign)
|
||||
{
|
||||
assert(encoded != nullptr);
|
||||
|
||||
callsign.clear();
|
||||
|
||||
uint64_t enc = (uint64_t(encoded[0U]) << 40) +
|
||||
(uint64_t(encoded[1U]) << 32) +
|
||||
(uint64_t(encoded[2U]) << 24) +
|
||||
(uint64_t(encoded[3U]) << 16) +
|
||||
(uint64_t(encoded[4U]) << 8) +
|
||||
(uint64_t(encoded[5U]) << 0);
|
||||
|
||||
if (enc == 281474976710655ULL) {
|
||||
callsign = "ALL";
|
||||
return;
|
||||
}
|
||||
|
||||
if (enc >= 268697600000000ULL) {
|
||||
callsign = "Invalid";
|
||||
return;
|
||||
}
|
||||
|
||||
if (enc >= 262144000000000ULL) {
|
||||
callsign = "#";
|
||||
enc -= 262144000000000ULL;
|
||||
}
|
||||
|
||||
while (enc > 0ULL) {
|
||||
callsign += " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/."[enc % 40ULL];
|
||||
enc /= 40ULL;
|
||||
}
|
||||
}
|
||||
|
||||
void CM17Utils::splitFragmentLICH(const unsigned char* data, unsigned int& frag1, unsigned int& frag2, unsigned int& frag3, unsigned int& frag4)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
frag1 = frag2 = frag3 = frag4 = 0x00U;
|
||||
|
||||
unsigned int offset = 0U;
|
||||
unsigned int MASK = 0x800U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = READ_BIT1(data, offset) != 0x00U;
|
||||
if (b)
|
||||
frag1 |= MASK;
|
||||
}
|
||||
|
||||
MASK = 0x800U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = READ_BIT1(data, offset) != 0x00U;
|
||||
if (b)
|
||||
frag2 |= MASK;
|
||||
}
|
||||
|
||||
MASK = 0x800U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = READ_BIT1(data, offset) != 0x00U;
|
||||
if (b)
|
||||
frag3 |= MASK;
|
||||
}
|
||||
|
||||
MASK = 0x800U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = READ_BIT1(data, offset) != 0x00U;
|
||||
if (b)
|
||||
frag4 |= MASK;
|
||||
}
|
||||
}
|
||||
|
||||
void CM17Utils::splitFragmentLICHFEC(const unsigned char* data, unsigned int& frag1, unsigned int& frag2, unsigned int& frag3, unsigned int& frag4)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
frag1 = frag2 = frag3 = frag4 = 0x00U;
|
||||
|
||||
unsigned int offset = 0U;
|
||||
unsigned int MASK = 0x800000U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = READ_BIT1(data, offset) != 0x00U;
|
||||
if (b)
|
||||
frag1 |= MASK;
|
||||
}
|
||||
|
||||
MASK = 0x800000U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = READ_BIT1(data, offset) != 0x00U;
|
||||
if (b)
|
||||
frag2 |= MASK;
|
||||
}
|
||||
|
||||
MASK = 0x800000U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = READ_BIT1(data, offset) != 0x00U;
|
||||
if (b)
|
||||
frag3 |= MASK;
|
||||
}
|
||||
|
||||
MASK = 0x800000U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = READ_BIT1(data, offset) != 0x00U;
|
||||
if (b)
|
||||
frag4 |= MASK;
|
||||
}
|
||||
}
|
||||
|
||||
void CM17Utils::combineFragmentLICH(unsigned int frag1, unsigned int frag2, unsigned int frag3, unsigned int frag4, unsigned char* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
unsigned int offset = 0U;
|
||||
unsigned int MASK = 0x800U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = (frag1 & MASK) == MASK;
|
||||
WRITE_BIT1(data, offset, b);
|
||||
}
|
||||
|
||||
MASK = 0x800U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = (frag2 & MASK) == MASK;
|
||||
WRITE_BIT1(data, offset, b);
|
||||
}
|
||||
|
||||
MASK = 0x800U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = (frag3 & MASK) == MASK;
|
||||
WRITE_BIT1(data, offset, b);
|
||||
}
|
||||
|
||||
MASK = 0x800U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = (frag4 & MASK) == MASK;
|
||||
WRITE_BIT1(data, offset, b);
|
||||
}
|
||||
}
|
||||
|
||||
void CM17Utils::combineFragmentLICHFEC(unsigned int frag1, unsigned int frag2, unsigned int frag3, unsigned int frag4, unsigned char* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
unsigned int offset = 0U;
|
||||
unsigned int MASK = 0x800000U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = (frag1 & MASK) == MASK;
|
||||
WRITE_BIT1(data, offset, b);
|
||||
}
|
||||
|
||||
MASK = 0x800000U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = (frag2 & MASK) == MASK;
|
||||
WRITE_BIT1(data, offset, b);
|
||||
}
|
||||
|
||||
MASK = 0x800000U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = (frag3 & MASK) == MASK;
|
||||
WRITE_BIT1(data, offset, b);
|
||||
}
|
||||
|
||||
MASK = 0x800000U;
|
||||
for (unsigned int i = 0U; i < (M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) {
|
||||
bool b = (frag4 & MASK) == MASK;
|
||||
WRITE_BIT1(data, offset, b);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
48
M17Utils.h
48
M17Utils.h
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2020,2023 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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; either version 2 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 for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(M17Utils_H)
|
||||
#define M17Utils_H
|
||||
|
||||
#include "Defines.h"
|
||||
|
||||
#if defined(USE_M17)
|
||||
|
||||
#include <string>
|
||||
|
||||
class CM17Utils {
|
||||
public:
|
||||
CM17Utils();
|
||||
~CM17Utils();
|
||||
|
||||
static void encodeCallsign(const std::string& callsign, unsigned char* encoded);
|
||||
static void decodeCallsign(const unsigned char* encoded, std::string& callsign);
|
||||
|
||||
static void splitFragmentLICH(const unsigned char* data, unsigned int& frag1, unsigned int& frag2, unsigned int& frag3, unsigned int& frag4);
|
||||
static void splitFragmentLICHFEC(const unsigned char* data, unsigned int& frag1, unsigned int& frag2, unsigned int& frag3, unsigned int& frag4);
|
||||
|
||||
static void combineFragmentLICH(unsigned int frag1, unsigned int frag2, unsigned int frag3, unsigned int frag4, unsigned char* data);
|
||||
static void combineFragmentLICHFEC(unsigned int frag1, unsigned int frag2, unsigned int frag3, unsigned int frag4, unsigned char* data);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
31
MMDVM.ini
31
MMDVM.ini
|
|
@ -82,10 +82,8 @@ RFLevel=100
|
|||
# YSFTXLevel=50
|
||||
# P25TXLevel=50
|
||||
# NXDNTXLevel=50
|
||||
# M17TXLevel=50
|
||||
# POCSAGTXLevel=50
|
||||
# FMTXLevel=50
|
||||
# AX25TXLevel=50
|
||||
RSSIMappingFile=RSSI.dat
|
||||
UseCOSAsLockout=0
|
||||
Trace=0
|
||||
|
|
@ -153,13 +151,6 @@ RemoteGateway=0
|
|||
TXHang=5
|
||||
# ModeHang=10
|
||||
|
||||
[M17]
|
||||
Enable=1
|
||||
CAN=0
|
||||
SelfOnly=0
|
||||
TXHang=5
|
||||
# ModeHang=10
|
||||
|
||||
[POCSAG]
|
||||
Enable=1
|
||||
Frequency=439987500
|
||||
|
|
@ -210,14 +201,6 @@ MaxDevLevel=90
|
|||
ExtAudioBoost=1
|
||||
# ModeHang=10
|
||||
|
||||
[AX.25]
|
||||
Enable=1
|
||||
TXDelay=300
|
||||
RXTwist=6
|
||||
SlotTime=30
|
||||
PPersist=128
|
||||
Trace=1
|
||||
|
||||
[D-Star Network]
|
||||
Enable=1
|
||||
LocalAddress=127.0.0.1
|
||||
|
|
@ -267,15 +250,6 @@ GatewayPort=14020
|
|||
# ModeHang=3
|
||||
Debug=0
|
||||
|
||||
[M17 Network]
|
||||
Enable=1
|
||||
LocalAddress=127.0.0.1
|
||||
LocalPort=17011
|
||||
GatewayAddress=127.0.0.1
|
||||
GatewayPort=17010
|
||||
# ModeHang=3
|
||||
Debug=0
|
||||
|
||||
[POCSAG Network]
|
||||
Enable=1
|
||||
LocalAddress=127.0.0.1
|
||||
|
|
@ -298,14 +272,9 @@ RXAudioGain=1.0
|
|||
# ModeHang=3
|
||||
Debug=0
|
||||
|
||||
[AX.25 Network]
|
||||
Enable=1
|
||||
Debug=0
|
||||
|
||||
[Lock File]
|
||||
Enable=0
|
||||
File=/tmp/MMDVM_Active.lck
|
||||
|
||||
[Remote Control]
|
||||
Enable=0
|
||||
|
||||
|
|
|
|||
|
|
@ -253,10 +253,8 @@ m_dmrEnabled(false),
|
|||
m_ysfEnabled(false),
|
||||
m_p25Enabled(false),
|
||||
m_nxdnEnabled(false),
|
||||
m_m17Enabled(false),
|
||||
m_pocsagEnabled(false),
|
||||
m_fmEnabled(false),
|
||||
m_ax25Enabled(false),
|
||||
m_cwIdTime(0U),
|
||||
#if defined(USE_DMR) || defined(USE_P25)
|
||||
m_dmrLookup(nullptr),
|
||||
|
|
@ -928,7 +926,7 @@ int CMMDVMHost::run()
|
|||
|
||||
#if defined(USE_DSTAR)
|
||||
len = m_modem->readDStarData(data);
|
||||
if (m_dstar != nullptr && len > 0U) {
|
||||
if (m_dstar != nullptr && m_dstarEnabled && len > 0U) {
|
||||
if (m_mode == MODE_IDLE) {
|
||||
bool ret = m_dstar->writeModem(data, len);
|
||||
if (ret) {
|
||||
|
|
@ -947,7 +945,7 @@ int CMMDVMHost::run()
|
|||
|
||||
#if defined(USE_DMR)
|
||||
len = m_modem->readDMRData1(data);
|
||||
if (m_dmr != nullptr && len > 0U) {
|
||||
if (m_dmr != nullptr && m_dmrEnabled && len > 0U) {
|
||||
if (m_mode == MODE_IDLE) {
|
||||
if (m_duplex) {
|
||||
bool ret = m_dmr->processWakeup(data);
|
||||
|
|
@ -984,7 +982,7 @@ int CMMDVMHost::run()
|
|||
}
|
||||
|
||||
len = m_modem->readDMRData2(data);
|
||||
if (m_dmr != nullptr && len > 0U) {
|
||||
if (m_dmr != nullptr && m_dmrEnabled && len > 0U) {
|
||||
if (m_mode == MODE_IDLE) {
|
||||
if (m_duplex) {
|
||||
bool ret = m_dmr->processWakeup(data);
|
||||
|
|
@ -1023,7 +1021,7 @@ int CMMDVMHost::run()
|
|||
|
||||
#if defined(USE_YSF)
|
||||
len = m_modem->readYSFData(data);
|
||||
if (m_ysf != nullptr && len > 0U) {
|
||||
if (m_ysf != nullptr && m_ysfEnabled && len > 0U) {
|
||||
if (m_mode == MODE_IDLE) {
|
||||
bool ret = m_ysf->writeModem(data, len);
|
||||
if (ret) {
|
||||
|
|
@ -1042,7 +1040,7 @@ int CMMDVMHost::run()
|
|||
|
||||
#if defined(USE_P25)
|
||||
len = m_modem->readP25Data(data);
|
||||
if (m_p25 != nullptr && len > 0U) {
|
||||
if (m_p25 != nullptr && m_p25Enabled && len > 0U) {
|
||||
if (m_mode == MODE_IDLE) {
|
||||
bool ret = m_p25->writeModem(data, len);
|
||||
if (ret) {
|
||||
|
|
@ -1061,7 +1059,7 @@ int CMMDVMHost::run()
|
|||
|
||||
#if defined(USE_NXDN)
|
||||
len = m_modem->readNXDNData(data);
|
||||
if (m_nxdn != nullptr && len > 0U) {
|
||||
if (m_nxdn != nullptr && m_nxdnEnabled && len > 0U) {
|
||||
if (m_mode == MODE_IDLE) {
|
||||
bool ret = m_nxdn->writeModem(data, len);
|
||||
if (ret) {
|
||||
|
|
@ -1099,7 +1097,7 @@ int CMMDVMHost::run()
|
|||
|
||||
#if defined(USE_FM)
|
||||
len = m_modem->readFMData(data);
|
||||
if (m_fm != nullptr && len > 0U) {
|
||||
if (m_fm != nullptr && m_fmEnabled && len > 0U) {
|
||||
if (m_mode == MODE_IDLE) {
|
||||
bool ret = m_fm->writeModem(data, len);
|
||||
if (ret) {
|
||||
|
|
@ -1491,7 +1489,7 @@ int CMMDVMHost::run()
|
|||
}
|
||||
break;
|
||||
case DMR_BEACONS::NETWORK:
|
||||
if (m_dmrNetwork != nullptr) {
|
||||
if (m_dmrNetwork != nullptr && m_dmrEnabled) {
|
||||
bool beacon = m_dmrNetwork->wantsBeacon();
|
||||
if (beacon) {
|
||||
if ((m_mode == MODE_IDLE || m_mode == MODE_DMR) && !m_modem->hasTX()) {
|
||||
|
|
@ -1526,7 +1524,7 @@ int CMMDVMHost::run()
|
|||
pocsagTimer.clock(ms);
|
||||
if (pocsagTimer.isRunning() && pocsagTimer.hasExpired()) {
|
||||
assert(m_pocsagNetwork != nullptr);
|
||||
m_pocsagNetwork->enable(m_mode == MODE_IDLE || m_mode == MODE_POCSAG);
|
||||
m_pocsagNetwork->enable((m_mode == MODE_IDLE || m_mode == MODE_POCSAG) && m_pocsagEnabled);
|
||||
pocsagTimer.start();
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1673,7 +1671,9 @@ bool CMMDVMHost::createModem()
|
|||
std::string uartPort = m_conf.getModemUARTPort();
|
||||
unsigned int uartSpeed = m_conf.getModemUARTSpeed();
|
||||
std::string i2cPort = m_conf.getModemI2CPort();
|
||||
#if defined(__linux__)
|
||||
unsigned int i2cAddress = m_conf.getModemI2CAddress();
|
||||
#endif
|
||||
std::string modemAddress = m_conf.getModemModemAddress();
|
||||
unsigned short modemPort = m_conf.getModemModemPort();
|
||||
std::string localAddress = m_conf.getModemLocalAddress();
|
||||
|
|
@ -1852,8 +1852,8 @@ bool CMMDVMHost::createModem()
|
|||
return false;
|
||||
|
||||
m_modem->setPort(port);
|
||||
m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_m17Enabled, m_pocsagEnabled, m_fmEnabled, m_ax25Enabled);
|
||||
m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, m17TXLevel, pocsagTXLevel, fmTXLevel, ax25TXLevel);
|
||||
m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled, m_fmEnabled);
|
||||
m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel);
|
||||
m_modem->setRFParams(rxFrequency, rxOffset, txFrequency, txOffset, txDCOffset, rxDCOffset, rfLevel, pocsagFrequency);
|
||||
#if defined(USE_DMR)
|
||||
m_modem->setDMRParams(colorCode);
|
||||
|
|
|
|||
73
MMDVMHost.h
73
MMDVMHost.h
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2015-2021,2023 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2015-2021,2025 by Jonathan Naylor G4KLX
|
||||
*
|
||||
* 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
|
||||
|
|
@ -23,20 +24,16 @@
|
|||
#include "POCSAGNetwork.h"
|
||||
#include "POCSAGControl.h"
|
||||
#include "DStarNetwork.h"
|
||||
#include "AX25Network.h"
|
||||
#include "NXDNNetwork.h"
|
||||
#include "DStarControl.h"
|
||||
#include "AX25Control.h"
|
||||
#include "DMRControl.h"
|
||||
#include "YSFControl.h"
|
||||
#include "P25Control.h"
|
||||
#include "NXDNControl.h"
|
||||
#include "M17Control.h"
|
||||
#include "NXDNLookup.h"
|
||||
#include "YSFNetwork.h"
|
||||
#include "P25Network.h"
|
||||
#include "DMRNetwork.h"
|
||||
#include "M17Network.h"
|
||||
#include "FMNetwork.h"
|
||||
#include "DMRLookup.h"
|
||||
#include "FMControl.h"
|
||||
|
|
@ -191,6 +188,59 @@ private:
|
|||
std::string m_lockFileName;
|
||||
CRemoteControl* m_remoteControl;
|
||||
bool m_fixedMode;
|
||||
CConf m_conf;
|
||||
CModem* m_modem;
|
||||
CDStarControl* m_dstar;
|
||||
CDMRControl* m_dmr;
|
||||
CYSFControl* m_ysf;
|
||||
CP25Control* m_p25;
|
||||
CNXDNControl* m_nxdn;
|
||||
CPOCSAGControl* m_pocsag;
|
||||
CFMControl* m_fm;
|
||||
CDStarNetwork* m_dstarNetwork;
|
||||
IDMRNetwork* m_dmrNetwork;
|
||||
CYSFNetwork* m_ysfNetwork;
|
||||
CP25Network* m_p25Network;
|
||||
INXDNNetwork* m_nxdnNetwork;
|
||||
CPOCSAGNetwork* m_pocsagNetwork;
|
||||
CFMNetwork* m_fmNetwork;
|
||||
CDisplay* m_display;
|
||||
unsigned char m_mode;
|
||||
unsigned int m_dstarRFModeHang;
|
||||
unsigned int m_dmrRFModeHang;
|
||||
unsigned int m_ysfRFModeHang;
|
||||
unsigned int m_p25RFModeHang;
|
||||
unsigned int m_nxdnRFModeHang;
|
||||
unsigned int m_fmRFModeHang;
|
||||
unsigned int m_dstarNetModeHang;
|
||||
unsigned int m_dmrNetModeHang;
|
||||
unsigned int m_ysfNetModeHang;
|
||||
unsigned int m_p25NetModeHang;
|
||||
unsigned int m_nxdnNetModeHang;
|
||||
unsigned int m_pocsagNetModeHang;
|
||||
unsigned int m_fmNetModeHang;
|
||||
CTimer m_modeTimer;
|
||||
CTimer m_dmrTXTimer;
|
||||
CTimer m_cwIdTimer;
|
||||
bool m_duplex;
|
||||
unsigned int m_timeout;
|
||||
bool m_dstarEnabled;
|
||||
bool m_dmrEnabled;
|
||||
bool m_ysfEnabled;
|
||||
bool m_p25Enabled;
|
||||
bool m_nxdnEnabled;
|
||||
bool m_pocsagEnabled;
|
||||
bool m_fmEnabled;
|
||||
unsigned int m_cwIdTime;
|
||||
CDMRLookup* m_dmrLookup;
|
||||
CNXDNLookup* m_nxdnLookup;
|
||||
std::string m_callsign;
|
||||
unsigned int m_id;
|
||||
std::string m_cwCallsign;
|
||||
bool m_lockFileEnabled;
|
||||
std::string m_lockFileName;
|
||||
CRemoteControl* m_remoteControl;
|
||||
bool m_fixedMode;
|
||||
|
||||
CTimer m_serialTimer;
|
||||
unsigned char* m_serialBuffer;
|
||||
|
|
@ -233,8 +283,23 @@ private:
|
|||
void remoteControl(const std::string& commandString);
|
||||
void processModeCommand(unsigned char mode, unsigned int timeout);
|
||||
void processEnableCommand(bool& mode, bool enabled);
|
||||
void readParams();
|
||||
bool createModem();
|
||||
bool createDStarNetwork();
|
||||
bool createDMRNetwork();
|
||||
bool createYSFNetwork();
|
||||
bool createP25Network();
|
||||
bool createNXDNNetwork();
|
||||
bool createPOCSAGNetwork();
|
||||
bool createFMNetwork();
|
||||
|
||||
void remoteControl();
|
||||
void processModeCommand(unsigned char mode, unsigned int timeout);
|
||||
|
||||
void setMode(unsigned char mode);
|
||||
void setMode(unsigned char mode);
|
||||
void enableModemMode(bool& mode, bool enabled);
|
||||
void processEnableModeCommand(unsigned char mode, bool hasController, bool& modeEnabled, bool enableMode);
|
||||
|
||||
void createLockFile(const char* mode) const;
|
||||
void removeLockFile() const;
|
||||
|
|
|
|||
|
|
@ -28,26 +28,26 @@
|
|||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
|
|
@ -161,9 +161,6 @@
|
|||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AMBEFEC.h" />
|
||||
<ClInclude Include="AX25Control.h" />
|
||||
<ClInclude Include="AX25Defines.h" />
|
||||
<ClInclude Include="AX25Network.h" />
|
||||
<ClInclude Include="BCH.h" />
|
||||
<ClInclude Include="BPTC19696.h" />
|
||||
<ClInclude Include="Conf.h" />
|
||||
|
|
@ -198,13 +195,6 @@
|
|||
<ClInclude Include="DMRLookup.h" />
|
||||
<ClInclude Include="IIRDirectForm1Filter.h" />
|
||||
<ClInclude Include="Log.h" />
|
||||
<ClInclude Include="M17Control.h" />
|
||||
<ClInclude Include="M17Convolution.h" />
|
||||
<ClInclude Include="M17CRC.h" />
|
||||
<ClInclude Include="M17Defines.h" />
|
||||
<ClInclude Include="M17LSF.h" />
|
||||
<ClInclude Include="M17Network.h" />
|
||||
<ClInclude Include="M17Utils.h" />
|
||||
<ClInclude Include="MMDVMHost.h" />
|
||||
<ClInclude Include="ModemPort.h" />
|
||||
<ClInclude Include="MQTTConnection.h" />
|
||||
|
|
@ -265,8 +255,6 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AMBEFEC.cpp" />
|
||||
<ClCompile Include="AX25Control.cpp" />
|
||||
<ClCompile Include="AX25Network.cpp" />
|
||||
<ClCompile Include="BCH.cpp" />
|
||||
<ClCompile Include="BPTC19696.cpp" />
|
||||
<ClCompile Include="Conf.cpp" />
|
||||
|
|
@ -298,12 +286,6 @@
|
|||
<ClCompile Include="Hamming.cpp" />
|
||||
<ClCompile Include="IIRDirectForm1Filter.cpp" />
|
||||
<ClCompile Include="Log.cpp" />
|
||||
<ClCompile Include="M17Control.cpp" />
|
||||
<ClCompile Include="M17Convolution.cpp" />
|
||||
<ClCompile Include="M17CRC.cpp" />
|
||||
<ClCompile Include="M17LSF.cpp" />
|
||||
<ClCompile Include="M17Network.cpp" />
|
||||
<ClCompile Include="M17Utils.cpp" />
|
||||
<ClCompile Include="MMDVMHost.cpp" />
|
||||
<ClCompile Include="ModemPort.cpp" />
|
||||
<ClCompile Include="MQTTConnection.cpp" />
|
||||
|
|
|
|||
|
|
@ -248,42 +248,12 @@
|
|||
<ClInclude Include="UserDBentry.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AX25Control.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AX25Network.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NXDNIcomNetwork.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="NXDNKenwoodNetwork.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="M17Defines.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="M17Control.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="M17Network.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="M17LSF.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="M17CRC.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="M17Convolution.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="M17Utils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AX25Defines.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FMNetwork.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -529,36 +499,12 @@
|
|||
<ClCompile Include="UserDBentry.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AX25Control.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AX25Network.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="NXDNIcomNetwork.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="NXDNKenwoodNetwork.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="M17Control.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="M17Network.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="M17CRC.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="M17Utils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="M17LSF.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="M17Convolution.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FMNetwork.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
|||
6
Makefile
6
Makefile
|
|
@ -7,10 +7,10 @@ LIBS = -lpthread -lutil -lsamplerate -lmosquitto
|
|||
LDFLAGS = -g -L/usr/local/lib
|
||||
|
||||
OBJECTS = \
|
||||
AMBEFEC.o BCH.o AX25Control.o AX25Network.o BPTC19696.o Conf.o CRC.o DMRAccessControl.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o \
|
||||
AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o DMRAccessControl.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o \
|
||||
DMREmbeddedData.o DMRFullLC.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o \
|
||||
DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o Hamming.o I2CController.o IIRDirectForm1Filter.o Log.o M17Control.o \
|
||||
M17Convolution.o M17CRC.o M17LSF.o M17Network.o M17Utils.o MMDVMHost.o MQTTConnection.o Modem.o ModemPort.o Mutex.o NullController.o NXDNAudio.o \
|
||||
DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o Hamming.o I2CController.o IIRDirectForm1Filter.o Log.o \
|
||||
MMDVMHost.o MQTTConnection.o Modem.o ModemPort.o Mutex.o NullController.o NXDNAudio.o \
|
||||
NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \
|
||||
NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \
|
||||
POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o StopWatch.o Sync.o Thread.o Timer.o UARTController.o \
|
||||
|
|
|
|||
20
Modem.cpp
20
Modem.cpp
|
|
@ -21,9 +21,7 @@
|
|||
#include "YSFDefines.h"
|
||||
#include "P25Defines.h"
|
||||
#include "NXDNDefines.h"
|
||||
#include "AX25Defines.h"
|
||||
#include "POCSAGDefines.h"
|
||||
#include "M17Defines.h"
|
||||
#include "Thread.h"
|
||||
#include "Modem.h"
|
||||
#include "Utils.h"
|
||||
|
|
@ -136,10 +134,8 @@ const unsigned char CAP1_DMR = 0x02U;
|
|||
const unsigned char CAP1_YSF = 0x04U;
|
||||
const unsigned char CAP1_P25 = 0x08U;
|
||||
const unsigned char CAP1_NXDN = 0x10U;
|
||||
const unsigned char CAP1_M17 = 0x20U;
|
||||
const unsigned char CAP1_FM = 0x40U;
|
||||
const unsigned char CAP2_POCSAG = 0x01U;
|
||||
const unsigned char CAP2_AX25 = 0x02U;
|
||||
|
||||
CModem::CModem(bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug) :
|
||||
m_protocolVersion(0U),
|
||||
|
|
@ -393,7 +389,7 @@ void CModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int tx
|
|||
#endif
|
||||
}
|
||||
|
||||
void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool m17Enabled, bool pocsagEnabled, bool fmEnabled, bool ax25Enabled)
|
||||
void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled, bool fmEnabled)
|
||||
{
|
||||
#if defined(USE_DSTAR)
|
||||
m_dstarEnabled = dstarEnabled;
|
||||
|
|
@ -424,7 +420,7 @@ void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled,
|
|||
#endif
|
||||
}
|
||||
|
||||
void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float m17TXLevel, float pocsagTXLevel, float fmTXLevel, float ax25TXLevel)
|
||||
void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagTXLevel, float fmTXLevel)
|
||||
{
|
||||
m_rxLevel = rxLevel;
|
||||
m_cwIdTXLevel = cwIdTXLevel;
|
||||
|
|
@ -1132,7 +1128,7 @@ void CModem::clock(unsigned int ms)
|
|||
}
|
||||
|
||||
m_inactivityTimer.start();
|
||||
// LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u,%u,%u,%u,%u,%u lockout=%d, cd=%d", m_buffer[m_offset + 2U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, m_nxdnSpace, m_m17Space, m_pocsagSpace, m_fmSpace, m_ax25Space, int(m_lockout), int(m_cd));
|
||||
// LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u,%u,%u,%u lockout=%d, cd=%d", m_buffer[m_offset + 2U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, m_nxdnSpace, m_pocsagSpace, m_fmSpace, int(m_lockout), int(m_cd));
|
||||
break;
|
||||
|
||||
case MMDVM_TRANSPARENT: {
|
||||
|
|
@ -2284,11 +2280,6 @@ bool CModem::hasNXDN() const
|
|||
return (m_capabilities1 & CAP1_NXDN) == CAP1_NXDN;
|
||||
}
|
||||
|
||||
bool CModem::hasM17() const
|
||||
{
|
||||
return (m_capabilities1 & CAP1_M17) == CAP1_M17;
|
||||
}
|
||||
|
||||
bool CModem::hasFM() const
|
||||
{
|
||||
return (m_capabilities1 & CAP1_FM) == CAP1_FM;
|
||||
|
|
@ -2299,11 +2290,6 @@ bool CModem::hasPOCSAG() const
|
|||
return (m_capabilities2 & CAP2_POCSAG) == CAP2_POCSAG;
|
||||
}
|
||||
|
||||
bool CModem::hasAX25() const
|
||||
{
|
||||
return (m_capabilities2 & CAP2_AX25) == CAP2_AX25;
|
||||
}
|
||||
|
||||
unsigned int CModem::getVersion() const
|
||||
{
|
||||
return m_protocolVersion;
|
||||
|
|
|
|||
2
Modem.h
2
Modem.h
|
|
@ -84,10 +84,8 @@ public:
|
|||
bool hasYSF() const;
|
||||
bool hasP25() const;
|
||||
bool hasNXDN() const;
|
||||
bool hasM17() const;
|
||||
bool hasPOCSAG() const;
|
||||
bool hasFM() const;
|
||||
bool hasAX25() const;
|
||||
|
||||
unsigned int getVersion() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -995,6 +995,9 @@ void CNXDNControl::clock(unsigned int ms)
|
|||
if (m_network != nullptr)
|
||||
writeNetwork();
|
||||
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
m_rfTimeoutTimer.clock(ms);
|
||||
m_netTimeoutTimer.clock(ms);
|
||||
|
||||
|
|
@ -1084,6 +1087,18 @@ void CNXDNControl::enable(bool enabled)
|
|||
m_queue.clear();
|
||||
|
||||
// Reset the RF section
|
||||
switch (m_rfState) {
|
||||
case RPT_RF_STATE::LISTENING:
|
||||
case RPT_RF_STATE::REJECTED:
|
||||
case RPT_RF_STATE::INVALID:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (m_rfTimeoutTimer.isRunning()) {
|
||||
LogMessage("NXDN, RF user has timed out");
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_rfState = RPT_RF_STATE::LISTENING;
|
||||
|
||||
m_rfMask = 0x00U;
|
||||
|
|
@ -1092,6 +1107,16 @@ void CNXDNControl::enable(bool enabled)
|
|||
m_rfTimeoutTimer.stop();
|
||||
|
||||
// Reset the networking section
|
||||
switch(m_netState) {
|
||||
case RPT_NET_STATE::IDLE:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (m_netTimeoutTimer.isRunning()) {
|
||||
LogMessage("NXDN, network user has timed out");
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_netState = RPT_NET_STATE::IDLE;
|
||||
|
||||
m_netMask = 0x00U;
|
||||
|
|
|
|||
|
|
@ -756,6 +756,9 @@ void CP25Control::clock(unsigned int ms)
|
|||
if (m_network != nullptr)
|
||||
writeNetwork();
|
||||
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
m_rfTimeout.clock(ms);
|
||||
m_netTimeout.clock(ms);
|
||||
|
||||
|
|
|
|||
12
README.md
12
README.md
|
|
@ -1,20 +1,18 @@
|
|||
These are the source files for building the MMDVMHost, the program that
|
||||
interfaces to the MMDVM or DVMega on the one side, and a suitable network on
|
||||
the other. It supports D-Star, DMR, P25 Phase 1, NXDN, System Fusion, M17,
|
||||
POCSAG, FM, and AX.25 on the MMDVM, and D-Star, DMR, and System Fusion on the DVMega.
|
||||
the other. It supports D-Star, DMR, P25 Phase 1, NXDN, System Fusion,
|
||||
POCSAG, and FM on the MMDVM, and D-Star, DMR, and System Fusion on the DVMega.
|
||||
|
||||
On the D-Star side the MMDVMHost interfaces with the ircDDB Gateway, on DMR it
|
||||
connects to the DMR Gateway to allow for connection to multiple DMR networks,
|
||||
or a single network directly. on System Fusion it connects to the YSF Gateway to allow
|
||||
access to the FCS and YSF networks. On P25 it connects to the P25 Gateway. On
|
||||
NXDN it connects to the NXDN Gateway which provides access to the NXDN and
|
||||
NXCore talk groups. On M17 it uses the M17 Gateway to access the M17 reflector system.
|
||||
It uses the DAPNET Gateway to access DAPNET to receive
|
||||
paging messages. Finally it uses the FM Gateway to interface to existing FM
|
||||
networks.
|
||||
NXCore talk groups. It uses the DAPNET Gateway to access DAPNET to receive
|
||||
paging messages.
|
||||
|
||||
It builds on 32-bit and 64-bit Linux as well as on Windows using Visual Studio
|
||||
2019 on x86 and x64.
|
||||
2022 on x86 and x64.
|
||||
|
||||
This software is licenced under the GPL v2 and is primarily intended for amateur and
|
||||
educational use.
|
||||
|
|
|
|||
25
Sync.cpp
25
Sync.cpp
|
|
@ -23,7 +23,6 @@
|
|||
#include "YSFDefines.h"
|
||||
#include "P25Defines.h"
|
||||
#include "NXDNDefines.h"
|
||||
#include "M17Defines.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
|
@ -93,27 +92,3 @@ void CSync::addNXDNSync(unsigned char* data)
|
|||
data[i] = (data[i] & ~NXDN_FSW_BYTES_MASK[i]) | NXDN_FSW_BYTES[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(USE_M17)
|
||||
void CSync::addM17LinkSetupSync(unsigned char* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
::memcpy(data, M17_LINK_SETUP_SYNC_BYTES, M17_SYNC_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
void CSync::addM17StreamSync(unsigned char* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
::memcpy(data, M17_STREAM_SYNC_BYTES, M17_SYNC_LENGTH_BYTES);
|
||||
}
|
||||
|
||||
void CSync::addM17EOTSync(unsigned char* data)
|
||||
{
|
||||
assert(data != nullptr);
|
||||
|
||||
::memcpy(data, M17_EOT_SYNC_BYTES, M17_SYNC_LENGTH_BYTES);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
6
Sync.h
6
Sync.h
|
|
@ -45,12 +45,6 @@ public:
|
|||
static void addNXDNSync(unsigned char* data);
|
||||
#endif
|
||||
|
||||
#if defined(USE_M17)
|
||||
static void addM17LinkSetupSync(unsigned char* data);
|
||||
static void addM17StreamSync(unsigned char* data);
|
||||
static void addM17EOTSync(unsigned char* data);
|
||||
#endif
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ int CUDPSocket::lookup(const std::string& hostname, unsigned short port, sockadd
|
|||
/* Port is always digits, no needs to lookup service */
|
||||
hints.ai_flags |= AI_NUMERICSERV;
|
||||
|
||||
int err = ::getaddrinfo(hostname.empty() ? nullptr : hostname.c_str(), portstr.c_str(), &hints, &res);
|
||||
int err = ::getaddrinfo(hostname.empty() ? NULL : hostname.c_str(), portstr.c_str(), &hints, &res);
|
||||
if (err != 0) {
|
||||
sockaddr_in* paddr = (sockaddr_in*)&addr;
|
||||
::memset(paddr, 0x00U, address_length = sizeof(sockaddr_in));
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@
|
|||
#if !defined(VERSION_H)
|
||||
#define VERSION_H
|
||||
|
||||
const char* VERSION = "20250424";
|
||||
const char* VERSION = "20250826";
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1050,6 +1050,9 @@ void CYSFControl::clock(unsigned int ms)
|
|||
if (m_network != nullptr)
|
||||
writeNetwork();
|
||||
|
||||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
m_rfTimeoutTimer.clock(ms);
|
||||
m_netTimeoutTimer.clock(ms);
|
||||
|
||||
|
|
@ -1160,6 +1163,18 @@ void CYSFControl::enable(bool enabled)
|
|||
m_queue.clear();
|
||||
|
||||
// Reset the RF section
|
||||
switch (m_rfState) {
|
||||
case RPT_RF_STATE::LISTENING:
|
||||
case RPT_RF_STATE::REJECTED:
|
||||
case RPT_RF_STATE::INVALID:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (m_rfTimeoutTimer.isRunning()) {
|
||||
LogMessage("YSF, RF user has timed out");
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_rfState = RPT_RF_STATE::LISTENING;
|
||||
|
||||
m_rfTimeoutTimer.stop();
|
||||
|
|
@ -1170,6 +1185,16 @@ void CYSFControl::enable(bool enabled)
|
|||
m_rfDest = nullptr;
|
||||
|
||||
// Reset the networking section
|
||||
switch(m_netState) {
|
||||
case RPT_NET_STATE::IDLE:
|
||||
break;
|
||||
|
||||
default:
|
||||
if (m_netTimeoutTimer.isRunning()) {
|
||||
LogMessage("YSF, network user has timed out");
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_netState = RPT_NET_STATE::IDLE;
|
||||
|
||||
m_netTimeoutTimer.stop();
|
||||
|
|
|
|||
44
schema.json
44
schema.json
|
|
@ -1,11 +1,9 @@
|
|||
{
|
||||
"$defs": {
|
||||
"mmdvm_mode": {"type": "string", "enum": ["lockout", "idle", "error", "CW", "D-Star", "DMR", "YSF", "P25", "NXDN", "POCSAG", "FM", "M17"]},
|
||||
"mmdvm_mode": {"type": "string", "enum": ["lockout", "idle", "error", "CW", "D-Star", "DMR", "YSF", "P25", "NXDN", "POCSAG", "FM"]},
|
||||
"dstar_callsign": {"type": "string", "minLength": 8, "maxLength": 8},
|
||||
"dstar_extension": {"type": "string", "minLength": 4, "maxLength": 4},
|
||||
"ysf_callsign": {"type": "string", "minLength": 10, "maxLength": 10},
|
||||
"ax25_callsign": {"type": "string", "minLength": 1, "maxLength": 9},
|
||||
"m17_callsign": {"type": "string", "minLength": 9, "maxLength": 9},
|
||||
"dmr_id": {"type": "integer", "minimum": 1, "maximum": 16777215},
|
||||
"p25_id": {"type": "integer", "minimum": 1, "maximum": 65535},
|
||||
"nxdn_id": {"type": "integer", "minimum": 1, "maximum": 65535},
|
||||
|
|
@ -14,12 +12,9 @@
|
|||
"mode": {"type": "string", "enum": ["voice", "data"]},
|
||||
"ysf_mode": {"type": "string", "enum": ["voice_vw", "voice_dn", "data_fr"]},
|
||||
"dg-id": {"type": "integer", "minimum": 0, "maximum": 99},
|
||||
"ax25_type": {"type": "string"},
|
||||
"dmr_slot": {"type": "integer", "enum": [1, 2]},
|
||||
"source": {"type": "string", "enum": ["rf", "network"]},
|
||||
"fm_state": {"type": "string", "enum": ["listening", "kerchunk_rf", "relaying_rf", "relaying_wait_rf", "timeout_rf", "timeout_wait_rf", "kerchunk_ext", "relaying_ext", "relaying_wait_ext", "timeout_ext", "timeout_wait_ext", "hang", "unknown"]},
|
||||
"m17_traffic_type": {"type": "string", "enum": ["audio", "audio_data", "data"]},
|
||||
"ax25_pid": {"type": "string"},
|
||||
"pocsag_source": {"type": "string", "enum": ["local", "network"]},
|
||||
"pocsag_functional": {"type": "string", "enum": ["numeric", "alphanumeric", "alert_1", "alert_2", "end"]},
|
||||
"action": {"type": "string", "enum": ["invalid", "rejected", "start", "late_entry", "end", "lost"]},
|
||||
|
|
@ -183,42 +178,5 @@
|
|||
"timestamp": {"$ref": "#/$defs/timestamp"},
|
||||
"state": {"$ref": "#/$defs/fm_state"},
|
||||
"required": ["timestamp", "state"]
|
||||
},
|
||||
|
||||
"AX.25": {
|
||||
"type": "object",
|
||||
"timestamp": {"$ref": "#/$defs/timestamp"},
|
||||
"source": {"$ref": "#/$defs/source"},
|
||||
"source_cs": {"$ref": "#/$defs/ax25_callsign"},
|
||||
"destination_cs": {"$ref": "#/$defs/ax25_callsign"},
|
||||
"digipeaters": {
|
||||
"type": "array",
|
||||
"callsign": {"$ref": "#/$defs/ax25_callsign"},
|
||||
"repeated": {"type": "boolean"}
|
||||
},
|
||||
"type": {"$ref": "#/$defs/ax25_type"},
|
||||
"pid": {"$ref": "#/$defs/ax25_pid"},
|
||||
"rssi": {"$ref": "#/$defs/rssi"},
|
||||
"ber": {"$ref": "#/$defs/ber"},
|
||||
"data": {"type": "string"},
|
||||
"required": ["timestamp", "source", "destination", "source", "type"]
|
||||
},
|
||||
|
||||
"M17": {
|
||||
"type": "object",
|
||||
"timestamp": {"$ref": "#/$defs/timestamp"},
|
||||
"source_cs": {"$ref": "#/$defs/m17_callsign"},
|
||||
"destination_cs": {"$ref": "#/$defs/m17_callsign"},
|
||||
"source": {"$ref": "#/$defs/source"},
|
||||
"action": {"$ref": "#/$defs/action"},
|
||||
"traffic_type": {"$ref": "#/$defs/m17_traffic_type"},
|
||||
"duration": {"$ref": "#/$defs/duration"},
|
||||
"ber": {"$ref": "#/$defs/ber"},
|
||||
"rssi": {
|
||||
"min": {"$ref": "#/$defs/rssi"},
|
||||
"max": {"$ref": "#/$defs/rssi"},
|
||||
"ave": {"$ref": "#/$defs/rssi"}
|
||||
},
|
||||
"required": ["timestamp", "action"]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue