diff --git a/AX25Control.cpp b/AX25Control.cpp deleted file mode 100644 index fb77370..0000000 --- a/AX25Control.cpp +++ /dev/null @@ -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 -#include -#include -#include - - -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, "", (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, "", (data[n] >> 5) & 0x07U); - break; - case 0x05U: - ::sprintf(t, "", (data[n] >> 5) & 0x07U); - break; - case 0x09U: - ::sprintf(t, "", (data[n] >> 5) & 0x07U); - break; - case 0x0DU: - ::sprintf(t, "", (data[n] >> 5) & 0x07U); - break; - default: - ::sprintf(t, "", (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 += ""; - break; - case 0x2FU: - text += ""; - break; - case 0x43U: - text += ""; - break; - case 0x0FU: - text += ""; - break; - case 0x63U: - text += ""; - break; - case 0x87U: - text += ""; - break; - case 0x03U: - text += ""; - break; - case 0xAFU: - text += ""; - break; - case 0xE3U: - text += ""; - break; - default: - text += ""; - 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 diff --git a/AX25Control.h b/AX25Control.h deleted file mode 100644 index 3f0bd6a..0000000 --- a/AX25Control.h +++ /dev/null @@ -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 - -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 - diff --git a/AX25Defines.h b/AX25Defines.h deleted file mode 100644 index a0f4c80..0000000 --- a/AX25Defines.h +++ /dev/null @@ -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 diff --git a/AX25Network.cpp b/AX25Network.cpp deleted file mode 100644 index 77ee176..0000000 --- a/AX25Network.cpp +++ /dev/null @@ -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 -#include -#include - -// 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 - diff --git a/AX25Network.h b/AX25Network.h deleted file mode 100644 index 8251936..0000000 --- a/AX25Network.h +++ /dev/null @@ -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 -#include - -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 m_buffer; - CMutex m_mutex; - bool m_debug; - bool m_enabled; -}; - -#endif - -#endif - diff --git a/Conf.cpp b/Conf.cpp index 5789fd0..758fa7c 100644 --- a/Conf.cpp +++ b/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) diff --git a/Conf.h b/Conf.h index c6230ff..cccac5d 100644 --- a/Conf.h +++ b/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; diff --git a/DMRSlot.cpp b/DMRSlot.cpp index f661283..7e4dbf2 100644 --- a/DMRSlot.cpp +++ b/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; diff --git a/DStarControl.cpp b/DStarControl.cpp index 771c66c..605c6fb 100644 --- a/DStarControl.cpp +++ b/DStarControl.cpp @@ -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; diff --git a/Defines.h b/Defines.h index 760263f..67ca0ad 100644 --- a/Defines.h +++ b/Defines.h @@ -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; diff --git a/M17CRC.cpp b/M17CRC.cpp deleted file mode 100644 index bf80713..0000000 --- a/M17CRC.cpp +++ /dev/null @@ -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 -#include - -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 - diff --git a/M17CRC.h b/M17CRC.h deleted file mode 100644 index 51a1287..0000000 --- a/M17CRC.h +++ /dev/null @@ -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 - -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 - diff --git a/M17Control.cpp b/M17Control.cpp deleted file mode 100644 index ea8f9f0..0000000 --- a/M17Control.cpp +++ /dev/null @@ -1,1171 +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 "M17Control.h" -#include "M17Convolution.h" -#include "M17Utils.h" -#include "M17CRC.h" -#include "Golay24128.h" -#include "Utils.h" -#include "Sync.h" -#include "Log.h" - -#if defined(USE_M17) - -#include -#include -#include -#include - -const unsigned int INTERLEAVER[] = { - 0U, 137U, 90U, 227U, 180U, 317U, 270U, 39U, 360U, 129U, 82U, 219U, 172U, 309U, 262U, 31U, 352U, 121U, 74U, 211U, 164U, - 301U, 254U, 23U, 344U, 113U, 66U, 203U, 156U, 293U, 246U, 15U, 336U, 105U, 58U, 195U, 148U, 285U, 238U, 7U, 328U, 97U, - 50U, 187U, 140U, 277U, 230U, 367U, 320U, 89U, 42U, 179U, 132U, 269U, 222U, 359U, 312U, 81U, 34U, 171U, 124U, 261U, 214U, - 351U, 304U, 73U, 26U, 163U, 116U, 253U, 206U, 343U, 296U, 65U, 18U, 155U, 108U, 245U, 198U, 335U, 288U, 57U, 10U, 147U, - 100U, 237U, 190U, 327U, 280U, 49U, 2U, 139U, 92U, 229U, 182U, 319U, 272U, 41U, 362U, 131U, 84U, 221U, 174U, 311U, 264U, - 33U, 354U, 123U, 76U, 213U, 166U, 303U, 256U, 25U, 346U, 115U, 68U, 205U, 158U, 295U, 248U, 17U, 338U, 107U, 60U, 197U, - 150U, 287U, 240U, 9U, 330U, 99U, 52U, 189U, 142U, 279U, 232U, 1U, 322U, 91U, 44U, 181U, 134U, 271U, 224U, 361U, 314U, 83U, - 36U, 173U, 126U, 263U, 216U, 353U, 306U, 75U, 28U, 165U, 118U, 255U, 208U, 345U, 298U, 67U, 20U, 157U, 110U, 247U, 200U, - 337U, 290U, 59U, 12U, 149U, 102U, 239U, 192U, 329U, 282U, 51U, 4U, 141U, 94U, 231U, 184U, 321U, 274U, 43U, 364U, 133U, 86U, - 223U, 176U, 313U, 266U, 35U, 356U, 125U, 78U, 215U, 168U, 305U, 258U, 27U, 348U, 117U, 70U, 207U, 160U, 297U, 250U, 19U, - 340U, 109U, 62U, 199U, 152U, 289U, 242U, 11U, 332U, 101U, 54U, 191U, 144U, 281U, 234U, 3U, 324U, 93U, 46U, 183U, 136U, 273U, - 226U, 363U, 316U, 85U, 38U, 175U, 128U, 265U, 218U, 355U, 308U, 77U, 30U, 167U, 120U, 257U, 210U, 347U, 300U, 69U, 22U, - 159U, 112U, 249U, 202U, 339U, 292U, 61U, 14U, 151U, 104U, 241U, 194U, 331U, 284U, 53U, 6U, 143U, 96U, 233U, 186U, 323U, - 276U, 45U, 366U, 135U, 88U, 225U, 178U, 315U, 268U, 37U, 358U, 127U, 80U, 217U, 170U, 307U, 260U, 29U, 350U, 119U, 72U, - 209U, 162U, 299U, 252U, 21U, 342U, 111U, 64U, 201U, 154U, 291U, 244U, 13U, 334U, 103U, 56U, 193U, 146U, 283U, 236U, 5U, - 326U, 95U, 48U, 185U, 138U, 275U, 228U, 365U, 318U, 87U, 40U, 177U, 130U, 267U, 220U, 357U, 310U, 79U, 32U, 169U, 122U, - 259U, 212U, 349U, 302U, 71U, 24U, 161U, 114U, 251U, 204U, 341U, 294U, 63U, 16U, 153U, 106U, 243U, 196U, 333U, 286U, 55U, - 8U, 145U, 98U, 235U, 188U, 325U, 278U, 47U}; - -const unsigned char SCRAMBLER[] = { - 0x00U, 0x00U, 0xD6U, 0xB5U, 0xE2U, 0x30U, 0x82U, 0xFFU, 0x84U, 0x62U, 0xBAU, 0x4EU, 0x96U, 0x90U, 0xD8U, 0x98U, 0xDDU, - 0x5DU, 0x0CU, 0xC8U, 0x52U, 0x43U, 0x91U, 0x1DU, 0xF8U, 0x6EU, 0x68U, 0x2FU, 0x35U, 0xDAU, 0x14U, 0xEAU, 0xCDU, 0x76U, - 0x19U, 0x8DU, 0xD5U, 0x80U, 0xD1U, 0x33U, 0x87U, 0x13U, 0x57U, 0x18U, 0x2DU, 0x29U, 0x78U, 0xC3U}; - -const unsigned int RSSI_COUNT = 25U; // 25 * 40ms = 1000ms -const unsigned int BER_COUNT = 25U * 272U; // 25 * 40ms = 1000ms - -const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; - -#define WRITE_BIT(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_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) - -CM17Control::CM17Control(const std::string& callsign, unsigned int can, bool selfOnly, bool allowEncryption, CM17Network* network, unsigned int timeout, bool duplex, CRSSIInterpolator* rssiMapper) : -m_callsign(callsign), -m_can(can), -m_selfOnly(selfOnly), -m_allowEncryption(allowEncryption), -m_network(network), -m_duplex(duplex), -m_queue(5000U, "M17 Control"), -m_source(), -m_dest(), -m_rfState(RPT_RF_STATE::LISTENING), -m_netState(RPT_NET_STATE::IDLE), -m_rfTimeoutTimer(1000U, timeout), -m_netTimeoutTimer(1000U, timeout), -m_networkWatchdog(1000U, 0U, 1500U), -m_elapsed(), -m_rfFrames(0U), -m_netFrames(0U), -m_rfErrs(0U), -m_rfBits(1U), -m_rfLSFCount(0U), -m_rfCurrentRFLSF(), -m_rfCurrentNetLSF(), -m_rfCollectingLSF(), -m_rfCollectedLSF(), -m_rfLSFn(0U), -m_netLSF(), -m_netLSFn(0U), -m_rfTextBits(0x00U), -m_netTextBits(0x00U), -m_rfText(nullptr), -m_netText(nullptr), -m_rssiMapper(rssiMapper), -m_rssi(0), -m_maxRSSI(0), -m_minRSSI(0), -m_aveRSSI(0), -m_rssiCountTotal(0U), -m_rssiAccum(0), -m_rssiCount(0U), -m_bitsCount(0U), -m_bitErrsAccum(0U), -m_enabled(true) -{ - assert(rssiMapper != nullptr); - - m_rfText = new char[4U * M17_META_LENGTH_BYTES]; - m_netText = new char[4U * M17_META_LENGTH_BYTES]; -} - -CM17Control::~CM17Control() -{ - delete[] m_netText; - delete[] m_rfText; -} - -bool CM17Control::writeModem(unsigned char* data, unsigned int len) -{ - assert(data != nullptr); - - if (!m_enabled) - return false; - - unsigned char type = data[0U]; - - if (type == TAG_LOST && (m_rfState == RPT_RF_STATE::AUDIO || m_rfState == RPT_RF_STATE::DATA_AUDIO)) { - if (m_rssi != 0U) { - LogMessage("M17, transmission lost from %s to %s, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", m_source.c_str(), m_dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); - writeJSONRF("lost", float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); - } else { - LogMessage("M17, transmission lost from %s to %s, %.1f seconds, BER: %.1f%%", m_source.c_str(), m_dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); - writeJSONRF("lost", float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); - } - writeEndRF(); - return false; - } - - if ((type == TAG_LOST) && (m_rfState == RPT_RF_STATE::DATA)) { - writeEndRF(); - return false; - } - - if ((type == TAG_LOST) && (m_rfState == RPT_RF_STATE::REJECTED)) { - writeEndRF(); - return false; - } - - if (type == TAG_LOST) { - m_rfState = RPT_RF_STATE::LISTENING; - return false; - } - - // Have we got RSSI bytes on the end? - if (len == (M17_FRAME_LENGTH_BYTES + 4U)) { - uint16_t raw = 0U; - raw |= (data[50U] << 8) & 0xFF00U; - raw |= (data[51U] << 0) & 0x00FFU; - - // Convert the raw RSSI to dBm - int m_rssi = m_rssiMapper->interpolate(raw); - if (m_rssi != 0) - LogDebug("M17, raw RSSI: %u, reported RSSI: %d dBm", raw, m_rssi); - - if (m_rssi < m_minRSSI) - m_minRSSI = m_rssi; - if (m_rssi > m_maxRSSI) - m_maxRSSI = m_rssi; - - m_aveRSSI += m_rssi; - m_rssiCountTotal++; - - m_rssiAccum += m_rssi; - m_rssiCountTotal++; - } - - unsigned char temp[M17_FRAME_LENGTH_BYTES]; - decorrelator(data + 2U, temp); - interleaver(temp, data + 2U); - - if ((m_rfState == RPT_RF_STATE::LISTENING) && (data[0U] == TAG_HEADER)) { - m_rfCurrentRFLSF.reset(); - m_rfCurrentNetLSF.reset(); - - CM17Convolution conv; - unsigned char frame[M17_LSF_LENGTH_BYTES]; - unsigned int ber = conv.decodeLinkSetup(data + 2U + M17_SYNC_LENGTH_BYTES, frame); - - bool valid = CM17CRC::checkCRC16(frame, M17_LSF_LENGTH_BYTES); - if (valid) { - m_rfCurrentNetLSF.setLinkSetup(frame); - - bool ret = processRFHeader(false); - if (!ret) { - m_rfCurrentRFLSF.reset(); - m_rfCurrentNetLSF.reset(); - return false; - } - - LogDebug("M17, link setup frame: errs: %u/368 (%.1f%%)", ber, float(ber) / 3.68F); - - m_rfFrames = 0U; - m_rfErrs = ber; - m_rfBits = 368U; - - m_rfCollectingLSF.reset(); - m_rfCollectedLSF.reset(); - m_rfTimeoutTimer.start(); - - m_minRSSI = m_rssi; - m_maxRSSI = m_rssi; - m_aveRSSI = m_rssi; - m_rssiCountTotal = 1U; - - m_rssiAccum = m_rssi; - m_rssiCount = 1U; - - m_bitErrsAccum = 0U; - m_bitsCount = 0U; - - m_rfLSFn = 0U; - m_rfLSFCount = 0U; - m_rfTextBits = 0x00U; - ::memset(m_rfText, 0x00U, 4U * M17_META_LENGTH_BYTES); - - return true; - } else { - m_rfState = RPT_RF_STATE::LATE_ENTRY; - return false; - } - } - - if ((m_rfState == RPT_RF_STATE::LISTENING) && (data[0U] == TAG_DATA)) { - m_rfState = RPT_RF_STATE::LATE_ENTRY; - m_rfCurrentRFLSF.reset(); - m_rfCurrentNetLSF.reset(); - } - - if ((m_rfState == RPT_RF_STATE::LATE_ENTRY) && (data[0U] == TAG_DATA)) { - unsigned int lich1, lich2, lich3, lich4; - bool valid1 = CGolay24128::decode24128(data + 2U + M17_SYNC_LENGTH_BYTES + 0U, lich1); - bool valid2 = CGolay24128::decode24128(data + 2U + M17_SYNC_LENGTH_BYTES + 3U, lich2); - bool valid3 = CGolay24128::decode24128(data + 2U + M17_SYNC_LENGTH_BYTES + 6U, lich3); - bool valid4 = CGolay24128::decode24128(data + 2U + M17_SYNC_LENGTH_BYTES + 9U, lich4); - - if (!valid1 || !valid2 || !valid3 || !valid4) - return false; - - unsigned char lich[M17_LICH_FRAGMENT_LENGTH_BYTES]; - CM17Utils::combineFragmentLICH(lich1, lich2, lich3, lich4, lich); - - m_rfLSFn = (lich4 >> 5) & 0x07U; - m_rfCurrentNetLSF.setFragment(lich, m_rfLSFn); - - bool valid = m_rfCurrentNetLSF.isValid(); - if (valid) { - bool ret = processRFHeader(true); - if (!ret) { - m_rfCurrentRFLSF.reset(); - m_rfCurrentNetLSF.reset(); - return false; - } - - m_rfFrames = 0U; - m_rfErrs = 0U; - m_rfBits = 1U; - - m_rfCollectingLSF.reset(); - m_rfCollectedLSF.reset(); - m_rfTimeoutTimer.start(); - - m_minRSSI = m_rssi; - m_maxRSSI = m_rssi; - m_aveRSSI = m_rssi; - m_rssiCountTotal = 1U; - - m_rssiAccum = m_rssi; - m_rssiCount = 1U; - - m_bitErrsAccum = 0U; - m_bitsCount = 0U; - - m_rfLSFCount = 0U; - m_rfTextBits = 0x00U; - ::memset(m_rfText, 0x00U, 4U * M17_META_LENGTH_BYTES); - - // Fall through - } else { - return false; - } - } - - if (((m_rfState == RPT_RF_STATE::AUDIO) || (m_rfState == RPT_RF_STATE::DATA_AUDIO)) && (data[0U] == TAG_DATA)) { - // Keep looking at the running LSF in case of changed META field data - unsigned int lich1, lich2, lich3, lich4; - bool valid1 = CGolay24128::decode24128(data + 2U + M17_SYNC_LENGTH_BYTES + 0U, lich1); - bool valid2 = CGolay24128::decode24128(data + 2U + M17_SYNC_LENGTH_BYTES + 3U, lich2); - bool valid3 = CGolay24128::decode24128(data + 2U + M17_SYNC_LENGTH_BYTES + 6U, lich3); - bool valid4 = CGolay24128::decode24128(data + 2U + M17_SYNC_LENGTH_BYTES + 9U, lich4); - - if (valid1 && valid2 && valid3 && valid4) { - unsigned char lich[M17_LICH_FRAGMENT_LENGTH_BYTES]; - CM17Utils::combineFragmentLICH(lich1, lich2, lich3, lich4, lich); - - unsigned int n = (lich4 >> 5) & 0x07U; - m_rfCollectingLSF.setFragment(lich, n); - - // If the latest LSF is valid, save it and start collecting the next one - bool valid = m_rfCollectingLSF.isValid(); - if (valid) { - m_rfCollectedLSF = m_rfCollectingLSF; - m_rfCollectingLSF.reset(); - - unsigned char encryptionType = m_rfCollectedLSF.getEncryptionType(); - unsigned char encryptionSubType = m_rfCollectedLSF.getEncryptionSubType(); - if (encryptionType == M17_ENCRYPTION_TYPE_NONE && encryptionSubType == M17_ENCRYPTION_SUB_TYPE_TEXT) { - unsigned char meta[20U]; - m_rfCollectedLSF.getMeta(meta); - CUtils::dump(1U, "M17, LSF text data fragment", meta, M17_META_LENGTH_BYTES); - - m_rfTextBits |= meta[0U]; - - switch (meta[0U] & 0x0FU) { - case 0x01U: - ::memcpy(m_rfText + 0U, meta + 1U, M17_META_LENGTH_BYTES - 1U); - break; - case 0x02U: - ::memcpy(m_rfText + 13U, meta + 1U, M17_META_LENGTH_BYTES - 1U); - break; - case 0x04U: - ::memcpy(m_rfText + 26U, meta + 1U, M17_META_LENGTH_BYTES - 1U); - break; - case 0x08U: - ::memcpy(m_rfText + 39U, meta + 1U, M17_META_LENGTH_BYTES - 1U); - break; - default: - break; - } - - if (m_rfTextBits == 0x11U || m_rfTextBits == 0x33U || m_rfTextBits == 0x77U || m_rfTextBits == 0xFFU) { - LogMessage("M17, text Data: \"%s\"", m_rfText); - writeJSONText(m_rfText); - m_rfTextBits = 0x00U; - } - } - } - } - - // Update the currently transmitted LSF when the fragement number is zero - if (m_rfLSFn == 0U) { - bool valid = m_rfCollectedLSF.isValid(); - if (valid) { - m_rfCurrentNetLSF = m_rfCollectedLSF; - m_rfCollectedLSF.reset(); - - m_rfLSFCount++; - if (m_rfLSFCount > 7U) { - createRFLSF(true); - m_rfLSFCount = 0U; - } else { - createRFLSF(false); - } - } - } - - CM17Convolution conv; - unsigned char frame[M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES]; - unsigned int errors = conv.decodeData(data + 2U + M17_SYNC_LENGTH_BYTES + M17_LICH_FRAGMENT_FEC_LENGTH_BYTES, frame); - - uint16_t fn = (frame[0U] << 8) + (frame[1U] << 0); - - LogDebug("M17, audio: FN: %u, errs: %u/272 (%.1f%%)", fn, errors, float(errors) / 2.72F); - - m_rfBits += 272U; - m_rfErrs += errors; - - m_bitErrsAccum += errors; - m_bitsCount += 272U; - - writeJSONBER(); - writeJSONRSSI(); - - if (m_duplex) { - unsigned char rfData[2U + M17_FRAME_LENGTH_BYTES]; - - rfData[0U] = TAG_DATA; - rfData[1U] = 0x00U; - - // Generate the sync - CSync::addM17StreamSync(rfData + 2U); - - unsigned char lich[M17_LICH_FRAGMENT_LENGTH_BYTES]; - m_rfCurrentRFLSF.getFragment(lich, m_rfLSFn); - - // Add the fragment number - lich[5U] = (m_rfLSFn & 0x07U) << 5; - - unsigned int frag1, frag2, frag3, frag4; - CM17Utils::splitFragmentLICH(lich, frag1, frag2, frag3, frag4); - - // Add Golay to the LICH fragment here - unsigned int lich1 = CGolay24128::encode24128(frag1); - unsigned int lich2 = CGolay24128::encode24128(frag2); - unsigned int lich3 = CGolay24128::encode24128(frag3); - unsigned int lich4 = CGolay24128::encode24128(frag4); - - CM17Utils::combineFragmentLICHFEC(lich1, lich2, lich3, lich4, rfData + 2U + M17_SYNC_LENGTH_BYTES); - - // Add the Convolution FEC - conv.encodeData(frame, rfData + 2U + M17_SYNC_LENGTH_BYTES + M17_LICH_FRAGMENT_FEC_LENGTH_BYTES); - - unsigned char temp[M17_FRAME_LENGTH_BYTES]; - interleaver(rfData + 2U, temp); - decorrelator(temp, rfData + 2U); - - writeQueueRF(rfData); - } - - if (m_network != nullptr && m_rfTimeoutTimer.isRunning() && !m_rfTimeoutTimer.hasExpired()) { - unsigned char netData[M17_LSF_LENGTH_BYTES + M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES]; - - m_rfCurrentNetLSF.getNetwork(netData + 0U); - - // Copy the FN and payload from the frame - ::memcpy(netData + M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES, frame, M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES); - // Remove any erronous EOF from the FN - netData[M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + 0U] &= 0x7FU; - - // The CRC is added in the networking code - - m_network->write(netData); - } - - m_rfFrames++; - - m_rfLSFn++; - if (m_rfLSFn >= 6U) - m_rfLSFn = 0U; - - return true; - } - - if (((m_rfState == RPT_RF_STATE::AUDIO) || (m_rfState == RPT_RF_STATE::DATA_AUDIO)) && (data[0U] == TAG_EOT)) { - if (m_duplex) { - unsigned char rfData[M17_FRAME_LENGTH_BYTES + 2U]; - - rfData[0U] = TAG_EOT; - rfData[1U] = 0x00U; - - // Generate the sync - for (unsigned int i = 0U; i < M17_FRAME_LENGTH_BYTES; i += M17_SYNC_LENGTH_BYTES) - CSync::addM17EOTSync(rfData + 2U + i); - - writeQueueRF(rfData); - } - - if (m_network != nullptr && m_rfTimeoutTimer.isRunning() && !m_rfTimeoutTimer.hasExpired()) { - unsigned char netData[M17_LSF_LENGTH_BYTES + M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES]; - - m_rfCurrentNetLSF.getNetwork(netData + 0U); - - // Add a EOF FN and silence for the EOF frame - netData[M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + 0U] = 0x80U; - netData[M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + 1U] = 0x00U; - - if (m_rfState == RPT_RF_STATE::AUDIO) { - ::memcpy(netData + M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + M17_FN_LENGTH_BYTES + 0U, M17_3200_SILENCE, 8U); - ::memcpy(netData + M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + M17_FN_LENGTH_BYTES + 8U, M17_3200_SILENCE, 8U); - } else if (m_rfState == RPT_RF_STATE::DATA_AUDIO) { - ::memcpy(netData + M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + M17_FN_LENGTH_BYTES + 0U, M17_1600_SILENCE, 8U); - ::memset(netData + M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + M17_FN_LENGTH_BYTES + 8U, 0x00U, 8U); - } else { - ::memset(netData + M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + M17_FN_LENGTH_BYTES + 0U, 0x00U, 8U); - ::memset(netData + M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + M17_FN_LENGTH_BYTES + 8U, 0x00U, 8U); - } - - // The CRC is added in the networking code - - m_network->write(netData); - } - - if (m_rssi != 0) { - LogMessage("M17, received RF end of transmission from %s to %s, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", m_source.c_str(), m_dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); - writeJSONRF("end", float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); - } else { - LogMessage("M17, received RF end of transmission from %s to %s, %.1f seconds, BER: %.1f%%", m_source.c_str(), m_dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); - writeJSONRF("end", float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); - } - writeEndRF(); - - return true; - } - - return false; -} - -unsigned int CM17Control::readModem(unsigned char* data) -{ - assert(data != nullptr); - - if (m_queue.isEmpty()) - return 0U; - - unsigned char len = 0U; - m_queue.getData(&len, 1U); - - m_queue.getData(data, len); - - return len; -} - -void CM17Control::writeEndRF() -{ - m_rfState = RPT_RF_STATE::LISTENING; - - m_rfTimeoutTimer.stop(); - - m_source.clear(); - m_dest.clear(); - - m_rfCurrentRFLSF.reset(); - m_rfCurrentNetLSF.reset(); - m_rfCollectingLSF.reset(); - m_rfCollectedLSF.reset(); - - if (m_netState == RPT_NET_STATE::IDLE) { - if (m_network != nullptr) - m_network->reset(); - } -} - -void CM17Control::writeEndNet() -{ - m_netState = RPT_NET_STATE::IDLE; - - m_netTimeoutTimer.stop(); - m_networkWatchdog.stop(); - - m_source.clear(); - m_dest.clear(); - - m_netLSF.reset(); - - if (m_network != nullptr) - m_network->reset(); -} - -void CM17Control::writeNetwork() -{ - unsigned char netData[100U]; - bool exists = m_network->read(netData); - if (!exists) - return; - - if (!m_enabled) - return; - - if ((m_rfState != RPT_RF_STATE::LISTENING) && (m_rfState != RPT_RF_STATE::LATE_ENTRY) && (m_netState == RPT_NET_STATE::IDLE)) { - m_network->reset(); - return; - } - - m_networkWatchdog.start(); - - if (!m_allowEncryption) { - CM17LSF lsf; - lsf.setNetwork(netData); - - unsigned char type = lsf.getEncryptionType(); - if (type != M17_ENCRYPTION_TYPE_NONE) { - m_network->reset(); - return; - } - } - - if (m_netState == RPT_NET_STATE::IDLE) { - m_netLSF.setNetwork(netData); - - m_source = m_netLSF.getSource(); - m_dest = m_netLSF.getDest(); - - m_netLSF.setSource(m_callsign); - m_netLSF.setCAN(m_can); - - unsigned char dataType = m_netLSF.getDataType(); - switch (dataType) { - case M17_DATA_TYPE_DATA: - LogMessage("M17, received network data transmission from %s to %s", m_source.c_str(), m_dest.c_str()); - m_netState = RPT_NET_STATE::DATA; - writeJSONNet("start", m_netState, m_source, m_dest); - break; - case M17_DATA_TYPE_VOICE: - LogMessage("M17, received network voice transmission from %s to %s", m_source.c_str(), m_dest.c_str()); - m_netState = RPT_NET_STATE::AUDIO; - writeJSONNet("start", m_netState, m_source, m_dest); - break; - case M17_DATA_TYPE_VOICE_DATA: - LogMessage("M17, received network voice + data transmission from %s to %s", m_source.c_str(), m_dest.c_str()); - m_netState = RPT_NET_STATE::DATA_AUDIO; - writeJSONNet("start", m_netState, m_source, m_dest); - break; - default: - LogMessage("M17, received network unknown transmission from %s to %s", m_source.c_str(), m_dest.c_str()); - m_network->reset(); - return; - } - - m_netTimeoutTimer.start(); - m_elapsed.start(); - m_netFrames = 0U; - m_netLSFn = 0U; - m_netTextBits = 0x00U; - ::memset(m_netText, 0x00U, 4U * M17_META_LENGTH_BYTES); - - // Create a dummy start message - unsigned char start[M17_FRAME_LENGTH_BYTES + 2U]; - - start[0U] = TAG_HEADER; - start[1U] = 0x00U; - - // Generate the sync - CSync::addM17LinkSetupSync(start + 2U); - - unsigned char setup[M17_LSF_LENGTH_BYTES]; - m_netLSF.getLinkSetup(setup); - - // Add the convolution FEC - CM17Convolution conv; - conv.encodeLinkSetup(setup, start + 2U + M17_SYNC_LENGTH_BYTES); - - unsigned char temp[M17_FRAME_LENGTH_BYTES]; - interleaver(start + 2U, temp); - decorrelator(temp, start + 2U); - - writeQueueNet(start); - } - - if ((m_netState == RPT_NET_STATE::AUDIO) || (m_netState == RPT_NET_STATE::DATA_AUDIO)) { - // Refresh the LSF every six frames in case the META field changes - if (m_netLSFn == 0U) { - m_netLSF.setNetwork(netData); - - m_netLSF.setSource(m_callsign); - m_netLSF.setCAN(m_can); - - unsigned char encryptionType = m_netLSF.getEncryptionType(); - unsigned char encryptionSubType = m_netLSF.getEncryptionSubType(); - if (encryptionType == M17_ENCRYPTION_TYPE_NONE && encryptionSubType == M17_ENCRYPTION_SUB_TYPE_TEXT) { - unsigned char meta[20U]; - m_netLSF.getMeta(meta); - CUtils::dump(1U, "M17, LSF text data fragment", meta, M17_META_LENGTH_BYTES); - - m_netTextBits |= meta[0U]; - - switch (meta[0U] & 0x0FU) { - case 0x01U: - ::memcpy(m_netText + 0U, meta + 1U, M17_META_LENGTH_BYTES - 1U); - break; - case 0x02U: - ::memcpy(m_netText + 13U, meta + 1U, M17_META_LENGTH_BYTES - 1U); - break; - case 0x04U: - ::memcpy(m_netText + 26U, meta + 1U, M17_META_LENGTH_BYTES - 1U); - break; - case 0x08U: - ::memcpy(m_netText + 39U, meta + 1U, M17_META_LENGTH_BYTES - 1U); - break; - default: - break; - } - - if (m_netTextBits == 0x11U || m_netTextBits == 0x33U || m_netTextBits == 0x77U || m_netTextBits == 0xFFU) { - LogMessage("M17, text Data: \"%s\"", m_netText); - writeJSONText(m_netText); - m_netTextBits = 0x00U; - } - } - } - - unsigned char data[M17_FRAME_LENGTH_BYTES + 2U]; - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - // Generate the sync - CSync::addM17StreamSync(data + 2U); - - m_netFrames++; - - // Add the fragment LICH - unsigned char lich[M17_LICH_FRAGMENT_LENGTH_BYTES]; - m_netLSF.getFragment(lich, m_netLSFn); - - // Add the fragment number - lich[5U] = (m_netLSFn & 0x07U) << 5; - - unsigned int frag1, frag2, frag3, frag4; - CM17Utils::splitFragmentLICH(lich, frag1, frag2, frag3, frag4); - - // Add Golay to the LICH fragment here - unsigned int lich1 = CGolay24128::encode24128(frag1); - unsigned int lich2 = CGolay24128::encode24128(frag2); - unsigned int lich3 = CGolay24128::encode24128(frag3); - unsigned int lich4 = CGolay24128::encode24128(frag4); - - CM17Utils::combineFragmentLICHFEC(lich1, lich2, lich3, lich4, data + 2U + M17_SYNC_LENGTH_BYTES); - - // Add the FN and the data/audio - unsigned char payload[M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES]; - - // Copy the FN minus the EOF marker - payload[0U] = netData[28U] & 0x7FU; - payload[1U] = netData[29U]; - - ::memcpy(payload + 2U, netData + 30U, M17_PAYLOAD_LENGTH_BYTES); - - // Add the Convolution FEC - CM17Convolution conv; - conv.encodeData(payload, data + 2U + M17_SYNC_LENGTH_BYTES + M17_LICH_FRAGMENT_FEC_LENGTH_BYTES); - - unsigned char temp[M17_FRAME_LENGTH_BYTES]; - interleaver(data + 2U, temp); - decorrelator(temp, data + 2U); - - writeQueueNet(data); - - m_netLSFn++; - if (m_netLSFn >= 6U) - m_netLSFn = 0U; - - // EOT handling - uint16_t fn = (netData[28U] << 8) + (netData[29U] << 0); - if ((fn & 0x8000U) == 0x8000U) { - LogMessage("M17, received network end of transmission from %s to %s, %.1f seconds", m_source.c_str(), m_dest.c_str(), float(m_netFrames) / 25.0F); - writeJSONNet("end", float(m_netFrames) / 25.0F); - - unsigned char data[M17_FRAME_LENGTH_BYTES + 2U]; - - data[0U] = TAG_EOT; - data[1U] = 0x00U; - - // Generate the sync - for (unsigned int i = 0U; i < M17_FRAME_LENGTH_BYTES; i += M17_SYNC_LENGTH_BYTES) - CSync::addM17EOTSync(data + 2U + i); - - writeQueueNet(data); - - writeEndNet(); - } - } -} - -bool CM17Control::processRFHeader(bool lateEntry) -{ - unsigned char packetStream = m_rfCurrentNetLSF.getPacketStream(); - if (packetStream == M17_PACKET_TYPE) - return false; - - unsigned char can = m_rfCurrentNetLSF.getCAN(); - if (can != m_can) - return false; - - m_source = m_rfCurrentNetLSF.getSource(); - m_dest = m_rfCurrentNetLSF.getDest(); - - if (!m_allowEncryption) { - unsigned char type = m_rfCurrentNetLSF.getEncryptionType(); - if (type != M17_ENCRYPTION_TYPE_NONE) { - LogMessage("M17, access attempt with encryption from %s to %s", m_source.c_str(), m_dest.c_str()); - m_rfState = RPT_RF_STATE::REJECTED; - writeJSONRF("rejected", RPT_RF_STATE::AUDIO, m_source, m_dest); - return true; - } - } - - if (m_selfOnly) { - bool ret = checkCallsign(m_source); - if (!ret) { - LogMessage("M17, invalid access attempt from %s to %s", m_source.c_str(), m_dest.c_str()); - m_rfState = RPT_RF_STATE::REJECTED; - writeJSONRF("rejected", RPT_RF_STATE::AUDIO, m_source, m_dest); - return true; - } - } - - unsigned char dataType = m_rfCurrentNetLSF.getDataType(); - switch (dataType) { - case M17_DATA_TYPE_DATA: - LogMessage("M17, received RF%sdata transmission from %s to %s", lateEntry ? " late entry " : " ", m_source.c_str(), m_dest.c_str()); - m_rfState = RPT_RF_STATE::DATA; - writeJSONRF(lateEntry ? "late_entry" : "start", m_rfState, m_source, m_dest); - break; - case M17_DATA_TYPE_VOICE: - LogMessage("M17, received RF%svoice transmission from %s to %s", lateEntry ? " late entry " : " ", m_source.c_str(), m_dest.c_str()); - m_rfState = RPT_RF_STATE::AUDIO; - writeJSONRF(lateEntry ? "late_entry" : "start", m_rfState, m_source, m_dest); - break; - case M17_DATA_TYPE_VOICE_DATA: - LogMessage("M17, received RF%svoice + data transmission from %s to %s", lateEntry ? " late entry " : " ", m_source.c_str(), m_dest.c_str()); - m_rfState = RPT_RF_STATE::DATA_AUDIO; - writeJSONRF(lateEntry ? "late_entry" : "start", m_rfState, m_source, m_dest); - break; - default: - return false; - } - - createRFLSF(true); - - if (m_duplex) { - unsigned char data[M17_FRAME_LENGTH_BYTES + 2U]; - - // Create a Link Setup frame - data[0U] = TAG_HEADER; - data[1U] = 0x00U; - - // Generate the sync - CSync::addM17LinkSetupSync(data + 2U); - - unsigned char setup[M17_LSF_LENGTH_BYTES]; - m_rfCurrentRFLSF.getLinkSetup(setup); - - // Add the convolution FEC - CM17Convolution conv; - conv.encodeLinkSetup(setup, data + 2U + M17_SYNC_LENGTH_BYTES); - - unsigned char temp[M17_FRAME_LENGTH_BYTES]; - interleaver(data + 2U, temp); - decorrelator(temp, data + 2U); - - writeQueueRF(data); - } - - return true; -} - -void CM17Control::createRFLSF(bool addCallsign) -{ - m_rfCurrentRFLSF = m_rfCurrentNetLSF; - - m_rfCurrentRFLSF.setSource(m_callsign); - - if (addCallsign) { - m_rfCurrentRFLSF.setEncryptionType(M17_ENCRYPTION_TYPE_NONE); - m_rfCurrentRFLSF.setEncryptionSubType(M17_ENCRYPTION_SUB_TYPE_CALLSIGNS); - - // Copy the encoded source into the META field - unsigned char meta[M17_META_LENGTH_BYTES]; - ::memset(meta, 0x00U, M17_META_LENGTH_BYTES); - CM17Utils::encodeCallsign(m_source, meta + 0U); - - m_rfCurrentRFLSF.setMeta(meta); - } -} - -void CM17Control::clock(unsigned int ms) -{ - if (m_network != nullptr) - writeNetwork(); - - m_rfTimeoutTimer.clock(ms); - m_netTimeoutTimer.clock(ms); - - if ((m_netState == RPT_NET_STATE::AUDIO) || (m_netState == RPT_NET_STATE::DATA_AUDIO)) { - m_networkWatchdog.clock(ms); - - if (m_networkWatchdog.hasExpired()) { - LogMessage("M17, network watchdog has expired, %.1f seconds", float(m_netFrames) / 25.0F); - writeJSONNet("lost", float(m_netFrames) / 25.0F); - writeEndNet(); - } - } -} - -void CM17Control::writeQueueRF(const unsigned char *data) -{ - assert(data != nullptr); - - if (m_netState != RPT_NET_STATE::IDLE) - return; - - if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) - return; - - const unsigned char len = M17_FRAME_LENGTH_BYTES + 2U; - - unsigned int space = m_queue.freeSpace(); - if (space < (len + 1U)) { - LogError("M17, overflow in the M17 RF queue"); - return; - } - - m_queue.addData(&len, 1U); - - m_queue.addData(data, len); -} - -void CM17Control::writeQueueNet(const unsigned char *data) -{ - assert(data != nullptr); - - if (m_netTimeoutTimer.isRunning() && m_netTimeoutTimer.hasExpired()) - return; - - const unsigned char len = M17_FRAME_LENGTH_BYTES + 2U; - - unsigned int space = m_queue.freeSpace(); - if (space < (len + 1U)) { - LogError("M17, overflow in the M17 RF queue"); - return; - } - - m_queue.addData(&len, 1U); - - m_queue.addData(data, len); -} - -void CM17Control::interleaver(const unsigned char* in, unsigned char* out) const -{ - assert(in != nullptr); - assert(out != nullptr); - - for (unsigned int i = 0U; i < (M17_FRAME_LENGTH_BITS - M17_SYNC_LENGTH_BITS); i++) { - unsigned int n1 = i + M17_SYNC_LENGTH_BITS; - bool b = READ_BIT(in, n1) != 0U; - unsigned int n2 = INTERLEAVER[i] + M17_SYNC_LENGTH_BITS; - WRITE_BIT(out, n2, b); - } -} - -void CM17Control::decorrelator(const unsigned char* in, unsigned char* out) const -{ - assert(in != nullptr); - assert(out != nullptr); - - for (unsigned int i = M17_SYNC_LENGTH_BYTES; i < M17_FRAME_LENGTH_BYTES; i++) { - out[i] = in[i] ^ SCRAMBLER[i]; - } -} - -bool CM17Control::checkCallsign(const std::string& callsign) const -{ - size_t len = m_callsign.size(); - - return m_callsign.compare(0U, len, callsign, 0U, len) == 0; -} - -bool CM17Control::isBusy() const -{ - return (m_rfState != RPT_RF_STATE::LISTENING) || (m_netState != RPT_NET_STATE::IDLE); -} - -void CM17Control::enable(bool enabled) -{ - if (!enabled && m_enabled) { - m_queue.clear(); - - // Reset the RF section - m_rfState = RPT_RF_STATE::LISTENING; - - m_rfTimeoutTimer.stop(); - - // Reset the networking section - m_netState = RPT_NET_STATE::IDLE; - - m_netTimeoutTimer.stop(); - m_networkWatchdog.stop(); - } - - m_enabled = enabled; -} - -void CM17Control::writeJSONRSSI() -{ - if (m_rssi == 0) - return; - - if (m_rssiCountTotal >= RSSI_COUNT) { - nlohmann::json json; - - json["timestamp"] = CUtils::createTimestamp(); - json["mode"] = "M17"; - - json["value"] = m_rssiAccum / int(m_rssiCountTotal); - - WriteJSON("RSSI", json); - - m_rssiAccum = 0; - m_rssiCountTotal = 0U; - } -} - -void CM17Control::writeJSONBER() -{ - if (m_bitsCount >= BER_COUNT) { - nlohmann::json json; - - json["timestamp"] = CUtils::createTimestamp(); - json["mode"] = "M17"; - - json["value"] = float(m_bitErrsAccum * 100U) / float(m_bitsCount); - - WriteJSON("BER", json); - - m_bitErrsAccum = 0U; - m_bitsCount = 1U; - } -} - -void CM17Control::writeJSONText(const char* text) -{ - assert(text != nullptr); - - nlohmann::json json; - - json["timestamp"] = CUtils::createTimestamp(); - json["mode"] = "M17"; - - json["value"] = std::string(text); - - WriteJSON("Text", json); -} - -void CM17Control::writeJSONRF(const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest) -{ - assert(action != nullptr); - - nlohmann::json json; - - writeJSONRF(json, action, state, source, dest); - - WriteJSON("M17", json); -} - -void CM17Control::writeJSONRF(const char* action, float duration, float ber) -{ - assert(action != nullptr); - - nlohmann::json json; - - writeJSONRF(json, action, duration, ber); - - WriteJSON("M17", json); -} - -void CM17Control::writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI) -{ - assert(action != nullptr); - - nlohmann::json json; - - writeJSONRF(json, action, duration, ber); - - nlohmann::json rssi; - rssi["min"] = minRSSI; - rssi["max"] = maxRSSI; - rssi["ave"] = aveRSSI; - - json["rssi"] = rssi; - - WriteJSON("M17", json); -} - -void CM17Control::writeJSONNet(const char* action, RPT_NET_STATE state, const std::string& source, const std::string& dest) -{ - assert(action != nullptr); - - nlohmann::json json; - - writeJSONNet(json, action, state, source, dest); - - WriteJSON("M17", json); -} - -void CM17Control::writeJSONNet(const char* action, float duration) -{ - assert(action != nullptr); - - nlohmann::json json; - - writeJSONNet(json, action); - - json["duration"] = duration; - - WriteJSON("M17", json); -} - -void CM17Control::writeJSONRF(nlohmann::json& json, const char* action) -{ - assert(action != nullptr); - - json["timestamp"] = CUtils::createTimestamp(); - - json["action"] = action; -} - -void CM17Control::writeJSONRF(nlohmann::json& json, const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest) -{ - assert(action != nullptr); - - json["timestamp"] = CUtils::createTimestamp(); - - json["source_cs"] = source; - json["destination_cs"] = dest; - - json["source"] = "rf"; - json["action"] = action; - - switch (state) { - case RPT_RF_STATE::AUDIO: - json["traffic_type"] = "audio"; - break; - case RPT_RF_STATE::DATA_AUDIO: - json["traffic_type"] = "audio_data"; - break; - case RPT_RF_STATE::DATA: - json["traffic_type"] = "data"; - break; - default: - break; - } -} - -void CM17Control::writeJSONRF(nlohmann::json& json, const char* action, float duration, float ber) -{ - assert(action != nullptr); - - writeJSONRF(json, action); - - json["duration"] = duration; - json["ber"] = ber; -} - -void CM17Control::writeJSONNet(nlohmann::json& json, const char* action) -{ - assert(action != nullptr); - - json["timestamp"] = CUtils::createTimestamp(); - - json["action"] = action; -} - -void CM17Control::writeJSONNet(nlohmann::json& json, const char* action, RPT_NET_STATE state, const std::string& source, const std::string& dest) -{ - assert(action != nullptr); - - json["timestamp"] = CUtils::createTimestamp(); - - json["source_cs"] = source; - json["destination_cs"] = dest; - - json["source"] = "network"; - json["action"] = action; - - switch (state) { - case RPT_NET_STATE::AUDIO: - json["traffic_type"] = "audio"; - break; - case RPT_NET_STATE::DATA_AUDIO: - json["traffic_type"] = "audio_data"; - break; - case RPT_NET_STATE::DATA: - json["traffic_type"] = "data"; - break; - default: - break; - } -} - -#endif - diff --git a/M17Control.h b/M17Control.h deleted file mode 100644 index d161d9f..0000000 --- a/M17Control.h +++ /dev/null @@ -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 - -#include - -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 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 - diff --git a/M17Convolution.cpp b/M17Convolution.cpp deleted file mode 100644 index cb3f133..0000000 --- a/M17Convolution.cpp +++ /dev/null @@ -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 -#include -#include -#include - -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 - diff --git a/M17Convolution.h b/M17Convolution.h deleted file mode 100644 index 9a975cc..0000000 --- a/M17Convolution.h +++ /dev/null @@ -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 - -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 - diff --git a/M17Defines.h b/M17Defines.h deleted file mode 100644 index 5db7f7c..0000000 --- a/M17Defines.h +++ /dev/null @@ -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 diff --git a/M17LSF.cpp b/M17LSF.cpp deleted file mode 100644 index 50607b0..0000000 --- a/M17LSF.cpp +++ /dev/null @@ -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 -#include - -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 - diff --git a/M17LSF.h b/M17LSF.h deleted file mode 100644 index 1e387e2..0000000 --- a/M17LSF.h +++ /dev/null @@ -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 - -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 - diff --git a/M17Network.cpp b/M17Network.cpp deleted file mode 100644 index b62564f..0000000 --- a/M17Network.cpp +++ /dev/null @@ -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 -#include -#include - -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 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 - diff --git a/M17Network.h b/M17Network.h deleted file mode 100644 index b50a244..0000000 --- a/M17Network.h +++ /dev/null @@ -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 -#include - -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 m_buffer; - std::mt19937 m_random; - CTimer m_timer; - - void sendPing(); -}; - -#endif - -#endif - diff --git a/M17Utils.cpp b/M17Utils.cpp deleted file mode 100644 index ae84520..0000000 --- a/M17Utils.cpp +++ /dev/null @@ -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 -#include - -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 diff --git a/M17Utils.h b/M17Utils.h deleted file mode 100644 index 9498078..0000000 --- a/M17Utils.h +++ /dev/null @@ -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 - -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 - diff --git a/MMDVM.ini b/MMDVM.ini index 81496c7..fca2d66 100644 --- a/MMDVM.ini +++ b/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 - diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index d6a4e2f..26edfbc 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -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); diff --git a/MMDVMHost.h b/MMDVMHost.h index a917b95..e930977 100644 --- a/MMDVMHost.h +++ b/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; diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index 8dcaa45..0bb9837 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -28,26 +28,26 @@ Application true - v142 + v143 Unicode Application false - v142 + v143 true Unicode Application true - v142 + v143 Unicode Application false - v142 + v143 true Unicode @@ -161,9 +161,6 @@ - - - @@ -198,13 +195,6 @@ - - - - - - - @@ -265,8 +255,6 @@ - - @@ -298,12 +286,6 @@ - - - - - - diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index 9379343..db9235e 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -248,42 +248,12 @@ Header Files - - Header Files - - - Header Files - Header Files Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files @@ -529,36 +499,12 @@ Source Files - - Source Files - - - Source Files - Source Files Source Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - Source Files diff --git a/Makefile b/Makefile index 9b4db9a..ecbb135 100644 --- a/Makefile +++ b/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 \ diff --git a/Modem.cpp b/Modem.cpp index 0ff2551..7793578 100644 --- a/Modem.cpp +++ b/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; diff --git a/Modem.h b/Modem.h index cc88422..a2dcb43 100644 --- a/Modem.h +++ b/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; diff --git a/NXDNControl.cpp b/NXDNControl.cpp index 842f675..981a42e 100644 --- a/NXDNControl.cpp +++ b/NXDNControl.cpp @@ -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; diff --git a/P25Control.cpp b/P25Control.cpp index 6da36c9..c9bc7f4 100644 --- a/P25Control.cpp +++ b/P25Control.cpp @@ -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); diff --git a/README.md b/README.md index 5a80511..2588063 100644 --- a/README.md +++ b/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. diff --git a/Sync.cpp b/Sync.cpp index 3b324f0..7984423 100644 --- a/Sync.cpp +++ b/Sync.cpp @@ -23,7 +23,6 @@ #include "YSFDefines.h" #include "P25Defines.h" #include "NXDNDefines.h" -#include "M17Defines.h" #include #include @@ -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 - diff --git a/Sync.h b/Sync.h index be182f6..d32d463 100644 --- a/Sync.h +++ b/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: }; diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 6b5abcf..ff6f504 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -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)); diff --git a/Version.h b/Version.h index 5481626..e6314a6 100644 --- a/Version.h +++ b/Version.h @@ -19,6 +19,6 @@ #if !defined(VERSION_H) #define VERSION_H -const char* VERSION = "20250424"; +const char* VERSION = "20250826"; #endif diff --git a/YSFControl.cpp b/YSFControl.cpp index 134e4b0..52020c4 100644 --- a/YSFControl.cpp +++ b/YSFControl.cpp @@ -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(); diff --git a/schema.json b/schema.json index f167e41..ba647ad 100644 --- a/schema.json +++ b/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"] } }