diff --git a/AX25Control.cpp b/AX25Control.cpp new file mode 100644 index 0000000..3b108fc --- /dev/null +++ b/AX25Control.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2020 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" + +#include +#include +#include +#include + +// #define DUMP_AX25 + +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) : +m_network(network), +m_trace(trace), +m_enabled(true), +m_fp(NULL) +{ +} + +CAX25Control::~CAX25Control() +{ +} + +bool CAX25Control::writeModem(unsigned char *data, unsigned int len) +{ + assert(data != NULL); + + if (!m_enabled) + return false; + + if (m_trace) + decode(data, len); + + CUtils::dump(1U, "AX.25 received packet", data, len); + + if (m_network == NULL) + return true; + + return m_network->write(data, len); +} + +unsigned int CAX25Control::readModem(unsigned char* data) +{ + assert(data != NULL); + + if (m_network == NULL) + return 0U; + + if (!m_enabled) + return 0U; + + unsigned int length = m_network->read(data, 500U); + + if (length > 0U) + CUtils::dump(1U, "AX.25 transmitted packet", data, length); + + return length; +} + +bool CAX25Control::openFile() +{ + if (m_fp != NULL) + return true; + + time_t t; + ::time(&t); + + struct tm* tm = ::localtime(&t); + + char name[100U]; + ::sprintf(name, "AX25_%04d%02d%02d_%02d%02d%02d.ambe", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + m_fp = ::fopen(name, "wb"); + if (m_fp == NULL) + return false; + + ::fwrite("AX25", 1U, 4U, m_fp); + + return true; +} + +bool CAX25Control::writeFile(const unsigned char* data, unsigned int length) +{ + if (m_fp == NULL) + return false; + + ::fwrite(&length, 1U, sizeof(unsigned int), m_fp); + ::fwrite(data, 1U, length, m_fp); + + return true; +} + +void CAX25Control::closeFile() +{ + if (m_fp != NULL) { + ::fclose(m_fp); + m_fp = NULL; + } +} + +void CAX25Control::enable(bool enabled) +{ + m_enabled = enabled; +} + +void CAX25Control::decode(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + 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); +} + +bool CAX25Control::decodeAddress(const unsigned char* data, std::string& text, bool isDigi) const +{ + assert(data != NULL); + + 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; +} diff --git a/AX25Control.h b/AX25Control.h new file mode 100644 index 0000000..b4fed2a --- /dev/null +++ b/AX25Control.h @@ -0,0 +1,50 @@ +/* + * 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(AX25Control_H) +#define AX25Control_H + +#include "AX25Network.h" + +#include + +class CAX25Control { +public: + CAX25Control(CAX25Network* network, bool trace); + ~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; + bool m_enabled; + FILE* m_fp; + + void decode(const unsigned char* data, unsigned int length); + bool decodeAddress(const unsigned char* data, std::string& text, bool isDigi = false) const; + bool openFile(); + bool writeFile(const unsigned char* data, unsigned int length); + void closeFile(); +}; + +#endif diff --git a/AX25Defines.h b/AX25Defines.h new file mode 100644 index 0000000..a0f4c80 --- /dev/null +++ b/AX25Defines.h @@ -0,0 +1,39 @@ +/* + * 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 new file mode 100644 index 0000000..d7f876d --- /dev/null +++ b/AX25Network.cpp @@ -0,0 +1,183 @@ +/* + * 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. + */ + +#include "AX25Network.h" +#include "AX25Defines.h" +#include "Defines.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 500U; + + +CAX25Network::CAX25Network(const std::string& port, unsigned int speed, bool debug) : +m_serial(port, speed, false), +m_txData(NULL), +m_rxData(NULL), +m_rxLength(0U), +m_rxLastChar(0U), +m_debug(debug), +m_enabled(false) +{ + assert(!port.empty()); + assert(speed > 0U); + + m_txData = new unsigned char[BUFFER_LENGTH]; + m_rxData = new unsigned char[BUFFER_LENGTH]; +} + +CAX25Network::~CAX25Network() +{ + delete[] m_txData; + delete[] m_rxData; +} + +bool CAX25Network::open() +{ + LogMessage("Opening AX25 network connection"); + + return m_serial.open(); +} + +bool CAX25Network::write(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + if (!m_enabled) + return true; + + unsigned int txLength = 0U; + + m_txData[txLength++] = AX25_FEND; + m_txData[txLength++] = AX25_KISS_DATA; + + for (unsigned int i = 0U; i < length; i++) { + unsigned char c = data[i]; + + switch (c) { + case AX25_FEND: + m_txData[txLength++] = AX25_FESC; + m_txData[txLength++] = AX25_TFEND; + break; + case AX25_FESC: + m_txData[txLength++] = AX25_FESC; + m_txData[txLength++] = AX25_TFESC; + break; + default: + m_txData[txLength++] = c; + break; + } + } + + m_txData[txLength++] = AX25_FEND; + + if (m_debug) + CUtils::dump(1U, "AX25 Network Data Sent", m_txData, txLength); + + return m_serial.write(m_txData, txLength); +} + +unsigned int CAX25Network::read(unsigned char* data, unsigned int length) +{ + assert(data != NULL); + + bool complete = false; + + unsigned char c; + while (m_serial.read(&c, 1U) > 0) { + if (m_rxLength == 0U && c == AX25_FEND) + m_rxData[m_rxLength++] = c; + else if (m_rxLength > 0U) + m_rxData[m_rxLength++] = c; + + if (m_rxLength > 1U && c == AX25_FEND) { + complete = true; + break; + } + } + + if (!m_enabled) + return 0U; + + if (!complete) + return 0U; + + if (m_rxLength == 0U) + return 0U; + + if (m_rxData[1U] != AX25_KISS_DATA) { + m_rxLength = 0U; + return 0U; + } + + complete = false; + + unsigned int dataLen = 0U; + for (unsigned int i = 2U; i < m_rxLength; i++) { + unsigned char c = m_rxData[i]; + + if (c == AX25_FEND) { + complete = true; + break; + } else if (c == AX25_TFEND && m_rxLastChar == AX25_FESC) { + data[dataLen++] = AX25_FEND; + } else if (c == AX25_TFESC && m_rxLastChar == AX25_FESC) { + data[dataLen++] = AX25_FESC; + } else { + data[dataLen++] = c; + } + + m_rxLastChar = c; + } + + if (!complete) + return 0U; + + if (m_debug) + CUtils::dump(1U, "AX25 Network Data Received", m_rxData, m_rxLength); + + m_rxLength = 0U; + m_rxLastChar = 0U; + + return dataLen; +} + +void CAX25Network::reset() +{ +} + +void CAX25Network::close() +{ + m_serial.close(); + + LogMessage("Closing AX25 network connection"); +} + +void CAX25Network::enable(bool enabled) +{ + m_enabled = enabled; + + if (enabled != m_enabled) { + m_rxLastChar = 0U; + m_rxLength = 0U; + } +} diff --git a/AX25Network.h b/AX25Network.h new file mode 100644 index 0000000..f46de15 --- /dev/null +++ b/AX25Network.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef AX25Network_H +#define AX25Network_H + +#if defined(_WIN32) || defined(_WIN64) +#include "SerialController.h" +#else +#include "PseudoTTYController.h" +#endif + +#include +#include + +class CAX25Network { +public: + CAX25Network(const std::string& port, unsigned int speed, 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 reset(); + + void close(); + +private: +#if defined(_WIN32) || defined(_WIN64) + CSerialController m_serial; +#else + CPseudoTTYController m_serial; +#endif + unsigned char* m_txData; + unsigned char* m_rxData; + unsigned int m_rxLength; + unsigned char m_rxLastChar; + bool m_debug; + bool m_enabled; +}; + +#endif diff --git a/CASTInfo.cpp b/CASTInfo.cpp index 603ee20..4078d2d 100644 --- a/CASTInfo.cpp +++ b/CASTInfo.cpp @@ -21,7 +21,7 @@ static bool networkInfoInitialized = false; static unsigned char passCounter = 0; -CCASTInfo::CCASTInfo(CModem* modem) : +CCASTInfo::CCASTInfo(IModem* modem) : CDisplay(), m_modem(modem), m_ipaddress() @@ -130,6 +130,16 @@ void CCASTInfo::clearNXDNInt() { } +void CCASTInfo::writeM17Int(const char* source, const char* dest, const char* type) +{ + if (m_modem != NULL) + m_modem->writeM17Info(source, dest, type); +} + +void CCASTInfo::clearM17Int() +{ +} + void CCASTInfo::writePOCSAGInt(uint32_t ric, const std::string& message) { if (m_modem != NULL) diff --git a/CASTInfo.h b/CASTInfo.h index 8945dcd..38092b7 100644 --- a/CASTInfo.h +++ b/CASTInfo.h @@ -28,7 +28,7 @@ class CCASTInfo : public CDisplay { public: - CCASTInfo(CModem* modem); + CCASTInfo(IModem* modem); virtual ~CCASTInfo(); virtual bool open(); @@ -57,6 +57,9 @@ protected: virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); virtual void clearNXDNInt(); + virtual void writeM17Int(const char* source, const char* dest, const char* type); + virtual void clearM17Int(); + virtual void writePOCSAGInt(uint32_t ric, const std::string& message); virtual void clearPOCSAGInt(); @@ -64,7 +67,7 @@ protected: virtual void clearCWInt(); private: - CModem* m_modem; + IModem* m_modem; std::string m_ipaddress; }; diff --git a/Conf.cpp b/Conf.cpp index bc2d6a5..4dbafbf 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -28,36 +28,40 @@ const int BUFFER_SIZE = 500; enum SECTION { - SECTION_NONE, - SECTION_GENERAL, - SECTION_INFO, - SECTION_LOG, - SECTION_CWID, - SECTION_DMRID_LOOKUP, - SECTION_NXDNID_LOOKUP, - SECTION_MODEM, - SECTION_TRANSPARENT, - SECTION_UMP, - SECTION_DSTAR, - SECTION_DMR, - SECTION_FUSION, - SECTION_P25, - SECTION_NXDN, - SECTION_POCSAG, - SECTION_FM, - SECTION_DSTAR_NETWORK, - SECTION_DMR_NETWORK, - SECTION_FUSION_NETWORK, - SECTION_P25_NETWORK, - SECTION_NXDN_NETWORK, - SECTION_POCSAG_NETWORK, - SECTION_TFTSERIAL, - SECTION_HD44780, - SECTION_NEXTION, - SECTION_OLED, - SECTION_LCDPROC, - SECTION_LOCK_FILE, - SECTION_REMOTE_CONTROL + SECTION_NONE, + SECTION_GENERAL, + SECTION_INFO, + SECTION_LOG, + SECTION_CWID, + SECTION_DMRID_LOOKUP, + SECTION_NXDNID_LOOKUP, + SECTION_MODEM, + SECTION_TRANSPARENT, + SECTION_DSTAR, + SECTION_DMR, + SECTION_FUSION, + SECTION_P25, + SECTION_NXDN, + SECTION_M17, + SECTION_POCSAG, + SECTION_FM, + SECTION_AX25, + SECTION_DSTAR_NETWORK, + SECTION_DMR_NETWORK, + SECTION_FUSION_NETWORK, + SECTION_P25_NETWORK, + SECTION_NXDN_NETWORK, + SECTION_M17_NETWORK, + SECTION_POCSAG_NETWORK, + SECTION_FM_NETWORK, + SECTION_AX25_NETWORK, + SECTION_TFTSERIAL, + SECTION_HD44780, + SECTION_NEXTION, + SECTION_OLED, + SECTION_LCDPROC, + SECTION_LOCK_FILE, + SECTION_REMOTE_CONTROL }; CConf::CConf(const std::string& file) : @@ -91,6 +95,7 @@ m_nxdnIdLookupFile(), m_nxdnIdLookupTime(0U), m_modemPort(), m_modemProtocol("uart"), +m_modemSpeed(115200U), m_modemAddress(0x22), m_modemRXInvert(false), m_modemTXInvert(false), @@ -109,8 +114,10 @@ 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), @@ -120,8 +127,6 @@ m_transparentRemoteAddress(), m_transparentRemotePort(0U), m_transparentLocalPort(0U), m_transparentSendFrameType(0U), -m_umpEnabled(false), -m_umpPort(), m_dstarEnabled(false), m_dstarModule("C"), m_dstarSelfOnly(false), @@ -172,6 +177,12 @@ m_nxdnSelfOnly(false), m_nxdnRemoteGateway(false), m_nxdnTXHang(5U), m_nxdnModeHang(10U), +m_m17Enabled(false), +m_m17ColorCode(1U), +m_m17SelfOnly(false), +m_m17AllowEncryption(false), +m_m17TXHang(5U), +m_m17ModeHang(10U), m_pocsagEnabled(false), m_pocsagFrequency(0U), m_fmEnabled(false), @@ -202,9 +213,19 @@ m_fmKerchunkTime(0U), m_fmHangTime(7U), m_fmAccessMode(1U), m_fmCOSInvert(false), +m_fmNoiseSquelch(false), +m_fmSquelchHighThreshold(30U), +m_fmSquelchLowThreshold(20U), m_fmRFAudioBoost(1U), m_fmMaxDevLevel(90.0F), m_fmExtAudioBoost(1U), +m_fmModeHang(10U), +m_ax25Enabled(false), +m_ax25TXDelay(300U), +m_ax25RXTwist(6), +m_ax25SlotTime(30U), +m_ax25PPersist(128U), +m_ax25Trace(false), m_dstarNetworkEnabled(false), m_dstarGatewayAddress(), m_dstarGatewayPort(0U), @@ -244,6 +265,12 @@ m_nxdnLocalAddress(), m_nxdnLocalPort(0U), m_nxdnNetworkModeHang(3U), m_nxdnNetworkDebug(false), +m_m17NetworkEnabled(false), +m_m17GatewayAddress(), +m_m17GatewayPort(0U), +m_m17LocalPort(0U), +m_m17NetworkModeHang(3U), +m_m17NetworkDebug(false), m_pocsagNetworkEnabled(false), m_pocsagGatewayAddress(), m_pocsagGatewayPort(0U), @@ -251,6 +278,18 @@ m_pocsagLocalAddress(), m_pocsagLocalPort(0U), m_pocsagNetworkModeHang(3U), m_pocsagNetworkDebug(false), +m_fmNetworkEnabled(false), +m_fmGatewayAddress(), +m_fmGatewayPort(0U), +m_fmLocalAddress(), +m_fmLocalPort(0U), +m_fmSampleRate(8000U), +m_fmNetworkModeHang(3U), +m_fmNetworkDebug(false), +m_ax25NetworkEnabled(false), +m_ax25NetworkPort(), +m_ax25NetworkSpeed(9600U), +m_ax25NetworkDebug(false), m_tftSerialPort("/dev/ttyAMA0"), m_tftSerialBrightness(50U), m_hd44780Rows(2U), @@ -296,369 +335,368 @@ CConf::~CConf() bool CConf::read() { - FILE* fp = ::fopen(m_file.c_str(), "rt"); - if (fp == NULL) { - ::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str()); - return false; - } + FILE* fp = ::fopen(m_file.c_str(), "rt"); + if (fp == NULL) { + ::fprintf(stderr, "Couldn't open the .ini file - %s\n", m_file.c_str()); + return false; + } - SECTION section = SECTION_NONE; + SECTION section = SECTION_NONE; - char buffer[BUFFER_SIZE]; - while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) { - if (buffer[0U] == '#') - continue; + char buffer[BUFFER_SIZE]; + while (::fgets(buffer, BUFFER_SIZE, fp) != NULL) { + if (buffer[0U] == '#') + continue; - if (buffer[0U] == '[') { - if (::strncmp(buffer, "[General]", 9U) == 0) - section = SECTION_GENERAL; - else if (::strncmp(buffer, "[Info]", 6U) == 0) - section = SECTION_INFO; - else if (::strncmp(buffer, "[Log]", 5U) == 0) - section = SECTION_LOG; - else if (::strncmp(buffer, "[CW Id]", 7U) == 0) - section = SECTION_CWID; - else if (::strncmp(buffer, "[DMR Id Lookup]", 15U) == 0) - section = SECTION_DMRID_LOOKUP; - else if (::strncmp(buffer, "[NXDN Id Lookup]", 16U) == 0) - section = SECTION_NXDNID_LOOKUP; - else if (::strncmp(buffer, "[Modem]", 7U) == 0) - section = SECTION_MODEM; - else if (::strncmp(buffer, "[Transparent Data]", 18U) == 0) - section = SECTION_TRANSPARENT; - else if (::strncmp(buffer, "[UMP]", 5U) == 0) - section = SECTION_UMP; - else if (::strncmp(buffer, "[D-Star]", 8U) == 0) - section = SECTION_DSTAR; - else if (::strncmp(buffer, "[DMR]", 5U) == 0) - section = SECTION_DMR; - else if (::strncmp(buffer, "[System Fusion]", 15U) == 0) - section = SECTION_FUSION; - else if (::strncmp(buffer, "[P25]", 5U) == 0) - section = SECTION_P25; - else if (::strncmp(buffer, "[NXDN]", 6U) == 0) - section = SECTION_NXDN; - else if (::strncmp(buffer, "[POCSAG]", 8U) == 0) - section = SECTION_POCSAG; - else if (::strncmp(buffer, "[FM]", 4U) == 0) - section = SECTION_FM; - else if (::strncmp(buffer, "[D-Star Network]", 16U) == 0) - section = SECTION_DSTAR_NETWORK; - else if (::strncmp(buffer, "[DMR Network]", 13U) == 0) - section = SECTION_DMR_NETWORK; - else if (::strncmp(buffer, "[System Fusion Network]", 23U) == 0) - section = SECTION_FUSION_NETWORK; - else if (::strncmp(buffer, "[P25 Network]", 13U) == 0) - section = SECTION_P25_NETWORK; - else if (::strncmp(buffer, "[NXDN Network]", 14U) == 0) - section = SECTION_NXDN_NETWORK; - else if (::strncmp(buffer, "[POCSAG Network]", 16U) == 0) - section = SECTION_POCSAG_NETWORK; - else if (::strncmp(buffer, "[TFT Serial]", 12U) == 0) - section = SECTION_TFTSERIAL; - else if (::strncmp(buffer, "[HD44780]", 9U) == 0) - section = SECTION_HD44780; - else if (::strncmp(buffer, "[Nextion]", 9U) == 0) - section = SECTION_NEXTION; - else if (::strncmp(buffer, "[OLED]", 6U) == 0) - section = SECTION_OLED; - else if (::strncmp(buffer, "[LCDproc]", 9U) == 0) - section = SECTION_LCDPROC; - else if (::strncmp(buffer, "[Lock File]", 11U) == 0) - section = SECTION_LOCK_FILE; - else if (::strncmp(buffer, "[Remote Control]", 16U) == 0) - section = SECTION_REMOTE_CONTROL; - else - section = SECTION_NONE; + if (buffer[0U] == '[') { + if (::strncmp(buffer, "[General]", 9U) == 0) + section = SECTION_GENERAL; + else if (::strncmp(buffer, "[Info]", 6U) == 0) + section = SECTION_INFO; + else if (::strncmp(buffer, "[Log]", 5U) == 0) + section = SECTION_LOG; + else if (::strncmp(buffer, "[CW Id]", 7U) == 0) + section = SECTION_CWID; + else if (::strncmp(buffer, "[DMR Id Lookup]", 15U) == 0) + section = SECTION_DMRID_LOOKUP; + else if (::strncmp(buffer, "[NXDN Id Lookup]", 16U) == 0) + section = SECTION_NXDNID_LOOKUP; + else if (::strncmp(buffer, "[Modem]", 7U) == 0) + section = SECTION_MODEM; + else if (::strncmp(buffer, "[Transparent Data]", 18U) == 0) + section = SECTION_TRANSPARENT; + else if (::strncmp(buffer, "[D-Star]", 8U) == 0) + section = SECTION_DSTAR; + else if (::strncmp(buffer, "[DMR]", 5U) == 0) + section = SECTION_DMR; + else if (::strncmp(buffer, "[System Fusion]", 15U) == 0) + section = SECTION_FUSION; + else if (::strncmp(buffer, "[P25]", 5U) == 0) + section = SECTION_P25; + else if (::strncmp(buffer, "[NXDN]", 6U) == 0) + section = SECTION_NXDN; + else if (::strncmp(buffer, "[M17]", 5U) == 0) + section = SECTION_M17; + else if (::strncmp(buffer, "[POCSAG]", 8U) == 0) + section = SECTION_POCSAG; + else if (::strncmp(buffer, "[FM]", 4U) == 0) + section = SECTION_FM; + else if (::strncmp(buffer, "[AX.25]", 7U) == 0) + section = SECTION_AX25; + else if (::strncmp(buffer, "[D-Star Network]", 16U) == 0) + section = SECTION_DSTAR_NETWORK; + else if (::strncmp(buffer, "[DMR Network]", 13U) == 0) + section = SECTION_DMR_NETWORK; + else if (::strncmp(buffer, "[System Fusion Network]", 23U) == 0) + section = SECTION_FUSION_NETWORK; + else if (::strncmp(buffer, "[P25 Network]", 13U) == 0) + section = SECTION_P25_NETWORK; + else if (::strncmp(buffer, "[NXDN Network]", 14U) == 0) + section = SECTION_NXDN_NETWORK; + else if (::strncmp(buffer, "[M17 Network]", 13U) == 0) + section = SECTION_M17_NETWORK; + else if (::strncmp(buffer, "[POCSAG Network]", 16U) == 0) + section = SECTION_POCSAG_NETWORK; + else if (::strncmp(buffer, "[FM Network]", 12U) == 0) + section = SECTION_FM_NETWORK; + else if (::strncmp(buffer, "[AX.25 Network]", 15U) == 0) + section = SECTION_AX25_NETWORK; + else if (::strncmp(buffer, "[TFT Serial]", 12U) == 0) + section = SECTION_TFTSERIAL; + else if (::strncmp(buffer, "[HD44780]", 9U) == 0) + section = SECTION_HD44780; + else if (::strncmp(buffer, "[Nextion]", 9U) == 0) + section = SECTION_NEXTION; + else if (::strncmp(buffer, "[OLED]", 6U) == 0) + section = SECTION_OLED; + else if (::strncmp(buffer, "[LCDproc]", 9U) == 0) + section = SECTION_LCDPROC; + else if (::strncmp(buffer, "[Lock File]", 11U) == 0) + section = SECTION_LOCK_FILE; + else if (::strncmp(buffer, "[Remote Control]", 16U) == 0) + section = SECTION_REMOTE_CONTROL; + else + section = SECTION_NONE; - continue; - } - - char* key = ::strtok(buffer, " \t=\r\n"); - if (key == NULL) - continue; - - char* value = ::strtok(NULL, "\r\n"); - if (value == NULL) - continue; - - // Remove quotes from the value - size_t len = ::strlen(value); - if (len > 1U && *value == '"' && value[len - 1U] == '"') { - value[len - 1U] = '\0'; - value++; - } else { - char *p; - - // if value is not quoted, remove after # (to make comment) - if ((p = strchr(value, '#')) != NULL) - *p = '\0'; - - // remove trailing tab/space - for (p = value + strlen(value) - 1; - p >= value && (*p == '\t' || *p == ' '); p--) - *p = '\0'; - } - - if (section == SECTION_GENERAL) { - if (::strcmp(key, "Callsign") == 0) { - // Convert the callsign to upper case - for (unsigned int i = 0U; value[i] != 0; i++) - value[i] = ::toupper(value[i]); - m_fmCallsign = m_cwIdCallsign = m_callsign = value; - } else if (::strcmp(key, "Id") == 0) - m_id = m_p25Id = m_dmrId = (unsigned int)::atoi(value); - else if (::strcmp(key, "Timeout") == 0) - m_fmTimeout = m_timeout = (unsigned int)::atoi(value); - 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_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "RFModeHang") == 0) - m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "NetModeHang") == 0) - m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Display") == 0) - m_display = value; - else if (::strcmp(key, "Daemon") == 0) - m_daemon = ::atoi(value) == 1; - } else if (section == SECTION_INFO) { - if (::strcmp(key, "TXFrequency") == 0) - m_pocsagFrequency = m_txFrequency = (unsigned int)::atoi(value); - else if (::strcmp(key, "RXFrequency") == 0) - m_rxFrequency = (unsigned int)::atoi(value); - else if (::strcmp(key, "Power") == 0) - m_power = (unsigned int)::atoi(value); - else if (::strcmp(key, "Latitude") == 0) - m_latitude = float(::atof(value)); - else if (::strcmp(key, "Longitude") == 0) - m_longitude = float(::atof(value)); - else if (::strcmp(key, "Height") == 0) - m_height = ::atoi(value); - else if (::strcmp(key, "Location") == 0) - m_location = value; - else if (::strcmp(key, "Description") == 0) - m_description = value; - else if (::strcmp(key, "URL") == 0) - m_url = value; - } else if (section == SECTION_LOG) { - if (::strcmp(key, "FilePath") == 0) - m_logFilePath = value; - else if (::strcmp(key, "FileRoot") == 0) - m_logFileRoot = value; - else if (::strcmp(key, "FileLevel") == 0) - m_logFileLevel = (unsigned int)::atoi(value); - else if (::strcmp(key, "DisplayLevel") == 0) - m_logDisplayLevel = (unsigned int)::atoi(value); - else if (::strcmp(key, "FileRotate") == 0) - m_logFileRotate = ::atoi(value) == 1; - } else if (section == SECTION_CWID) { - if (::strcmp(key, "Enable") == 0) - m_cwIdEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Time") == 0) - m_cwIdTime = (unsigned int)::atoi(value); - else if (::strcmp(key, "Callsign") == 0) { - // Convert the callsign to upper case - for (unsigned int i = 0U; value[i] != 0; i++) - value[i] = ::toupper(value[i]); - m_cwIdCallsign = value; + continue; } - } else if (section == SECTION_DMRID_LOOKUP) { - if (::strcmp(key, "File") == 0) - m_dmrIdLookupFile = value; - else if (::strcmp(key, "Time") == 0) - m_dmrIdLookupTime = (unsigned int)::atoi(value); - } else if (section == SECTION_NXDNID_LOOKUP) { - if (::strcmp(key, "File") == 0) - m_nxdnIdLookupFile = value; - else if (::strcmp(key, "Time") == 0) - m_nxdnIdLookupTime = (unsigned int)::atoi(value); - } else if (section == SECTION_MODEM) { - if (::strcmp(key, "Port") == 0) - m_modemPort = value; - else if (::strcmp(key, "Protocol") == 0) - m_modemProtocol = value; - else if (::strcmp(key, "Address") == 0) - m_modemAddress = (unsigned int)::strtoul(value, NULL, 16); - else if (::strcmp(key, "RXInvert") == 0) - m_modemRXInvert = ::atoi(value) == 1; - else if (::strcmp(key, "TXInvert") == 0) - m_modemTXInvert = ::atoi(value) == 1; - else if (::strcmp(key, "PTTInvert") == 0) - m_modemPTTInvert = ::atoi(value) == 1; - else if (::strcmp(key, "TXDelay") == 0) - m_modemTXDelay = (unsigned int)::atoi(value); - else if (::strcmp(key, "DMRDelay") == 0) - m_modemDMRDelay = (unsigned int)::atoi(value); - else if (::strcmp(key, "RXOffset") == 0) - m_modemRXOffset = ::atoi(value); - else if (::strcmp(key, "TXOffset") == 0) - m_modemTXOffset = ::atoi(value); - else if (::strcmp(key, "RXDCOffset") == 0) - m_modemRXDCOffset = ::atoi(value); - else if (::strcmp(key, "TXDCOffset") == 0) - m_modemTXDCOffset = ::atoi(value); - else if (::strcmp(key, "RFLevel") == 0) - m_modemRFLevel = float(::atof(value)); - else if (::strcmp(key, "RXLevel") == 0) - m_modemRXLevel = float(::atof(value)); - else if (::strcmp(key, "TXLevel") == 0) - m_modemFMTXLevel = m_modemCWIdTXLevel = m_modemDStarTXLevel = m_modemDMRTXLevel = m_modemYSFTXLevel = m_modemP25TXLevel = m_modemNXDNTXLevel = float(::atof(value)); - else if (::strcmp(key, "CWIdTXLevel") == 0) - m_modemCWIdTXLevel = float(::atof(value)); - else if (::strcmp(key, "D-StarTXLevel") == 0) - m_modemDStarTXLevel = float(::atof(value)); - else if (::strcmp(key, "DMRTXLevel") == 0) - m_modemDMRTXLevel = float(::atof(value)); - else if (::strcmp(key, "YSFTXLevel") == 0) - m_modemYSFTXLevel = float(::atof(value)); - else if (::strcmp(key, "P25TXLevel") == 0) - m_modemP25TXLevel = float(::atof(value)); - else if (::strcmp(key, "NXDNTXLevel") == 0) - m_modemNXDNTXLevel = float(::atof(value)); - else if (::strcmp(key, "POCSAGTXLevel") == 0) - m_modemPOCSAGTXLevel = float(::atof(value)); - else if (::strcmp(key, "FMTXLevel") == 0) - m_modemFMTXLevel = float(::atof(value)); - else if (::strcmp(key, "RSSIMappingFile") == 0) - m_modemRSSIMappingFile = value; - else if (::strcmp(key, "UseCOSAsLockout") == 0) - m_modemUseCOSAsLockout = ::atoi(value) == 1; - else if (::strcmp(key, "Trace") == 0) - m_modemTrace = ::atoi(value) == 1; - else if (::strcmp(key, "Debug") == 0) - m_modemDebug = ::atoi(value) == 1; - } else if (section == SECTION_TRANSPARENT) { - if (::strcmp(key, "Enable") == 0) - m_transparentEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "RemoteAddress") == 0) - m_transparentRemoteAddress = value; - else if (::strcmp(key, "RemotePort") == 0) - m_transparentRemotePort = (unsigned int)::atoi(value); - else if (::strcmp(key, "LocalPort") == 0) - m_transparentLocalPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "SendFrameType") == 0) - m_transparentSendFrameType = (unsigned int)::atoi(value); - } else if (section == SECTION_UMP) { - if (::strcmp(key, "Enable") == 0) - m_umpEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Port") == 0) - m_umpPort = value; - } else if (section == SECTION_DSTAR) { - if (::strcmp(key, "Enable") == 0) - m_dstarEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Module") == 0) { - // Convert the module to upper case - for (unsigned int i = 0U; value[i] != 0; i++) - value[i] = ::toupper(value[i]); - m_dstarModule = value; - } else if (::strcmp(key, "SelfOnly") == 0) - m_dstarSelfOnly = ::atoi(value) == 1; - else if (::strcmp(key, "BlackList") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - if (::strlen(p) > 0U) { - for (unsigned int i = 0U; p[i] != 0; i++) - p[i] = ::toupper(p[i]); - std::string callsign = std::string(p); - callsign.resize(DSTAR_LONG_CALLSIGN_LENGTH, ' '); - m_dstarBlackList.push_back(callsign); + + char* key = ::strtok(buffer, " \t=\r\n"); + if (key == NULL) + continue; + + char* value = ::strtok(NULL, "\r\n"); + if (value == NULL) + continue; + + // Remove quotes from the value + size_t len = ::strlen(value); + if (len > 1U && *value == '"' && value[len - 1U] == '"') { + value[len - 1U] = '\0'; + value++; + } else { + // if value is not quoted, remove after # (to make comment) + ::strtok(value, "#"); + } + + if (section == SECTION_GENERAL) { + if (::strcmp(key, "Callsign") == 0) { + // Convert the callsign to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_fmCallsign = m_cwIdCallsign = m_callsign = value; + } else if (::strcmp(key, "Id") == 0) + m_id = m_p25Id = m_dmrId = (unsigned int)::atoi(value); + else if (::strcmp(key, "Timeout") == 0) + m_fmTimeout = m_timeout = (unsigned int)::atoi(value); + 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); + else if (::strcmp(key, "RFModeHang") == 0) + m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = m_nxdnModeHang = m_m17ModeHang = 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, "Display") == 0) + m_display = value; + else if (::strcmp(key, "Daemon") == 0) + m_daemon = ::atoi(value) == 1; + } else if (section == SECTION_INFO) { + if (::strcmp(key, "TXFrequency") == 0) + m_pocsagFrequency = m_txFrequency = (unsigned int)::atoi(value); + else if (::strcmp(key, "RXFrequency") == 0) + m_rxFrequency = (unsigned int)::atoi(value); + else if (::strcmp(key, "Power") == 0) + m_power = (unsigned int)::atoi(value); + else if (::strcmp(key, "Latitude") == 0) + m_latitude = float(::atof(value)); + else if (::strcmp(key, "Longitude") == 0) + m_longitude = float(::atof(value)); + else if (::strcmp(key, "Height") == 0) + m_height = ::atoi(value); + else if (::strcmp(key, "Location") == 0) + m_location = value; + else if (::strcmp(key, "Description") == 0) + m_description = value; + else if (::strcmp(key, "URL") == 0) + m_url = value; + } else if (section == SECTION_LOG) { + if (::strcmp(key, "FilePath") == 0) + m_logFilePath = value; + else if (::strcmp(key, "FileRoot") == 0) + m_logFileRoot = value; + else if (::strcmp(key, "FileLevel") == 0) + m_logFileLevel = (unsigned int)::atoi(value); + else if (::strcmp(key, "DisplayLevel") == 0) + m_logDisplayLevel = (unsigned int)::atoi(value); + else if (::strcmp(key, "FileRotate") == 0) + m_logFileRotate = ::atoi(value) == 1; + } else if (section == SECTION_CWID) { + if (::strcmp(key, "Enable") == 0) + m_cwIdEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Time") == 0) + m_cwIdTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "Callsign") == 0) { + // Convert the callsign to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_cwIdCallsign = value; + } + } else if (section == SECTION_DMRID_LOOKUP) { + if (::strcmp(key, "File") == 0) + m_dmrIdLookupFile = value; + else if (::strcmp(key, "Time") == 0) + m_dmrIdLookupTime = (unsigned int)::atoi(value); + } else if (section == SECTION_NXDNID_LOOKUP) { + if (::strcmp(key, "File") == 0) + m_nxdnIdLookupFile = value; + else if (::strcmp(key, "Time") == 0) + m_nxdnIdLookupTime = (unsigned int)::atoi(value); + } else if (section == SECTION_MODEM) { + if (::strcmp(key, "Port") == 0) + m_modemPort = value; + else if (::strcmp(key, "Protocol") == 0) + m_modemProtocol = value; + else if (::strcmp(key, "Speed") == 0) + m_modemSpeed = (unsigned int)::atoi(value); + else if (::strcmp(key, "Address") == 0) + m_modemAddress = (unsigned int)::strtoul(value, NULL, 16); + else if (::strcmp(key, "RXInvert") == 0) + m_modemRXInvert = ::atoi(value) == 1; + else if (::strcmp(key, "TXInvert") == 0) + m_modemTXInvert = ::atoi(value) == 1; + else if (::strcmp(key, "PTTInvert") == 0) + m_modemPTTInvert = ::atoi(value) == 1; + else if (::strcmp(key, "TXDelay") == 0) + m_ax25TXDelay = m_modemTXDelay = (unsigned int)::atoi(value); + else if (::strcmp(key, "DMRDelay") == 0) + m_modemDMRDelay = (unsigned int)::atoi(value); + else if (::strcmp(key, "RXOffset") == 0) + m_modemRXOffset = ::atoi(value); + else if (::strcmp(key, "TXOffset") == 0) + m_modemTXOffset = ::atoi(value); + else if (::strcmp(key, "RXDCOffset") == 0) + m_modemRXDCOffset = ::atoi(value); + else if (::strcmp(key, "TXDCOffset") == 0) + m_modemTXDCOffset = ::atoi(value); + else if (::strcmp(key, "RFLevel") == 0) + m_modemRFLevel = float(::atof(value)); + 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)); + else if (::strcmp(key, "CWIdTXLevel") == 0) + m_modemCWIdTXLevel = float(::atof(value)); + else if (::strcmp(key, "D-StarTXLevel") == 0) + m_modemDStarTXLevel = float(::atof(value)); + else if (::strcmp(key, "DMRTXLevel") == 0) + m_modemDMRTXLevel = float(::atof(value)); + else if (::strcmp(key, "YSFTXLevel") == 0) + m_modemYSFTXLevel = float(::atof(value)); + else if (::strcmp(key, "P25TXLevel") == 0) + m_modemP25TXLevel = float(::atof(value)); + else if (::strcmp(key, "NXDNTXLevel") == 0) + m_modemNXDNTXLevel = float(::atof(value)); + else if (::strcmp(key, "M17TXLevel") == 0) + m_modemM17TXLevel = float(::atof(value)); + else if (::strcmp(key, "POCSAGTXLevel") == 0) + m_modemPOCSAGTXLevel = float(::atof(value)); + else if (::strcmp(key, "FMTXLevel") == 0) + m_modemFMTXLevel = float(::atof(value)); + else if (::strcmp(key, "AX25TXLevel") == 0) + m_modemAX25TXLevel = float(::atof(value)); + else if (::strcmp(key, "RSSIMappingFile") == 0) + m_modemRSSIMappingFile = value; + else if (::strcmp(key, "Trace") == 0) + m_modemTrace = ::atoi(value) == 1; + else if (::strcmp(key, "Debug") == 0) + m_modemDebug = ::atoi(value) == 1; + } else if (section == SECTION_TRANSPARENT) { + if (::strcmp(key, "Enable") == 0) + m_transparentEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "RemoteAddress") == 0) + m_transparentRemoteAddress = value; + else if (::strcmp(key, "RemotePort") == 0) + m_transparentRemotePort = (unsigned int)::atoi(value); + else if (::strcmp(key, "LocalPort") == 0) + m_transparentLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "SendFrameType") == 0) + m_transparentSendFrameType = (unsigned int)::atoi(value); + } else if (section == SECTION_DSTAR) { + if (::strcmp(key, "Enable") == 0) + m_dstarEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Module") == 0) { + // Convert the module to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_dstarModule = value; + } else if (::strcmp(key, "SelfOnly") == 0) + m_dstarSelfOnly = ::atoi(value) == 1; + else if (::strcmp(key, "BlackList") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + if (::strlen(p) > 0U) { + for (unsigned int i = 0U; p[i] != 0; i++) + p[i] = ::toupper(p[i]); + std::string callsign = std::string(p); + callsign.resize(DSTAR_LONG_CALLSIGN_LENGTH, ' '); + m_dstarBlackList.push_back(callsign); + } + p = ::strtok(NULL, ",\r\n"); } - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "WhiteList") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - if (::strlen(p) > 0U) { - for (unsigned int i = 0U; p[i] != 0; i++) - p[i] = ::toupper(p[i]); - std::string callsign = std::string(p); - callsign.resize(DSTAR_LONG_CALLSIGN_LENGTH, ' '); - m_dstarWhiteList.push_back(callsign); - } - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "AckReply") == 0) - m_dstarAckReply = ::atoi(value) == 1; - else if (::strcmp(key, "AckTime") == 0) - m_dstarAckTime = (unsigned int)::atoi(value); - else if (::strcmp(key, "AckMessage") == 0) - m_dstarAckMessage = ::atoi(value) == 1; - else if (::strcmp(key, "ErrorReply") == 0) - m_dstarErrorReply = ::atoi(value) == 1; - else if (::strcmp(key, "RemoteGateway") == 0) - m_dstarRemoteGateway = ::atoi(value) == 1; - else if (::strcmp(key, "ModeHang") == 0) - m_dstarModeHang = (unsigned int)::atoi(value); - } else if (section == SECTION_DMR) { - if (::strcmp(key, "Enable") == 0) - m_dmrEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Beacons") == 0) - m_dmrBeacons = ::atoi(value) == 1 ? DMR_BEACONS_NETWORK : DMR_BEACONS_OFF; - else if (::strcmp(key, "BeaconInterval") == 0) { - m_dmrBeacons = m_dmrBeacons != DMR_BEACONS_OFF ? DMR_BEACONS_TIMED : DMR_BEACONS_OFF; - m_dmrBeaconInterval = (unsigned int)::atoi(value); - } else if (::strcmp(key, "BeaconDuration") == 0) - m_dmrBeaconDuration = (unsigned int)::atoi(value); - else if (::strcmp(key, "Id") == 0) - m_dmrId = (unsigned int)::atoi(value); - else if (::strcmp(key, "ColorCode") == 0) - m_dmrColorCode = (unsigned int)::atoi(value); - else if (::strcmp(key, "SelfOnly") == 0) - m_dmrSelfOnly = ::atoi(value) == 1; - else if (::strcmp(key, "EmbeddedLCOnly") == 0) - m_dmrEmbeddedLCOnly = ::atoi(value) == 1; - else if (::strcmp(key, "DumpTAData") == 0) - m_dmrDumpTAData = ::atoi(value) == 1; - else if (::strcmp(key, "Prefixes") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - unsigned int prefix = (unsigned int)::atoi(p); - if (prefix > 0U && prefix <= 999U) - m_dmrPrefixes.push_back(prefix); - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "BlackList") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - unsigned int id = (unsigned int)::atoi(p); - if (id > 0U) - m_dmrBlackList.push_back(id); - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "WhiteList") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - unsigned int id = (unsigned int)::atoi(p); - if (id > 0U) - m_dmrWhiteList.push_back(id); - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "Slot1TGWhiteList") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - unsigned int id = (unsigned int)::atoi(p); - if (id > 0U) - m_dmrSlot1TGWhiteList.push_back(id); - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "Slot2TGWhiteList") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - unsigned int id = (unsigned int)::atoi(p); - if (id > 0U) - m_dmrSlot2TGWhiteList.push_back(id); - p = ::strtok(NULL, ",\r\n"); - } - } else if (::strcmp(key, "TXHang") == 0) - m_dmrTXHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "CallHang") == 0) - m_dmrCallHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_dmrModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "OVCM") == 0) - switch(::atoi(value)) { + } else if (::strcmp(key, "WhiteList") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + if (::strlen(p) > 0U) { + for (unsigned int i = 0U; p[i] != 0; i++) + p[i] = ::toupper(p[i]); + std::string callsign = std::string(p); + callsign.resize(DSTAR_LONG_CALLSIGN_LENGTH, ' '); + m_dstarWhiteList.push_back(callsign); + } + p = ::strtok(NULL, ",\r\n"); + } + } else if (::strcmp(key, "AckReply") == 0) + m_dstarAckReply = ::atoi(value) == 1; + else if (::strcmp(key, "AckTime") == 0) + m_dstarAckTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "AckMessage") == 0) + m_dstarAckMessage = ::atoi(value) == 1; + else if (::strcmp(key, "ErrorReply") == 0) + m_dstarErrorReply = ::atoi(value) == 1; + else if (::strcmp(key, "RemoteGateway") == 0) + m_dstarRemoteGateway = ::atoi(value) == 1; + else if (::strcmp(key, "ModeHang") == 0) + m_dstarModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_DMR) { + if (::strcmp(key, "Enable") == 0) + m_dmrEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Beacons") == 0) + m_dmrBeacons = ::atoi(value) == 1 ? DMR_BEACONS_NETWORK : DMR_BEACONS_OFF; + else if (::strcmp(key, "BeaconInterval") == 0) { + m_dmrBeacons = m_dmrBeacons != DMR_BEACONS_OFF ? DMR_BEACONS_TIMED : DMR_BEACONS_OFF; + m_dmrBeaconInterval = (unsigned int)::atoi(value); + } else if (::strcmp(key, "BeaconDuration") == 0) + m_dmrBeaconDuration = (unsigned int)::atoi(value); + else if (::strcmp(key, "Id") == 0) + m_dmrId = (unsigned int)::atoi(value); + else if (::strcmp(key, "ColorCode") == 0) + m_dmrColorCode = (unsigned int)::atoi(value); + else if (::strcmp(key, "SelfOnly") == 0) + m_dmrSelfOnly = ::atoi(value) == 1; + else if (::strcmp(key, "EmbeddedLCOnly") == 0) + m_dmrEmbeddedLCOnly = ::atoi(value) == 1; + else if (::strcmp(key, "DumpTAData") == 0) + m_dmrDumpTAData = ::atoi(value) == 1; + else if (::strcmp(key, "Prefixes") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + unsigned int prefix = (unsigned int)::atoi(p); + if (prefix > 0U && prefix <= 999U) + m_dmrPrefixes.push_back(prefix); + p = ::strtok(NULL, ",\r\n"); + } + } else if (::strcmp(key, "BlackList") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + unsigned int id = (unsigned int)::atoi(p); + if (id > 0U) + m_dmrBlackList.push_back(id); + p = ::strtok(NULL, ",\r\n"); + } + } else if (::strcmp(key, "WhiteList") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + unsigned int id = (unsigned int)::atoi(p); + if (id > 0U) + m_dmrWhiteList.push_back(id); + p = ::strtok(NULL, ",\r\n"); + } + } else if (::strcmp(key, "Slot1TGWhiteList") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + unsigned int id = (unsigned int)::atoi(p); + if (id > 0U) + m_dmrSlot1TGWhiteList.push_back(id); + p = ::strtok(NULL, ",\r\n"); + } + } else if (::strcmp(key, "Slot2TGWhiteList") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + unsigned int id = (unsigned int)::atoi(p); + if (id > 0U) + m_dmrSlot2TGWhiteList.push_back(id); + p = ::strtok(NULL, ",\r\n"); + } + } else if (::strcmp(key, "TXHang") == 0) + m_dmrTXHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "CallHang") == 0) + m_dmrCallHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_dmrModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "OVCM") == 0) { + switch (::atoi(value)) { case 1: m_dmrOVCM = DMR_OVCM_RX_ON; break; @@ -671,321 +709,392 @@ bool CConf::read() default: m_dmrOVCM = DMR_OVCM_OFF; break; + } } - } else if (section == SECTION_FUSION) { - if (::strcmp(key, "Enable") == 0) - m_fusionEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "LowDeviation") == 0) - m_fusionLowDeviation = ::atoi(value) == 1; - else if (::strcmp(key, "RemoteGateway") == 0) - m_fusionRemoteGateway = ::atoi(value) == 1; - else if (::strcmp(key, "SelfOnly") == 0) - m_fusionSelfOnly = ::atoi(value) == 1; - else if (::strcmp(key, "TXHang") == 0) - m_fusionTXHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_fusionModeHang = (unsigned int)::atoi(value); - } else if (section == SECTION_P25) { - if (::strcmp(key, "Enable") == 0) - m_p25Enabled = ::atoi(value) == 1; - else if (::strcmp(key, "Id") == 0) - m_p25Id = (unsigned int)::atoi(value); - else if (::strcmp(key, "NAC") == 0) - m_p25NAC = (unsigned int)::strtoul(value, NULL, 16); - else if (::strcmp(key, "OverrideUIDCheck") == 0) - m_p25OverrideUID = ::atoi(value) == 1; - else if (::strcmp(key, "SelfOnly") == 0) - m_p25SelfOnly = ::atoi(value) == 1; - else if (::strcmp(key, "RemoteGateway") == 0) - m_p25RemoteGateway = ::atoi(value) == 1; - else if (::strcmp(key, "TXHang") == 0) - m_p25TXHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_p25ModeHang = (unsigned int)::atoi(value); - } else if (section == SECTION_NXDN) { - if (::strcmp(key, "Enable") == 0) - m_nxdnEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Id") == 0) - m_nxdnId = (unsigned int)::atoi(value); - else if (::strcmp(key, "RAN") == 0) - m_nxdnRAN = (unsigned int)::atoi(value); - else if (::strcmp(key, "SelfOnly") == 0) - m_nxdnSelfOnly = ::atoi(value) == 1; - else if (::strcmp(key, "RemoteGateway") == 0) - m_nxdnRemoteGateway = ::atoi(value) == 1; - else if (::strcmp(key, "TXHang") == 0) - m_nxdnTXHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_nxdnModeHang = (unsigned int)::atoi(value); - } else if (section == SECTION_POCSAG) { - if (::strcmp(key, "Enable") == 0) - m_pocsagEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Frequency") == 0) - m_pocsagFrequency = (unsigned int)::atoi(value); - } - else if (section == SECTION_FM) { - if (::strcmp(key, "Enable") == 0) - m_fmEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Callsign") == 0) { - // Convert the callsign to upper case - for (unsigned int i = 0U; value[i] != 0; i++) - value[i] = ::toupper(value[i]); - m_fmCallsign = value; - } else if (::strcmp(key, "CallsignSpeed") == 0) - m_fmCallsignSpeed = (unsigned int)::atoi(value); - else if (::strcmp(key, "CallsignFrequency") == 0) - m_fmCallsignFrequency = (unsigned int)::atoi(value); - else if (::strcmp(key, "CallsignTime") == 0) - m_fmCallsignTime = (unsigned int)::atoi(value); - else if (::strcmp(key, "CallsignHoldoff") == 0) - m_fmCallsignHoldoff = (unsigned int)::atoi(value); - else if (::strcmp(key, "CallsignHighLevel") == 0) - m_fmCallsignHighLevel = float(::atof(value)); - else if (::strcmp(key, "CallsignLowLevel") == 0) - m_fmCallsignLowLevel = float(::atof(value)); - else if (::strcmp(key, "CallsignAtStart") == 0) - m_fmCallsignAtStart = ::atoi(value) == 1; - else if (::strcmp(key, "CallsignAtEnd") == 0) - m_fmCallsignAtEnd = ::atoi(value) == 1; - else if (::strcmp(key, "CallsignAtLatch") == 0) - m_fmCallsignAtLatch = ::atoi(value) == 1; - else if (::strcmp(key, "RFAck") == 0) { - // Convert the ack to upper case - for (unsigned int i = 0U; value[i] != 0; i++) - value[i] = ::toupper(value[i]); - m_fmRFAck = value; - } else if (::strcmp(key, "ExtAck") == 0) { - // Convert the ack to upper case - for (unsigned int i = 0U; value[i] != 0; i++) - value[i] = ::toupper(value[i]); - m_fmExtAck = value; - } else if (::strcmp(key, "AckSpeed") == 0) - m_fmAckSpeed = (unsigned int)::atoi(value); - else if (::strcmp(key, "AckFrequency") == 0) - m_fmAckFrequency = (unsigned int)::atoi(value); - else if (::strcmp(key, "AckMinTime") == 0) - m_fmAckMinTime = (unsigned int)::atoi(value); - else if (::strcmp(key, "AckDelay") == 0) - m_fmAckDelay = (unsigned int)::atoi(value); - else if (::strcmp(key, "AckLevel") == 0) - m_fmAckLevel = float(::atof(value)); - else if (::strcmp(key, "Timeout") == 0) - m_fmTimeout = (unsigned int)::atoi(value); - else if (::strcmp(key, "TimeoutLevel") == 0) - m_fmTimeoutLevel = float(::atof(value)); - else if (::strcmp(key, "CTCSSFrequency") == 0) - m_fmCTCSSFrequency = float(::atof(value)); - else if (::strcmp(key, "CTCSSThreshold") == 0) - m_fmCTCSSHighThreshold = m_fmCTCSSLowThreshold = (unsigned int)::atoi(value); - else if (::strcmp(key, "CTCSSHighThreshold") == 0) - m_fmCTCSSHighThreshold = (unsigned int)::atoi(value); - else if (::strcmp(key, "CTCSSLowThreshold") == 0) - m_fmCTCSSLowThreshold = (unsigned int)::atoi(value); - else if (::strcmp(key, "CTCSSLevel") == 0) - m_fmCTCSSLevel = float(::atof(value)); - else if (::strcmp(key, "KerchunkTime") == 0) - m_fmKerchunkTime = (unsigned int)::atoi(value); - else if (::strcmp(key, "HangTime") == 0) - m_fmHangTime = (unsigned int)::atoi(value); - else if (::strcmp(key, "AccessMode") == 0) - m_fmAccessMode = (unsigned int)::atoi(value); - else if (::strcmp(key, "COSInvert") == 0) - m_fmCOSInvert = ::atoi(value) == 1; - else if (::strcmp(key, "RFAudioBoost") == 0) - m_fmRFAudioBoost = (unsigned int)::atoi(value); - else if (::strcmp(key, "MaxDevLevel") == 0) - m_fmMaxDevLevel = float(::atof(value)); - else if (::strcmp(key, "ExtAudioBoost") == 0) - m_fmExtAudioBoost = (unsigned int)::atoi(value); - } else if (section == SECTION_DSTAR_NETWORK) { - if (::strcmp(key, "Enable") == 0) - m_dstarNetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "GatewayAddress") == 0) - m_dstarGatewayAddress = value; - else if (::strcmp(key, "GatewayPort") == 0) - m_dstarGatewayPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "LocalPort") == 0) - m_dstarLocalPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_dstarNetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Debug") == 0) - m_dstarNetworkDebug = ::atoi(value) == 1; - } else if (section == SECTION_DMR_NETWORK) { - if (::strcmp(key, "Enable") == 0) - m_dmrNetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Type") == 0) - m_dmrNetworkType = value; - else if (::strcmp(key, "Address") == 0) - m_dmrNetworkAddress = value; - else if (::strcmp(key, "Port") == 0) - m_dmrNetworkPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "Local") == 0) - m_dmrNetworkLocal = (unsigned int)::atoi(value); - else if (::strcmp(key, "Password") == 0) - m_dmrNetworkPassword = value; - else if (::strcmp(key, "Options") == 0) - m_dmrNetworkOptions = value; - else if (::strcmp(key, "Debug") == 0) - m_dmrNetworkDebug = ::atoi(value) == 1; - else if (::strcmp(key, "Jitter") == 0) - m_dmrNetworkJitter = (unsigned int)::atoi(value); - else if (::strcmp(key, "Slot1") == 0) - m_dmrNetworkSlot1 = ::atoi(value) == 1; - else if (::strcmp(key, "Slot2") == 0) - m_dmrNetworkSlot2 = ::atoi(value) == 1; - else if (::strcmp(key, "ModeHang") == 0) - m_dmrNetworkModeHang = (unsigned int)::atoi(value); - } else if (section == SECTION_FUSION_NETWORK) { - if (::strcmp(key, "Enable") == 0) - m_fusionNetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "LocalAddress") == 0) - m_fusionNetworkMyAddress = value; - else if (::strcmp(key, "LocalPort") == 0) - m_fusionNetworkMyPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "GatewayAddress") == 0) - m_fusionNetworkGatewayAddress = value; - else if (::strcmp(key, "GatewayPort") == 0) - m_fusionNetworkGatewayPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_fusionNetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Debug") == 0) - m_fusionNetworkDebug = ::atoi(value) == 1; - } else if (section == SECTION_P25_NETWORK) { - if (::strcmp(key, "Enable") == 0) - m_p25NetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "GatewayAddress") == 0) - m_p25GatewayAddress = value; - else if (::strcmp(key, "GatewayPort") == 0) - m_p25GatewayPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "LocalPort") == 0) - m_p25LocalPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_p25NetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Debug") == 0) - m_p25NetworkDebug = ::atoi(value) == 1; - } 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) - m_nxdnLocalPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "GatewayAddress") == 0) - m_nxdnGatewayAddress = value; - else if (::strcmp(key, "GatewayPort") == 0) - m_nxdnGatewayPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_nxdnNetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Debug") == 0) - m_nxdnNetworkDebug = ::atoi(value) == 1; - } else if (section == SECTION_POCSAG_NETWORK) { - if (::strcmp(key, "Enable") == 0) - m_pocsagNetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "LocalAddress") == 0) - m_pocsagLocalAddress = value; - else if (::strcmp(key, "LocalPort") == 0) - m_pocsagLocalPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "GatewayAddress") == 0) - m_pocsagGatewayAddress = value; - else if (::strcmp(key, "GatewayPort") == 0) - m_pocsagGatewayPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "ModeHang") == 0) - m_pocsagNetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Debug") == 0) - m_pocsagNetworkDebug = ::atoi(value) == 1; - } else if (section == SECTION_TFTSERIAL) { - if (::strcmp(key, "Port") == 0) - m_tftSerialPort = value; - else if (::strcmp(key, "Brightness") == 0) - m_tftSerialBrightness = (unsigned int)::atoi(value); - } else if (section == SECTION_HD44780) { - if (::strcmp(key, "Rows") == 0) - m_hd44780Rows = (unsigned int)::atoi(value); - else if (::strcmp(key, "Columns") == 0) - m_hd44780Columns = (unsigned int)::atoi(value); - else if (::strcmp(key, "I2CAddress") == 0) - m_hd44780i2cAddress = (unsigned int)::strtoul(value, NULL, 16); - else if (::strcmp(key, "PWM") == 0) - m_hd44780PWM = ::atoi(value) == 1; - else if (::strcmp(key, "PWMPin") == 0) - m_hd44780PWMPin = (unsigned int)::atoi(value); - else if (::strcmp(key, "PWMBright") == 0) - m_hd44780PWMBright = (unsigned int)::atoi(value); - else if (::strcmp(key, "PWMDim") == 0) - m_hd44780PWMDim = (unsigned int)::atoi(value); - else if (::strcmp(key, "DisplayClock") == 0) - m_hd44780DisplayClock = ::atoi(value) == 1; - else if (::strcmp(key, "UTC") == 0) - m_hd44780UTC = ::atoi(value) == 1; - else if (::strcmp(key, "Pins") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != NULL) { - unsigned int pin = (unsigned int)::atoi(p); - m_hd44780Pins.push_back(pin); - p = ::strtok(NULL, ",\r\n"); + } else if (section == SECTION_FUSION) { + if (::strcmp(key, "Enable") == 0) + m_fusionEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LowDeviation") == 0) + m_fusionLowDeviation = ::atoi(value) == 1; + else if (::strcmp(key, "RemoteGateway") == 0) + m_fusionRemoteGateway = ::atoi(value) == 1; + else if (::strcmp(key, "SelfOnly") == 0) + m_fusionSelfOnly = ::atoi(value) == 1; + else if (::strcmp(key, "TXHang") == 0) + m_fusionTXHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_fusionModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_P25) { + if (::strcmp(key, "Enable") == 0) + m_p25Enabled = ::atoi(value) == 1; + else if (::strcmp(key, "Id") == 0) + m_p25Id = (unsigned int)::atoi(value); + else if (::strcmp(key, "NAC") == 0) + m_p25NAC = (unsigned int)::strtoul(value, NULL, 16); + else if (::strcmp(key, "OverrideUIDCheck") == 0) + m_p25OverrideUID = ::atoi(value) == 1; + else if (::strcmp(key, "SelfOnly") == 0) + m_p25SelfOnly = ::atoi(value) == 1; + else if (::strcmp(key, "RemoteGateway") == 0) + m_p25RemoteGateway = ::atoi(value) == 1; + else if (::strcmp(key, "TXHang") == 0) + m_p25TXHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_p25ModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_NXDN) { + if (::strcmp(key, "Enable") == 0) + m_nxdnEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Id") == 0) + m_nxdnId = (unsigned int)::atoi(value); + else if (::strcmp(key, "RAN") == 0) + m_nxdnRAN = (unsigned int)::atoi(value); + else if (::strcmp(key, "SelfOnly") == 0) + m_nxdnSelfOnly = ::atoi(value) == 1; + else if (::strcmp(key, "RemoteGateway") == 0) + m_nxdnRemoteGateway = ::atoi(value) == 1; + else if (::strcmp(key, "TXHang") == 0) + m_nxdnTXHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_nxdnModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_M17) { + if (::strcmp(key, "Enable") == 0) + m_m17Enabled = ::atoi(value) == 1; + else if (::strcmp(key, "ColorCode") == 0) + m_m17ColorCode = (unsigned int)::atoi(value); + else if (::strcmp(key, "SelfOnly") == 0) + m_m17SelfOnly = ::atoi(value) == 1; + else if (::strcmp(key, "AllowEncryption") == 0) + m_m17AllowEncryption = ::atoi(value) == 1; + else if (::strcmp(key, "TXHang") == 0) + m_m17TXHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_m17ModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_POCSAG) { + if (::strcmp(key, "Enable") == 0) + m_pocsagEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Frequency") == 0) + m_pocsagFrequency = (unsigned int)::atoi(value); + } else if (section == SECTION_FM) { + if (::strcmp(key, "Enable") == 0) + m_fmEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Callsign") == 0) { + // Convert the callsign to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_fmCallsign = value; + } else if (::strcmp(key, "CallsignSpeed") == 0) + m_fmCallsignSpeed = (unsigned int)::atoi(value); + else if (::strcmp(key, "CallsignFrequency") == 0) + m_fmCallsignFrequency = (unsigned int)::atoi(value); + else if (::strcmp(key, "CallsignTime") == 0) + m_fmCallsignTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "CallsignHoldoff") == 0) + m_fmCallsignHoldoff = (unsigned int)::atoi(value); + else if (::strcmp(key, "CallsignHighLevel") == 0) + m_fmCallsignHighLevel = float(::atof(value)); + else if (::strcmp(key, "CallsignLowLevel") == 0) + m_fmCallsignLowLevel = float(::atof(value)); + else if (::strcmp(key, "CallsignAtStart") == 0) + m_fmCallsignAtStart = ::atoi(value) == 1; + else if (::strcmp(key, "CallsignAtEnd") == 0) + m_fmCallsignAtEnd = ::atoi(value) == 1; + else if (::strcmp(key, "CallsignAtLatch") == 0) + m_fmCallsignAtLatch = ::atoi(value) == 1; + else if (::strcmp(key, "RFAck") == 0) { + // Convert the ack to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_fmRFAck = value; + } else if (::strcmp(key, "ExtAck") == 0) { + // Convert the ack to upper case + for (unsigned int i = 0U; value[i] != 0; i++) + value[i] = ::toupper(value[i]); + m_fmExtAck = value; + } else if (::strcmp(key, "AckSpeed") == 0) + m_fmAckSpeed = (unsigned int)::atoi(value); + else if (::strcmp(key, "AckFrequency") == 0) + m_fmAckFrequency = (unsigned int)::atoi(value); + else if (::strcmp(key, "AckMinTime") == 0) + m_fmAckMinTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "AckDelay") == 0) + m_fmAckDelay = (unsigned int)::atoi(value); + else if (::strcmp(key, "AckLevel") == 0) + m_fmAckLevel = float(::atof(value)); + else if (::strcmp(key, "Timeout") == 0) + m_fmTimeout = (unsigned int)::atoi(value); + else if (::strcmp(key, "TimeoutLevel") == 0) + m_fmTimeoutLevel = float(::atof(value)); + else if (::strcmp(key, "CTCSSFrequency") == 0) + m_fmCTCSSFrequency = float(::atof(value)); + else if (::strcmp(key, "CTCSSThreshold") == 0) + m_fmCTCSSHighThreshold = m_fmCTCSSLowThreshold = (unsigned int)::atoi(value); + else if (::strcmp(key, "CTCSSHighThreshold") == 0) + m_fmCTCSSHighThreshold = (unsigned int)::atoi(value); + else if (::strcmp(key, "CTCSSLowThreshold") == 0) + m_fmCTCSSLowThreshold = (unsigned int)::atoi(value); + else if (::strcmp(key, "CTCSSLevel") == 0) + m_fmCTCSSLevel = float(::atof(value)); + else if (::strcmp(key, "KerchunkTime") == 0) + m_fmKerchunkTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "HangTime") == 0) + m_fmHangTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "AccessMode") == 0) + m_fmAccessMode = ::atoi(value); + else if (::strcmp(key, "COSInvert") == 0) + m_fmCOSInvert = ::atoi(value) == 1; + else if (::strcmp(key, "NoiseSquelch") == 0) + m_fmNoiseSquelch = ::atoi(value) == 1; + else if (::strcmp(key, "SquelchThreshold") == 0) + m_fmSquelchHighThreshold = m_fmSquelchLowThreshold = (unsigned int)::atoi(value); + else if (::strcmp(key, "SquelchHighThreshold") == 0) + m_fmSquelchHighThreshold = (unsigned int)::atoi(value); + else if (::strcmp(key, "SquelchLowThreshold") == 0) + m_fmSquelchLowThreshold = (unsigned int)::atoi(value); + else if (::strcmp(key, "RFAudioBoost") == 0) + m_fmRFAudioBoost = (unsigned int)::atoi(value); + else if (::strcmp(key, "MaxDevLevel") == 0) + m_fmMaxDevLevel = float(::atof(value)); + else if (::strcmp(key, "ExtAudioBoost") == 0) + m_fmExtAudioBoost = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_fmModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_AX25) { + if (::strcmp(key, "Enable") == 0) + m_ax25Enabled = ::atoi(value) == 1; + else if (::strcmp(key, "TXDelay") == 0) + m_ax25TXDelay = (unsigned int)::atoi(value); + else if (::strcmp(key, "RXTwist") == 0) + m_ax25RXTwist = ::atoi(value); + else if (::strcmp(key, "SlotTime") == 0) + m_ax25SlotTime = (unsigned int)::atoi(value); + else if (::strcmp(key, "PPersist") == 0) + m_ax25PPersist = (unsigned int)::atoi(value); + else if (::strcmp(key, "Trace") == 0) + m_ax25Trace = ::atoi(value) == 1; + } else if (section == SECTION_DSTAR_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_dstarNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "GatewayAddress") == 0) + m_dstarGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_dstarGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "LocalPort") == 0) + m_dstarLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_dstarNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_dstarNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_DMR_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_dmrNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Type") == 0) + m_dmrNetworkType = value; + else if (::strcmp(key, "Address") == 0) + m_dmrNetworkAddress = value; + else if (::strcmp(key, "Port") == 0) + m_dmrNetworkPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "Local") == 0) + m_dmrNetworkLocal = (unsigned int)::atoi(value); + else if (::strcmp(key, "Password") == 0) + m_dmrNetworkPassword = value; + else if (::strcmp(key, "Options") == 0) + m_dmrNetworkOptions = value; + else if (::strcmp(key, "Debug") == 0) + m_dmrNetworkDebug = ::atoi(value) == 1; + else if (::strcmp(key, "Jitter") == 0) + m_dmrNetworkJitter = (unsigned int)::atoi(value); + else if (::strcmp(key, "Slot1") == 0) + m_dmrNetworkSlot1 = ::atoi(value) == 1; + else if (::strcmp(key, "Slot2") == 0) + m_dmrNetworkSlot2 = ::atoi(value) == 1; + else if (::strcmp(key, "ModeHang") == 0) + m_dmrNetworkModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_FUSION_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_fusionNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalAddress") == 0) + m_fusionNetworkMyAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_fusionNetworkMyPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_fusionNetworkGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_fusionNetworkGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_fusionNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_fusionNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_P25_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_p25NetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "GatewayAddress") == 0) + m_p25GatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_p25GatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "LocalPort") == 0) + m_p25LocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_p25NetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_p25NetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_NXDN_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_nxdnNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalAddress") == 0) + m_nxdnLocalAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_nxdnLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_nxdnGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_nxdnGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_nxdnNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_nxdnNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_M17_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_m17NetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalPort") == 0) + m_m17LocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_m17GatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_m17GatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_m17NetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_m17NetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_POCSAG_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_pocsagNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalAddress") == 0) + m_pocsagLocalAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_pocsagLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_pocsagGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_pocsagGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_pocsagNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_pocsagNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_FM_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_fmNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalAddress") == 0) + m_fmLocalAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_fmLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_fmGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_fmGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "SampleRate") == 0) + m_fmSampleRate = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_fmNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_fmNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_AX25_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_ax25NetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Port") == 0) + m_ax25NetworkPort = value; + else if (::strcmp(key, "Speed") == 0) + m_ax25NetworkSpeed = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_ax25NetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_TFTSERIAL) { + if (::strcmp(key, "Port") == 0) + m_tftSerialPort = value; + else if (::strcmp(key, "Brightness") == 0) + m_tftSerialBrightness = (unsigned int)::atoi(value); + } else if (section == SECTION_HD44780) { + if (::strcmp(key, "Rows") == 0) + m_hd44780Rows = (unsigned int)::atoi(value); + else if (::strcmp(key, "Columns") == 0) + m_hd44780Columns = (unsigned int)::atoi(value); + else if (::strcmp(key, "I2CAddress") == 0) + m_hd44780i2cAddress = (unsigned int)::strtoul(value, NULL, 16); + else if (::strcmp(key, "PWM") == 0) + m_hd44780PWM = ::atoi(value) == 1; + else if (::strcmp(key, "PWMPin") == 0) + m_hd44780PWMPin = (unsigned int)::atoi(value); + else if (::strcmp(key, "PWMBright") == 0) + m_hd44780PWMBright = (unsigned int)::atoi(value); + else if (::strcmp(key, "PWMDim") == 0) + m_hd44780PWMDim = (unsigned int)::atoi(value); + else if (::strcmp(key, "DisplayClock") == 0) + m_hd44780DisplayClock = ::atoi(value) == 1; + else if (::strcmp(key, "UTC") == 0) + m_hd44780UTC = ::atoi(value) == 1; + else if (::strcmp(key, "Pins") == 0) { + char* p = ::strtok(value, ",\r\n"); + while (p != NULL) { + unsigned int pin = (unsigned int)::atoi(p); + m_hd44780Pins.push_back(pin); + p = ::strtok(NULL, ",\r\n"); + } } + } else if (section == SECTION_NEXTION) { + if (::strcmp(key, "Port") == 0) + m_nextionPort = value; + else if (::strcmp(key, "Brightness") == 0) + m_nextionIdleBrightness = m_nextionBrightness = (unsigned int)::atoi(value); + else if (::strcmp(key, "DisplayClock") == 0) + m_nextionDisplayClock = ::atoi(value) == 1; + else if (::strcmp(key, "UTC") == 0) + m_nextionUTC = ::atoi(value) == 1; + else if (::strcmp(key, "IdleBrightness") == 0) + m_nextionIdleBrightness = (unsigned int)::atoi(value); + else if (::strcmp(key, "ScreenLayout") == 0) + m_nextionScreenLayout = (unsigned int)::strtoul(value, NULL, 0); + else if (::strcmp(key, "DisplayTempInFahrenheit") == 0) + m_nextionTempInFahrenheit = ::atoi(value) == 1; + } else if (section == SECTION_OLED) { + if (::strcmp(key, "Type") == 0) + m_oledType = (unsigned char)::atoi(value); + else if (::strcmp(key, "Brightness") == 0) + m_oledBrightness = (unsigned char)::atoi(value); + else if (::strcmp(key, "Invert") == 0) + m_oledInvert = ::atoi(value) == 1; + else if (::strcmp(key, "Scroll") == 0) + m_oledScroll = ::atoi(value) == 1; + else if (::strcmp(key, "Rotate") == 0) + m_oledRotate = ::atoi(value) == 1; + else if (::strcmp(key, "LogoScreensaver") == 0) + m_oledLogoScreensaver = ::atoi(value) == 1; + } else if (section == SECTION_LCDPROC) { + if (::strcmp(key, "Address") == 0) + m_lcdprocAddress = value; + else if (::strcmp(key, "Port") == 0) + m_lcdprocPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "LocalPort") == 0) + m_lcdprocLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "DisplayClock") == 0) + m_lcdprocDisplayClock = ::atoi(value) == 1; + else if (::strcmp(key, "UTC") == 0) + m_lcdprocUTC = ::atoi(value) == 1; + else if (::strcmp(key, "DimOnIdle") == 0) + m_lcdprocDimOnIdle = ::atoi(value) == 1; + } else if (section == SECTION_LOCK_FILE) { + if (::strcmp(key, "Enable") == 0) + m_lockFileEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "File") == 0) + m_lockFileName = value; + } else if (section == SECTION_REMOTE_CONTROL) { + if (::strcmp(key, "Enable") == 0) + m_remoteControlEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Port") == 0) + m_remoteControlPort = (unsigned int)::atoi(value); } - } else if (section == SECTION_NEXTION) { - if (::strcmp(key, "Port") == 0) - m_nextionPort = value; - else if (::strcmp(key, "Brightness") == 0) - m_nextionIdleBrightness = m_nextionBrightness = (unsigned int)::atoi(value); - else if (::strcmp(key, "DisplayClock") == 0) - m_nextionDisplayClock = ::atoi(value) == 1; - else if (::strcmp(key, "UTC") == 0) - m_nextionUTC = ::atoi(value) == 1; - else if (::strcmp(key, "IdleBrightness") == 0) - m_nextionIdleBrightness = (unsigned int)::atoi(value); - else if (::strcmp(key, "ScreenLayout") == 0) - m_nextionScreenLayout = (unsigned int)::strtoul(value, NULL, 0); - else if (::strcmp(key, "DisplayTempInFahrenheit") == 0) - m_nextionTempInFahrenheit = ::atoi(value) == 1; - } else if (section == SECTION_OLED) { - if (::strcmp(key, "Type") == 0) - m_oledType = (unsigned char)::atoi(value); - else if (::strcmp(key, "Brightness") == 0) - m_oledBrightness = (unsigned char)::atoi(value); - else if (::strcmp(key, "Invert") == 0) - m_oledInvert = ::atoi(value) == 1; - else if (::strcmp(key, "Scroll") == 0) - m_oledScroll = ::atoi(value) == 1; - else if (::strcmp(key, "Rotate") == 0) - m_oledRotate = ::atoi(value) == 1; - else if (::strcmp(key, "LogoScreensaver") == 0) - m_oledLogoScreensaver = ::atoi(value) == 1; - } else if (section == SECTION_LCDPROC) { - if (::strcmp(key, "Address") == 0) - m_lcdprocAddress = value; - else if (::strcmp(key, "Port") == 0) - m_lcdprocPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "LocalPort") == 0) - m_lcdprocLocalPort = (unsigned int)::atoi(value); - else if (::strcmp(key, "DisplayClock") == 0) - m_lcdprocDisplayClock = ::atoi(value) == 1; - else if (::strcmp(key, "UTC") == 0) - m_lcdprocUTC = ::atoi(value) == 1; - else if (::strcmp(key, "DimOnIdle") == 0) - m_lcdprocDimOnIdle = ::atoi(value) == 1; - } else if (section == SECTION_LOCK_FILE) { - if (::strcmp(key, "Enable") == 0) - m_lockFileEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "File") == 0) - m_lockFileName = value; - } else if (section == SECTION_REMOTE_CONTROL) { - if (::strcmp(key, "Enable") == 0) - m_remoteControlEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Address") == 0) - m_remoteControlAddress = value; - else if (::strcmp(key, "Port") == 0) - m_remoteControlPort = (unsigned int)::atoi(value); } - } - ::fclose(fp); + ::fclose(fp); - return true; + return true; } std::string CConf::getCallsign() const @@ -1133,6 +1242,11 @@ std::string CConf::getModemProtocol() const return m_modemProtocol; } +unsigned int CConf::getModemSpeed() const +{ + return m_modemSpeed; +} + unsigned int CConf::getModemAddress() const { return m_modemAddress; @@ -1223,6 +1337,11 @@ float CConf::getModemNXDNTXLevel() const return m_modemNXDNTXLevel; } +float CConf::getModemM17TXLevel() const +{ + return m_modemM17TXLevel; +} + float CConf::getModemPOCSAGTXLevel() const { return m_modemPOCSAGTXLevel; @@ -1233,6 +1352,11 @@ float CConf::getModemFMTXLevel() const return m_modemFMTXLevel; } +float CConf::getModemAX25TXLevel() const +{ + return m_modemAX25TXLevel; +} + std::string CConf::getModemRSSIMappingFile () const { return m_modemRSSIMappingFile; @@ -1278,16 +1402,6 @@ unsigned int CConf::getTransparentSendFrameType() const return m_transparentSendFrameType; } -bool CConf::getUMPEnabled() const -{ - return m_umpEnabled; -} - -std::string CConf::getUMPPort() const -{ - return m_umpPort; -} - bool CConf::getDStarEnabled() const { return m_dstarEnabled; @@ -1538,6 +1652,36 @@ unsigned int CConf::getNXDNModeHang() const return m_nxdnModeHang; } +bool CConf::getM17Enabled() const +{ + return m_m17Enabled; +} + +unsigned int CConf::getM17ColorCode() const +{ + return m_m17ColorCode; +} + +bool CConf::getM17SelfOnly() const +{ + return m_m17SelfOnly; +} + +bool CConf::getM17AllowEncryption() const +{ + return m_m17AllowEncryption; +} + +unsigned int CConf::getM17TXHang() const +{ + return m_m17TXHang; +} + +unsigned int CConf::getM17ModeHang() const +{ + return m_m17ModeHang; +} + bool CConf::getPOCSAGEnabled() const { return m_pocsagEnabled; @@ -1688,6 +1832,21 @@ bool CConf::getFMCOSInvert() const return m_fmCOSInvert; } +bool CConf::getFMNoiseSquelch() const +{ + return m_fmNoiseSquelch; +} + +unsigned int CConf::getFMSquelchHighThreshold() const +{ + return m_fmSquelchHighThreshold; +} + +unsigned int CConf::getFMSquelchLowThreshold() const +{ + return m_fmSquelchLowThreshold; +} + unsigned int CConf::getFMRFAudioBoost() const { return m_fmRFAudioBoost; @@ -1703,6 +1862,41 @@ unsigned int CConf::getFMExtAudioBoost() const return m_fmExtAudioBoost; } +unsigned int CConf::getFMModeHang() const +{ + return m_fmModeHang; +} + +bool CConf::getAX25Enabled() const +{ + return m_ax25Enabled; +} + +unsigned int CConf::getAX25TXDelay() const +{ + return m_ax25TXDelay; +} + +int CConf::getAX25RXTwist() const +{ + return m_ax25RXTwist; +} + +unsigned int CConf::getAX25SlotTime() const +{ + return m_ax25SlotTime; +} + +unsigned int CConf::getAX25PPersist() const +{ + return m_ax25PPersist; +} + +bool CConf::getAX25Trace() const +{ + return m_ax25Trace; +} + bool CConf::getDStarNetworkEnabled() const { return m_dstarNetworkEnabled; @@ -1898,6 +2092,36 @@ bool CConf::getNXDNNetworkDebug() const return m_nxdnNetworkDebug; } +bool CConf::getM17NetworkEnabled() const +{ + return m_m17NetworkEnabled; +} + +std::string CConf::getM17GatewayAddress() const +{ + return m_m17GatewayAddress; +} + +unsigned int CConf::getM17GatewayPort() const +{ + return m_m17GatewayPort; +} + +unsigned int CConf::getM17LocalPort() const +{ + return m_m17LocalPort; +} + +unsigned int CConf::getM17NetworkModeHang() const +{ + return m_m17NetworkModeHang; +} + +bool CConf::getM17NetworkDebug() const +{ + return m_m17NetworkDebug; +} + bool CConf::getPOCSAGNetworkEnabled() const { return m_pocsagNetworkEnabled; @@ -1933,6 +2157,66 @@ bool CConf::getPOCSAGNetworkDebug() const return m_pocsagNetworkDebug; } +bool CConf::getFMNetworkEnabled() const +{ + return m_fmNetworkEnabled; +} + +std::string CConf::getFMGatewayAddress() const +{ + return m_fmGatewayAddress; +} + +unsigned int CConf::getFMGatewayPort() const +{ + return m_fmGatewayPort; +} + +std::string CConf::getFMLocalAddress() const +{ + return m_fmLocalAddress; +} + +unsigned int CConf::getFMLocalPort() const +{ + return m_fmLocalPort; +} + +unsigned int CConf::getFMSampleRate() const +{ + return m_fmSampleRate; +} + +unsigned int CConf::getFMNetworkModeHang() const +{ + return m_fmNetworkModeHang; +} + +bool CConf::getFMNetworkDebug() const +{ + return m_fmNetworkDebug; +} + +bool CConf::getAX25NetworkEnabled() const +{ + return m_ax25NetworkEnabled; +} + +std::string CConf::getAX25NetworkPort() const +{ + return m_ax25NetworkPort; +} + +unsigned int CConf::getAX25NetworkSpeed() const +{ + return m_ax25NetworkSpeed; +} + +bool CConf::getAX25NetworkDebug() const +{ + return m_ax25NetworkDebug; +} + std::string CConf::getTFTSerialPort() const { return m_tftSerialPort; @@ -2053,7 +2337,6 @@ bool CConf::getOLEDLogoScreensaver() const return m_oledLogoScreensaver; } - std::string CConf::getLCDprocAddress() const { return m_lcdprocAddress; diff --git a/Conf.h b/Conf.h index da2f78e..92fa8ce 100644 --- a/Conf.h +++ b/Conf.h @@ -72,6 +72,7 @@ public: // The Modem section std::string getModemPort() const; std::string getModemProtocol() const; + unsigned int getModemSpeed() const; unsigned int getModemAddress() const; bool getModemRXInvert() const; bool getModemTXInvert() const; @@ -90,8 +91,10 @@ public: float getModemYSFTXLevel() const; float getModemP25TXLevel() const; float getModemNXDNTXLevel() const; + float getModemM17TXLevel() const; float getModemPOCSAGTXLevel() const; float getModemFMTXLevel() const; + float getModemAX25TXLevel() const; std::string getModemRSSIMappingFile() const; bool getModemUseCOSAsLockout() const; bool getModemTrace() const; @@ -104,10 +107,6 @@ public: unsigned int getTransparentLocalPort() const; unsigned int getTransparentSendFrameType() const; - // The UMP section - bool getUMPEnabled() const; - std::string getUMPPort() const; - // The D-Star section bool getDStarEnabled() const; std::string getDStarModule() const; @@ -168,10 +167,26 @@ public: unsigned int getNXDNTXHang() const; unsigned int getNXDNModeHang() const; + // The M17 section + bool getM17Enabled() const; + unsigned int getM17ColorCode() const; + bool getM17SelfOnly() const; + bool getM17AllowEncryption() const; + unsigned int getM17TXHang() const; + unsigned int getM17ModeHang() const; + // The POCSAG section bool getPOCSAGEnabled() const; unsigned int getPOCSAGFrequency() const; + // 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; + // The FM Section bool getFMEnabled() const; std::string getFMCallsign() const; @@ -201,9 +216,13 @@ public: unsigned int getFMHangTime() const; unsigned int getFMAccessMode() const; bool getFMCOSInvert() const; + bool getFMNoiseSquelch() const; + unsigned int getFMSquelchHighThreshold() const; + unsigned int getFMSquelchLowThreshold() const; unsigned int getFMRFAudioBoost() const; float getFMMaxDevLevel() const; unsigned int getFMExtAudioBoost() const; + unsigned int getFMModeHang() const; // The D-Star Network section bool getDStarNetworkEnabled() const; @@ -254,6 +273,14 @@ public: unsigned int getNXDNNetworkModeHang() const; bool getNXDNNetworkDebug() const; + // The M17 Network section + bool getM17NetworkEnabled() const; + std::string getM17GatewayAddress() const; + unsigned int getM17GatewayPort() const; + unsigned int getM17LocalPort() const; + unsigned int getM17NetworkModeHang() const; + bool getM17NetworkDebug() const; + // The POCSAG Network section bool getPOCSAGNetworkEnabled() const; std::string getPOCSAGGatewayAddress() const; @@ -263,6 +290,22 @@ public: unsigned int getPOCSAGNetworkModeHang() const; bool getPOCSAGNetworkDebug() const; + // The FM Network section + bool getFMNetworkEnabled() const; + std::string getFMGatewayAddress() const; + unsigned int getFMGatewayPort() const; + std::string getFMLocalAddress() const; + unsigned int getFMLocalPort() const; + unsigned int getFMSampleRate() const; + unsigned int getFMNetworkModeHang() const; + bool getFMNetworkDebug() const; + + // The AX.25 Network section + bool getAX25NetworkEnabled() const; + std::string getAX25NetworkPort() const; + unsigned int getAX25NetworkSpeed() const; + bool getAX25NetworkDebug() const; + // The TFTSERIAL section std::string getTFTSerialPort() const; unsigned int getTFTSerialBrightness() const; @@ -350,6 +393,7 @@ private: std::string m_modemPort; std::string m_modemProtocol; + unsigned int m_modemSpeed; unsigned int m_modemAddress; bool m_modemRXInvert; bool m_modemTXInvert; @@ -368,8 +412,10 @@ 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; @@ -381,9 +427,6 @@ private: unsigned int m_transparentLocalPort; unsigned int m_transparentSendFrameType; - bool m_umpEnabled; - std::string m_umpPort; - bool m_dstarEnabled; std::string m_dstarModule; bool m_dstarSelfOnly; @@ -439,6 +482,13 @@ private: unsigned int m_nxdnTXHang; unsigned int m_nxdnModeHang; + bool m_m17Enabled; + unsigned int m_m17ColorCode; + bool m_m17SelfOnly; + bool m_m17AllowEncryption; + unsigned int m_m17TXHang; + unsigned int m_m17ModeHang; + bool m_pocsagEnabled; unsigned int m_pocsagFrequency; @@ -470,9 +520,20 @@ private: unsigned int m_fmHangTime; unsigned int m_fmAccessMode; bool m_fmCOSInvert; + bool m_fmNoiseSquelch; + unsigned int m_fmSquelchHighThreshold; + unsigned int m_fmSquelchLowThreshold; unsigned int m_fmRFAudioBoost; float m_fmMaxDevLevel; unsigned int m_fmExtAudioBoost; + unsigned int m_fmModeHang; + + bool m_ax25Enabled; + unsigned int m_ax25TXDelay; + int m_ax25RXTwist; + unsigned int m_ax25SlotTime; + unsigned int m_ax25PPersist; + bool m_ax25Trace; bool m_dstarNetworkEnabled; std::string m_dstarGatewayAddress; @@ -518,6 +579,13 @@ private: unsigned int m_nxdnNetworkModeHang; bool m_nxdnNetworkDebug; + bool m_m17NetworkEnabled; + std::string m_m17GatewayAddress; + unsigned int m_m17GatewayPort; + unsigned int m_m17LocalPort; + unsigned int m_m17NetworkModeHang; + bool m_m17NetworkDebug; + bool m_pocsagNetworkEnabled; std::string m_pocsagGatewayAddress; unsigned int m_pocsagGatewayPort; @@ -526,6 +594,20 @@ private: unsigned int m_pocsagNetworkModeHang; bool m_pocsagNetworkDebug; + bool m_fmNetworkEnabled; + std::string m_fmGatewayAddress; + unsigned int m_fmGatewayPort; + std::string m_fmLocalAddress; + unsigned int m_fmLocalPort; + unsigned int m_fmSampleRate; + unsigned int m_fmNetworkModeHang; + bool m_fmNetworkDebug; + + bool m_ax25NetworkEnabled; + std::string m_ax25NetworkPort; + unsigned int m_ax25NetworkSpeed; + bool m_ax25NetworkDebug; + std::string m_tftSerialPort; unsigned int m_tftSerialBrightness; diff --git a/DMRControl.cpp b/DMRControl.cpp index 18ababd..a5efb21 100644 --- a/DMRControl.cpp +++ b/DMRControl.cpp @@ -21,7 +21,7 @@ #include #include -CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector& prefixes, const std::vector& blacklist, const std::vector& whitelist, const std::vector& slot1TGWhitelist, const std::vector& slot2TGWhitelist, unsigned int timeout, CModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter, DMR_OVCM_TYPES ovcm) : +CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector& prefixes, const std::vector& blacklist, const std::vector& whitelist, const std::vector& slot1TGWhitelist, const std::vector& slot2TGWhitelist, unsigned int timeout, IModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter, DMR_OVCM_TYPES ovcm) : m_colorCode(colorCode), m_modem(modem), m_network(network), diff --git a/DMRControl.h b/DMRControl.h index 438a9f3..31f5f7f 100644 --- a/DMRControl.h +++ b/DMRControl.h @@ -31,7 +31,7 @@ class CDMRControl { public: - CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector& prefixes, const std::vector& blacklist, const std::vector& whitelist, const std::vector& slot1TGWhitelist, const std::vector& slot2TGWhitelist, unsigned int timeout, CModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter, DMR_OVCM_TYPES ovcm); + CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector& prefixes, const std::vector& blacklist, const std::vector& whitelist, const std::vector& slot1TGWhitelist, const std::vector& slot2TGWhitelist, unsigned int timeout, IModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter, DMR_OVCM_TYPES ovcm); ~CDMRControl(); bool processWakeup(const unsigned char* data); @@ -50,7 +50,7 @@ public: private: unsigned int m_colorCode; - CModem* m_modem; + IModem* m_modem; IDMRNetwork* m_network; CDMRSlot m_slot1; CDMRSlot m_slot2; diff --git a/DMRSlot.cpp b/DMRSlot.cpp index c21f4ca..4209860 100644 --- a/DMRSlot.cpp +++ b/DMRSlot.cpp @@ -37,7 +37,7 @@ unsigned int CDMRSlot::m_colorCode = 0U; bool CDMRSlot::m_embeddedLCOnly = false; bool CDMRSlot::m_dumpTAData = true; -CModem* CDMRSlot::m_modem = NULL; +IModem* CDMRSlot::m_modem = NULL; IDMRNetwork* CDMRSlot::m_network = NULL; CDisplay* CDMRSlot::m_display = NULL; bool CDMRSlot::m_duplex = true; @@ -1896,7 +1896,7 @@ void CDMRSlot::writeQueueNet(const unsigned char *data) m_queue.addData(data, len); } -void CDMRSlot::init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter, DMR_OVCM_TYPES ovcm) +void CDMRSlot::init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, IModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter, DMR_OVCM_TYPES ovcm) { assert(modem != NULL); assert(display != NULL); diff --git a/DMRSlot.h b/DMRSlot.h index 7a4c804..789e6ce 100644 --- a/DMRSlot.h +++ b/DMRSlot.h @@ -62,7 +62,7 @@ public: void enable(bool enabled); - static void init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter, DMR_OVCM_TYPES ovcm); + static void init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, IModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter, DMR_OVCM_TYPES ovcm); private: unsigned int m_slotNo; @@ -117,7 +117,7 @@ private: static bool m_embeddedLCOnly; static bool m_dumpTAData; - static CModem* m_modem; + static IModem* m_modem; static IDMRNetwork* m_network; static CDisplay* m_display; static bool m_duplex; diff --git a/DStarControl.cpp b/DStarControl.cpp index 794b748..8eafe7a 100644 --- a/DStarControl.cpp +++ b/DStarControl.cpp @@ -101,8 +101,8 @@ m_netSkipDTMFBlankingFrames(0U) m_gateway = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH]; m_lastFrame = new unsigned char[DSTAR_FRAME_LENGTH_BYTES + 1U]; - m_rfVoiceSyncData = new unsigned char[MODEM_DATA_LEN]; - m_netVoiceSyncData = new unsigned char[MODEM_DATA_LEN]; + m_rfVoiceSyncData = new unsigned char[DSTAR_MODEM_DATA_LEN]; + m_netVoiceSyncData = new unsigned char[DSTAR_MODEM_DATA_LEN]; std::string call = callsign; call.resize(DSTAR_LONG_CALLSIGN_LENGTH - 1U, ' '); @@ -149,7 +149,7 @@ unsigned int CDStarControl::maybeFixupVoiceFrame( unsigned char mini_header_type = mini_header & DSTAR_SLOW_DATA_TYPE_MASK; if (n == 0U) { - ::memcpy(voice_sync_data, data, MODEM_DATA_LEN); + ::memcpy(voice_sync_data, data, DSTAR_MODEM_DATA_LEN); *voice_sync_data_len = len; } else if ((n % 2U != 0U) && ((mini_header_type == DSTAR_SLOW_DATA_TYPE_FASTDATA01) || diff --git a/Defines.h b/Defines.h index ebd75eb..511ee8b 100644 --- a/Defines.h +++ b/Defines.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018,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 @@ -26,6 +26,7 @@ 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; @@ -39,7 +40,7 @@ const unsigned char TAG_DATA = 0x01U; const unsigned char TAG_LOST = 0x02U; const unsigned char TAG_EOT = 0x03U; -const unsigned int MODEM_DATA_LEN = 220U; +const unsigned int DSTAR_MODEM_DATA_LEN = 220U; enum HW_TYPE { HWT_MMDVM, diff --git a/Display.cpp b/Display.cpp index 39b0a09..7d4b1a9 100644 --- a/Display.cpp +++ b/Display.cpp @@ -21,14 +21,12 @@ #include "SerialController.h" #include "ModemSerialPort.h" #include "NullDisplay.h" -#include "TFTSerial.h" #include "TFTSurenoo.h" #include "LCDproc.h" #include "Nextion.h" #include "CASTInfo.h" #include "Conf.h" #include "Modem.h" -#include "UMP.h" #include "Log.h" #if defined(HD44780) @@ -335,6 +333,40 @@ void CDisplay::clearNXDN() } } +void CDisplay::writeM17(const char* source, const char* dest, const char* type) +{ + assert(source != NULL); + assert(dest != NULL); + assert(type != NULL); + + m_timer1.start(); + m_mode1 = MODE_IDLE; + + writeM17Int(source, dest, type); +} + +void CDisplay::writeM17RSSI(unsigned char rssi) +{ + if (rssi != 0U) + writeM17RSSIInt(rssi); +} + +void CDisplay::writeM17BER(float ber) +{ + writeM17BERInt(ber); +} + +void CDisplay::clearM17() +{ + if (m_timer1.hasExpired()) { + clearM17Int(); + m_timer1.stop(); + m_mode1 = MODE_IDLE; + } else { + m_mode1 = MODE_M17; + } +} + void CDisplay::writePOCSAG(uint32_t ric, const std::string& message) { m_timer1.start(); @@ -392,6 +424,11 @@ void CDisplay::clock(unsigned int ms) m_mode1 = MODE_IDLE; m_timer1.stop(); break; + case MODE_M17: + clearM17Int(); + m_mode1 = MODE_IDLE; + m_timer1.stop(); + break; case MODE_POCSAG: clearPOCSAGInt(); m_mode1 = MODE_IDLE; @@ -482,6 +519,14 @@ void CDisplay::writeNXDNBERInt(float ber) { } +void CDisplay::writeM17RSSIInt(unsigned char rssi) +{ +} + +void CDisplay::writeM17BERInt(float ber) +{ +} + int CDisplay::writeNXDNIntEx(const class CUserDBentry& source, bool group, unsigned int dest, const char* type) { /* return value definition is same as writeDMRIntEx() */ @@ -490,17 +535,17 @@ int CDisplay::writeNXDNIntEx(const class CUserDBentry& source, bool group, unsig /* Factory method extracted from MMDVMHost.cpp - BG5HHP */ -CDisplay* CDisplay::createDisplay(const CConf& conf, CUMP* ump, CModem* modem) +CDisplay* CDisplay::createDisplay(const CConf& conf, IModem* modem) { - CDisplay *display = NULL; + CDisplay *display = NULL; - std::string type = conf.getDisplay(); + std::string type = conf.getDisplay(); unsigned int dmrid = conf.getDMRId(); LogInfo("Display Parameters"); LogInfo(" Type: %s", type.c_str()); - if (type == "TFT Serial" || type == "TFT Surenoo") { + if (type == "TFT Surenoo") { std::string port = conf.getTFTSerialPort(); unsigned int brightness = conf.getTFTSerialBrightness(); @@ -509,14 +554,11 @@ CDisplay* CDisplay::createDisplay(const CConf& conf, CUMP* ump, CModem* modem) ISerialPort* serial = NULL; if (port == "modem") - serial = new CModemSerialPort(modem); + serial = new IModemSerialPort(modem); else - serial = new CSerialController(port, (type == "TFT Serial") ? SERIAL_9600 : SERIAL_115200); + serial = new CSerialController(port, 115200U); - if (type == "TFT Surenoo") - display = new CTFTSurenoo(conf.getCallsign(), dmrid, serial, brightness, conf.getDuplex()); - else - display = new CTFTSerial(conf.getCallsign(), dmrid, serial, brightness); + display = new CTFTSurenoo(conf.getCallsign(), dmrid, serial, brightness, conf.getDuplex()); } else if (type == "Nextion") { std::string port = conf.getNextionPort(); unsigned int brightness = conf.getNextionBrightness(); @@ -555,21 +597,14 @@ CDisplay* CDisplay::createDisplay(const CConf& conf, CUMP* ump, CModem* modem) } if (port == "modem") { - ISerialPort* serial = new CModemSerialPort(modem); + ISerialPort* serial = new IModemSerialPort(modem); display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout, txFrequency, rxFrequency, displayTempInF); - } else if (port == "ump") { - if (ump != NULL) { - display = new CNextion(conf.getCallsign(), dmrid, ump, brightness, displayClock, utc, idleBrightness, screenLayout, txFrequency, rxFrequency, displayTempInF); - } else { - LogInfo(" NullDisplay loaded"); - display = new CNullDisplay; - } } else { - SERIAL_SPEED baudrate = SERIAL_9600; - if (screenLayout&0x0cU) - baudrate = SERIAL_115200; + unsigned int baudrate = 9600U; + if (screenLayout == 4U) + baudrate = 115200U; - LogInfo(" Display baudrate: %u ",baudrate); + LogInfo(" Display baudrate: %u ", baudrate); ISerialPort* serial = new CSerialController(port, baudrate); display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout, txFrequency, rxFrequency, displayTempInF); } diff --git a/Display.h b/Display.h index e23b3ec..c5e7d72 100644 --- a/Display.h +++ b/Display.h @@ -21,14 +21,14 @@ #include "Timer.h" #include "UserDBentry.h" +#include "Modem.h" #include #include class CConf; -class CModem; -class CUMP; +class IModem; class CDisplay { @@ -72,6 +72,11 @@ public: void writeNXDNBER(float ber); void clearNXDN(); + void writeM17(const char* source, const char* dest, const char* type); + void writeM17RSSI(unsigned char rssi); + void writeM17BER(float ber); + void clearM17(); + void writePOCSAG(uint32_t ric, const std::string& message); void clearPOCSAG(); @@ -81,7 +86,7 @@ public: void clock(unsigned int ms); - static CDisplay* createDisplay(const CConf& conf, CUMP* ump, CModem* modem); + static CDisplay* createDisplay(const CConf& conf, IModem* modem); protected: virtual void setIdleInt() = 0; @@ -118,6 +123,11 @@ protected: virtual void writeNXDNBERInt(float ber); virtual void clearNXDNInt() = 0; + virtual void writeM17Int(const char* source, const char* dest, const char* type) = 0; + virtual void writeM17RSSIInt(unsigned char rssi); + virtual void writeM17BERInt(float ber); + virtual void clearM17Int() = 0; + virtual void writePOCSAGInt(uint32_t ric, const std::string& message) = 0; virtual void clearPOCSAGInt() = 0; diff --git a/FMControl.cpp b/FMControl.cpp new file mode 100644 index 0000000..9a2a78a --- /dev/null +++ b/FMControl.cpp @@ -0,0 +1,179 @@ +/* + * 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. + */ + +#include "FMControl.h" + +#include + +#if defined(DUMP_RF_AUDIO) +#include +#endif + +#define SWAP_BYTES_16(a) (((a >> 8) & 0x00FFU) | ((a << 8) & 0xFF00U)) + +const float DEEMPHASIS_GAIN_DB = 0.0F; +const float PREEMPHASIS_GAIN_DB = 13.0F; +const float FILTER_GAIN_DB = 0.0F; +const unsigned int FM_MASK = 0x00000FFFU; + +CFMControl::CFMControl(CFMNetwork* network) : +m_network(network), +m_enabled(false), +m_incomingRFAudio(1600U, "Incoming RF FM Audio"), +m_preemphasis (NULL), +m_deemphasis (NULL), +m_filterStage1(NULL), +m_filterStage2(NULL), +m_filterStage3(NULL) +{ + m_preemphasis = new CIIRDirectForm1Filter(8.315375384336983F,-7.03334621603483F,0.0F,1.0F,0.282029168302153F,0.0F, PREEMPHASIS_GAIN_DB); + m_deemphasis = new CIIRDirectForm1Filter(0.07708787090460224F,0.07708787090460224F,0.0F,1.0F,-0.8458242581907955F,0.0F, DEEMPHASIS_GAIN_DB); + + //cheby type 1 0.2dB cheby type 1 3rd order 300-2700Hz fs=8000 + m_filterStage1 = new CIIRDirectForm1Filter(0.29495028f, 0.0f, -0.29495028f, 1.0f, -0.61384624f, -0.057158668f, FILTER_GAIN_DB); + m_filterStage2 = new CIIRDirectForm1Filter(1.0f, 2.0f, 1.0f, 1.0f, 0.9946123f, 0.6050482f, FILTER_GAIN_DB); + m_filterStage3 = new CIIRDirectForm1Filter(1.0f, -2.0f, 1.0f, 1.0f, -1.8414584f, 0.8804949f, FILTER_GAIN_DB); +} + +CFMControl::~CFMControl() +{ + delete m_preemphasis ; + delete m_deemphasis ; + + delete m_filterStage1; + delete m_filterStage2; + delete m_filterStage3; +} + +bool CFMControl::writeModem(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (m_network == NULL) + return true; + + if (data[0U] == TAG_HEADER) + return true; + + if (data[0U] == TAG_EOT) + return m_network->writeEOT(); + + if (data[0U] != TAG_DATA) + return false; + + m_incomingRFAudio.addData(data + 1U, length - 1U); + unsigned int bufferLength = m_incomingRFAudio.dataSize(); + if (bufferLength > 252U)//168 samples 12-bit + bufferLength = 252U; + + if (bufferLength >= 3U) { + bufferLength = bufferLength - bufferLength % 3U; //round down to nearest multiple of 3 + unsigned char bufferData[252U]; + m_incomingRFAudio.getData(bufferData, bufferLength); + + unsigned int pack = 0U; + unsigned char* packPointer = (unsigned char*)&pack; + float out[168U]; + unsigned int nOut = 0U; + short unpackedSamples[2U]; + + for (unsigned int i = 0U; i < bufferLength; i += 3U) { + //extract unsigned 12 bit unsigned sample pairs packed into 3 bytes to 16 bit signed + packPointer[0U] = bufferData[i]; + packPointer[1U] = bufferData[i + 1U]; + packPointer[2U] = bufferData[i + 2U]; + unpackedSamples[1U] = short(int(pack & FM_MASK) - 2048); + unpackedSamples[0U] = short(int(pack >> 12) - 2048); + + //process unpacked sample pair + for (unsigned char j = 0U; j < 2U; j++) { + // Convert to float (-1.0 to +1.0) + float sampleFloat = float(unpackedSamples[j]) / 2048.0F; + + // De-emphasise and remove CTCSS + sampleFloat = m_deemphasis->filter(sampleFloat); + out[nOut++] = m_filterStage3->filter(m_filterStage2->filter(m_filterStage1->filter(sampleFloat))); + } + } + +#if defined(DUMP_RF_AUDIO) + FILE * audiofile = fopen("./audiodump.bin", "ab"); + if(audiofile != NULL) { + fwrite(out, sizeof(float), nOut, audiofile); + fclose(audiofile); + } +#endif + return m_network->writeData(out, nOut); + } + + return true; +} + +unsigned int CFMControl::readModem(unsigned char* data, unsigned int space) +{ + assert(data != NULL); + assert(space > 0U); + + if (m_network == NULL) + return 0U; + + if (space > 252U) + space = 252U; + + float netData[168U]; // Modem can handle up to 168 samples at a time + unsigned int length = m_network->read(netData, 168U); + if (length == 0U) + return 0U; + + unsigned int pack = 0U; + unsigned char* packPointer = (unsigned char*)&pack; + unsigned int nData = 0U; + + for (unsigned int i = 0; i < length; i++) { + // Pre-emphasis + float sampleFloat = m_preemphasis->filter(netData[i]); + + // Convert float to 12-bit samples (0 to 4095) + unsigned int sample12bit = (unsigned int)((sampleFloat + 1.0F) * 2048.0F + 0.5F); + + // pack 2 samples onto 3 bytes + if((i & 1U) == 0) { + pack = 0U; + pack = sample12bit << 12; + } else { + pack |= sample12bit; + + data[nData++] = packPointer[0U]; + data[nData++] = packPointer[1U]; + data[nData++] = packPointer[2U]; + } + } + + return nData; +} + +void CFMControl::clock(unsigned int ms) +{ + // May not be needed +} + +void CFMControl::enable(bool enabled) +{ + // May not be needed +} diff --git a/FMControl.h b/FMControl.h new file mode 100644 index 0000000..ac2142e --- /dev/null +++ b/FMControl.h @@ -0,0 +1,55 @@ +/* + * 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(FMControl_H) +#define FMControl_H + +#include "FMNetwork.h" +#include "Defines.h" +#include "IIRDirectForm1Filter.h" + +// Uncomment this to dump audio to a raw audio file +// The file will be written in same folder as executable +// Toplay the file : ffplay -autoexit -f u16be -ar 8000 audiodump.bin +// #define DUMP_RF_AUDIO + +class CFMControl { +public: + CFMControl(CFMNetwork* network); + ~CFMControl(); + + bool writeModem(const unsigned char* data, unsigned int length); + + unsigned int readModem(unsigned char* data, unsigned int space); + + void clock(unsigned int ms); + + void enable(bool enabled); + +private: + CFMNetwork* m_network; + bool m_enabled; + CRingBuffer m_incomingRFAudio; + CIIRDirectForm1Filter* m_preemphasis; + CIIRDirectForm1Filter* m_deemphasis; + CIIRDirectForm1Filter* m_filterStage1; + CIIRDirectForm1Filter* m_filterStage2; + CIIRDirectForm1Filter* m_filterStage3; +}; + +#endif diff --git a/FMNetwork.cpp b/FMNetwork.cpp new file mode 100644 index 0000000..bf76ccb --- /dev/null +++ b/FMNetwork.cpp @@ -0,0 +1,289 @@ +/* + * 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. + */ + +#include "FMNetwork.h" +#include "Defines.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 500U; + +CFMNetwork::CFMNetwork(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, unsigned int sampleRate, bool debug) : +m_socket(localAddress, localPort), +m_addr(), +m_addrLen(0U), +m_sampleRate(sampleRate), +m_debug(debug), +m_enabled(false), +m_buffer(2000U, "FM Network"), +m_pollTimer(1000U, 5U) +{ + assert(gatewayPort > 0U); + assert(!gatewayAddress.empty()); + assert(sampleRate > 0U); + + if (CUDPSocket::lookup(gatewayAddress, gatewayPort, m_addr, m_addrLen) != 0) + m_addrLen = 0U; + +#if !defined(_WIN32) && !defined(_WIN64) + int error; + m_incoming = ::src_new(SRC_SINC_FASTEST, 1, &error); + m_outgoing = ::src_new(SRC_SINC_FASTEST, 1, &error); + + assert(m_incoming != NULL); + assert(m_outgoing != NULL); +#endif +} + +CFMNetwork::~CFMNetwork() +{ +#if !defined(_WIN32) && !defined(_WIN64) + ::src_delete(m_incoming); + ::src_delete(m_outgoing); +#endif +} + +bool CFMNetwork::open() +{ + if (m_addrLen == 0U) { + LogError("Unable to resolve the address of the FM Gateway"); + return false; + } + + LogMessage("Opening FM network connection"); + + m_pollTimer.start(); + + return m_socket.open(m_addr); +} + +bool CFMNetwork::writeData(float* data, unsigned int nSamples) +{ + assert(data != NULL); + assert(nSamples > 0U); + +#if !defined(_WIN32) && !defined(_WIN64) + assert(m_outgoing != NULL); + + float out[1000U]; + SRC_DATA src; + + if (m_sampleRate != 8000U) { + src.data_in = data; + src.data_out = out; + src.input_frames = nSamples; + src.output_frames = 1000; + src.end_of_input = 0; + src.src_ratio = double(m_sampleRate) / 8000.0; + + int ret = ::src_process(m_outgoing, &src); + if (ret != 0) { + LogError("Error up/downsampling of the output audio has an error - %s", src_strerror(ret)); + return false; + } + } else { + src.data_out = data; + src.output_frames_gen = nSamples; + } +#endif + + unsigned int length = 3U; + + unsigned char buffer[1500U]; + ::memset(buffer, 0x00U, 1500U); + + buffer[0U] = 'F'; + buffer[1U] = 'M'; + buffer[2U] = 'D'; + +#if defined(_WIN32) || defined(_WIN64) + for (long i = 0L; i < nSamples; i++) { + unsigned short val = (unsigned short)((data[i] + 1.0F) * 32767.0F + 0.5F); +#else + for (long i = 0L; i < src.output_frames_gen; i++) { + unsigned short val = (unsigned short)((src.data_out[i] + 1.0F) * 32767.0F + 0.5F); +#endif + + buffer[length++] = (val >> 8) & 0xFFU; + buffer[length++] = (val >> 0) & 0xFFU; + } + + if (m_debug) + CUtils::dump(1U, "FM Network Data Sent", buffer, length); + + return m_socket.write(buffer, length, m_addr, m_addrLen); +} + +bool CFMNetwork::writeEOT() +{ + unsigned char buffer[10U]; + ::memset(buffer, 0x00U, 10U); + + buffer[0U] = 'F'; + buffer[1U] = 'M'; + buffer[2U] = 'E'; + + if (m_debug) + CUtils::dump(1U, "FM Network End of Transmission Sent", buffer, 3U); + + return m_socket.write(buffer, 3U, m_addr, m_addrLen); +} + +void CFMNetwork::clock(unsigned int ms) +{ + m_pollTimer.clock(ms); + if (m_pollTimer.hasExpired()) { + writePoll(); + m_pollTimer.start(); + } + + unsigned char buffer[BUFFER_LENGTH]; + + sockaddr_storage addr; + unsigned int addrlen; + int length = m_socket.read(buffer, BUFFER_LENGTH, addr, addrlen); + if (length <= 0) + return; + + // Check if the data is for us + if (!CUDPSocket::match(addr, m_addr)) { + LogMessage("FM packet received from an invalid source"); + return; + } + + // Ignore incoming polls + if (::memcmp(buffer, "FMP", 3U) == 0) + return; + + // Invalid packet type? + if (::memcmp(buffer, "FMD", 3U) != 0) + return; + + if (!m_enabled) + return; + + if (m_debug) + CUtils::dump(1U, "FM Network Data Received", buffer, length); + + m_buffer.addData(buffer + 3U, length - 3U); +} + +unsigned int CFMNetwork::read(float* data, unsigned int nSamples) +{ + assert(data != NULL); + assert(nSamples > 0U); + + unsigned int bytes = m_buffer.dataSize() / sizeof(unsigned short); + if (bytes == 0U) + return 0U; + + if (bytes < nSamples) + nSamples = bytes; + + unsigned char buffer[1500U]; + m_buffer.getData(buffer, nSamples * sizeof(unsigned short)); + +#if !defined(_WIN32) && !defined(_WIN64) + assert(m_incoming != NULL); + + SRC_DATA src; + + if (m_sampleRate != 8000U) { + float in[750U]; + + for (unsigned int i = 0U; i < nSamples; i++) { + unsigned short val = ((buffer[i * 2U + 0U] & 0xFFU) << 8) + + ((buffer[i * 2U + 1U] & 0xFFU) << 0); + in[i] = (float(val) - 32768.0F) / 32768.0F; + } + + src.data_in = in; + src.data_out = data; + src.input_frames = nSamples; + src.output_frames = 750; + src.end_of_input = 0; + src.src_ratio = 8000.0 / double(m_sampleRate); + + int ret = ::src_process(m_incoming, &src); + if (ret != 0) { + LogError("Error up/downsampling of the input audio has an error - %s", src_strerror(ret)); + return 0U; + } + + return src.output_frames_gen; + } else { +#endif + for (unsigned int i = 0U; i < nSamples; i++) { + unsigned short val = ((buffer[i * 2U + 0U] & 0xFFU) << 8) + + ((buffer[i * 2U + 1U] & 0xFFU) << 0); + data[i] = (float(val) - 32768.0F) / 32768.0F; + } + + return nSamples; +#if !defined(_WIN32) && !defined(_WIN64) + } +#endif +} + +void CFMNetwork::reset() +{ +#if !defined(_WIN32) && !defined(_WIN64) + assert(m_incoming != NULL); + assert(m_outgoing != NULL); + + ::src_reset(m_incoming); + ::src_reset(m_outgoing); +#endif + + m_buffer.clear(); +} + +void CFMNetwork::close() +{ + m_socket.close(); + + LogMessage("Closing FM network connection"); +} + +void CFMNetwork::enable(bool enabled) +{ + if (enabled && !m_enabled) + reset(); + else if (!enabled && m_enabled) + reset(); + + m_enabled = enabled; +} + +bool CFMNetwork::writePoll() +{ + unsigned char buffer[3U]; + + buffer[0U] = 'F'; + buffer[1U] = 'M'; + buffer[2U] = 'P'; + + if (m_debug) + CUtils::dump(1U, "FM Network Poll Sent", buffer, 3U); + + return m_socket.write(buffer, 3U, m_addr, m_addrLen); +} diff --git a/FMNetwork.h b/FMNetwork.h new file mode 100644 index 0000000..aa99751 --- /dev/null +++ b/FMNetwork.h @@ -0,0 +1,71 @@ +/* + * 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. + */ + +#ifndef FMNetwork_H +#define FMNetwork_H + +#include "RingBuffer.h" +#include "UDPSocket.h" +#include "Timer.h" + +#if !defined(_WIN32) && !defined(_WIN64) +#include +#endif + +#include +#include + +class CFMNetwork { +public: + CFMNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, unsigned int sampleRate, bool debug); + ~CFMNetwork(); + + bool open(); + + void enable(bool enabled); + + bool writeData(float* data, unsigned int nSamples); + + bool writeEOT(); + + unsigned int read(float* data, unsigned int nSamples); + + void reset(); + + void close(); + + void clock(unsigned int ms); + +private: + CUDPSocket m_socket; + sockaddr_storage m_addr; + unsigned int m_addrLen; + unsigned int m_sampleRate; + bool m_debug; + bool m_enabled; + CRingBuffer m_buffer; + CTimer m_pollTimer; +#if !defined(_WIN32) && !defined(_WIN64) + SRC_STATE* m_incoming; + SRC_STATE* m_outgoing; +#endif + + bool writePoll(); +}; + +#endif diff --git a/I2CController.cpp b/I2CController.cpp index 247129c..9886443 100644 --- a/I2CController.cpp +++ b/I2CController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2004,2007-2011,2013,2014-2017 by Jonathan Naylor G4KLX + * Copyright (C) 2002-2004,2007-2011,2013,2014-2017,2020 by Jonathan Naylor G4KLX * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX * * This program is free software; you can redistribute it and/or modify @@ -17,6 +17,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#if defined(__linux__) + #include "I2CController.h" #include "Log.h" @@ -24,52 +26,18 @@ #include #include - -#if defined(_WIN32) || defined(_WIN64) - -#include -#include - -CI2CController::CI2CController(const std::string& device, SERIAL_SPEED speed, unsigned int address, bool assertRTS) : -CSerialController(device, speed, assertRTS), -m_address(address) -{ -} - -CI2CController::~CI2CController() -{ -} - -bool CI2CController::open() -{ - return CSerialController::open(); -} - -int CI2CController::read(unsigned char* buffer, unsigned int length) -{ - return CSerialController::read(buffer, length); -} - -int CI2CController::write(const unsigned char* buffer, unsigned int length) -{ - return CSerialController::write(buffer, length); -} - -#else - #include #include #include #include #include #include -#if defined(__linux__) #include -#endif -CI2CController::CI2CController(const std::string& device, SERIAL_SPEED speed, unsigned int address, bool assertRTS) : -CSerialController(device, speed, assertRTS), -m_address(address) +CI2CController::CI2CController(const std::string& device, unsigned int address) : +m_device(device), +m_address(address), +m_fd(-1) { } @@ -81,7 +49,6 @@ bool CI2CController::open() { assert(m_fd == -1); -#if defined(__linux__) m_fd = ::open(m_device.c_str(), O_RDWR); if (m_fd < 0) { LogError("Cannot open device - %s", m_device.c_str()); @@ -89,19 +56,16 @@ bool CI2CController::open() } if (::ioctl(m_fd, I2C_TENBIT, 0) < 0) { - LogError("CI2C: failed to set 7bitaddress"); + LogError("I2C: failed to set 7bitaddress"); ::close(m_fd); return false; } if (::ioctl(m_fd, I2C_SLAVE, m_address) < 0) { - LogError("CI2C: Failed to acquire bus access/talk to slave 0x%02X", m_address); + LogError("I2C: Failed to acquire bus access/talk to slave 0x%02X", m_address); ::close(m_fd); return false; } -#else - #warning "I2C controller supports Linux only" -#endif return true; } @@ -117,7 +81,6 @@ int CI2CController::read(unsigned char* buffer, unsigned int length) unsigned int offset = 0U; while (offset < length) { -#if defined(__linux__) ssize_t n = ::read(m_fd, buffer + offset, 1U); if (n < 0) { if (errno != EAGAIN) { @@ -128,7 +91,6 @@ int CI2CController::read(unsigned char* buffer, unsigned int length) if (n > 0) offset += n; -#endif } return length; @@ -144,10 +106,7 @@ int CI2CController::write(const unsigned char* buffer, unsigned int length) unsigned int ptr = 0U; while (ptr < length) { - ssize_t n = 0U; -#if defined(__linux__) - n = ::write(m_fd, buffer + ptr, 1U); -#endif + ssize_t n = ::write(m_fd, buffer + ptr, 1U); if (n < 0) { if (errno != EAGAIN) { LogError("Error returned from write(), errno=%d", errno); @@ -162,4 +121,12 @@ int CI2CController::write(const unsigned char* buffer, unsigned int length) return length; } +void CI2CController::close() +{ + assert(m_fd != -1); + + ::close(m_fd); + m_fd = -1; +} + #endif diff --git a/I2CController.h b/I2CController.h index 6e59672..281c587 100644 --- a/I2CController.h +++ b/I2CController.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2004,2007-2009,2011-2013,2015-2017 by Jonathan Naylor G4KLX + * Copyright (C) 2002-2004,2007-2009,2011-2013,2015-2017,2020 by Jonathan Naylor G4KLX * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX * * This program is free software; you can redistribute it and/or modify @@ -20,11 +20,15 @@ #ifndef I2CController_H #define I2CController_H -#include "SerialController.h" +#if defined(__linux__) -class CI2CController : public CSerialController { +#include "SerialPort.h" + +#include + +class CI2CController : public ISerialPort { public: - CI2CController(const std::string& device, SERIAL_SPEED speed, unsigned int address = 0x22U, bool assertRTS = false); + CI2CController(const std::string& device, unsigned int address = 0x22U); virtual ~CI2CController(); virtual bool open(); @@ -33,8 +37,14 @@ public: virtual int write(const unsigned char* buffer, unsigned int length); + virtual void close(); + private: + std::string m_device; unsigned int m_address; + int m_fd; }; #endif + +#endif diff --git a/IIRDirectForm1Filter.cpp b/IIRDirectForm1Filter.cpp new file mode 100644 index 0000000..946acdd --- /dev/null +++ b/IIRDirectForm1Filter.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2015-2020 by Jonathan Naylor G4KLX + * Copyright (C) 2020 by Geoffrey Merck - F4FXL KC3FRA + * + * 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 "IIRDirectForm1Filter.h" +#include "math.h" + +CIIRDirectForm1Filter::CIIRDirectForm1Filter(float b0, float b1, float b2, float , float a1, float a2, float addtionalGaindB) : +m_x2(0.0F), +m_y2(0.0F), +m_x1(0.0F), +m_y1(0.0F), +m_b0(b0), +m_b1(b1), +m_b2(b2), +m_a1(a1), +m_a2(a2), +m_additionalGainLin(0.0F) +{ + m_additionalGainLin = ::powf(10.0F, addtionalGaindB / 20.0F); +} + +float CIIRDirectForm1Filter::filter(float sample) +{ + float output = m_b0 * sample + + m_b1 * m_x1 + + m_b2 * m_x2 + - m_a1 * m_y1 + - m_a2 * m_y2; + + m_x2 = m_x1; + m_y2 = m_y1; + m_x1 = sample; + m_y1 = output; + + return output * m_additionalGainLin; +} + +void CIIRDirectForm1Filter::reset() +{ + m_x1 = 0.0f; + m_x2 = 0.0f; + m_y1 = 0.0f; + m_y2 = 0.0f; +} diff --git a/IIRDirectForm1Filter.h b/IIRDirectForm1Filter.h new file mode 100644 index 0000000..f575f7f --- /dev/null +++ b/IIRDirectForm1Filter.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015-2020 by Jonathan Naylor G4KLX + * Copyright (C) 2020 by Geoffrey Merck - F4FXL KC3FRA + * + * 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(IIRDIRECTFORM1FILTER_H) +#define IIRDIRECTFORM1FILTER_H + +class CIIRDirectForm1Filter +{ +public: + CIIRDirectForm1Filter(float b0, float b1, float b2, float, float a1, float a2, float additionalGaindB); + float filter(float sample); + void reset(); + +private: +// delay line + float m_x2; // x[n-2] + float m_y2; // y[n-2] + float m_x1; // x[n-1] + float m_y1; // y[n-1] + + // coefficients + // FIR + float m_b0; + float m_b1; + float m_b2; + // IIR + float m_a1; + float m_a2; + + float m_additionalGainLin; +}; + + +#endif \ No newline at end of file diff --git a/ISSUES.txt b/ISSUES.txt deleted file mode 100644 index 537f79f..0000000 --- a/ISSUES.txt +++ /dev/null @@ -1,9 +0,0 @@ -D-Star: On some radios, the header is not decoded correctly. It looks like frequency drift at the beginning of the transmission. - -DMR: DMO mode doesn't wake up older radios like other radios do. - -YSF: No known issues. - -P25: Upgrade the filters, processing power in the Due permiting. - -NXDN: No known issues. diff --git a/Images/M17.bmp b/Images/M17.bmp new file mode 100644 index 0000000..8d88e8e Binary files /dev/null and b/Images/M17.bmp differ diff --git a/LCDproc.cpp b/LCDproc.cpp index 19ac15e..0ef66eb 100644 --- a/LCDproc.cpp +++ b/LCDproc.cpp @@ -95,6 +95,7 @@ const unsigned int DMR_RSSI_COUNT = 4U; // 4 * 360ms = 1440ms const unsigned int YSF_RSSI_COUNT = 13U; // 13 * 100ms = 1300ms const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms const unsigned int NXDN_RSSI_COUNT = 28U; // 28 * 40ms = 1120ms +const unsigned int M17_RSSI_COUNT = 28U; // 28 * 40ms = 1120ms CLCDproc::CLCDproc(std::string address, unsigned int port, unsigned int localPort, const std::string& callsign, unsigned int dmrid, bool displayClock, bool utc, bool duplex, bool dimOnIdle) : CDisplay(), @@ -188,6 +189,7 @@ void CLCDproc::setIdleInt() socketPrintf(m_socketfd, "screen_set YSF -priority hidden"); socketPrintf(m_socketfd, "screen_set P25 -priority hidden"); socketPrintf(m_socketfd, "screen_set NXDN -priority hidden"); + socketPrintf(m_socketfd, "screen_set M17 -priority hidden"); socketPrintf(m_socketfd, "widget_set Status Status %u %u Idle", m_cols - 3, m_rows); socketPrintf(m_socketfd, "output 0"); // Clear all LEDs } @@ -207,6 +209,7 @@ void CLCDproc::setErrorInt(const char* text) socketPrintf(m_socketfd, "screen_set YSF -priority hidden"); socketPrintf(m_socketfd, "screen_set P25 -priority hidden"); socketPrintf(m_socketfd, "screen_set NXDN -priority hidden"); + socketPrintf(m_socketfd, "screen_set M17 -priority hidden"); socketPrintf(m_socketfd, "widget_set Status Status %u %u Error", m_cols - 4, m_rows); socketPrintf(m_socketfd, "output 0"); // Clear all LEDs } @@ -224,6 +227,7 @@ void CLCDproc::setLockoutInt() socketPrintf(m_socketfd, "screen_set YSF -priority hidden"); socketPrintf(m_socketfd, "screen_set P25 -priority hidden"); socketPrintf(m_socketfd, "screen_set NXDN -priority hidden"); + socketPrintf(m_socketfd, "screen_set M17 -priority hidden"); socketPrintf(m_socketfd, "widget_set Status Status %u %u Lockout", m_cols - 6, m_rows); socketPrintf(m_socketfd, "output 0"); // Clear all LEDs } @@ -243,6 +247,7 @@ void CLCDproc::setQuitInt() socketPrintf(m_socketfd, "screen_set YSF -priority hidden"); socketPrintf(m_socketfd, "screen_set P25 -priority hidden"); socketPrintf(m_socketfd, "screen_set NXDN -priority hidden"); + socketPrintf(m_socketfd, "screen_set M17 -priority hidden"); socketPrintf(m_socketfd, "widget_set Status Status %u %u Stopped", m_cols - 6, m_rows); socketPrintf(m_socketfd, "output 0"); // Clear all LEDs } @@ -260,6 +265,7 @@ void CLCDproc::setFMInt() socketPrintf(m_socketfd, "screen_set YSF -priority hidden"); socketPrintf(m_socketfd, "screen_set P25 -priority hidden"); socketPrintf(m_socketfd, "screen_set NXDN -priority hidden"); + socketPrintf(m_socketfd, "screen_set M17 -priority hidden"); socketPrintf(m_socketfd, "widget_set Status Status %u %u FM", m_cols - 6, m_rows); socketPrintf(m_socketfd, "output 0"); // Clear all LEDs } @@ -556,6 +562,51 @@ void CLCDproc::clearNXDNInt() socketPrintf(m_socketfd, "output 16"); // Set LED5 color green } +void CLCDproc::writeM17Int(const char* source, const char* dest, const char* type) +{ + assert(source != NULL); + assert(dest != NULL); + assert(type != NULL); + + m_clockDisplayTimer.stop(); // Stop the clock display + + socketPrintf(m_socketfd, "screen_set M17 -priority foreground"); + socketPrintf(m_socketfd, "widget_set M17 Mode 1 1 M17"); + + if (m_rows == 2U) { + socketPrintf(m_socketfd, "widget_set M17 Line2 1 2 15 2 h 3 \"%.9s > %.9s\"", source, dest); + } + else { + socketPrintf(m_socketfd, "widget_set M17 Line2 1 2 15 2 h 3 \"%.9s >\"", source); + socketPrintf(m_socketfd, "widget_set M17 Line3 1 3 15 3 h 3 \"%.9ss\"", dest); + socketPrintf(m_socketfd, "output 255"); // Set LED5 color red + } + + m_dmr = false; + m_rssiCount1 = 0U; +} + +void CLCDproc::writeM17RSSIInt(unsigned char rssi) +{ + if (m_rssiCount1 == 0U) { + socketPrintf(m_socketfd, "widget_set M17 Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi); + } + + m_rssiCount1++; + if (m_rssiCount1 >= M17_RSSI_COUNT) + m_rssiCount1 = 0U; +} + +void CLCDproc::clearM17Int() +{ + m_clockDisplayTimer.stop(); // Stop the clock display + + socketPrintf(m_socketfd, "widget_set M17 Line2 1 2 15 2 h 3 \"Listening\""); + socketPrintf(m_socketfd, "widget_set M17 Line3 1 3 15 3 h 3 \"\""); + socketPrintf(m_socketfd, "widget_set M17 Line4 1 4 15 4 h 3 \"\""); + socketPrintf(m_socketfd, "output 16"); // Set LED5 color green +} + void CLCDproc::writePOCSAGInt(uint32_t ric, const std::string& message) { } @@ -850,5 +901,21 @@ void CLCDproc::defineScreens() socketPrintf(m_socketfd, "widget_set NXDN Line4 4 2 15 2 h 3 \" \""); */ +// The M17 Screen + + socketPrintf(m_socketfd, "screen_add M17"); + socketPrintf(m_socketfd, "screen_set M17 -name M17 -heartbeat on -priority hidden -backlight on"); + + socketPrintf(m_socketfd, "widget_add M17 Mode string"); + socketPrintf(m_socketfd, "widget_add M17 Line2 scroller"); + socketPrintf(m_socketfd, "widget_add M17 Line3 scroller"); + socketPrintf(m_socketfd, "widget_add M17 Line4 scroller"); + + /* Do we need to pre-populate the values?? + socketPrintf(m_socketfd, "widget_set M17 Line3 2 1 15 1 h 3 \"Listening\""); + socketPrintf(m_socketfd, "widget_set M17 Line3 3 1 15 1 h 3 \" \""); + socketPrintf(m_socketfd, "widget_set M17 Line4 4 2 15 2 h 3 \" \""); + */ + m_screensDefined = true; } diff --git a/LCDproc.h b/LCDproc.h index 896e8a6..553968a 100644 --- a/LCDproc.h +++ b/LCDproc.h @@ -62,6 +62,10 @@ protected: virtual void writeNXDNRSSIInt(unsigned char rssi); virtual void clearNXDNInt(); + virtual void writeM17Int(const char* source, const char* dest, const char* type); + virtual void writeM17RSSIInt(unsigned char rssi); + virtual void clearM17Int(); + virtual void writePOCSAGInt(uint32_t ric, const std::string& message); virtual void clearPOCSAGInt(); diff --git a/M17CRC.cpp b/M17CRC.cpp new file mode 100644 index 0000000..7ba4da6 --- /dev/null +++ b/M17CRC.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2018,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. + */ + +#include "M17CRC.h" + +#include +#include + +const uint16_t CRC_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::checkCRC(const unsigned char* in, unsigned int nBytes) +{ + assert(in != NULL); + assert(nBytes > 2U); + + uint16_t crc = createCRC(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::encodeCRC(unsigned char* in, unsigned int nBytes) +{ + assert(in != NULL); + assert(nBytes > 2U); + + uint16_t crc = createCRC(in, nBytes - 2U); + + in[nBytes - 2U] = (crc >> 8) & 0xFFU; + in[nBytes - 1U] = (crc >> 0) & 0xFFU; +} + +uint16_t CM17CRC::createCRC(const unsigned char* in, unsigned int nBytes) +{ + assert(in != NULL); + + uint16_t crc = 0xFFFFU; + + for (unsigned int i = 0U; i < nBytes; i++) + crc = (crc << 8) ^ CRC_TABLE[((crc >> 8) ^ uint16_t(in[i])) & 0x00FFU]; + + return crc; +} + diff --git a/M17CRC.h b/M17CRC.h new file mode 100644 index 0000000..22c8f31 --- /dev/null +++ b/M17CRC.h @@ -0,0 +1,34 @@ +/* + * 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(M17CRC_H) +#define M17CRC_H + +#include + +class CM17CRC +{ +public: + static bool checkCRC(const unsigned char* in, unsigned int nBytes); + static void encodeCRC(unsigned char* in, unsigned int nBytes); + +private: + static uint16_t createCRC(const unsigned char* in, unsigned int nBytes); +}; + +#endif diff --git a/M17Control.cpp b/M17Control.cpp new file mode 100644 index 0000000..9be8607 --- /dev/null +++ b/M17Control.cpp @@ -0,0 +1,802 @@ +/* + * Copyright (C) 2015-2020 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" + +#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}; + +// #define DUMP_M17 + +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 colorCode, bool selfOnly, bool allowEncryption, CM17Network* network, CDisplay* display, unsigned int timeout, bool duplex, CRSSIInterpolator* rssiMapper) : +m_callsign(callsign), +m_colorCode(colorCode), +m_selfOnly(selfOnly), +m_allowEncryption(allowEncryption), +m_network(network), +m_display(display), +m_duplex(duplex), +m_queue(5000U, "M17 Control"), +m_rfState(RS_RF_LISTENING), +m_netState(RS_NET_IDLE), +m_rfTimeoutTimer(1000U, timeout), +m_netTimeoutTimer(1000U, timeout), +m_packetTimer(1000U, 0U, 200U), +m_networkWatchdog(1000U, 0U, 1500U), +m_elapsed(), +m_rfFrames(0U), +m_netFrames(0U), +m_rfFN(0U), +m_rfErrs(0U), +m_rfBits(1U), +m_rfLICH(), +m_rfLICHn(0U), +m_netLICH(), +m_netLICHn(0U), +m_rssiMapper(rssiMapper), +m_rssi(0U), +m_maxRSSI(0U), +m_minRSSI(0U), +m_aveRSSI(0U), +m_rssiCount(0U), +m_enabled(true), +m_fp(NULL) +{ + assert(display != NULL); + assert(rssiMapper != NULL); +} + +CM17Control::~CM17Control() +{ +} + +bool CM17Control::writeModem(unsigned char* data, unsigned int len) +{ + assert(data != NULL); + + if (!m_enabled) + return false; + + unsigned char type = data[0U]; + + if (type == TAG_LOST && m_rfState == RS_RF_AUDIO) { + std::string source = m_rfLICH.getSource(); + std::string dest = m_rfLICH.getDest(); + + if (m_rssi != 0U) + LogMessage("M17, transmission lost from %s to %s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", source.c_str(), dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + else + LogMessage("M17, transmission lost from %s to %s, %.1f seconds, BER: %.1f%%", source.c_str(), dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeEndRF(); + return false; + } + + if (type == TAG_LOST && m_rfState == RS_RF_DATA) { + writeEndRF(); + return false; + } + + if (type == TAG_LOST) { + m_rfState = RS_RF_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 rssi = m_rssiMapper->interpolate(raw); + if (rssi != 0) + LogDebug("M17, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi); + + // RSSI is always reported as positive + m_rssi = (rssi >= 0) ? rssi : -rssi; + + if (m_rssi > m_minRSSI) + m_minRSSI = m_rssi; + if (m_rssi < m_maxRSSI) + m_maxRSSI = m_rssi; + + m_aveRSSI += m_rssi; + m_rssiCount++; + } + + unsigned char temp[M17_FRAME_LENGTH_BYTES]; + decorrelator(data + 2U, temp); + interleaver(temp, data + 2U); + + if (m_rfState == RS_RF_LISTENING && data[0U] == TAG_HEADER) { + m_rfLICH.reset(); + + CM17Convolution conv; + unsigned char frame[M17_LICH_LENGTH_BYTES]; + conv.decodeLinkSetup(data + 2U + M17_SYNC_LENGTH_BYTES, frame); + + bool valid = CM17CRC::checkCRC(frame, M17_LICH_LENGTH_BYTES); + if (valid) { + m_rfFrames = 0U; + m_rfErrs = 0U; + m_rfBits = 1U; + m_rfTimeoutTimer.start(); + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; + m_rssiCount = 1U; + m_rfLICHn = 0U; + m_rfFN = 0U; + +#if defined(DUMP_M17) + openFile(); +#endif + m_rfLICH.setLinkSetup(frame); + + m_rfState = RS_RF_LATE_ENTRY; + + return true; + } else { + m_rfState = RS_RF_LATE_ENTRY; + } + } + + if (m_rfState == RS_RF_LATE_ENTRY && data[0U] == TAG_DATA) { + unsigned int frag1, frag2, frag3, frag4; + CM17Utils::splitFragmentLICHFEC(data + 2U + M17_SYNC_LENGTH_BYTES, frag1, frag2, frag3, frag4); + + unsigned int lich1 = CGolay24128::decode24128(frag1); + unsigned int lich2 = CGolay24128::decode24128(frag2); + unsigned int lich3 = CGolay24128::decode24128(frag3); + unsigned int lich4 = CGolay24128::decode24128(frag4); + + unsigned int colorCode = (lich4 >> 7) & 0x1FU; + if (colorCode != m_colorCode) + return false; + + bool lateEntry = false; + if (!m_rfLICH.isValid()) { + unsigned char lich[M17_LICH_FRAGMENT_LENGTH_BYTES]; + CM17Utils::combineFragmentLICH(lich1, lich2, lich3, lich4, lich); + + unsigned int n = (lich4 >> 4) & 0x07U; + m_rfLICH.setFragment(lich, n); + + lateEntry = true; + } + + bool valid = m_rfLICH.isValid(); + if (valid) { + m_rfFrames = 0U; + m_rfErrs = 0U; + m_rfBits = 1U; + m_rfTimeoutTimer.start(); + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; + m_rssiCount = 1U; + m_rfLICHn = 0U; + +#if defined(DUMP_M17) + openFile(); +#endif + std::string source = m_rfLICH.getSource(); + std::string dest = m_rfLICH.getDest(); + + if (m_selfOnly) { + bool ret = checkCallsign(source); + if (!ret) { + LogMessage("M17, invalid access attempt from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_REJECTED; + return false; + } + } + + unsigned char dataType = m_rfLICH.getDataType(); + switch (dataType) { + case 1U: + LogMessage("M17, received RF %s data transmission from %s to %s", lateEntry ? "late entry" : "", source.c_str(), dest.c_str()); + m_rfState = RS_RF_DATA; + break; + case 2U: + LogMessage("M17, received RF %s voice transmission from %s to %s", lateEntry ? "late entry" : "", source.c_str(), dest.c_str()); + m_rfState = RS_RF_AUDIO; + break; + case 3U: + LogMessage("M17, received RF %s voice + data transmission from %s to %s", lateEntry ? "late entry" : "", source.c_str(), dest.c_str()); + m_rfState = RS_RF_AUDIO; + break; + default: + LogMessage("M17, received RF %s unknown transmission from %s to %s", lateEntry ? "late entry" : "", source.c_str(), dest.c_str()); + m_rfState = RS_RF_DATA; + break; + } + + m_display->writeM17(source.c_str(), dest.c_str(), "R"); + + if (m_duplex) { + // Create a Link Setup frame + data[0U] = TAG_HEADER; + data[1U] = 0x00U; + + // Generate the sync + CSync::addM17HeaderSync(data + 2U); + + unsigned char setup[M17_LICH_LENGTH_BYTES]; + m_rfLICH.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); + } + + // Fall through to the next section + } + } + + if ((m_rfState == RS_RF_AUDIO || m_rfState == RS_RF_DATA) && data[0U] == TAG_DATA) { +#if defined(DUMP_M17) + writeFile(data + 2U); +#endif + CM17Convolution conv; + unsigned char frame[M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES]; + conv.decodeData(data + 2U + M17_SYNC_LENGTH_BYTES + M17_LICH_FRAGMENT_FEC_LENGTH_BYTES, frame); + + bool valid = CM17CRC::checkCRC(frame, M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES); + if (valid) { + m_rfFN = (frame[0U] << 8) + (frame[1U] << 0); + } else { + // Create a silence frame + m_rfFN++; + + // The new FN + frame[0U] = m_rfFN >> 8; + frame[1U] = m_rfFN >> 0; + + // Add silent audio + unsigned char dataType = m_rfLICH.getDataType(); + switch (dataType) { + case 2U: + ::memcpy(frame + M17_FN_LENGTH_BYTES + 0U, M17_3200_SILENCE, 8U); + ::memcpy(frame + M17_FN_LENGTH_BYTES + 8U, M17_3200_SILENCE, 8U); + break; + case 3U: + ::memcpy(frame + M17_FN_LENGTH_BYTES + 0U, M17_1600_SILENCE, 8U); + break; + default: + break; + } + + // Add the CRC + CM17CRC::encodeCRC(frame, M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES); + } + + unsigned char rfData[2U + M17_FRAME_LENGTH_BYTES]; + + rfData[0U] = TAG_DATA; + rfData[1U] = 0x00U; + + // Generate the sync + CSync::addM17DataSync(rfData + 2U); + + unsigned char lich[M17_LICH_FRAGMENT_LENGTH_BYTES]; + m_netLICH.getFragment(lich, m_rfLICHn); + + unsigned int frag1, frag2, frag3, frag4; + CM17Utils::splitFragmentLICH(lich, frag1, frag2, frag3, frag4); + + // Add the Color Code and fragment number + frag4 |= (m_rfLICHn & 0x07U) << 4; + frag4 |= (m_colorCode & 0x1FU) << 7; + + // 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); + + // Calculate the BER + if (valid) { + unsigned int errors = 0U; + for (unsigned int i = 2U; i < (M17_FRAME_LENGTH_BYTES + 2U); i++) + errors += countBits(rfData[i] ^ data[i]); + + LogDebug("M17, FN: %u, errs: %u/384 (%.1f%%)", m_rfFN, errors, float(errors) / 3.84F); + + m_rfBits += M17_FRAME_LENGTH_BITS; + m_rfErrs += errors; + + float ber = float(m_rfErrs) / float(m_rfBits); + m_display->writeM17BER(ber); + } + + unsigned char temp[M17_FRAME_LENGTH_BYTES]; + interleaver(rfData + 2U, temp); + decorrelator(rfData, data + 2U); + + if (m_duplex) + writeQueueRF(rfData); + + unsigned char netData[M17_LICH_LENGTH_BYTES + M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES]; + + m_rfLICH.getNetwork(netData + 0U); + + // Copy the FN and payload from the frame + ::memcpy(netData + M17_LICH_LENGTH_BYTES - M17_CRC_LENGTH_BYTES, frame, M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES); + + // The CRC is added in the networking code + + writeNetwork(netData); + + m_rfFrames++; + + m_rfLICHn++; + if (m_rfLICHn >= 6U) + m_rfLICHn = 0U; + + // EOT? + if ((m_rfFN & 0x8000U) == 0x8000U) { + std::string source = m_rfLICH.getSource(); + std::string dest = m_rfLICH.getDest(); + + if (m_rssi != 0U) + LogMessage("M17, received RF end of transmission from %s to %s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", source.c_str(), dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + else + LogMessage("M17, received RF end of transmission from %s to %s, %.1f seconds, BER: %.1f%%", source.c_str(), dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeEndRF(); + } + + return true; + } + + if (m_rfState == RS_RF_REJECTED && data[0U] == TAG_DATA) { + CM17Convolution conv; + unsigned char frame[M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES]; + conv.decodeData(data + 2U + M17_SYNC_LENGTH_BYTES + M17_LICH_FRAGMENT_FEC_LENGTH_BYTES, frame); + + bool valid = CM17CRC::checkCRC(frame, M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES); + if (valid) { + // Handle the EOT for rejected frames + unsigned int fn = (frame[0U] << 8) + (frame[1U] << 0); + if ((fn & 0x8000U) == 0x8000U) + writeEndRF(); + } + + return false; + } + + return false; +} + +unsigned int CM17Control::readModem(unsigned char* data) +{ + assert(data != NULL); + + 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 = RS_RF_LISTENING; + + m_rfTimeoutTimer.stop(); + + m_rfLICH.reset(); + + if (m_netState == RS_NET_IDLE) { + m_display->clearM17(); + + if (m_network != NULL) + m_network->reset(); + } + +#if defined(DUMP_M17) + closeFile(); +#endif +} + +void CM17Control::writeEndNet() +{ + m_netState = RS_NET_IDLE; + + m_netTimeoutTimer.stop(); + m_networkWatchdog.stop(); + m_packetTimer.stop(); + + m_netLICH.reset(); + + m_display->clearM17(); + + if (m_network != NULL) + 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 != RS_RF_LISTENING && m_netState == RS_NET_IDLE) + return; + + m_networkWatchdog.start(); + + m_netLICH.setNetwork(netData); + + if (!m_allowEncryption) { + bool ret = m_rfLICH.isNONCENull(); + if (!ret) + return; + } + + if (m_netState == RS_NET_IDLE) { + std::string source = m_netLICH.getSource(); + std::string dest = m_netLICH.getDest(); + + unsigned char dataType = m_netLICH.getDataType(); + switch (dataType) { + case 1U: + LogMessage("M17, received network data transmission from %s to %s", source.c_str(), dest.c_str()); + m_netState = RS_NET_DATA; + break; + case 2U: + LogMessage("M17, received network voice transmission from %s to %s", source.c_str(), dest.c_str()); + m_netState = RS_NET_AUDIO; + break; + case 3U: + LogMessage("M17, received network voice + data transmission from %s to %s", source.c_str(), dest.c_str()); + m_netState = RS_NET_AUDIO; + break; + default: + LogMessage("M17, received network unknown transmission from %s to %s", source.c_str(), dest.c_str()); + m_netState = RS_NET_DATA; + break; + } + + m_display->writeM17(source.c_str(), dest.c_str(), "N"); + + m_netTimeoutTimer.start(); + m_packetTimer.start(); + m_elapsed.start(); + m_netFrames = 0U; + m_netLICHn = 0U; + + // Create a dummy start message + unsigned char start[M17_FRAME_LENGTH_BYTES + 2U]; + + start[0U] = TAG_HEADER; + start[1U] = 0x00U; + + // Generate the sync + CSync::addM17HeaderSync(start + 2U); + + unsigned char setup[M17_LICH_LENGTH_BYTES]; + m_netLICH.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); + } + + unsigned char data[M17_FRAME_LENGTH_BYTES + 2U]; + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + // Generate the sync + CSync::addM17DataSync(data + 2U); + + m_netFrames++; + + // Add the fragment LICH + unsigned char lich[M17_LICH_FRAGMENT_LENGTH_BYTES]; + m_netLICH.getFragment(lich, m_netLICHn); + + unsigned int frag1, frag2, frag3, frag4; + CM17Utils::splitFragmentLICH(lich, frag1, frag2, frag3, frag4); + + // Add the Color Code and fragment number + frag4 |= (m_netLICHn & 0x07U) << 4; + frag4 |= (m_colorCode & 0x1FU) << 7; + + // 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 + M17_CRC_LENGTH_BYTES]; + ::memcpy(payload, netData + 28U, M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES); + + // Add the CRC + CM17CRC::encodeCRC(payload, M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_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_netLICHn++; + if (m_netLICHn >= 6U) + m_netLICHn = 0U; + + // EOT handling + uint16_t fn = (netData[28U] << 8) + (netData[29U] << 0); + if ((fn & 0x8000U) == 0x8000U) { + std::string source = m_netLICH.getSource(); + std::string dest = m_netLICH.getDest(); + LogMessage("M17, received network end of transmission from %s to %s, %.1f seconds", source.c_str(), dest.c_str(), float(m_netFrames) / 25.0F); + writeEndNet(); + } +} + +void CM17Control::clock(unsigned int ms) +{ + if (m_network != NULL) + writeNetwork(); + + m_rfTimeoutTimer.clock(ms); + m_netTimeoutTimer.clock(ms); + + if (m_netState == RS_NET_AUDIO) { + m_networkWatchdog.clock(ms); + + if (m_networkWatchdog.hasExpired()) { + LogMessage("M17, network watchdog has expired, %.1f seconds", float(m_netFrames) / 25.0F); + writeEndNet(); + } + } +} + +void CM17Control::writeQueueRF(const unsigned char *data) +{ + assert(data != NULL); + + if (m_netState != RS_NET_IDLE) + return; + + if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) + return; + + 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 != NULL); + + if (m_netTimeoutTimer.isRunning() && m_netTimeoutTimer.hasExpired()) + return; + + 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::writeNetwork(const unsigned char *data) +{ + assert(data != NULL); + + if (m_network == NULL) + return; + + if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) + return; + + m_network->write(data); +} + +void CM17Control::interleaver(const unsigned char* in, unsigned char* out) const +{ + assert(in != NULL); + assert(out != NULL); + + 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 != NULL); + assert(out != NULL); + + 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::openFile() +{ + if (m_fp != NULL) + return true; + + time_t t; + ::time(&t); + + struct tm* tm = ::localtime(&t); + + char name[100U]; + ::sprintf(name, "M17_%04d%02d%02d_%02d%02d%02d.ambe", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + m_fp = ::fopen(name, "wb"); + if (m_fp == NULL) + return false; + + ::fwrite("M17", 1U, 3U, m_fp); + + return true; +} + +bool CM17Control::writeFile(const unsigned char* data) +{ + if (m_fp == NULL) + return false; + + ::fwrite(data, 1U, M17_FRAME_LENGTH_BYTES, m_fp); + + return true; +} + +void CM17Control::closeFile() +{ + if (m_fp != NULL) { + ::fclose(m_fp); + m_fp = NULL; + } +} + +bool CM17Control::isBusy() const +{ + return m_rfState != RS_RF_LISTENING || m_netState != RS_NET_IDLE; +} + +void CM17Control::enable(bool enabled) +{ + if (!enabled && m_enabled) { + m_queue.clear(); + + // Reset the RF section + m_rfState = RS_RF_LISTENING; + + m_rfTimeoutTimer.stop(); + + // Reset the networking section + m_netState = RS_NET_IDLE; + + m_netTimeoutTimer.stop(); + m_networkWatchdog.stop(); + m_packetTimer.stop(); + } + + m_enabled = enabled; +} + +unsigned int CM17Control::countBits(unsigned char byte) +{ + unsigned int count = 0U; + + const unsigned char* p = &byte; + + for (unsigned int i = 0U; i < 8U; i++) { + if (READ_BIT(p, i) != 0U) + count++; + } + + return count; +} diff --git a/M17Control.h b/M17Control.h new file mode 100644 index 0000000..276478c --- /dev/null +++ b/M17Control.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2015-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(M17Control_H) +#define M17Control_H + +#include "RSSIInterpolator.h" +#include "M17Network.h" +#include "M17Defines.h" +#include "RingBuffer.h" +#include "StopWatch.h" +#include "M17LICH.h" +#include "Display.h" +#include "Defines.h" +#include "Timer.h" +#include "Modem.h" + +#include + +class CM17Control { +public: + CM17Control(const std::string& callsign, unsigned int colorCode, bool selfOnly, bool allowEncryption, CM17Network* network, CDisplay* display, 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_colorCode; + bool m_selfOnly; + bool m_allowEncryption; + CM17Network* m_network; + CDisplay* m_display; + bool m_duplex; + CRingBuffer m_queue; + RPT_RF_STATE m_rfState; + RPT_NET_STATE m_netState; + CTimer m_rfTimeoutTimer; + CTimer m_netTimeoutTimer; + CTimer m_packetTimer; + CTimer m_networkWatchdog; + CStopWatch m_elapsed; + unsigned int m_rfFrames; + unsigned int m_netFrames; + unsigned int m_rfFN; + unsigned int m_rfErrs; + unsigned int m_rfBits; + CM17LICH m_rfLICH; + unsigned int m_rfLICHn; + CM17LICH m_netLICH; + unsigned int m_netLICHn; + CRSSIInterpolator* m_rssiMapper; + unsigned char m_rssi; + unsigned char m_maxRSSI; + unsigned char m_minRSSI; + unsigned int m_aveRSSI; + unsigned int m_rssiCount; + bool m_enabled; + FILE* m_fp; + + void writeQueueRF(const unsigned char* data); + void writeQueueNet(const unsigned char* data); + void writeNetwork(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; + + unsigned int countBits(unsigned char byte); + + void writeEndRF(); + void writeEndNet(); + + bool openFile(); + bool writeFile(const unsigned char* data); + void closeFile(); +}; + +#endif diff --git a/M17Convolution.cpp b/M17Convolution.cpp new file mode 100644 index 0000000..1f64a76 --- /dev/null +++ b/M17Convolution.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2009-2016,2018,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. + */ + +#include "M17Convolution.h" + +#include +#include +#include +#include + +const unsigned int PUNCTURE_LIST_LINK_SETUP[] = { + 3U, 6U, 9U, 12U, 19U, 22U, 25U, 28U, 35U, 38U, 41U, 44U, 51U, 54U, 57U, 64U, 67U, 70U, 73U, 80U, 83U, 86U, 89U, 96U, 99U, 102U, + 105U, 112U, 115U, 118U, 125U, 128U, 131U, 134U, 141U, 144U, 147U, 150U, 157U, 160U, 163U, 166U, 173U, 176U, 179U, 186U, 189U, + 192U, 195U, 202U, 205U, 208U, 211U, 218U, 221U, 224U, 227U, 234U, 237U, 240U, 247U, 250U, 253U, 256U, 263U, 266U, 269U, 272U, + 279U, 282U, 285U, 288U, 295U, 298U, 301U, 308U, 311U, 314U, 317U, 324U, 327U, 330U, 333U, 340U, 343U, 346U, 349U, 356U, 359U, + 362U, 369U, 372U, 375U, 378U, 385U, 388U, 391U, 394U, 401U, 404U, 407U, 410U, 417U, 420U, 423U, 430U, 433U, 436U, 439U, 446U, + 449U, 452U, 455U, 462U, 465U, 468U, 471U, 478U, 481U, 484U}; + +const unsigned int PUNCTURE_LIST_DATA[] = { + 5U, 11U, 17U, 20U, 23U, 29U, 35U, 46U, 52U, 58U, 61U, 64U, 70U, 76U, 87U, 93U, 99U, 102U, 105U, 111U, 117U, 128U, 134U, 140U, + 143U, 146U, 152U, 158U, 169U, 175U, 181U, 184U, 187U, 193U, 199U, 210U, 216U, 222U, 225U, 228U, 234U, 240U, 251U, 257U, 263U, + 266U, 269U, 275U, 281U, 292U, 298U, 304U, 307U, 310U, 316U, 322U}; + +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(NULL), +m_metrics2(NULL), +m_oldMetrics(NULL), +m_newMetrics(NULL), +m_decisions(NULL), +m_dp(NULL) +{ + m_metrics1 = new uint16_t[16U]; + m_metrics2 = new uint16_t[16U]; + 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 != NULL); + assert(out != NULL); + + 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 != NULL); + assert(out != NULL); + + unsigned char temp1[21U]; + ::memset(temp1, 0x00U, 21U); + ::memcpy(temp1, in, 20U); + + unsigned char temp2[41U]; + encode(temp1, temp2, 164U); + + unsigned int n = 0U; + unsigned int index = 0U; + for (unsigned int i = 0U; i < 328U; i++) { + if (i != PUNCTURE_LIST_DATA[index]) { + bool b = READ_BIT1(temp2, i); + WRITE_BIT1(out, n, b); + n++; + } else { + index++; + } + } +} + +void CM17Convolution::decodeLinkSetup(const unsigned char* in, unsigned char* out) +{ + assert(in != NULL); + assert(out != NULL); + + uint8_t temp[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; + } + + for (unsigned int i = 0U; i < 8U; i++) + temp[n++] = 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); + } + + chainback(out, 240U); +} + +void CM17Convolution::decodeData(const unsigned char* in, unsigned char* out) +{ + assert(in != NULL); + assert(out != NULL); + + uint8_t temp[350U]; + + 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; + } + + for (unsigned int i = 0U; i < 8U; i++) + temp[n++] = 0U; + + start(); + + n = 0U; + for (unsigned int i = 0U; i < 164U; i++) { + uint8_t s0 = temp[n++]; + uint8_t s1 = temp[n++]; + + decode(s0, s1); + } + + chainback(out, 160U); +} + +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; +} + +void CM17Convolution::chainback(unsigned char* out, unsigned int nBits) +{ + assert(out != NULL); + + 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); + } +} + +void CM17Convolution::encode(const unsigned char* in, unsigned char* out, unsigned int nBits) const +{ + assert(in != NULL); + assert(out != NULL); + 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++; + } +} diff --git a/M17Convolution.h b/M17Convolution.h new file mode 100644 index 0000000..e3f5d82 --- /dev/null +++ b/M17Convolution.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2015,2016,2018,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(M17Convolution_H) +#define M17Convolution_H + +#include + +class CM17Convolution { +public: + CM17Convolution(); + ~CM17Convolution(); + + void decodeLinkSetup(const unsigned char* in, unsigned char* out); + void 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); + void chainback(unsigned char* out, unsigned int nBits); + + void encode(const unsigned char* in, unsigned char* out, unsigned int nBits) const; +}; + +#endif diff --git a/M17Defines.h b/M17Defines.h new file mode 100644 index 0000000..5129d9a --- /dev/null +++ b/M17Defines.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016,2017,2018,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(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_HEADER_SYNC_BYTES[] = {0x5DU, 0xDDU}; +const unsigned char M17_DATA_SYNC_BYTES[] = {0xDDU, 0xDDU}; + +const unsigned int M17_SYNC_LENGTH_BITS = 16U; +const unsigned int M17_SYNC_LENGTH_BYTES = M17_SYNC_LENGTH_BITS / 8U; + +const unsigned int M17_LICH_LENGTH_BITS = 240U; +const unsigned int M17_LICH_LENGTH_BYTES = M17_LICH_LENGTH_BITS / 8U; + +const unsigned int M17_LICH_FRAGMENT_LENGTH_BITS = M17_LICH_LENGTH_BITS / 6U; +const unsigned int M17_LICH_FRAGMENT_LENGTH_BYTES = M17_LICH_FRAGMENT_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_NONCE_LENGTH_BITS = 112U; +const unsigned int M17_NONCE_LENGTH_BYTES = M17_NONCE_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[] = {0x01U, 0x00U, 0x04U, 0x00U, 0x25U, 0x75U, 0xDDU, 0xF2U}; + +#endif diff --git a/M17LICH.cpp b/M17LICH.cpp new file mode 100644 index 0000000..e99fd8d --- /dev/null +++ b/M17LICH.cpp @@ -0,0 +1,143 @@ +/* + * 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. + */ + +#include "M17LICH.h" +#include "M17Utils.h" +#include "M17Defines.h" +#include "M17CRC.h" + +#include +#include + +CM17LICH::CM17LICH() : +m_lich(NULL), +m_valid(false) +{ + m_lich = new unsigned char[M17_LICH_LENGTH_BYTES]; +} + +CM17LICH::~CM17LICH() +{ + delete[] m_lich; +} + +void CM17LICH::getNetwork(unsigned char* data) const +{ + assert(data != NULL); + + ::memcpy(data, m_lich, M17_LICH_LENGTH_BYTES); +} + +void CM17LICH::setNetwork(const unsigned char* data) +{ + assert(data != NULL); + + ::memcpy(m_lich, data, M17_LICH_LENGTH_BYTES); + + m_valid = true; +} + +std::string CM17LICH::getSource() const +{ + std::string callsign; + CM17Utils::decodeCallsign(m_lich + 6U, callsign); + + return callsign; +} + +void CM17LICH::setSource(const std::string& callsign) +{ + CM17Utils::encodeCallsign(callsign, m_lich + 6U); +} + +std::string CM17LICH::getDest() const +{ + std::string callsign; + CM17Utils::decodeCallsign(m_lich + 0U, callsign); + + return callsign; +} + +void CM17LICH::setDest(const std::string& callsign) +{ + CM17Utils::encodeCallsign(callsign, m_lich + 0U); +} + +unsigned char CM17LICH::getDataType() const +{ + return (m_lich[13U] >> 1) & 0x03U; +} + +void CM17LICH::setDataType(unsigned char type) +{ + m_lich[13U] &= 0xF9U; + m_lich[13U] |= (type << 1) & 0x06U; +} + +bool CM17LICH::isNONCENull() const +{ + return ::memcmp(m_lich + 14U, M17_NULL_NONCE, M17_NONCE_LENGTH_BYTES) == 0; +} + +void CM17LICH::reset() +{ + ::memset(m_lich, 0x00U, 30U); + + m_valid = false; +} + +bool CM17LICH::isValid() const +{ + return m_valid; +} + +void CM17LICH::getLinkSetup(unsigned char* data) const +{ + assert(data != NULL); + + ::memcpy(data, m_lich, M17_LICH_LENGTH_BYTES); + + CM17CRC::encodeCRC(data, M17_LICH_LENGTH_BYTES); +} + +void CM17LICH::setLinkSetup(const unsigned char* data) +{ + assert(data != NULL); + + ::memcpy(m_lich, data, M17_LICH_LENGTH_BYTES); + + m_valid = CM17CRC::checkCRC(m_lich, M17_LICH_LENGTH_BYTES); +} + +void CM17LICH::getFragment(unsigned char* data, unsigned int n) const +{ + assert(data != NULL); + + CM17CRC::encodeCRC(m_lich, M17_LICH_LENGTH_BYTES); + + ::memcpy(data, m_lich + (n * M17_LICH_FRAGMENT_LENGTH_BYTES), M17_LICH_FRAGMENT_LENGTH_BYTES); +} + +void CM17LICH::setFragment(const unsigned char* data, unsigned int n) +{ + assert(data != NULL); + + ::memcpy(m_lich + (n * M17_LICH_FRAGMENT_LENGTH_BYTES), data, M17_LICH_FRAGMENT_LENGTH_BYTES); + + m_valid = CM17CRC::checkCRC(m_lich, M17_LICH_LENGTH_BYTES); +} diff --git a/M17LICH.h b/M17LICH.h new file mode 100644 index 0000000..804de3e --- /dev/null +++ b/M17LICH.h @@ -0,0 +1,57 @@ +/* + * 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(M17LICH_H) +#define M17LICH_H + +#include + +class CM17LICH { +public: + CM17LICH(); + ~CM17LICH(); + + 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 getDataType() const; + void setDataType(unsigned char type); + + bool isNONCENull() const; + + 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); + +private: + unsigned char* m_lich; + bool m_valid; +}; + +#endif diff --git a/M17Network.cpp b/M17Network.cpp new file mode 100644 index 0000000..56a2135 --- /dev/null +++ b/M17Network.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2009-2014,2016,2019,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. + */ + +#include "M17Network.h" +#include "M17Defines.h" +#include "M17Utils.h" +#include "Defines.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 200U; + +CM17Network::CM17Network(unsigned int localPort, const std::string& gwyAddress, unsigned int gwyPort, bool debug) : +m_socket(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(gwyAddress, gwyPort, 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 != NULL); + + 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 data 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 Data 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 != NULL); + + 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) + m_buffer.clear(); + + m_enabled = enabled; +} + +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 data transmitted", buffer, 4U); + + m_socket.write(buffer, 4U, m_addr, m_addrLen); +} diff --git a/M17Network.h b/M17Network.h new file mode 100644 index 0000000..e65e1f3 --- /dev/null +++ b/M17Network.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2009-2014,2016,2018,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. + */ + +#ifndef M17Network_H +#define M17Network_H + +#include "M17Defines.h" +#include "RingBuffer.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CM17Network { +public: + CM17Network(unsigned int localPort, const std::string& gwyAddress, unsigned int gwyPort, 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); + +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 diff --git a/M17Utils.cpp b/M17Utils.cpp new file mode 100644 index 0000000..1ba33b1 --- /dev/null +++ b/M17Utils.cpp @@ -0,0 +1,209 @@ +/* + * 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. + */ + +#include "M17Utils.h" +#include "M17Defines.h" + +#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 != NULL); + + unsigned int len = callsign.size(); + if (len > 9U) + len = 9U; + + uint64_t enc = 0ULL; + for (int i = len - 1; i >= 0; i--) { + 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 != NULL); + + 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 >= 262144000000000ULL) // 40^9 + return; + + 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 != NULL); + + 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 != NULL); + + 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 != NULL); + + 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 != NULL); + + 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); + } +} diff --git a/M17Utils.h b/M17Utils.h new file mode 100644 index 0000000..c6e27ce --- /dev/null +++ b/M17Utils.h @@ -0,0 +1,41 @@ +/* + * 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(M17Utils_H) +#define M17Utils_H + +#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 diff --git a/MMDVM.ini b/MMDVM.ini index 2b3dcd1..7810965 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -44,9 +44,10 @@ Time=24 [Modem] # Port=/dev/ttyACM0 -# Port=/dev/ttyAMA0 -Port=\\.\COM4 +Port=/dev/ttyAMA0 +# Port=\\.\COM4 Protocol=uart +Speed=460800 # Address=0x22 TXInvert=1 RXInvert=0 @@ -66,8 +67,10 @@ RFLevel=100 # YSFTXLevel=50 # P25TXLevel=50 # NXDNTXLevel=50 +# M17TXLevel-50 # POCSAGTXLevel=50 # FMTXLevel=50 +# AX25TXLevel=50 RSSIMappingFile=RSSI.dat UseCOSAsLockout=0 Trace=0 @@ -80,11 +83,6 @@ RemotePort=40094 LocalPort=40095 # SendFrameType=0 -[UMP] -Enable=0 -# Port=\\.\COM4 -Port=/dev/ttyACM1 - [D-Star] Enable=1 Module=C @@ -140,6 +138,13 @@ RemoteGateway=0 TXHang=5 # ModeHang=10 +[M17] +Enable=1 +ColorCode=3 +SelfOnly=0 +TXHang=5 +# ModeHang=10 + [POCSAG] Enable=1 Frequency=439987500 @@ -174,9 +179,22 @@ KerchunkTime=0 HangTime=7 AccessMode=1 COSInvert=0 +NoiseSquelch=0 +SquelchThreshold=30 +# SquelchHighThreshold=30 +# SquelchLowThreshold=20 RFAudioBoost=1 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 @@ -229,6 +247,14 @@ GatewayPort=14020 # ModeHang=3 Debug=0 +[M17 Network] +Enable=1 +GatewayAddress=127.0.0.1 +GatewayPort=17010 +LocalPort=17011 +# ModeHang=3 +Debug=0 + [POCSAG Network] Enable=1 LocalAddress=127.0.0.1 @@ -238,6 +264,22 @@ GatewayPort=4800 # ModeHang=3 Debug=0 +[FM Network] +Enable=1 +LocalAddress=127.0.0.1 +LocalPort=3810 +GatewayAddress=127.0.0.1 +GatewayPort=4810 +SampleRate=8000 +# ModeHang=3 +Debug=0 + +[AX.25 Network] +Enable=1 +Port=/dev/ttyp7 +Speed=9600 +Debug=0 + [TFT Serial] # Port=modem Port=/dev/ttyAMA0 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index c1dbdba..8c66568 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -23,6 +23,8 @@ #include "NXDNIcomNetwork.h" #include "RSSIInterpolator.h" #include "SerialController.h" +#include "SerialModem.h" +#include "NullModem.h" #include "Version.h" #include "StopWatch.h" #include "Defines.h" @@ -122,27 +124,36 @@ m_dmr(NULL), m_ysf(NULL), m_p25(NULL), m_nxdn(NULL), +m_m17(NULL), m_pocsag(NULL), +m_fm(NULL), +m_ax25(NULL), m_dstarNetwork(NULL), m_dmrNetwork(NULL), m_ysfNetwork(NULL), m_p25Network(NULL), m_nxdnNetwork(NULL), +m_m17Network(NULL), m_pocsagNetwork(NULL), +m_fmNetwork(NULL), +m_ax25Network(NULL), m_display(NULL), -m_ump(NULL), m_mode(MODE_IDLE), m_dstarRFModeHang(10U), m_dmrRFModeHang(10U), m_ysfRFModeHang(10U), m_p25RFModeHang(10U), m_nxdnRFModeHang(10U), +m_m17RFModeHang(10U), +m_fmRFModeHang(10U), m_dstarNetModeHang(3U), m_dmrNetModeHang(3U), m_ysfNetModeHang(3U), m_p25NetModeHang(3U), m_nxdnNetModeHang(3U), +m_m17NetModeHang(3U), m_pocsagNetModeHang(3U), +m_fmNetModeHang(3U), m_modeTimer(1000U), m_dmrTXTimer(1000U), m_cwIdTimer(1000U), @@ -153,8 +164,10 @@ 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), m_dmrLookup(NULL), m_nxdnLookup(NULL), @@ -273,21 +286,7 @@ int CMMDVMHost::run() if (!ret) return 1; - if (m_conf.getUMPEnabled()) { - std::string port = m_conf.getUMPPort(); - - LogInfo("Universal MMDVM Peripheral"); - LogInfo(" Port: %s", port.c_str()); - - m_ump = new CUMP(port); - bool ret = m_ump->open(); - if (!ret) { - delete m_ump; - m_ump = NULL; - } - } - - m_display = CDisplay::createDisplay(m_conf,m_ump,m_modem); + m_display = CDisplay::createDisplay(m_conf, m_modem); if (m_dstarEnabled && m_conf.getDStarNetworkEnabled()) { ret = createDStarNetwork(); @@ -319,12 +318,30 @@ int CMMDVMHost::run() return 1; } + if (m_m17Enabled && m_conf.getM17NetworkEnabled()) { + ret = createM17Network(); + if (!ret) + return 1; + } + if (m_pocsagEnabled && m_conf.getPOCSAGNetworkEnabled()) { ret = createPOCSAGNetwork(); if (!ret) return 1; } + if (m_fmEnabled && m_conf.getFMNetworkEnabled()) { + ret = createFMNetwork(); + if (!ret) + return 1; + } + + if (m_ax25Enabled && m_conf.getAX25NetworkEnabled()) { + ret = createAX25Network(); + if (!ret) + return 1; + } + sockaddr_storage transparentAddress; unsigned int transparentAddrLen; CUDPSocket* transparentSocket = NULL; @@ -501,7 +518,6 @@ int CMMDVMHost::run() else if (ovcm == DMR_OVCM_ON) LogInfo(" OVCM: on"); - switch (dmrBeacons) { case DMR_BEACONS_NETWORK: { unsigned int dmrBeaconDuration = m_conf.getDMRBeaconDuration(); @@ -604,6 +620,23 @@ int CMMDVMHost::run() m_nxdn = new CNXDNControl(ran, id, selfOnly, m_nxdnNetwork, m_display, m_timeout, m_duplex, remoteGateway, m_nxdnLookup, rssi); } + if (m_m17Enabled) { + bool selfOnly = m_conf.getM17SelfOnly(); + unsigned int colorCode = m_conf.getM17ColorCode(); + bool allowEncryption = m_conf.getM17AllowEncryption(); + unsigned int txHang = m_conf.getM17TXHang(); + m_m17RFModeHang = m_conf.getM17ModeHang(); + + LogInfo("M17 RF Parameters"); + LogInfo(" Self Only: %s", selfOnly ? "yes" : "no"); + LogInfo(" Color Code: %u", colorCode); + LogInfo(" Allow Encryption: %s", allowEncryption ? "yes" : "no"); + LogInfo(" TX Hang: %us", txHang); + LogInfo(" Mode Hang: %us", m_m17RFModeHang); + + m_m17 = new CM17Control(m_callsign, colorCode, selfOnly, allowEncryption, m_m17Network, m_display, m_timeout, m_duplex, rssi); + } + CTimer pocsagTimer(1000U, 30U); if (m_pocsagEnabled) { @@ -618,6 +651,29 @@ int CMMDVMHost::run() pocsagTimer.start(); } + if (m_ax25Enabled) { + unsigned int txDelay = m_conf.getAX25TXDelay(); + int rxTwist = m_conf.getAX25RXTwist(); + unsigned int slotTime = m_conf.getAX25SlotTime(); + unsigned int pPersist = m_conf.getAX25PPersist(); + bool trace = m_conf.getAX25Trace(); + + LogInfo("AX.25 RF Parameters"); + LogInfo(" TX Delay: %ums", txDelay); + LogInfo(" RX Twist: %d", rxTwist); + LogInfo(" Slot Time: %ums", slotTime); + LogInfo(" P-Persist: %u", pPersist); + LogInfo(" Trace: %s", trace ? "yes" : "no"); + + m_ax25 = new CAX25Control(m_ax25Network, trace); + } + + if (m_fmEnabled) { + m_fmRFModeHang = m_conf.getFMModeHang(); + + m_fm = new CFMControl(m_fmNetwork); + } + bool remoteControlEnabled = m_conf.getRemoteControlEnabled(); if (remoteControlEnabled) { std::string address = m_conf.getRemoteControlAddress(); @@ -641,14 +697,11 @@ int CMMDVMHost::run() LogMessage("MMDVMHost-%s is running", VERSION); while (!m_killed) { - bool lockout1 = m_modem->hasLockout(); - bool lockout2 = false; + bool lockout = m_modem->hasLockout(); - if (m_ump != NULL) - lockout2 = m_ump->getLockout(); - if ((lockout1 || lockout2) && m_mode != MODE_LOCKOUT) + if (lockout && m_mode != MODE_LOCKOUT) setMode(MODE_LOCKOUT); - else if ((!lockout1 && !lockout2) && m_mode == MODE_LOCKOUT) + else if (!lockout && m_mode == MODE_LOCKOUT) setMode(MODE_IDLE); bool error = m_modem->hasError(); @@ -657,20 +710,7 @@ int CMMDVMHost::run() else if (!error && m_mode == MODE_ERROR) setMode(MODE_IDLE); - unsigned char mode = m_modem->getMode(); - if (mode == MODE_FM && m_mode != MODE_FM) - setMode(mode); - else if (mode != MODE_FM && m_mode == MODE_FM) - setMode(mode); - - if (m_ump != NULL) { - bool tx = m_modem->hasTX(); - m_ump->setTX(tx); - bool cd = m_modem->hasCD(); - m_ump->setCD(cd); - } - - unsigned char data[MODEM_DATA_LEN]; + unsigned char data[500U]; unsigned int len; bool ret; @@ -812,6 +852,47 @@ int CMMDVMHost::run() } } + len = m_modem->readM17Data(data); + if (m_m17 != NULL && len > 0U) { + if (m_mode == MODE_IDLE) { + bool ret = m_m17->writeModem(data, len); + if (ret) { + m_modeTimer.setTimeout(m_m17RFModeHang); + setMode(MODE_M17); + } + } else if (m_mode == MODE_M17) { + m_m17->writeModem(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("M17 modem data received when in mode %u", m_mode); + } + } + + len = m_modem->readFMData(data); + if (m_fm != NULL && len > 0U) { + if (m_mode == MODE_IDLE) { + bool ret = m_fm->writeModem(data, len); + if (ret) { + m_modeTimer.setTimeout(m_fmRFModeHang); + setMode(MODE_FM); + } + } else if (m_mode == MODE_FM) { + m_fm->writeModem(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("FM modem data received when in mode %u", m_mode); + } + } + + len = m_modem->readAX25Data(data); + if (m_ax25 != NULL && len > 0U) { + if (m_mode == MODE_IDLE || m_mode == MODE_FM) { + m_ax25->writeModem(data, len); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("NXDN modem data received when in mode %u", m_mode); + } + } + len = m_modem->readTransparentData(data); if (transparentSocket != NULL && len > 0U) transparentSocket->write(data, len, transparentAddress, transparentAddrLen); @@ -943,6 +1024,25 @@ int CMMDVMHost::run() } } + if (m_m17 != NULL) { + ret = m_modem->hasM17Space(); + if (ret) { + len = m_m17->readModem(data); + if (len > 0U) { + if (m_mode == MODE_IDLE) { + m_modeTimer.setTimeout(m_m17NetModeHang); + setMode(MODE_M17); + } + if (m_mode == MODE_M17) { + m_modem->writeM17Data(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("M17 data received when in mode %u", m_mode); + } + } + } + } + if (m_pocsag != NULL) { ret = m_modem->hasPOCSAGSpace(); if (ret) { @@ -962,6 +1062,40 @@ int CMMDVMHost::run() } } + if (m_fm != NULL) { + unsigned int space = m_modem->getFMSpace(); + if (space > 0U) { + len = m_fm->readModem(data, space); + if (len > 0U) { + if (m_mode == MODE_IDLE) { + m_modeTimer.setTimeout(m_fmNetModeHang); + setMode(MODE_FM); + } + if (m_mode == MODE_FM) { + m_modem->writeFMData(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("FM data received when in mode %u", m_mode); + } + } + } + } + + if (m_ax25 != NULL) { + ret = m_modem->hasAX25Space(); + if (ret) { + len = m_ax25->readModem(data); + if (len > 0U) { + if (m_mode == MODE_IDLE || m_mode == MODE_FM) { + m_modem->writeAX25Data(data, len); + } + else if (m_mode != MODE_LOCKOUT) { + LogWarning("AX.25 data received when in mode %u", m_mode); + } + } + } + } + if (transparentSocket != NULL) { sockaddr_storage address; unsigned int addrlen; @@ -992,8 +1126,12 @@ int CMMDVMHost::run() m_p25->clock(ms); if (m_nxdn != NULL) m_nxdn->clock(ms); + if (m_m17 != NULL) + m_m17->clock(ms); if (m_pocsag != NULL) m_pocsag->clock(ms); + if (m_fm != NULL) + m_fm->clock(ms); if (m_dstarNetwork != NULL) m_dstarNetwork->clock(ms); @@ -1005,8 +1143,12 @@ int CMMDVMHost::run() m_p25Network->clock(ms); if (m_nxdnNetwork != NULL) m_nxdnNetwork->clock(ms); + if (m_m17Network != NULL) + m_m17Network->clock(ms); if (m_pocsagNetwork != NULL) m_pocsagNetwork->clock(ms); + if (m_fmNetwork != NULL) + m_fmNetwork->clock(ms); m_cwIdTimer.clock(ms); if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) { @@ -1068,9 +1210,6 @@ int CMMDVMHost::run() pocsagTimer.start(); } - if (m_ump != NULL) - m_ump->clock(ms); - if (ms < 5U) CThread::sleep(5U); } @@ -1083,11 +1222,6 @@ int CMMDVMHost::run() m_display->close(); delete m_display; - if (m_ump != NULL) { - m_ump->close(); - delete m_ump; - } - if (m_dmrLookup != NULL) m_dmrLookup->stop(); @@ -1119,11 +1253,26 @@ int CMMDVMHost::run() delete m_nxdnNetwork; } + if (m_m17Network != NULL) { + m_m17Network->close(); + delete m_m17Network; + } + if (m_pocsagNetwork != NULL) { m_pocsagNetwork->close(); delete m_pocsagNetwork; } + if (m_fmNetwork != NULL) { + m_fmNetwork->close(); + delete m_fmNetwork; + } + + if (m_ax25Network != NULL) { + m_ax25Network->close(); + delete m_ax25Network; + } + if (transparentSocket != NULL) { transparentSocket->close(); delete transparentSocket; @@ -1139,7 +1288,10 @@ int CMMDVMHost::run() delete m_ysf; delete m_p25; delete m_nxdn; + delete m_m17; delete m_pocsag; + delete m_fm; + delete m_ax25; return 0; } @@ -1148,6 +1300,7 @@ bool CMMDVMHost::createModem() { std::string port = m_conf.getModemPort(); std::string protocol = m_conf.getModemProtocol(); + unsigned int speed = m_conf.getModemSpeed(); unsigned int address = m_conf.getModemAddress(); bool rxInvert = m_conf.getModemRXInvert(); bool txInvert = m_conf.getModemTXInvert(); @@ -1161,8 +1314,10 @@ bool CMMDVMHost::createModem() float ysfTXLevel = m_conf.getModemYSFTXLevel(); float p25TXLevel = m_conf.getModemP25TXLevel(); float nxdnTXLevel = m_conf.getModemNXDNTXLevel(); + float m17TXLevel = m_conf.getModemM17TXLevel(); float pocsagTXLevel = m_conf.getModemPOCSAGTXLevel(); float fmTXLevel = m_conf.getModemFMTXLevel(); + float ax25TXLevel = m_conf.getModemAX25TXLevel(); bool trace = m_conf.getModemTrace(); bool debug = m_conf.getModemDebug(); unsigned int colorCode = m_conf.getDMRColorCode(); @@ -1170,6 +1325,7 @@ bool CMMDVMHost::createModem() unsigned int ysfTXHang = m_conf.getFusionTXHang(); unsigned int p25TXHang = m_conf.getP25TXHang(); unsigned int nxdnTXHang = m_conf.getNXDNTXHang(); + unsigned int m17TXHang = m_conf.getM17TXHang(); unsigned int rxFrequency = m_conf.getRXFrequency(); unsigned int txFrequency = m_conf.getTXFrequency(); unsigned int pocsagFrequency = m_conf.getPOCSAGFrequency(); @@ -1178,13 +1334,21 @@ bool CMMDVMHost::createModem() int rxDCOffset = m_conf.getModemRXDCOffset(); int txDCOffset = m_conf.getModemTXDCOffset(); float rfLevel = m_conf.getModemRFLevel(); + int rxTwist = m_conf.getAX25RXTwist(); + unsigned int ax25TXDelay = m_conf.getAX25TXDelay(); + unsigned int ax25SlotTime = m_conf.getAX25SlotTime(); + unsigned int ax25PPersist = m_conf.getAX25PPersist(); bool useCOSAsLockout = m_conf.getModemUseCOSAsLockout(); LogInfo("Modem Parameters"); LogInfo(" Port: %s", port.c_str()); +#if defined(__linux__) LogInfo(" Protocol: %s", protocol.c_str()); if (protocol == "i2c") - LogInfo(" i2c Address: %02X", address); + LogInfo(" I2C Address: %02X", address); + else +#endif + LogInfo(" Speed: %u", speed); LogInfo(" RX Invert: %s", rxInvert ? "yes" : "no"); LogInfo(" TX Invert: %s", txInvert ? "yes" : "no"); LogInfo(" PTT Invert: %s", pttInvert ? "yes" : "no"); @@ -1202,52 +1366,61 @@ bool CMMDVMHost::createModem() LogInfo(" YSF TX Level: %.1f%%", ysfTXLevel); LogInfo(" P25 TX Level: %.1f%%", p25TXLevel); LogInfo(" NXDN TX Level: %.1f%%", nxdnTXLevel); + LogInfo(" M17 TX Level: %.1f%%", m17TXLevel); LogInfo(" POCSAG TX Level: %.1f%%", pocsagTXLevel); LogInfo(" FM TX Level: %.1f%%", fmTXLevel); + LogInfo(" AX.25 TX Level: %.1f%%", ax25TXLevel); LogInfo(" TX Frequency: %uHz (%uHz)", txFrequency, txFrequency + txOffset); LogInfo(" Use COS as Lockout: %s", useCOSAsLockout ? "yes" : "no"); - m_modem = CModem::createModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, useCOSAsLockout, trace, debug); - m_modem->setSerialParams(protocol, address); - 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); + if (port == "NullModem") + m_modem = new CNullModem; + else + m_modem = new CSerialModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, useCOSAsLockout, trace, debug); + m_modem->setSerialParams(protocol, address, speed); + 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->setRFParams(rxFrequency, rxOffset, txFrequency, txOffset, txDCOffset, rxDCOffset, rfLevel, pocsagFrequency); m_modem->setDMRParams(colorCode); m_modem->setYSFParams(lowDeviation, ysfTXHang); m_modem->setP25Params(p25TXHang); m_modem->setNXDNParams(nxdnTXHang); + m_modem->setM17Params(m17TXHang); + m_modem->setAX25Params(rxTwist, ax25TXDelay, ax25SlotTime, ax25PPersist); if (m_fmEnabled) { - std::string callsign = m_conf.getFMCallsign(); - unsigned int callsignSpeed = m_conf.getFMCallsignSpeed(); - unsigned int callsignFrequency = m_conf.getFMCallsignFrequency(); - unsigned int callsignTime = m_conf.getFMCallsignTime(); - unsigned int callsignHoldoff = m_conf.getFMCallsignHoldoff(); - float callsignHighLevel = m_conf.getFMCallsignHighLevel(); - float callsignLowLevel = m_conf.getFMCallsignLowLevel(); - bool callsignAtStart = m_conf.getFMCallsignAtStart(); - bool callsignAtEnd = m_conf.getFMCallsignAtEnd(); - bool callsignAtLatch = m_conf.getFMCallsignAtLatch(); - std::string rfAck = m_conf.getFMRFAck(); - std::string extAck = m_conf.getFMExtAck(); - unsigned int ackSpeed = m_conf.getFMAckSpeed(); - unsigned int ackFrequency = m_conf.getFMAckFrequency(); - unsigned int ackMinTime = m_conf.getFMAckMinTime(); - unsigned int ackDelay = m_conf.getFMAckDelay(); - float ackLevel = m_conf.getFMAckLevel(); - unsigned int timeout = m_conf.getFMTimeout(); - float timeoutLevel = m_conf.getFMTimeoutLevel(); - float ctcssFrequency = m_conf.getFMCTCSSFrequency(); - unsigned int ctcssHighThreshold = m_conf.getFMCTCSSHighThreshold(); - unsigned int ctcssLowThreshold = m_conf.getFMCTCSSLowThreshold(); - float ctcssLevel = m_conf.getFMCTCSSLevel(); - unsigned int kerchunkTime = m_conf.getFMKerchunkTime(); - unsigned int hangTime = m_conf.getFMHangTime(); - unsigned int accessMode = m_conf.getFMAccessMode(); - bool cosInvert = m_conf.getFMCOSInvert(); - unsigned int rfAudioBoost = m_conf.getFMRFAudioBoost(); - float maxDevLevel = m_conf.getFMMaxDevLevel(); - unsigned int extAudioBoost = m_conf.getFMExtAudioBoost(); + std::string callsign = m_conf.getFMCallsign(); + unsigned int callsignSpeed = m_conf.getFMCallsignSpeed(); + unsigned int callsignFrequency = m_conf.getFMCallsignFrequency(); + unsigned int callsignTime = m_conf.getFMCallsignTime(); + unsigned int callsignHoldoff = m_conf.getFMCallsignHoldoff(); + float callsignHighLevel = m_conf.getFMCallsignHighLevel(); + float callsignLowLevel = m_conf.getFMCallsignLowLevel(); + bool callsignAtStart = m_conf.getFMCallsignAtStart(); + bool callsignAtEnd = m_conf.getFMCallsignAtEnd(); + bool callsignAtLatch = m_conf.getFMCallsignAtLatch(); + std::string rfAck = m_conf.getFMRFAck(); + unsigned int ackSpeed = m_conf.getFMAckSpeed(); + unsigned int ackFrequency = m_conf.getFMAckFrequency(); + unsigned int ackMinTime = m_conf.getFMAckMinTime(); + unsigned int ackDelay = m_conf.getFMAckDelay(); + float ackLevel = m_conf.getFMAckLevel(); + unsigned int timeout = m_conf.getFMTimeout(); + float timeoutLevel = m_conf.getFMTimeoutLevel(); + float ctcssFrequency = m_conf.getFMCTCSSFrequency(); + unsigned int ctcssHighThreshold = m_conf.getFMCTCSSHighThreshold(); + unsigned int ctcssLowThreshold = m_conf.getFMCTCSSLowThreshold(); + float ctcssLevel = m_conf.getFMCTCSSLevel(); + unsigned int kerchunkTime = m_conf.getFMKerchunkTime(); + unsigned int hangTime = m_conf.getFMHangTime(); + unsigned int accessMode = m_conf.getFMAccessMode(); + bool cosInvert = m_conf.getFMCOSInvert(); + bool noiseSquelch = m_conf.getFMNoiseSquelch(); + unsigned int squelchHighThreshold = m_conf.getFMSquelchHighThreshold(); + unsigned int squelchLowThreshold = m_conf.getFMSquelchLowThreshold(); + unsigned int rfAudioBoost = m_conf.getFMRFAudioBoost(); + float maxDevLevel = m_conf.getFMMaxDevLevel(); + unsigned int modeHangTime = m_conf.getFMModeHang(); LogInfo("FM Parameters"); LogInfo(" Callsign: %s", callsign.c_str()); @@ -1261,7 +1434,6 @@ bool CMMDVMHost::createModem() LogInfo(" Callsign At End: %s", callsignAtEnd ? "yes" : "no"); LogInfo(" Callsign At Latch: %s", callsignAtLatch ? "yes" : "no"); LogInfo(" RF Ack: %s", rfAck.c_str()); - // LogInfo(" Ext. Ack: %s", extAck.c_str()); LogInfo(" Ack Speed: %uWPM", ackSpeed); LogInfo(" Ack Frequency: %uHz", ackFrequency); LogInfo(" Ack Min Time: %us", ackMinTime); @@ -1277,13 +1449,30 @@ bool CMMDVMHost::createModem() LogInfo(" Hang Time: %us", hangTime); LogInfo(" Access Mode: %u", accessMode); LogInfo(" COS Invert: %s", cosInvert ? "yes" : "no"); + + LogInfo(" Noise Squelch: %s", noiseSquelch ? "yes" : "no"); + if (noiseSquelch) { + LogInfo(" Squelch High Threshold: %u", squelchHighThreshold); + LogInfo(" Squelch Low Threshold: %u", squelchLowThreshold); + } + LogInfo(" RF Audio Boost: x%u", rfAudioBoost); LogInfo(" Max. Deviation Level: %.1f%%", maxDevLevel); - // LogInfo(" Ext. Audio Boost: x%u", extAudioBoost); + LogInfo(" Mode Hang: %us", modeHangTime); m_modem->setFMCallsignParams(callsign, callsignSpeed, callsignFrequency, callsignTime, callsignHoldoff, callsignHighLevel, callsignLowLevel, callsignAtStart, callsignAtEnd, callsignAtLatch); m_modem->setFMAckParams(rfAck, ackSpeed, ackFrequency, ackMinTime, ackDelay, ackLevel); - m_modem->setFMMiscParams(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, hangTime, accessMode, cosInvert, rfAudioBoost, maxDevLevel); + m_modem->setFMMiscParams(timeout, timeoutLevel, ctcssFrequency, ctcssHighThreshold, ctcssLowThreshold, ctcssLevel, kerchunkTime, hangTime, accessMode, cosInvert, noiseSquelch, squelchHighThreshold, squelchLowThreshold, rfAudioBoost, maxDevLevel); + + if (m_conf.getFMNetworkEnabled()) { + std::string extAck = m_conf.getFMExtAck(); + unsigned int extAudioBoost = m_conf.getFMExtAudioBoost(); + + LogInfo(" Ext. Ack: %s", extAck.c_str()); + LogInfo(" Ext. Audio Boost: x%u", extAudioBoost); + + m_modem->setFMExtParams(extAck, extAudioBoost); + } } bool ret = m_modem->open(); @@ -1501,6 +1690,33 @@ bool CMMDVMHost::createNXDNNetwork() return true; } +bool CMMDVMHost::createM17Network() +{ + std::string gatewayAddress = m_conf.getM17GatewayAddress(); + unsigned int gatewayPort = m_conf.getM17GatewayPort(); + unsigned int localPort = m_conf.getM17LocalPort(); + m_m17NetModeHang = m_conf.getM17NetworkModeHang(); + bool debug = m_conf.getM17NetworkDebug(); + + LogInfo("M17 Network Parameters"); + LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); + LogInfo(" Gateway Port: %u", gatewayPort); + LogInfo(" Local Port: %u", localPort); + LogInfo(" Mode Hang: %us", m_m17NetModeHang); + + m_m17Network = new CM17Network(localPort, gatewayAddress, gatewayPort, debug); + bool ret = m_m17Network->open(); + if (!ret) { + delete m_m17Network; + m_m17Network = NULL; + return false; + } + + m_m17Network->enable(true); + + return true; +} + bool CMMDVMHost::createPOCSAGNetwork() { std::string gatewayAddress = m_conf.getPOCSAGGatewayAddress(); @@ -1531,6 +1747,62 @@ bool CMMDVMHost::createPOCSAGNetwork() return true; } +bool CMMDVMHost::createFMNetwork() +{ + std::string gatewayAddress = m_conf.getFMGatewayAddress(); + unsigned int gatewayPort = m_conf.getFMGatewayPort(); + std::string localAddress = m_conf.getFMLocalAddress(); + unsigned int localPort = m_conf.getFMLocalPort(); + unsigned int sampleRate = m_conf.getFMSampleRate(); + m_fmNetModeHang = m_conf.getFMNetworkModeHang(); + bool debug = m_conf.getFMNetworkDebug(); + + LogInfo("FM Network Parameters"); + LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); + LogInfo(" Gateway Port: %u", gatewayPort); + LogInfo(" Local Address: %s", localAddress.c_str()); + LogInfo(" Local Port: %u", localPort); + LogInfo(" Sample Rate: %u", sampleRate); + LogInfo(" Mode Hang: %us", m_fmNetModeHang); + + m_fmNetwork = new CFMNetwork(localAddress, localPort, gatewayAddress, gatewayPort, sampleRate, debug); + + bool ret = m_fmNetwork->open(); + if (!ret) { + delete m_fmNetwork; + m_fmNetwork = NULL; + return false; + } + + m_fmNetwork->enable(true); + + return true; +} + +bool CMMDVMHost::createAX25Network() +{ + std::string port = m_conf.getAX25NetworkPort(); + unsigned int speed = m_conf.getAX25NetworkSpeed(); + bool debug = m_conf.getAX25NetworkDebug(); + + LogInfo("AX.25 Network Parameters"); + LogInfo(" Port: %s", port.c_str()); + LogInfo(" Speed: %u", speed); + + m_ax25Network = new CAX25Network(port, speed, debug); + + bool ret = m_ax25Network->open(); + if (!ret) { + delete m_ax25Network; + m_ax25Network = NULL; + return false; + } + + m_ax25Network->enable(true); + + return true; +} + void CMMDVMHost::readParams() { m_dstarEnabled = m_conf.getDStarEnabled(); @@ -1538,8 +1810,10 @@ void CMMDVMHost::readParams() m_ysfEnabled = m_conf.getFusionEnabled(); m_p25Enabled = m_conf.getP25Enabled(); m_nxdnEnabled = m_conf.getNXDNEnabled(); + m_m17Enabled = m_conf.getM17Enabled(); m_pocsagEnabled = m_conf.getPOCSAGEnabled(); m_fmEnabled = m_conf.getFMEnabled(); + m_ax25Enabled = m_conf.getAX25Enabled(); m_duplex = m_conf.getDuplex(); m_callsign = m_conf.getCallsign(); m_id = m_conf.getId(); @@ -1555,8 +1829,10 @@ void CMMDVMHost::readParams() LogInfo(" YSF: %s", m_ysfEnabled ? "enabled" : "disabled"); LogInfo(" P25: %s", m_p25Enabled ? "enabled" : "disabled"); LogInfo(" NXDN: %s", m_nxdnEnabled ? "enabled" : "disabled"); + LogInfo(" M17: %s", m_m17Enabled ? "enabled" : "disabled"); LogInfo(" POCSAG: %s", m_pocsagEnabled ? "enabled" : "disabled"); LogInfo(" FM: %s", m_fmEnabled ? "enabled" : "disabled"); + LogInfo(" AX.25: %s", m_ax25Enabled ? "enabled" : "disabled"); } void CMMDVMHost::setMode(unsigned char mode) @@ -1576,8 +1852,14 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_m17Network != NULL) + m_m17Network->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); + if (m_ax25Network != NULL) + m_ax25Network->enable(false); if (m_dstar != NULL) m_dstar->enable(true); if (m_dmr != NULL) @@ -1588,11 +1870,15 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25->enable(false); if (m_nxdn != NULL) m_nxdn->enable(false); + if (m_m17 != NULL) + m_m17->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); + if (m_ax25 != NULL) + m_ax25->enable(false); m_modem->setMode(MODE_DSTAR); - if (m_ump != NULL) - m_ump->setMode(MODE_DSTAR); m_mode = MODE_DSTAR; m_modeTimer.start(); m_cwIdTimer.stop(); @@ -1610,8 +1896,14 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_m17Network != NULL) + m_m17Network->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); + if (m_ax25Network != NULL) + m_ax25Network->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1622,11 +1914,15 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25->enable(false); if (m_nxdn != NULL) m_nxdn->enable(false); + if (m_m17 != NULL) + m_m17->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); + if (m_ax25 != NULL) + m_ax25->enable(false); m_modem->setMode(MODE_DMR); - if (m_ump != NULL) - m_ump->setMode(MODE_DMR); if (m_duplex) { m_modem->writeDMRStart(true); m_dmrTXTimer.start(); @@ -1648,8 +1944,14 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_m17Network != NULL) + m_m17Network->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); + if (m_ax25Network != NULL) + m_ax25Network->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1660,11 +1962,15 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25->enable(false); if (m_nxdn != NULL) m_nxdn->enable(false); + if (m_m17 != NULL) + m_m17->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); + if (m_ax25 != NULL) + m_ax25->enable(false); m_modem->setMode(MODE_YSF); - if (m_ump != NULL) - m_ump->setMode(MODE_YSF); m_mode = MODE_YSF; m_modeTimer.start(); m_cwIdTimer.stop(); @@ -1682,8 +1988,14 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(true); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_m17Network != NULL) + m_m17Network->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); + if (m_ax25Network != NULL) + m_ax25Network->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1694,11 +2006,15 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25->enable(true); if (m_nxdn != NULL) m_nxdn->enable(false); + if (m_m17 != NULL) + m_m17->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); + if (m_ax25 != NULL) + m_ax25->enable(false); m_modem->setMode(MODE_P25); - if (m_ump != NULL) - m_ump->setMode(MODE_P25); m_mode = MODE_P25; m_modeTimer.start(); m_cwIdTimer.stop(); @@ -1716,6 +2032,52 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(true); + if (m_m17Network != NULL) + m_m17Network->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); + if (m_ax25Network != NULL) + m_ax25Network->enable(false); + if (m_dstar != NULL) + m_dstar->enable(false); + if (m_dmr != NULL) + m_dmr->enable(false); + if (m_ysf != NULL) + m_ysf->enable(false); + if (m_p25 != NULL) + m_p25->enable(false); + if (m_nxdn != NULL) + m_nxdn->enable(true); + if (m_m17 != NULL) + m_m17->enable(false); + if (m_pocsag != NULL) + m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); + if (m_ax25 != NULL) + m_ax25->enable(false); + m_modem->setMode(MODE_NXDN); + m_mode = MODE_NXDN; + m_modeTimer.start(); + m_cwIdTimer.stop(); + createLockFile("NXDN"); + break; + + case MODE_M17: + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(false); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); + if (m_p25Network != NULL) + m_p25Network->enable(false); + if (m_nxdnNetwork != NULL) + m_nxdnNetwork->enable(false); + if (m_m17Network != NULL) + m_m17Network->enable(true); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); if (m_dstar != NULL) @@ -1727,16 +2089,16 @@ void CMMDVMHost::setMode(unsigned char mode) if (m_p25 != NULL) m_p25->enable(false); if (m_nxdn != NULL) - m_nxdn->enable(true); + m_nxdn->enable(false); + if (m_m17 != NULL) + m_m17->enable(true); if (m_pocsag != NULL) m_pocsag->enable(false); - m_modem->setMode(MODE_NXDN); - if (m_ump != NULL) - m_ump->setMode(MODE_NXDN); - m_mode = MODE_NXDN; + m_modem->setMode(MODE_M17); + m_mode = MODE_M17; m_modeTimer.start(); m_cwIdTimer.stop(); - createLockFile("NXDN"); + createLockFile("M17"); break; case MODE_POCSAG: @@ -1750,8 +2112,14 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_m17Network != NULL) + m_m17Network->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(true); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); + if (m_ax25Network != NULL) + m_ax25Network->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1762,11 +2130,15 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25->enable(false); if (m_nxdn != NULL) m_nxdn->enable(false); + if (m_m17 != NULL) + m_m17->enable(false); if (m_pocsag != NULL) m_pocsag->enable(true); + if (m_fm != NULL) + m_fm->enable(false); + if (m_ax25 != NULL) + m_ax25->enable(false); m_modem->setMode(MODE_POCSAG); - if (m_ump != NULL) - m_ump->setMode(MODE_POCSAG); m_mode = MODE_POCSAG; m_modeTimer.start(); m_cwIdTimer.stop(); @@ -1784,8 +2156,14 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_m17Network != NULL) + m_m17Network->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(true); + if (m_ax25Network != NULL) + m_ax25Network->enable(true); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1796,17 +2174,22 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25->enable(false); if (m_nxdn != NULL) m_nxdn->enable(false); + if (m_m17 != NULL) + m_m17->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(true); + if (m_ax25 != NULL) + m_ax25->enable(true); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } - if (m_ump != NULL) - m_ump->setMode(MODE_FM); + m_modem->setMode(MODE_FM); m_display->setFM(); m_mode = MODE_FM; - m_modeTimer.stop(); + m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("FM"); break; @@ -1822,8 +2205,14 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_m17Network != NULL) + m_m17Network->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); + if (m_ax25Network != NULL) + m_ax25Network->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1834,15 +2223,19 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25->enable(false); if (m_nxdn != NULL) m_nxdn->enable(false); + if (m_m17 != NULL) + m_m17->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); + if (m_ax25 != NULL) + m_ax25->enable(false); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } m_modem->setMode(MODE_IDLE); - if (m_ump != NULL) - m_ump->setMode(MODE_IDLE); m_display->setLockout(); m_mode = MODE_LOCKOUT; m_modeTimer.stop(); @@ -1862,8 +2255,14 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_m17Network != NULL) + m_m17Network->enable(false); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(false); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(false); + if (m_ax25Network != NULL) + m_ax25Network->enable(false); if (m_dstar != NULL) m_dstar->enable(false); if (m_dmr != NULL) @@ -1874,14 +2273,18 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25->enable(false); if (m_nxdn != NULL) m_nxdn->enable(false); + if (m_m17 != NULL) + m_m17->enable(false); if (m_pocsag != NULL) m_pocsag->enable(false); + if (m_fm != NULL) + m_fm->enable(false); + if (m_ax25 != NULL) + m_ax25->enable(false); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } - if (m_ump != NULL) - m_ump->setMode(MODE_IDLE); m_display->setError("MODEM"); m_mode = MODE_ERROR; m_modeTimer.stop(); @@ -1900,8 +2303,14 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(true); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(true); + if (m_m17Network != NULL) + m_m17Network->enable(true); if (m_pocsagNetwork != NULL) m_pocsagNetwork->enable(true); + if (m_fmNetwork != NULL) + m_fmNetwork->enable(true); + if (m_ax25Network != NULL) + m_ax25Network->enable(true); if (m_dstar != NULL) m_dstar->enable(true); if (m_dmr != NULL) @@ -1912,15 +2321,19 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25->enable(true); if (m_nxdn != NULL) m_nxdn->enable(true); + if (m_m17 != NULL) + m_m17->enable(true); if (m_pocsag != NULL) m_pocsag->enable(true); + if (m_fm != NULL) + m_fm->enable(true); + if (m_ax25 != NULL) + m_ax25->enable(true); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } m_modem->setMode(MODE_IDLE); - if (m_ump != NULL) - m_ump->setMode(MODE_IDLE); if (m_mode == MODE_ERROR) { m_modem->sendCWId(m_callsign); m_cwIdTimer.setTimeout(m_cwIdTime); @@ -1991,78 +2404,102 @@ void CMMDVMHost::remoteControl() if (m_nxdn != NULL) processModeCommand(MODE_NXDN, m_nxdnRFModeHang); break; + case RCD_MODE_M17: + if (m_m17 != NULL) + processModeCommand(MODE_M17, m_m17RFModeHang); + break; case RCD_MODE_FM: - if (m_fmEnabled != false) + if (m_fmEnabled) processModeCommand(MODE_FM, 0); break; case RCD_ENABLE_DSTAR: - if (m_dstar != NULL && m_dstarEnabled==false) + if (m_dstar != NULL && !m_dstarEnabled) processEnableCommand(m_dstarEnabled, true); - if (m_dstarNetwork != NULL) - m_dstarNetwork->enable(true); + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(true); break; case RCD_ENABLE_DMR: - if (m_dmr != NULL && m_dmrEnabled==false) + if (m_dmr != NULL && !m_dmrEnabled) processEnableCommand(m_dmrEnabled, true); - if (m_dmrNetwork != NULL) - m_dmrNetwork->enable(true); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(true); break; case RCD_ENABLE_YSF: - if (m_ysf != NULL && m_ysfEnabled==false) + if (m_ysf != NULL && !m_ysfEnabled) processEnableCommand(m_ysfEnabled, true); - if (m_ysfNetwork != NULL) - m_ysfNetwork->enable(true); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(true); break; case RCD_ENABLE_P25: - if (m_p25 != NULL && m_p25Enabled==false) + if (m_p25 != NULL && !m_p25Enabled) processEnableCommand(m_p25Enabled, true); - if (m_p25Network != NULL) - m_p25Network->enable(true); + if (m_p25Network != NULL) + m_p25Network->enable(true); break; case RCD_ENABLE_NXDN: - if (m_nxdn != NULL && m_nxdnEnabled==false) + if (m_nxdn != NULL && !m_nxdnEnabled) processEnableCommand(m_nxdnEnabled, true); - if (m_nxdnNetwork != NULL) - m_nxdnNetwork->enable(true); + if (m_nxdnNetwork != NULL) + m_nxdnNetwork->enable(true); + break; + case RCD_ENABLE_M17: + if (m_m17 != NULL && m_m17Enabled == false) + processEnableCommand(m_m17Enabled, true); + if (m_m17Network != NULL) + m_m17Network->enable(true); break; case RCD_ENABLE_FM: - if (m_fmEnabled==false) + if (!m_fmEnabled) processEnableCommand(m_fmEnabled, true); break; + case RCD_ENABLE_AX25: + if (!m_ax25Enabled) + processEnableCommand(m_ax25Enabled, true); + break; case RCD_DISABLE_DSTAR: - if (m_dstar != NULL && m_dstarEnabled==true) + if (m_dstar != NULL && m_dstarEnabled) processEnableCommand(m_dstarEnabled, false); - if (m_dstarNetwork != NULL) - m_dstarNetwork->enable(false); + if (m_dstarNetwork != NULL) + m_dstarNetwork->enable(false); break; case RCD_DISABLE_DMR: - if (m_dmr != NULL && m_dmrEnabled==true) + if (m_dmr != NULL && m_dmrEnabled) processEnableCommand(m_dmrEnabled, false); - if (m_dmrNetwork != NULL) - m_dmrNetwork->enable(false); + if (m_dmrNetwork != NULL) + m_dmrNetwork->enable(false); break; case RCD_DISABLE_YSF: - if (m_ysf != NULL && m_ysfEnabled==true) + if (m_ysf != NULL && m_ysfEnabled) processEnableCommand(m_ysfEnabled, false); - if (m_ysfNetwork != NULL) - m_ysfNetwork->enable(false); + if (m_ysfNetwork != NULL) + m_ysfNetwork->enable(false); break; case RCD_DISABLE_P25: - if (m_p25 != NULL && m_p25Enabled==true) + if (m_p25 != NULL && m_p25Enabled) processEnableCommand(m_p25Enabled, false); - if (m_p25Network != NULL) - m_p25Network->enable(false); + if (m_p25Network != NULL) + m_p25Network->enable(false); break; case RCD_DISABLE_NXDN: - if (m_nxdn != NULL && m_nxdnEnabled==true) + if (m_nxdn != NULL && m_nxdnEnabled) processEnableCommand(m_nxdnEnabled, false); - if (m_nxdnNetwork != NULL) - m_nxdnNetwork->enable(false); + if (m_nxdnNetwork != NULL) + m_nxdnNetwork->enable(false); + break; + case RCD_DISABLE_M17: + if (m_m17 != NULL && m_m17Enabled == true) + processEnableCommand(m_m17Enabled, false); + if (m_m17Network != NULL) + m_m17Network->enable(false); break; case RCD_DISABLE_FM: - if (m_fmEnabled == true) + if (m_fmEnabled) processEnableCommand(m_fmEnabled, false); break; + case RCD_DISABLE_AX25: + if (m_ax25Enabled == true) + processEnableCommand(m_ax25Enabled, false); + break; case RCD_PAGE: if (m_pocsag != NULL) { unsigned int ric = m_remoteControl->getArgUInt(0U); @@ -2074,18 +2511,20 @@ void CMMDVMHost::remoteControl() } m_pocsag->sendPage(ric, text); } + break; case RCD_CW: setMode(MODE_IDLE); // Force the modem to go idle so that we can send the CW text. - if (!m_modem->hasTX()){ - std::string cwtext; - for (unsigned int i = 0U; i < m_remoteControl->getArgCount(); i++) { - if (i > 0U) - cwtext += " "; - cwtext += m_remoteControl->getArgString(i); - } - m_display->writeCW(); - m_modem->sendCWId(cwtext); - } + if (!m_modem->hasTX()) { + std::string cwtext; + for (unsigned int i = 0U; i < m_remoteControl->getArgCount(); i++) { + if (i > 0U) + cwtext += " "; + cwtext += m_remoteControl->getArgString(i); + } + m_display->writeCW(); + m_modem->sendCWId(cwtext); + } + break; default: break; } @@ -2112,8 +2551,10 @@ void CMMDVMHost::processModeCommand(unsigned char mode, unsigned int timeout) void CMMDVMHost::processEnableCommand(bool& mode, bool enabled) { LogDebug("Setting mode current=%s new=%s",mode ? "true" : "false",enabled ? "true" : "false"); - mode=enabled; - m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled, m_fmEnabled); + + mode = enabled; + + m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_m17Enabled, m_pocsagEnabled, m_fmEnabled, m_ax25Enabled); if (!m_modem->writeConfig()) LogError("Cannot write Config to MMDVM"); } diff --git a/MMDVMHost.h b/MMDVMHost.h index 735952a..c69318c 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -23,22 +23,27 @@ #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" #include "Display.h" #include "Timer.h" #include "Modem.h" #include "Conf.h" -#include "UMP.h" #include @@ -53,33 +58,42 @@ public: private: CConf m_conf; - CModem* m_modem; + IModem* m_modem; CDStarControl* m_dstar; CDMRControl* m_dmr; CYSFControl* m_ysf; CP25Control* m_p25; CNXDNControl* m_nxdn; + CM17Control* m_m17; CPOCSAGControl* m_pocsag; + CFMControl* m_fm; + CAX25Control* m_ax25; CDStarNetwork* m_dstarNetwork; IDMRNetwork* m_dmrNetwork; CYSFNetwork* m_ysfNetwork; CP25Network* m_p25Network; INXDNNetwork* m_nxdnNetwork; + CM17Network* m_m17Network; CPOCSAGNetwork* m_pocsagNetwork; + CFMNetwork* m_fmNetwork; + CAX25Network* m_ax25Network; CDisplay* m_display; - CUMP* m_ump; 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_m17RFModeHang; + 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_m17NetModeHang; unsigned int m_pocsagNetModeHang; + unsigned int m_fmNetModeHang; CTimer m_modeTimer; CTimer m_dmrTXTimer; CTimer m_cwIdTimer; @@ -90,8 +104,10 @@ private: bool m_ysfEnabled; bool m_p25Enabled; bool m_nxdnEnabled; + bool m_m17Enabled; bool m_pocsagEnabled; bool m_fmEnabled; + bool m_ax25Enabled; unsigned int m_cwIdTime; CDMRLookup* m_dmrLookup; CNXDNLookup* m_nxdnLookup; @@ -110,7 +126,10 @@ private: bool createYSFNetwork(); bool createP25Network(); bool createNXDNNetwork(); + bool createM17Network(); bool createPOCSAGNetwork(); + bool createFMNetwork(); + bool createAX25Network(); void remoteControl(); void processModeCommand(unsigned char mode, unsigned int timeout); diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index 8a7ebe4..95d241f 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -153,6 +153,9 @@ + + + @@ -183,13 +186,22 @@ + + - + + + + + + + + @@ -223,6 +235,7 @@ + @@ -231,16 +244,15 @@ + - - @@ -254,6 +266,8 @@ + + @@ -282,12 +296,20 @@ + + - + + + + + + + @@ -319,22 +341,22 @@ + + - - diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index b6aa8a8..d110fc5 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -80,9 +80,6 @@ Header Files - - Header Files - Header Files @@ -215,9 +212,6 @@ Header Files - - Header Files - Header Files @@ -272,9 +266,6 @@ Header Files - - Header Files - Header Files @@ -293,12 +284,57 @@ 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 + + + Header Files + + + Header Files + + + Header Files + + + Header Files + Header Files @@ -367,9 +403,6 @@ Source Files - - Source Files - Source Files @@ -493,9 +526,6 @@ Source Files - - Source Files - Source Files @@ -544,9 +574,6 @@ Source Files - - Source Files - Source Files @@ -565,12 +592,51 @@ Source Files + + Source Files + + + Source Files + Source Files Source Files + + 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 8dc7dfb..85a924a 100644 --- a/Makefile +++ b/Makefile @@ -2,19 +2,20 @@ CC = cc CXX = c++ -CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -LIBS = -lpthread -LDFLAGS = -g +CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -I/usr/local/include +LIBS = -lpthread -lsamplerate -lutil +LDFLAGS = -g -L/usr/local/lib OBJECTS = \ - AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMRDirectNetwork.o DMREMB.o \ - DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o \ - DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o LCDproc.o Log.o \ - MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.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 RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o StopWatch.o Sync.o SHA256.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o \ - UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o + AMBEFEC.o BCH.o AX25Control.o AX25Network.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \ + DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \ + DMRAccessControl.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 LCDproc.o Log.o M17Control.o M17Convolution.o M17CRC.o M17LICH.o M17Network.o M17Utils.o MMDVMHost.o \ + Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.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 PseudoTTYController.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o \ + RS241213.o RSSIInterpolator.o SerialController.o SerialModem.o SerialPort.o StopWatch.o Sync.o SHA256.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o \ + UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost RemoteCommand @@ -27,10 +28,33 @@ RemoteCommand: Log.o RemoteCommand.o UDPSocket.o %.o: %.cpp $(CXX) $(CFLAGS) -c -o $@ $< -install: +.PHONY install: +install: all install -m 755 MMDVMHost /usr/local/bin/ install -m 755 RemoteCommand /usr/local/bin/ +.PHONY install-service: +install-service: install /etc/MMDVM.ini + @useradd --user-group -M --system mmdvm --shell /bin/false || true + @usermod --groups dialout --append mmdvm || true + @mkdir /var/log/mmdvm || true + @chown mmdvm:mmdvm /var/log/mmdvm + @cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/ + @systemctl enable mmdvmhost.service + +/etc/MMDVM.ini: + @cp -n MMDVM.ini /etc/MMDVM.ini + @sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini + @sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini + @chown mmdvm:mmdvm /etc/MMDVM.ini + +.PHONY uninstall-service: +uninstall-service: + @systemctl stop mmdvmhost.service || true + @systemctl disable mmdvmhost.service || true + @rm -f /usr/local/bin/MMDVMHost || true + @rm -f /lib/systemd/system/mmdvmhost.service || true + clean: $(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h diff --git a/Makefile.Pi b/Makefile.Pi index aa7e3f4..2880c34 100644 --- a/Makefile.Pi +++ b/Makefile.Pi @@ -3,19 +3,19 @@ CC = cc CXX = c++ CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DRASPBERRY_PI -I/usr/local/include -LIBS = -lwiringPi -lwiringPiDev -lpthread +LIBS = -lwiringPi -lwiringPiDev -lpthread -lsamplerate -lutil LDFLAGS = -g -L/usr/local/lib OBJECTS = \ - AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMRDirectNetwork.o DMREMB.o \ - DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o \ - DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o LCDproc.o Log.o \ - MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.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 RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o StopWatch.o Sync.o SHA256.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o \ - UserDBentry.o Utils.o YSFControl.o YSFConvolution.o \ - YSFFICH.o YSFNetwork.o YSFPayload.o + AMBEFEC.o AX25Control.o AX25Network.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \ + DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \ + DMRAccessControl.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 LCDproc.o Log.o M17Control.o M17Convolution.o M17CRC.o M17LICH.o M17Network.o M17Utils.o MMDVMHost.o \ + Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.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 PseudoTTYController.o POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o \ + RS241213.o RSSIInterpolator.o SerialController.o SerialModem.o SerialPort.o StopWatch.o Sync.o SHA256.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o \ + UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost RemoteCommand @@ -28,10 +28,33 @@ RemoteCommand: Log.o RemoteCommand.o UDPSocket.o %.o: %.cpp $(CXX) $(CFLAGS) -c -o $@ $< -install: +.PHONY install: +install: all install -m 755 MMDVMHost /usr/local/bin/ install -m 755 RemoteCommand /usr/local/bin/ +.PHONY install-service: +install-service: install /etc/MMDVM.ini + @useradd --user-group -M --system mmdvm --shell /bin/false || true + @usermod --groups dialout --append mmdvm || true + @mkdir /var/log/mmdvm || true + @chown mmdvm:mmdvm /var/log/mmdvm + @cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/ + @systemctl enable mmdvmhost.service + +/etc/MMDVM.ini: + @cp -n MMDVM.ini /etc/MMDVM.ini + @sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini + @sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini + @chown mmdvm:mmdvm /etc/MMDVM.ini + +.PHONY uninstall-service: +uninstall-service: + @systemctl stop mmdvmhost.service || true + @systemctl disable mmdvmhost.service || true + @rm -f /usr/local/bin/MMDVMHost || true + @rm -f /lib/systemd/system/mmdvmhost.service || true + clean: $(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h diff --git a/Makefile.Pi.Adafruit b/Makefile.Pi.Adafruit index 01b7d9f..019b46a 100644 --- a/Makefile.Pi.Adafruit +++ b/Makefile.Pi.Adafruit @@ -4,18 +4,19 @@ CC = cc CXX = c++ CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DHD44780 -DADAFRUIT_DISPLAY -I/usr/local/include -LIBS = -lwiringPi -lwiringPiDev -lpthread +LIBS = -lwiringPi -lwiringPiDev -lpthread -lsamplerate -lutil LDFLAGS = -g -L/usr/local/lib OBJECTS = \ - AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMRDirectNetwork.o DMREMB.o \ - DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o \ - DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o \ + AMBEFEC.o AX25Control.o AX25Network.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \ + DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \ + DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \ + Hamming.o HD44780.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o M17Control.o M17Convolution.o M17CRC.o M17LICH.o M17Network.o M17Utils.o \ MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.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 RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o StopWatch.o Sync.o SHA256.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o \ - UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o + P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o POCSAGControl.o POCSAGNetwork.o QR1676.o \ + RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialModem.o SerialPort.o StopWatch.o Sync.o SHA256.o TFTSurenoo.o Thread.o \ + Timer.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost RemoteCommand @@ -28,10 +29,33 @@ RemoteCommand: Log.o RemoteCommand.o UDPSocket.o %.o: %.cpp $(CXX) $(CFLAGS) -c -o $@ $< -install: +.PHONY install: +install: all install -m 755 MMDVMHost /usr/local/bin/ install -m 755 RemoteCommand /usr/local/bin/ +.PHONY install-service: +install-service: install /etc/MMDVM.ini + @useradd --user-group -M --system mmdvm --shell /bin/false || true + @usermod --groups dialout --append mmdvm || true + @mkdir /var/log/mmdvm || true + @chown mmdvm:mmdvm /var/log/mmdvm + @cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/ + @systemctl enable mmdvmhost.service + +/etc/MMDVM.ini: + @cp -n MMDVM.ini /etc/MMDVM.ini + @sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini + @sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini + @chown mmdvm:mmdvm /etc/MMDVM.ini + +.PHONY uninstall-service: +uninstall-service: + @systemctl stop mmdvmhost.service || true + @systemctl disable mmdvmhost.service || true + @rm -f /usr/local/bin/MMDVMHost || true + @rm -f /lib/systemd/system/mmdvmhost.service || true + clean: $(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780 index 08458ca..811a0ac 100644 --- a/Makefile.Pi.HD44780 +++ b/Makefile.Pi.HD44780 @@ -3,18 +3,19 @@ CC = cc CXX = c++ CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DHD44780 -I/usr/local/include -LIBS = -lwiringPi -lwiringPiDev -lpthread +LIBS = -lwiringPi -lwiringPiDev -lpthread -lsamplerate -lutil LDFLAGS = -g -L/usr/local/lib OBJECTS = \ - AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMRDirectNetwork.o DMREMB.o \ - DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o \ - DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o \ + AMBEFEC.o AX25Control.o AX25Network.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \ + DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \ + DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \ + Hamming.o HD44780.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o M17Control.o M17Convolution.o M17CRC.o M17LICH.o M17Network.o M17Utils.o \ MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.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 RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o StopWatch.o Sync.o SHA256.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o \ - UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o + P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o POCSAGControl.o POCSAGNetwork.o QR1676.o \ + RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialModem.o SerialPort.o StopWatch.o Sync.o SHA256.o TFTSurenoo.o Thread.o \ + Timer.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost RemoteCommand @@ -27,10 +28,33 @@ RemoteCommand: Log.o RemoteCommand.o UDPSocket.o %.o: %.cpp $(CXX) $(CFLAGS) -c -o $@ $< -install: +.PHONY install: +install: all install -m 755 MMDVMHost /usr/local/bin/ install -m 755 RemoteCommand /usr/local/bin/ +.PHONY install-service: +install-service: install /etc/MMDVM.ini + @useradd --user-group -M --system mmdvm --shell /bin/false || true + @usermod --groups dialout --append mmdvm || true + @mkdir /var/log/mmdvm || true + @chown mmdvm:mmdvm /var/log/mmdvm + @cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/ + @systemctl enable mmdvmhost.service + +/etc/MMDVM.ini: + @cp -n MMDVM.ini /etc/MMDVM.ini + @sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini + @sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini + @chown mmdvm:mmdvm /etc/MMDVM.ini + +.PHONY uninstall-service: +uninstall-service: + @systemctl stop mmdvmhost.service || true + @systemctl disable mmdvmhost.service || true + @rm -f /usr/local/bin/MMDVMHost || true + @rm -f /lib/systemd/system/mmdvmhost.service || true + clean: $(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED index a58842b..a8beaec 100644 --- a/Makefile.Pi.OLED +++ b/Makefile.Pi.OLED @@ -3,7 +3,7 @@ CC = cc CXX = c++ CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DOLED -I/usr/local/include -LIBS = -lArduiPi_OLED -lpthread +LIBS = -lArduiPi_OLED -lpthread -lsamplerate -lutil # If you use NetBSD, add following CFLAGS #CFLAGS += -L/usr/local/lib -Wl,-rpath=/usr/local/lib @@ -11,14 +11,15 @@ LIBS = -lArduiPi_OLED -lpthread LDFLAGS = -g -L/usr/local/lib OBJECTS = \ - AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMRDirectNetwork.o DMREMB.o \ - DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o \ - DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o Golay24128.o Hamming.o I2CController.o OLED.o LCDproc.o Log.o \ - MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.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 RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o StopWatch.o Sync.o SHA256.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o \ - UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o + AMBEFEC.o AX25Control.o AX25Network.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \ + DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \ + DMRAccessControl.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 LCDproc.o Log.o M17Control.o M17Convolution.o M17CRC.o M17LICH.o M17Network.o M17Utils.o MMDVMHost.o \ + Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.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 OLED.o P25Audio.o P25Control.o \ + P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o POCSAGControl.o POCSAGNetwork.o QR1676.o \ + RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialModem.o SerialPort.o StopWatch.o Sync.o SHA256.o TFTSurenoo.o Thread.o \ + Timer.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost RemoteCommand @@ -31,10 +32,33 @@ RemoteCommand: Log.o RemoteCommand.o UDPSocket.o %.o: %.cpp $(CXX) $(CFLAGS) -c -o $@ $< -install: +.PHONY install: +install: all install -m 755 MMDVMHost /usr/local/bin/ install -m 755 RemoteCommand /usr/local/bin/ +.PHONY install-service: +install-service: install /etc/MMDVM.ini + @useradd --user-group -M --system mmdvm --shell /bin/false || true + @usermod --groups dialout --append mmdvm || true + @mkdir /var/log/mmdvm || true + @chown mmdvm:mmdvm /var/log/mmdvm + @cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/ + @systemctl enable mmdvmhost.service + +/etc/MMDVM.ini: + @cp -n MMDVM.ini /etc/MMDVM.ini + @sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini + @sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini + @chown mmdvm:mmdvm /etc/MMDVM.ini + +.PHONY uninstall-service: +uninstall-service: + @systemctl stop mmdvmhost.service || true + @systemctl disable mmdvmhost.service || true + @rm -f /usr/local/bin/MMDVMHost || true + @rm -f /lib/systemd/system/mmdvmhost.service || true + clean: $(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h diff --git a/Makefile.Pi.PCF8574 b/Makefile.Pi.PCF8574 index f9bfc81..21d4ed0 100644 --- a/Makefile.Pi.PCF8574 +++ b/Makefile.Pi.PCF8574 @@ -4,18 +4,19 @@ CC = cc CXX = c++ CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DHD44780 -DPCF8574_DISPLAY -I/usr/local/include -LIBS = -lwiringPi -lwiringPiDev -lpthread +LIBS = -lwiringPi -lwiringPiDev -lpthread -lsamplerate -lutil LDFLAGS = -g -L/usr/local/lib OBJECTS = \ - AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMRDirectNetwork.o DMREMB.o \ - DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTA.o \ - DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o I2CController.o LCDproc.o Log.o \ + AMBEFEC.o AX25Control.o AX25Network.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \ + DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \ + DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \ + Hamming.o HD44780.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o M17Control.o M17Convolution.o M17CRC.o M17LICH.o M17Network.o M17Utils.o \ MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullDisplay.o NullModem.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 RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o StopWatch.o Sync.o SHA256.o TFTSerial.o TFTSurenoo.o Thread.o Timer.o UDPSocket.o UMP.o UserDB.o \ - UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o + P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o POCSAGControl.o POCSAGNetwork.o QR1676.o \ + RemoteControl.o RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialModem.o SerialPort.o StopWatch.o Sync.o SHA256.o TFTSurenoo.o Thread.o \ + Timer.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost RemoteCommand @@ -28,10 +29,33 @@ RemoteCommand: Log.o RemoteCommand.o UDPSocket.o %.o: %.cpp $(CXX) $(CFLAGS) -c -o $@ $< -install: +.PHONY install: +install: all install -m 755 MMDVMHost /usr/local/bin/ install -m 755 RemoteCommand /usr/local/bin/ +.PHONY install-service: +install-service: install /etc/MMDVM.ini + @useradd --user-group -M --system mmdvm --shell /bin/false || true + @usermod --groups dialout --append mmdvm || true + @mkdir /var/log/mmdvm || true + @chown mmdvm:mmdvm /var/log/mmdvm + @cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/ + @systemctl enable mmdvmhost.service + +/etc/MMDVM.ini: + @cp -n MMDVM.ini /etc/MMDVM.ini + @sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini + @sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini + @chown mmdvm:mmdvm /etc/MMDVM.ini + +.PHONY uninstall-service: +uninstall-service: + @systemctl stop mmdvmhost.service || true + @systemctl disable mmdvmhost.service || true + @rm -f /usr/local/bin/MMDVMHost || true + @rm -f /lib/systemd/system/mmdvmhost.service || true + clean: $(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h diff --git a/Modem.cpp b/Modem.cpp index 75aca14..75c65dc 100644 --- a/Modem.cpp +++ b/Modem.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2018,2020 by Jonathan Naylor G4KLX + * 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 @@ -16,2151 +16,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "I2CController.h" -#include "DStarDefines.h" -#include "DMRDefines.h" -#include "YSFDefines.h" -#include "P25Defines.h" -#include "NXDNDefines.h" -#include "POCSAGDefines.h" -#include "Thread.h" #include "Modem.h" -#include "NullModem.h" -#include "Utils.h" -#include "Log.h" -#include -#include -#include -#include -#include - -#if defined(_WIN32) || defined(_WIN64) -#include -#else -#include -#endif - -const unsigned char MMDVM_FRAME_START = 0xE0U; - -const unsigned char MMDVM_GET_VERSION = 0x00U; -const unsigned char MMDVM_GET_STATUS = 0x01U; -const unsigned char MMDVM_SET_CONFIG = 0x02U; -const unsigned char MMDVM_SET_MODE = 0x03U; -const unsigned char MMDVM_SET_FREQ = 0x04U; - -const unsigned char MMDVM_SEND_CWID = 0x0AU; - -const unsigned char MMDVM_DSTAR_HEADER = 0x10U; -const unsigned char MMDVM_DSTAR_DATA = 0x11U; -const unsigned char MMDVM_DSTAR_LOST = 0x12U; -const unsigned char MMDVM_DSTAR_EOT = 0x13U; - -const unsigned char MMDVM_DMR_DATA1 = 0x18U; -const unsigned char MMDVM_DMR_LOST1 = 0x19U; -const unsigned char MMDVM_DMR_DATA2 = 0x1AU; -const unsigned char MMDVM_DMR_LOST2 = 0x1BU; -const unsigned char MMDVM_DMR_SHORTLC = 0x1CU; -const unsigned char MMDVM_DMR_START = 0x1DU; -const unsigned char MMDVM_DMR_ABORT = 0x1EU; - -const unsigned char MMDVM_YSF_DATA = 0x20U; -const unsigned char MMDVM_YSF_LOST = 0x21U; - -const unsigned char MMDVM_P25_HDR = 0x30U; -const unsigned char MMDVM_P25_LDU = 0x31U; -const unsigned char MMDVM_P25_LOST = 0x32U; - -const unsigned char MMDVM_NXDN_DATA = 0x40U; -const unsigned char MMDVM_NXDN_LOST = 0x41U; - -const unsigned char MMDVM_POCSAG_DATA = 0x50U; - -const unsigned char MMDVM_FM_PARAMS1 = 0x60U; -const unsigned char MMDVM_FM_PARAMS2 = 0x61U; -const unsigned char MMDVM_FM_PARAMS3 = 0x62U; - -const unsigned char MMDVM_ACK = 0x70U; -const unsigned char MMDVM_NAK = 0x7FU; - -const unsigned char MMDVM_SERIAL = 0x80U; - -const unsigned char MMDVM_TRANSPARENT = 0x90U; -const unsigned char MMDVM_QSO_INFO = 0x91U; - -const unsigned char MMDVM_DEBUG1 = 0xF1U; -const unsigned char MMDVM_DEBUG2 = 0xF2U; -const unsigned char MMDVM_DEBUG3 = 0xF3U; -const unsigned char MMDVM_DEBUG4 = 0xF4U; -const unsigned char MMDVM_DEBUG5 = 0xF5U; - -const unsigned int MAX_RESPONSES = 30U; - -const unsigned int BUFFER_LENGTH = 2000U; - - -CModem::CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug) : -m_port(port), -m_dmrColorCode(0U), -m_ysfLoDev(false), -m_ysfTXHang(4U), -m_p25TXHang(5U), -m_nxdnTXHang(5U), -m_duplex(duplex), -m_rxInvert(rxInvert), -m_txInvert(txInvert), -m_pttInvert(pttInvert), -m_txDelay(txDelay), -m_dmrDelay(dmrDelay), -m_rxLevel(0.0F), -m_cwIdTXLevel(0.0F), -m_dstarTXLevel(0.0F), -m_dmrTXLevel(0.0F), -m_ysfTXLevel(0.0F), -m_p25TXLevel(0.0F), -m_nxdnTXLevel(0.0F), -m_pocsagTXLevel(0.0F), -m_fmTXLevel(0.0F), -m_rfLevel(0.0F), -m_useCOSAsLockout(useCOSAsLockout), -m_trace(trace), -m_debug(debug), -m_rxFrequency(0U), -m_txFrequency(0U), -m_pocsagFrequency(0U), -m_dstarEnabled(false), -m_dmrEnabled(false), -m_ysfEnabled(false), -m_p25Enabled(false), -m_nxdnEnabled(false), -m_pocsagEnabled(false), -m_fmEnabled(false), -m_rxDCOffset(0), -m_txDCOffset(0), -m_serial(NULL), -m_buffer(NULL), -m_length(0U), -m_offset(0U), -m_rxDStarData(1000U, "Modem RX D-Star"), -m_txDStarData(1000U, "Modem TX D-Star"), -m_rxDMRData1(1000U, "Modem RX DMR1"), -m_rxDMRData2(1000U, "Modem RX DMR2"), -m_txDMRData1(1000U, "Modem TX DMR1"), -m_txDMRData2(1000U, "Modem TX DMR2"), -m_rxYSFData(1000U, "Modem RX YSF"), -m_txYSFData(1000U, "Modem TX YSF"), -m_rxP25Data(1000U, "Modem RX P25"), -m_txP25Data(1000U, "Modem TX P25"), -m_rxNXDNData(1000U, "Modem RX NXDN"), -m_txNXDNData(1000U, "Modem TX NXDN"), -m_txPOCSAGData(1000U, "Modem TX POCSAG"), -m_rxTransparentData(1000U, "Modem RX Transparent"), -m_txTransparentData(1000U, "Modem TX Transparent"), -m_sendTransparentDataFrameType(0U), -m_statusTimer(1000U, 0U, 250U), -m_inactivityTimer(1000U, 2U), -m_playoutTimer(1000U, 0U, 10U), -m_dstarSpace(0U), -m_dmrSpace1(0U), -m_dmrSpace2(0U), -m_ysfSpace(0U), -m_p25Space(0U), -m_nxdnSpace(0U), -m_pocsagSpace(0U), -m_tx(false), -m_cd(false), -m_lockout(false), -m_error(false), -m_mode(MODE_IDLE), -m_hwType(HWT_UNKNOWN), -m_fmCallsign(), -m_fmCallsignSpeed(20U), -m_fmCallsignFrequency(1000U), -m_fmCallsignTime(600U), -m_fmCallsignHoldoff(0U), -m_fmCallsignHighLevel(35.0F), -m_fmCallsignLowLevel(15.0F), -m_fmCallsignAtStart(true), -m_fmCallsignAtEnd(true), -m_fmCallsignAtLatch(true), -m_fmRfAck("K"), -m_fmAckSpeed(20U), -m_fmAckFrequency(1750U), -m_fmAckMinTime(4U), -m_fmAckDelay(1000U), -m_fmAckLevel(80.0F), -m_fmTimeout(120U), -m_fmTimeoutLevel(80.0F), -m_fmCtcssFrequency(88.4F), -m_fmCtcssHighThreshold(30U), -m_fmCtcssLowThreshold(20U), -m_fmCtcssLevel(10.0F), -m_fmKerchunkTime(0U), -m_fmHangTime(5U), -m_fmAccessMode(1U), -m_fmCOSInvert(false), -m_fmRFAudioBoost(1U), -m_fmMaxDevLevel(90.0F) -{ - m_buffer = new unsigned char[BUFFER_LENGTH]; - - assert(!port.empty()); -} - -CModem::~CModem() -{ - delete m_serial; - delete[] m_buffer; -} - -void CModem::setSerialParams(const std::string& protocol, unsigned int address) -{ - // Create the serial controller instance according the protocol specified in conf. - if (protocol == "i2c") - m_serial = new CI2CController(m_port, SERIAL_115200, address, true); - else - m_serial = new CSerialController(m_port, SERIAL_115200, true); -} - -void CModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency) -{ - m_rxFrequency = rxFrequency + rxOffset; - m_txFrequency = txFrequency + txOffset; - m_txDCOffset = txDCOffset; - m_rxDCOffset = rxDCOffset; - m_rfLevel = rfLevel; - m_pocsagFrequency = pocsagFrequency + txOffset; -} - -void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled, bool fmEnabled) -{ - m_dstarEnabled = dstarEnabled; - m_dmrEnabled = dmrEnabled; - m_ysfEnabled = ysfEnabled; - m_p25Enabled = p25Enabled; - m_nxdnEnabled = nxdnEnabled; - m_pocsagEnabled = pocsagEnabled; - m_fmEnabled = fmEnabled; -} - -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; - m_dstarTXLevel = dstarTXLevel; - m_dmrTXLevel = dmrTXLevel; - m_ysfTXLevel = ysfTXLevel; - m_p25TXLevel = p25TXLevel; - m_nxdnTXLevel = nxdnTXLevel; - m_pocsagTXLevel = pocsagTXLevel; - m_fmTXLevel = fmTXLevel; -} - -void CModem::setDMRParams(unsigned int colorCode) -{ - assert(colorCode < 16U); - - m_dmrColorCode = colorCode; -} - -void CModem::setYSFParams(bool loDev, unsigned int txHang) -{ - m_ysfLoDev = loDev; - m_ysfTXHang = txHang; -} - -void CModem::setP25Params(unsigned int txHang) -{ - m_p25TXHang = txHang; -} - -void CModem::setNXDNParams(unsigned int txHang) -{ - m_nxdnTXHang = txHang; -} - -void CModem::setTransparentDataParams(unsigned int sendFrameType) -{ - m_sendTransparentDataFrameType = sendFrameType; -} - -bool CModem::open() -{ - ::LogMessage("Opening the MMDVM"); - - bool ret = m_serial->open(); - if (!ret) - return false; - - ret = readVersion(); - if (!ret) { - m_serial->close(); - delete m_serial; - m_serial = NULL; - return false; - } else { - /* Stopping the inactivity timer here when a firmware version has been - successfuly read prevents the death spiral of "no reply from modem..." */ - m_inactivityTimer.stop(); - } - - ret = setFrequency(); - if (!ret) { - m_serial->close(); - delete m_serial; - m_serial = NULL; - return false; - } - - ret = setConfig(); - if (!ret) { - m_serial->close(); - delete m_serial; - m_serial = NULL; - return false; - } - - if (m_fmEnabled && m_duplex) { - ret = setFMCallsignParams(); - if (!ret) { - m_serial->close(); - delete m_serial; - m_serial = NULL; - return false; - } - - ret = setFMAckParams(); - if (!ret) { - m_serial->close(); - delete m_serial; - m_serial = NULL; - return false; - } - - ret = setFMMiscParams(); - if (!ret) { - m_serial->close(); - delete m_serial; - m_serial = NULL; - return false; - } - } - - m_statusTimer.start(); - - m_error = false; - m_offset = 0U; - - return true; -} - -void CModem::clock(unsigned int ms) -{ - assert(m_serial != NULL); - - // Poll the modem status every 250ms - m_statusTimer.clock(ms); - if (m_statusTimer.hasExpired()) { - readStatus(); - m_statusTimer.start(); - } - - m_inactivityTimer.clock(ms); - if (m_inactivityTimer.hasExpired()) { - LogError("No reply from the modem for some time, resetting it"); - m_error = true; - close(); - - CThread::sleep(2000U); // 2s - while (!open()) - CThread::sleep(5000U); // 5s - } - - RESP_TYPE_MMDVM type = getResponse(); - - if (type == RTM_TIMEOUT) { - // Nothing to do - } else if (type == RTM_ERROR) { - // Nothing to do - } else { - // type == RTM_OK - switch (m_buffer[2U]) { - case MMDVM_DSTAR_HEADER: { - if (m_trace) - CUtils::dump(1U, "RX D-Star Header", m_buffer, m_length); - - unsigned char data = m_length - 2U; - m_rxDStarData.addData(&data, 1U); - - data = TAG_HEADER; - m_rxDStarData.addData(&data, 1U); - - m_rxDStarData.addData(m_buffer + 3U, m_length - 3U); - } - break; - - case MMDVM_DSTAR_DATA: { - if (m_trace) - CUtils::dump(1U, "RX D-Star Data", m_buffer, m_length); - - unsigned char data = m_length - 2U; - m_rxDStarData.addData(&data, 1U); - - data = TAG_DATA; - m_rxDStarData.addData(&data, 1U); - - m_rxDStarData.addData(m_buffer + 3U, m_length - 3U); - } - break; - - case MMDVM_DSTAR_LOST: { - if (m_trace) - CUtils::dump(1U, "RX D-Star Lost", m_buffer, m_length); - - unsigned char data = 1U; - m_rxDStarData.addData(&data, 1U); - - data = TAG_LOST; - m_rxDStarData.addData(&data, 1U); - } - break; - - case MMDVM_DSTAR_EOT: { - if (m_trace) - CUtils::dump(1U, "RX D-Star EOT", m_buffer, m_length); - - unsigned char data = 1U; - m_rxDStarData.addData(&data, 1U); - - data = TAG_EOT; - m_rxDStarData.addData(&data, 1U); - } - break; - - case MMDVM_DMR_DATA1: { - if (m_trace) - CUtils::dump(1U, "RX DMR Data 1", m_buffer, m_length); - - unsigned char data = m_length - 2U; - m_rxDMRData1.addData(&data, 1U); - - if (m_buffer[3U] == (DMR_SYNC_DATA | DT_TERMINATOR_WITH_LC)) - data = TAG_EOT; - else - data = TAG_DATA; - m_rxDMRData1.addData(&data, 1U); - - m_rxDMRData1.addData(m_buffer + 3U, m_length - 3U); - } - break; - - case MMDVM_DMR_DATA2: { - if (m_trace) - CUtils::dump(1U, "RX DMR Data 2", m_buffer, m_length); - - unsigned char data = m_length - 2U; - m_rxDMRData2.addData(&data, 1U); - - if (m_buffer[3U] == (DMR_SYNC_DATA | DT_TERMINATOR_WITH_LC)) - data = TAG_EOT; - else - data = TAG_DATA; - m_rxDMRData2.addData(&data, 1U); - - m_rxDMRData2.addData(m_buffer + 3U, m_length - 3U); - } - break; - - case MMDVM_DMR_LOST1: { - if (m_trace) - CUtils::dump(1U, "RX DMR Lost 1", m_buffer, m_length); - - unsigned char data = 1U; - m_rxDMRData1.addData(&data, 1U); - - data = TAG_LOST; - m_rxDMRData1.addData(&data, 1U); - } - break; - - case MMDVM_DMR_LOST2: { - if (m_trace) - CUtils::dump(1U, "RX DMR Lost 2", m_buffer, m_length); - - unsigned char data = 1U; - m_rxDMRData2.addData(&data, 1U); - - data = TAG_LOST; - m_rxDMRData2.addData(&data, 1U); - } - break; - - case MMDVM_YSF_DATA: { - if (m_trace) - CUtils::dump(1U, "RX YSF Data", m_buffer, m_length); - - unsigned char data = m_length - 2U; - m_rxYSFData.addData(&data, 1U); - - data = TAG_DATA; - m_rxYSFData.addData(&data, 1U); - - m_rxYSFData.addData(m_buffer + 3U, m_length - 3U); - } - break; - - case MMDVM_YSF_LOST: { - if (m_trace) - CUtils::dump(1U, "RX YSF Lost", m_buffer, m_length); - - unsigned char data = 1U; - m_rxYSFData.addData(&data, 1U); - - data = TAG_LOST; - m_rxYSFData.addData(&data, 1U); - } - break; - - case MMDVM_P25_HDR: { - if (m_trace) - CUtils::dump(1U, "RX P25 Header", m_buffer, m_length); - - unsigned char data = m_length - 2U; - m_rxP25Data.addData(&data, 1U); - - data = TAG_HEADER; - m_rxP25Data.addData(&data, 1U); - - m_rxP25Data.addData(m_buffer + 3U, m_length - 3U); - } - break; - - case MMDVM_P25_LDU: { - if (m_trace) - CUtils::dump(1U, "RX P25 LDU", m_buffer, m_length); - - unsigned char data = m_length - 2U; - m_rxP25Data.addData(&data, 1U); - - data = TAG_DATA; - m_rxP25Data.addData(&data, 1U); - - m_rxP25Data.addData(m_buffer + 3U, m_length - 3U); - } - break; - - case MMDVM_P25_LOST: { - if (m_trace) - CUtils::dump(1U, "RX P25 Lost", m_buffer, m_length); - - unsigned char data = 1U; - m_rxP25Data.addData(&data, 1U); - - data = TAG_LOST; - m_rxP25Data.addData(&data, 1U); - } - break; - - case MMDVM_NXDN_DATA: { - if (m_trace) - CUtils::dump(1U, "RX NXDN Data", m_buffer, m_length); - - unsigned char data = m_length - 2U; - m_rxNXDNData.addData(&data, 1U); - - data = TAG_DATA; - m_rxNXDNData.addData(&data, 1U); - - m_rxNXDNData.addData(m_buffer + 3U, m_length - 3U); - } - break; - - case MMDVM_NXDN_LOST: { - if (m_trace) - CUtils::dump(1U, "RX NXDN Lost", m_buffer, m_length); - - unsigned char data = 1U; - m_rxNXDNData.addData(&data, 1U); - - data = TAG_LOST; - m_rxNXDNData.addData(&data, 1U); - } - break; - - case MMDVM_GET_STATUS: { - // if (m_trace) - // CUtils::dump(1U, "GET_STATUS", m_buffer, m_length); - - m_p25Space = 0U; - m_nxdnSpace = 0U; - m_pocsagSpace = 0U; - - m_mode = m_buffer[4U]; - - m_tx = (m_buffer[5U] & 0x01U) == 0x01U; - - bool adcOverflow = (m_buffer[5U] & 0x02U) == 0x02U; - if (adcOverflow) - LogError("MMDVM ADC levels have overflowed"); - - bool rxOverflow = (m_buffer[5U] & 0x04U) == 0x04U; - if (rxOverflow) - LogError("MMDVM RX buffer has overflowed"); - - bool txOverflow = (m_buffer[5U] & 0x08U) == 0x08U; - if (txOverflow) - LogError("MMDVM TX buffer has overflowed"); - - m_lockout = (m_buffer[5U] & 0x10U) == 0x10U; - - bool dacOverflow = (m_buffer[5U] & 0x20U) == 0x20U; - if (dacOverflow) - LogError("MMDVM DAC levels have overflowed"); - - m_cd = (m_buffer[5U] & 0x40U) == 0x40U; - - m_dstarSpace = m_buffer[6U]; - m_dmrSpace1 = m_buffer[7U]; - m_dmrSpace2 = m_buffer[8U]; - m_ysfSpace = m_buffer[9U]; - - if (m_length > 10U) - m_p25Space = m_buffer[10U]; - if (m_length > 11U) - m_nxdnSpace = m_buffer[11U]; - if (m_length > 12U) - m_pocsagSpace = m_buffer[12U]; - - m_inactivityTimer.start(); - // LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u,%u,%u lockout=%d, cd=%d", m_buffer[5U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, m_nxdnSpace, m_pocsagSpace, int(m_lockout), int(m_cd)); - } - break; - - case MMDVM_TRANSPARENT: { - if (m_trace) - CUtils::dump(1U, "RX Transparent Data", m_buffer, m_length); - - unsigned char offset = m_sendTransparentDataFrameType; - if (offset > 1U) offset = 1U; - unsigned char data = m_length - 3U + offset; - m_rxTransparentData.addData(&data, 1U); - - m_rxTransparentData.addData(m_buffer + 3U - offset, m_length - 3U + offset); - } - break; - - // These should not be received, but don't complain if we do - case MMDVM_GET_VERSION: - case MMDVM_ACK: - break; - - case MMDVM_NAK: - LogWarning("Received a NAK from the MMDVM, command = 0x%02X, reason = %u", m_buffer[3U], m_buffer[4U]); - break; - - case MMDVM_DEBUG1: - case MMDVM_DEBUG2: - case MMDVM_DEBUG3: - case MMDVM_DEBUG4: - case MMDVM_DEBUG5: - printDebug(); - break; - - case MMDVM_SERIAL: - //MMDVMHost does not process serial data from the display, - // so we send it to the transparent port if sendFrameType==1 - if (m_sendTransparentDataFrameType > 0U) { - if (m_trace) - CUtils::dump(1U, "RX Serial Data", m_buffer, m_length); - - unsigned char offset = m_sendTransparentDataFrameType; - if (offset > 1U) offset = 1U; - unsigned char data = m_length - 3U + offset; - m_rxTransparentData.addData(&data, 1U); - - m_rxTransparentData.addData(m_buffer + 3U - offset, m_length - 3U + offset); - break; //only break when sendFrameType>0, else message is unknown - } - default: - LogMessage("Unknown message, type: %02X", m_buffer[2U]); - CUtils::dump("Buffer dump", m_buffer, m_length); - break; - } - } - - // Only feed data to the modem if the playout timer has expired - m_playoutTimer.clock(ms); - if (!m_playoutTimer.hasExpired()) - return; - - if (m_dstarSpace > 1U && !m_txDStarData.isEmpty()) { - unsigned char buffer[4U]; - m_txDStarData.peek(buffer, 4U); - - if ((buffer[3U] == MMDVM_DSTAR_HEADER && m_dstarSpace > 4U) || - (buffer[3U] == MMDVM_DSTAR_DATA && m_dstarSpace > 1U) || - (buffer[3U] == MMDVM_DSTAR_EOT && m_dstarSpace > 1U)) { - unsigned char len = 0U; - m_txDStarData.getData(&len, 1U); - m_txDStarData.getData(m_buffer, len); - - switch (buffer[3U]) { - case MMDVM_DSTAR_HEADER: - if (m_trace) - CUtils::dump(1U, "TX D-Star Header", m_buffer, len); - m_dstarSpace -= 4U; - break; - case MMDVM_DSTAR_DATA: - if (m_trace) - CUtils::dump(1U, "TX D-Star Data", m_buffer, len); - m_dstarSpace -= 1U; - break; - default: - if (m_trace) - CUtils::dump(1U, "TX D-Star EOT", m_buffer, len); - m_dstarSpace -= 1U; - break; - } - - int ret = m_serial->write(m_buffer, len); - if (ret != int(len)) - LogWarning("Error when writing D-Star data to the MMDVM"); - - m_playoutTimer.start(); - } - } - - if (m_dmrSpace1 > 1U && !m_txDMRData1.isEmpty()) { - unsigned char len = 0U; - m_txDMRData1.getData(&len, 1U); - m_txDMRData1.getData(m_buffer, len); - - if (m_trace) - CUtils::dump(1U, "TX DMR Data 1", m_buffer, len); - - int ret = m_serial->write(m_buffer, len); - if (ret != int(len)) - LogWarning("Error when writing DMR data to the MMDVM"); - - m_playoutTimer.start(); - - m_dmrSpace1--; - } - - if (m_dmrSpace2 > 1U && !m_txDMRData2.isEmpty()) { - unsigned char len = 0U; - m_txDMRData2.getData(&len, 1U); - m_txDMRData2.getData(m_buffer, len); - - if (m_trace) - CUtils::dump(1U, "TX DMR Data 2", m_buffer, len); - - int ret = m_serial->write(m_buffer, len); - if (ret != int(len)) - LogWarning("Error when writing DMR data to the MMDVM"); - - m_playoutTimer.start(); - - m_dmrSpace2--; - } - - if (m_ysfSpace > 1U && !m_txYSFData.isEmpty()) { - unsigned char len = 0U; - m_txYSFData.getData(&len, 1U); - m_txYSFData.getData(m_buffer, len); - - if (m_trace) - CUtils::dump(1U, "TX YSF Data", m_buffer, len); - - int ret = m_serial->write(m_buffer, len); - if (ret != int(len)) - LogWarning("Error when writing YSF data to the MMDVM"); - - m_playoutTimer.start(); - - m_ysfSpace--; - } - - if (m_p25Space > 1U && !m_txP25Data.isEmpty()) { - unsigned char len = 0U; - m_txP25Data.getData(&len, 1U); - m_txP25Data.getData(m_buffer, len); - - if (m_trace) { - if (m_buffer[2U] == MMDVM_P25_HDR) - CUtils::dump(1U, "TX P25 HDR", m_buffer, len); - else - CUtils::dump(1U, "TX P25 LDU", m_buffer, len); - } - - int ret = m_serial->write(m_buffer, len); - if (ret != int(len)) - LogWarning("Error when writing P25 data to the MMDVM"); - - m_playoutTimer.start(); - - m_p25Space--; - } - - if (m_nxdnSpace > 1U && !m_txNXDNData.isEmpty()) { - unsigned char len = 0U; - m_txNXDNData.getData(&len, 1U); - m_txNXDNData.getData(m_buffer, len); - - if (m_trace) - CUtils::dump(1U, "TX NXDN Data", m_buffer, len); - - int ret = m_serial->write(m_buffer, len); - if (ret != int(len)) - LogWarning("Error when writing NXDN data to the MMDVM"); - - m_playoutTimer.start(); - - m_nxdnSpace--; - } - - if (m_pocsagSpace > 1U && !m_txPOCSAGData.isEmpty()) { - unsigned char len = 0U; - m_txPOCSAGData.getData(&len, 1U); - m_txPOCSAGData.getData(m_buffer, len); - - if (m_trace) - CUtils::dump(1U, "TX POCSAG Data", m_buffer, len); - - int ret = m_serial->write(m_buffer, len); - if (ret != int(len)) - LogWarning("Error when writing POCSAG data to the MMDVM"); - - m_playoutTimer.start(); - - m_pocsagSpace--; - } - - if (!m_txTransparentData.isEmpty()) { - unsigned char len = 0U; - m_txTransparentData.getData(&len, 1U); - m_txTransparentData.getData(m_buffer, len); - - if (m_trace) - CUtils::dump(1U, "TX Transparent Data", m_buffer, len); - - int ret = m_serial->write(m_buffer, len); - if (ret != int(len)) - LogWarning("Error when writing Transparent data to the MMDVM"); - } -} - -void CModem::close() -{ - assert(m_serial != NULL); - - ::LogMessage("Closing the MMDVM"); - - m_serial->close(); -} - -unsigned int CModem::readDStarData(unsigned char* data) -{ - assert(data != NULL); - - if (m_rxDStarData.isEmpty()) - return 0U; - - unsigned char len = 0U; - m_rxDStarData.getData(&len, 1U); - m_rxDStarData.getData(data, len); - - return len; -} - -unsigned int CModem::readDMRData1(unsigned char* data) -{ - assert(data != NULL); - - if (m_rxDMRData1.isEmpty()) - return 0U; - - unsigned char len = 0U; - m_rxDMRData1.getData(&len, 1U); - m_rxDMRData1.getData(data, len); - - return len; -} - -unsigned int CModem::readDMRData2(unsigned char* data) -{ - assert(data != NULL); - - if (m_rxDMRData2.isEmpty()) - return 0U; - - unsigned char len = 0U; - m_rxDMRData2.getData(&len, 1U); - m_rxDMRData2.getData(data, len); - - return len; -} - -unsigned int CModem::readYSFData(unsigned char* data) -{ - assert(data != NULL); - - if (m_rxYSFData.isEmpty()) - return 0U; - - unsigned char len = 0U; - m_rxYSFData.getData(&len, 1U); - m_rxYSFData.getData(data, len); - - return len; -} - -unsigned int CModem::readP25Data(unsigned char* data) -{ - assert(data != NULL); - - if (m_rxP25Data.isEmpty()) - return 0U; - - unsigned char len = 0U; - m_rxP25Data.getData(&len, 1U); - m_rxP25Data.getData(data, len); - - return len; -} - -unsigned int CModem::readNXDNData(unsigned char* data) -{ - assert(data != NULL); - - if (m_rxNXDNData.isEmpty()) - return 0U; - - unsigned char len = 0U; - m_rxNXDNData.getData(&len, 1U); - m_rxNXDNData.getData(data, len); - - return len; -} - -unsigned int CModem::readTransparentData(unsigned char* data) -{ - assert(data != NULL); - - if (m_rxTransparentData.isEmpty()) - return 0U; - - unsigned char len = 0U; - m_rxTransparentData.getData(&len, 1U); - m_rxTransparentData.getData(data, len); - - return len; -} - -// To be implemented later if needed -unsigned int CModem::readSerial(unsigned char* data, unsigned int length) -{ - assert(data != NULL); - assert(length > 0U); - - return 0U; -} - -bool CModem::hasDStarSpace() const -{ - unsigned int space = m_txDStarData.freeSpace() / (DSTAR_FRAME_LENGTH_BYTES + 4U); - - return space > 1U; -} - -bool CModem::writeDStarData(const unsigned char* data, unsigned int length) -{ - assert(data != NULL); - assert(length > 0U); - - unsigned char buffer[50U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = length + 2U; - - switch (data[0U]) { - case TAG_HEADER: - buffer[2U] = MMDVM_DSTAR_HEADER; - break; - case TAG_DATA: - buffer[2U] = MMDVM_DSTAR_DATA; - break; - case TAG_EOT: - buffer[2U] = MMDVM_DSTAR_EOT; - break; - default: - CUtils::dump(2U, "Unknown D-Star packet type", data, length); - return false; - } - - ::memcpy(buffer + 3U, data + 1U, length - 1U); - - unsigned char len = length + 2U; - m_txDStarData.addData(&len, 1U); - m_txDStarData.addData(buffer, len); - - return true; -} - -bool CModem::hasDMRSpace1() const -{ - unsigned int space = m_txDMRData1.freeSpace() / (DMR_FRAME_LENGTH_BYTES + 4U); - - return space > 1U; -} - -bool CModem::hasDMRSpace2() const -{ - unsigned int space = m_txDMRData2.freeSpace() / (DMR_FRAME_LENGTH_BYTES + 4U); - - return space > 1U; -} - -bool CModem::writeDMRData1(const unsigned char* data, unsigned int length) -{ - assert(data != NULL); - assert(length > 0U); - - if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) - return false; - - unsigned char buffer[40U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = length + 2U; - buffer[2U] = MMDVM_DMR_DATA1; - - ::memcpy(buffer + 3U, data + 1U, length - 1U); - - unsigned char len = length + 2U; - m_txDMRData1.addData(&len, 1U); - m_txDMRData1.addData(buffer, len); - - return true; -} - -bool CModem::writeDMRData2(const unsigned char* data, unsigned int length) -{ - assert(data != NULL); - assert(length > 0U); - - if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) - return false; - - unsigned char buffer[40U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = length + 2U; - buffer[2U] = MMDVM_DMR_DATA2; - - ::memcpy(buffer + 3U, data + 1U, length - 1U); - - unsigned char len = length + 2U; - m_txDMRData2.addData(&len, 1U); - m_txDMRData2.addData(buffer, len); - - return true; -} - -bool CModem::hasYSFSpace() const -{ - unsigned int space = m_txYSFData.freeSpace() / (YSF_FRAME_LENGTH_BYTES + 4U); - - return space > 1U; -} - -bool CModem::writeYSFData(const unsigned char* data, unsigned int length) -{ - assert(data != NULL); - assert(length > 0U); - - if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) - return false; - - unsigned char buffer[130U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = length + 2U; - buffer[2U] = MMDVM_YSF_DATA; - - ::memcpy(buffer + 3U, data + 1U, length - 1U); - - unsigned char len = length + 2U; - m_txYSFData.addData(&len, 1U); - m_txYSFData.addData(buffer, len); - - return true; -} - -bool CModem::hasP25Space() const -{ - unsigned int space = m_txP25Data.freeSpace() / (P25_LDU_FRAME_LENGTH_BYTES + 4U); - - return space > 1U; -} - -bool CModem::writeP25Data(const unsigned char* data, unsigned int length) -{ - assert(data != NULL); - assert(length > 0U); - - if (data[0U] != TAG_HEADER && data[0U] != TAG_DATA && data[0U] != TAG_EOT) - return false; - - unsigned char buffer[250U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = length + 2U; - buffer[2U] = (data[0U] == TAG_HEADER) ? MMDVM_P25_HDR : MMDVM_P25_LDU; - - ::memcpy(buffer + 3U, data + 1U, length - 1U); - - unsigned char len = length + 2U; - m_txP25Data.addData(&len, 1U); - m_txP25Data.addData(buffer, len); - - return true; -} - -bool CModem::hasNXDNSpace() const -{ - unsigned int space = m_txNXDNData.freeSpace() / (NXDN_FRAME_LENGTH_BYTES + 4U); - - return space > 1U; -} - -bool CModem::writeNXDNData(const unsigned char* data, unsigned int length) -{ - assert(data != NULL); - assert(length > 0U); - - if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) - return false; - - unsigned char buffer[130U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = length + 2U; - buffer[2U] = MMDVM_NXDN_DATA; - - ::memcpy(buffer + 3U, data + 1U, length - 1U); - - unsigned char len = length + 2U; - m_txNXDNData.addData(&len, 1U); - m_txNXDNData.addData(buffer, len); - - return true; -} - -bool CModem::hasPOCSAGSpace() const -{ - unsigned int space = m_txPOCSAGData.freeSpace() / (POCSAG_FRAME_LENGTH_BYTES + 4U); - - return space > 1U; -} - -bool CModem::writePOCSAGData(const unsigned char* data, unsigned int length) -{ - assert(data != NULL); - assert(length > 0U); - - unsigned char buffer[130U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = length + 3U; - buffer[2U] = MMDVM_POCSAG_DATA; - - ::memcpy(buffer + 3U, data, length); - - unsigned char len = length + 3U; - m_txPOCSAGData.addData(&len, 1U); - m_txPOCSAGData.addData(buffer, len); - - return true; -} - -bool CModem::writeTransparentData(const unsigned char* data, unsigned int length) -{ - assert(data != NULL); - assert(length > 0U); - - unsigned char buffer[250U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = length + 3U; - buffer[2U] = MMDVM_TRANSPARENT; - - if (m_sendTransparentDataFrameType > 0U) { - ::memcpy(buffer + 2U, data, length); - length--; - buffer[1U]--; - - //when sendFrameType==1 , only 0x80 and 0x90 (MMDVM_SERIAL and MMDVM_TRANSPARENT) are allowed - // and reverted to default (MMDVM_TRANSPARENT) for any other value - //when >1, frame type is not checked - if (m_sendTransparentDataFrameType == 1U) { - if ((buffer[2U] & 0xE0) != 0x80) - buffer[2U] = MMDVM_TRANSPARENT; - } - } else { - ::memcpy(buffer + 3U, data, length); - } - - unsigned char len = length + 3U; - m_txTransparentData.addData(&len, 1U); - m_txTransparentData.addData(buffer, len); - - return true; -} - -bool CModem::writeDStarInfo(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) -{ - assert(m_serial != NULL); - assert(my1 != NULL); - assert(my2 != NULL); - assert(your != NULL); - assert(type != NULL); - assert(reflector != NULL); - - unsigned char buffer[50U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 33U; - buffer[2U] = MMDVM_QSO_INFO; - - buffer[3U] = MODE_DSTAR; - - ::memcpy(buffer + 4U, my1, DSTAR_LONG_CALLSIGN_LENGTH); - ::memcpy(buffer + 12U, my2, DSTAR_SHORT_CALLSIGN_LENGTH); - - ::memcpy(buffer + 16U, your, DSTAR_LONG_CALLSIGN_LENGTH); - - ::memcpy(buffer + 24U, type, 1U); - - ::memcpy(buffer + 25U, reflector, DSTAR_LONG_CALLSIGN_LENGTH); - - return m_serial->write(buffer, 33U) != 33; -} - -bool CModem::writeDMRInfo(unsigned int slotNo, const std::string& src, bool group, const std::string& dest, const char* type) -{ - assert(m_serial != NULL); - assert(type != NULL); - - unsigned char buffer[50U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 47U; - buffer[2U] = MMDVM_QSO_INFO; - - buffer[3U] = MODE_DMR; - - buffer[4U] = slotNo; - - ::sprintf((char*)(buffer + 5U), "%20.20s", src.c_str()); - - buffer[25U] = group ? 'G' : 'I'; - - ::sprintf((char*)(buffer + 26U), "%20.20s", dest.c_str()); - - ::memcpy(buffer + 46U, type, 1U); - - return m_serial->write(buffer, 47U) != 47; -} - -bool CModem::writeYSFInfo(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) -{ - assert(m_serial != NULL); - assert(source != NULL); - assert(dest != NULL); - assert(type != NULL); - assert(origin != NULL); - - unsigned char buffer[40U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 36U; - buffer[2U] = MMDVM_QSO_INFO; - - buffer[3U] = MODE_YSF; - - ::memcpy(buffer + 4U, source, YSF_CALLSIGN_LENGTH); - ::memcpy(buffer + 14U, dest, YSF_CALLSIGN_LENGTH); - - ::memcpy(buffer + 24U, type, 1U); - - ::memcpy(buffer + 25U, origin, YSF_CALLSIGN_LENGTH); - - buffer[35U] = dgid; - - return m_serial->write(buffer, 36U) != 36; -} - -bool CModem::writeP25Info(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(m_serial != NULL); - assert(source != NULL); - assert(type != NULL); - - unsigned char buffer[40U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 31U; - buffer[2U] = MMDVM_QSO_INFO; - - buffer[3U] = MODE_DMR; - - ::sprintf((char*)(buffer + 4U), "%20.20s", source); - - buffer[24U] = group ? 'G' : 'I'; - - ::sprintf((char*)(buffer + 25U), "%05u", dest); // 16-bits - - ::memcpy(buffer + 30U, type, 1U); - - return m_serial->write(buffer, 31U) != 31; -} - -bool CModem::writeNXDNInfo(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(m_serial != NULL); - assert(source != NULL); - assert(type != NULL); - - unsigned char buffer[40U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 31U; - buffer[2U] = MMDVM_QSO_INFO; - - buffer[3U] = MODE_NXDN; - - ::sprintf((char*)(buffer + 4U), "%20.20s", source); - - buffer[24U] = group ? 'G' : 'I'; - - ::sprintf((char*)(buffer + 25U), "%05u", dest); // 16-bits - - ::memcpy(buffer + 30U, type, 1U); - - return m_serial->write(buffer, 31U) != 31; -} - -bool CModem::writePOCSAGInfo(unsigned int ric, const std::string& message) -{ - assert(m_serial != NULL); - - size_t length = message.size(); - - unsigned char buffer[250U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = length + 11U; - buffer[2U] = MMDVM_QSO_INFO; - - buffer[3U] = MODE_POCSAG; - - ::sprintf((char*)(buffer + 4U), "%07u", ric); // 21-bits - - ::memcpy(buffer + 11U, message.c_str(), length); - - int ret = m_serial->write(buffer, length + 11U); - - return ret != int(length + 11U); -} - -bool CModem::writeIPInfo(const std::string& address) -{ - assert(m_serial != NULL); - - size_t length = address.size(); - - unsigned char buffer[25U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = length + 4U; - buffer[2U] = MMDVM_QSO_INFO; - - buffer[3U] = 250U; - - ::memcpy(buffer + 4U, address.c_str(), length); - - int ret = m_serial->write(buffer, length + 4U); - - return ret != int(length + 4U); -} - -bool CModem::writeSerial(const unsigned char* data, unsigned int length) -{ - assert(m_serial != NULL); - assert(data != NULL); - assert(length > 0U); - - unsigned char buffer[250U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = length + 3U; - buffer[2U] = MMDVM_SERIAL; - - ::memcpy(buffer + 3U, data, length); - - int ret = m_serial->write(buffer, length + 3U); - - return ret != int(length + 3U); -} - -bool CModem::hasTX() const -{ - return m_tx; -} - -bool CModem::hasCD() const -{ - return m_cd; -} - -bool CModem::hasLockout() const -{ - return m_lockout; -} - -bool CModem::hasError() const -{ - return m_error; -} - -bool CModem::readVersion() -{ - assert(m_serial != NULL); - - CThread::sleep(2000U); // 2s - - for (unsigned int i = 0U; i < 6U; i++) { - unsigned char buffer[3U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 3U; - buffer[2U] = MMDVM_GET_VERSION; - - // CUtils::dump(1U, "Written", buffer, 3U); - - int ret = m_serial->write(buffer, 3U); - if (ret != 3) - return false; - -#if defined(__APPLE__) - m_serial->setNonblock(true); -#endif - - for (unsigned int count = 0U; count < MAX_RESPONSES; count++) { - CThread::sleep(10U); - RESP_TYPE_MMDVM resp = getResponse(); - if (resp == RTM_OK && m_buffer[2U] == MMDVM_GET_VERSION) { - if (::memcmp(m_buffer + 4U, "MMDVM ", 6U) == 0) - m_hwType = HWT_MMDVM; - else if (::memcmp(m_buffer + 4U, "DVMEGA", 6U) == 0) - m_hwType = HWT_DVMEGA; - else if (::memcmp(m_buffer + 4U, "ZUMspot", 7U) == 0) - m_hwType = HWT_MMDVM_ZUMSPOT; - else if (::memcmp(m_buffer + 4U, "MMDVM_HS_Hat", 12U) == 0) - m_hwType = HWT_MMDVM_HS_HAT; - else if (::memcmp(m_buffer + 4U, "MMDVM_HS_Dual_Hat", 17U) == 0) - m_hwType = HWT_MMDVM_HS_DUAL_HAT; - else if (::memcmp(m_buffer + 4U, "Nano_hotSPOT", 12U) == 0) - m_hwType = HWT_NANO_HOTSPOT; - else if (::memcmp(m_buffer + 4U, "Nano_DV", 7U) == 0) - m_hwType = HWT_NANO_DV; - else if (::memcmp(m_buffer + 4U, "D2RG_MMDVM_HS", 13U) == 0) - m_hwType = HWT_D2RG_MMDVM_HS; - else if (::memcmp(m_buffer + 4U, "MMDVM_HS-", 9U) == 0) - m_hwType = HWT_MMDVM_HS; - else if (::memcmp(m_buffer + 4U, "OpenGD77_HS", 11U) == 0) - m_hwType = HWT_OPENGD77_HS; - else if (::memcmp(m_buffer + 4U, "SkyBridge", 9U) == 0) - m_hwType = HWT_SKYBRIDGE; - - LogInfo("MMDVM protocol version: %u, description: %.*s", m_buffer[3U], m_length - 4U, m_buffer + 4U); - return true; - } - } - - CThread::sleep(1500U); - } - - LogError("Unable to read the firmware version after six attempts"); - - return false; -} - -bool CModem::readStatus() -{ - assert(m_serial != NULL); - - unsigned char buffer[3U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 3U; - buffer[2U] = MMDVM_GET_STATUS; - - // CUtils::dump(1U, "Written", buffer, 3U); - - return m_serial->write(buffer, 3U) == 3; -} - -bool CModem::writeConfig() -{ - return setConfig(); -} - -bool CModem::setConfig() -{ - assert(m_serial != NULL); - - unsigned char buffer[30U]; - - buffer[0U] = MMDVM_FRAME_START; - - buffer[1U] = 24U; - - buffer[2U] = MMDVM_SET_CONFIG; - - buffer[3U] = 0x00U; - if (m_rxInvert) - buffer[3U] |= 0x01U; - if (m_txInvert) - buffer[3U] |= 0x02U; - if (m_pttInvert) - buffer[3U] |= 0x04U; - if (m_ysfLoDev) - buffer[3U] |= 0x08U; - if (m_debug) - buffer[3U] |= 0x10U; - if (m_useCOSAsLockout) - buffer[3U] |= 0x20U; - if (!m_duplex) - buffer[3U] |= 0x80U; - - buffer[4U] = 0x00U; - if (m_dstarEnabled) - buffer[4U] |= 0x01U; - if (m_dmrEnabled) - buffer[4U] |= 0x02U; - if (m_ysfEnabled) - buffer[4U] |= 0x04U; - if (m_p25Enabled) - buffer[4U] |= 0x08U; - if (m_nxdnEnabled) - buffer[4U] |= 0x10U; - if (m_pocsagEnabled) - buffer[4U] |= 0x20U; - if (m_fmEnabled && m_duplex) - buffer[4U] |= 0x40U; - - buffer[5U] = m_txDelay / 10U; // In 10ms units - - buffer[6U] = MODE_IDLE; - - buffer[7U] = (unsigned char)(m_rxLevel * 2.55F + 0.5F); - - buffer[8U] = (unsigned char)(m_cwIdTXLevel * 2.55F + 0.5F); - - buffer[9U] = m_dmrColorCode; - - buffer[10U] = m_dmrDelay; - - buffer[11U] = 128U; // Was OscOffset - - buffer[12U] = (unsigned char)(m_dstarTXLevel * 2.55F + 0.5F); - buffer[13U] = (unsigned char)(m_dmrTXLevel * 2.55F + 0.5F); - buffer[14U] = (unsigned char)(m_ysfTXLevel * 2.55F + 0.5F); - buffer[15U] = (unsigned char)(m_p25TXLevel * 2.55F + 0.5F); - - buffer[16U] = (unsigned char)(m_txDCOffset + 128); - buffer[17U] = (unsigned char)(m_rxDCOffset + 128); - - buffer[18U] = (unsigned char)(m_nxdnTXLevel * 2.55F + 0.5F); - - buffer[19U] = (unsigned char)m_ysfTXHang; - - buffer[20U] = (unsigned char)(m_pocsagTXLevel * 2.55F + 0.5F); - - buffer[21U] = (unsigned char)(m_fmTXLevel * 2.55F + 0.5F); - - buffer[22U] = (unsigned char)m_p25TXHang; - - buffer[23U] = (unsigned char)m_nxdnTXHang; - - // CUtils::dump(1U, "Written", buffer, 24U); - - int ret = m_serial->write(buffer, 24U); - if (ret != 24) - return false; - - unsigned int count = 0U; - RESP_TYPE_MMDVM resp; - do { - CThread::sleep(10U); - - resp = getResponse(); - if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { - count++; - if (count >= MAX_RESPONSES) { - LogError("The MMDVM is not responding to the SET_CONFIG command"); - return false; - } - } - } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); - - // CUtils::dump(1U, "Response", m_buffer, m_length); - - if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { - LogError("Received a NAK to the SET_CONFIG command from the modem"); - return false; - } - - m_playoutTimer.start(); - - return true; -} - -bool CModem::setFrequency() -{ - assert(m_serial != NULL); - - unsigned char buffer[20U]; - unsigned char len; - unsigned int pocsagFrequency = 433000000U; - - if (m_pocsagEnabled) - pocsagFrequency = m_pocsagFrequency; - - if (m_hwType == HWT_DVMEGA) - len = 12U; - else { - buffer[12U] = (unsigned char)(m_rfLevel * 2.55F + 0.5F); - - buffer[13U] = (pocsagFrequency >> 0) & 0xFFU; - buffer[14U] = (pocsagFrequency >> 8) & 0xFFU; - buffer[15U] = (pocsagFrequency >> 16) & 0xFFU; - buffer[16U] = (pocsagFrequency >> 24) & 0xFFU; - - len = 17U; - } - - buffer[0U] = MMDVM_FRAME_START; - - buffer[1U] = len; - - buffer[2U] = MMDVM_SET_FREQ; - - buffer[3U] = 0x00U; - - buffer[4U] = (m_rxFrequency >> 0) & 0xFFU; - buffer[5U] = (m_rxFrequency >> 8) & 0xFFU; - buffer[6U] = (m_rxFrequency >> 16) & 0xFFU; - buffer[7U] = (m_rxFrequency >> 24) & 0xFFU; - - buffer[8U] = (m_txFrequency >> 0) & 0xFFU; - buffer[9U] = (m_txFrequency >> 8) & 0xFFU; - buffer[10U] = (m_txFrequency >> 16) & 0xFFU; - buffer[11U] = (m_txFrequency >> 24) & 0xFFU; - - // CUtils::dump(1U, "Written", buffer, len); - - int ret = m_serial->write(buffer, len); - if (ret != len) - return false; - - unsigned int count = 0U; - RESP_TYPE_MMDVM resp; - do { - CThread::sleep(10U); - - resp = getResponse(); - if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { - count++; - if (count >= MAX_RESPONSES) { - LogError("The MMDVM is not responding to the SET_FREQ command"); - return false; - } - } - } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); - - // CUtils::dump(1U, "Response", m_buffer, m_length); - - if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { - LogError("Received a NAK to the SET_FREQ command from the modem"); - return false; - } - - return true; -} - -RESP_TYPE_MMDVM CModem::getResponse() -{ - assert(m_serial != NULL); - - if (m_offset == 0U) { - // Get the start of the frame or nothing at all - int ret = m_serial->read(m_buffer + 0U, 1U); - if (ret < 0) { - LogError("Error when reading from the modem"); - return RTM_ERROR; - } - - if (ret == 0) - return RTM_TIMEOUT; - - if (m_buffer[0U] != MMDVM_FRAME_START) - return RTM_TIMEOUT; - - m_offset = 1U; - } - - if (m_offset == 1U) { - // Get the length of the frame - int ret = m_serial->read(m_buffer + 1U, 1U); - if (ret < 0) { - LogError("Error when reading from the modem"); - m_offset = 0U; - return RTM_ERROR; - } - - if (ret == 0) - return RTM_TIMEOUT; - - if (m_buffer[1U] >= 250U) { - LogError("Invalid length received from the modem - %u", m_buffer[1U]); - m_offset = 0U; - return RTM_ERROR; - } - - m_length = m_buffer[1U]; - m_offset = 2U; - } - - if (m_offset == 2U) { - // Get the frame type - int ret = m_serial->read(m_buffer + 2U, 1U); - if (ret < 0) { - LogError("Error when reading from the modem"); - m_offset = 0U; - return RTM_ERROR; - } - - if (ret == 0) - return RTM_TIMEOUT; - - m_offset = 3U; - } - - if (m_offset >= 3U) { - // Use later two byte length field - if (m_length == 0U) { - int ret = m_serial->read(m_buffer + 3U, 2U); - if (ret < 0) { - LogError("Error when reading from the modem"); - m_offset = 0U; - return RTM_ERROR; - } - - if (ret == 0) - return RTM_TIMEOUT; - - m_length = (m_buffer[3U] << 8) | m_buffer[4U]; - m_offset = 5U; - } - - while (m_offset < m_length) { - int ret = m_serial->read(m_buffer + m_offset, m_length - m_offset); - if (ret < 0) { - LogError("Error when reading from the modem"); - m_offset = 0U; - return RTM_ERROR; - } - - if (ret == 0) - return RTM_TIMEOUT; - - if (ret > 0) - m_offset += ret; - } - } - - m_offset = 0U; - - // CUtils::dump(1U, "Received", m_buffer, m_length); - - return RTM_OK; -} - -HW_TYPE CModem::getHWType() const -{ - return m_hwType; -} - -unsigned char CModem::getMode() const -{ - return m_mode; -} - -bool CModem::setMode(unsigned char mode) -{ - assert(m_serial != NULL); - - unsigned char buffer[4U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 4U; - buffer[2U] = MMDVM_SET_MODE; - buffer[3U] = mode; - - // CUtils::dump(1U, "Written", buffer, 4U); - - return m_serial->write(buffer, 4U) == 4; -} - -bool CModem::sendCWId(const std::string& callsign) -{ - assert(m_serial != NULL); - - unsigned int length = callsign.length(); - if (length > 200U) - length = 200U; - - unsigned char buffer[205U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = length + 3U; - buffer[2U] = MMDVM_SEND_CWID; - - for (unsigned int i = 0U; i < length; i++) - buffer[i + 3U] = callsign.at(i); - - // CUtils::dump(1U, "Written", buffer, length + 3U); - - return m_serial->write(buffer, length + 3U) == int(length + 3U); -} - -bool CModem::writeDMRStart(bool tx) -{ - assert(m_serial != NULL); - - if (tx && m_tx) - return true; - if (!tx && !m_tx) - return true; - - unsigned char buffer[4U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 4U; - buffer[2U] = MMDVM_DMR_START; - buffer[3U] = tx ? 0x01U : 0x00U; - - // CUtils::dump(1U, "Written", buffer, 4U); - - return m_serial->write(buffer, 4U) == 4; -} - -bool CModem::writeDMRAbort(unsigned int slotNo) -{ - assert(m_serial != NULL); - - if (slotNo == 1U) - m_txDMRData1.clear(); - else - m_txDMRData2.clear(); - - unsigned char buffer[4U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 4U; - buffer[2U] = MMDVM_DMR_ABORT; - buffer[3U] = slotNo; - - // CUtils::dump(1U, "Written", buffer, 4U); - - return m_serial->write(buffer, 4U) == 4; -} - -bool CModem::writeDMRShortLC(const unsigned char* lc) -{ - assert(m_serial != NULL); - assert(lc != NULL); - - unsigned char buffer[12U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 12U; - buffer[2U] = MMDVM_DMR_SHORTLC; - buffer[3U] = lc[0U]; - buffer[4U] = lc[1U]; - buffer[5U] = lc[2U]; - buffer[6U] = lc[3U]; - buffer[7U] = lc[4U]; - buffer[8U] = lc[5U]; - buffer[9U] = lc[6U]; - buffer[10U] = lc[7U]; - buffer[11U] = lc[8U]; - - // CUtils::dump(1U, "Written", buffer, 12U); - - return m_serial->write(buffer, 12U) == 12; -} - -void CModem::setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch) -{ - m_fmCallsign = callsign; - m_fmCallsignSpeed = callsignSpeed; - m_fmCallsignFrequency = callsignFrequency; - m_fmCallsignTime = callsignTime; - m_fmCallsignHoldoff = callsignHoldoff; - m_fmCallsignHighLevel = callsignHighLevel; - m_fmCallsignLowLevel = callsignLowLevel; - m_fmCallsignAtStart = callsignAtStart; - m_fmCallsignAtEnd = callsignAtEnd; - m_fmCallsignAtLatch = callsignAtLatch; -} - -void CModem::setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, unsigned int ackFrequency, unsigned int ackMinTime, unsigned int ackDelay, float ackLevel) -{ - m_fmRfAck = rfAck; - m_fmAckSpeed = ackSpeed; - m_fmAckFrequency = ackFrequency; - m_fmAckMinTime = ackMinTime; - m_fmAckDelay = ackDelay; - m_fmAckLevel = ackLevel; -} - -void CModem::setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssHighThreshold, unsigned int ctcssLowThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, unsigned int accessMode, bool cosInvert, unsigned int rfAudioBoost, float maxDevLevel) -{ - m_fmTimeout = timeout; - m_fmTimeoutLevel = timeoutLevel; - - m_fmCtcssFrequency = ctcssFrequency; - m_fmCtcssHighThreshold = ctcssHighThreshold; - m_fmCtcssLowThreshold = ctcssLowThreshold; - m_fmCtcssLevel = ctcssLevel; - - m_fmKerchunkTime = kerchunkTime; - m_fmHangTime = hangTime; - - m_fmAccessMode = accessMode; - m_fmCOSInvert = cosInvert; - - m_fmRFAudioBoost = rfAudioBoost; - m_fmMaxDevLevel = maxDevLevel; -} - -bool CModem::setFMCallsignParams() -{ - assert(m_serial != NULL); - - unsigned char buffer[80U]; - unsigned char len = 10U + m_fmCallsign.size(); - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = len; - buffer[2U] = MMDVM_FM_PARAMS1; - - buffer[3U] = m_fmCallsignSpeed; - buffer[4U] = m_fmCallsignFrequency / 10U; - buffer[5U] = m_fmCallsignTime; - buffer[6U] = m_fmCallsignHoldoff; - - buffer[7U] = (unsigned char)(m_fmCallsignHighLevel * 2.55F + 0.5F); - buffer[8U] = (unsigned char)(m_fmCallsignLowLevel * 2.55F + 0.5F); - - buffer[9U] = 0x00U; - if (m_fmCallsignAtStart) - buffer[9U] |= 0x01U; - if (m_fmCallsignAtEnd) - buffer[9U] |= 0x02U; - if (m_fmCallsignAtLatch) - buffer[9U] |= 0x04U; - - for (unsigned int i = 0U; i < m_fmCallsign.size(); i++) - buffer[10U + i] = m_fmCallsign.at(i); - - // CUtils::dump(1U, "Written", buffer, len); - - int ret = m_serial->write(buffer, len); - if (ret != len) - return false; - - unsigned int count = 0U; - RESP_TYPE_MMDVM resp; - do { - CThread::sleep(10U); - - resp = getResponse(); - if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { - count++; - if (count >= MAX_RESPONSES) { - LogError("The MMDVM is not responding to the SET_FM_PARAMS1 command"); - return false; - } - } - } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); - - // CUtils::dump(1U, "Response", m_buffer, m_length); - - if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { - LogError("Received a NAK to the SET_FM_PARAMS1 command from the modem"); - return false; - } - - return true; -} - -bool CModem::setFMAckParams() -{ - assert(m_serial != NULL); - - unsigned char buffer[80U]; - unsigned char len = 8U + m_fmRfAck.size(); - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = len; - buffer[2U] = MMDVM_FM_PARAMS2; - - buffer[3U] = m_fmAckSpeed; - buffer[4U] = m_fmAckFrequency / 10U; - buffer[5U] = m_fmAckMinTime; - buffer[6U] = m_fmAckDelay / 10U; - - buffer[7U] = (unsigned char)(m_fmAckLevel * 2.55F + 0.5F); - - for (unsigned int i = 0U; i < m_fmRfAck.size(); i++) - buffer[8U + i] = m_fmRfAck.at(i); - - // CUtils::dump(1U, "Written", buffer, len); - - int ret = m_serial->write(buffer, len); - if (ret != len) - return false; - - unsigned int count = 0U; - RESP_TYPE_MMDVM resp; - do { - CThread::sleep(10U); - - resp = getResponse(); - if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { - count++; - if (count >= MAX_RESPONSES) { - LogError("The MMDVM is not responding to the SET_FM_PARAMS2 command"); - return false; - } - } - } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); - - // CUtils::dump(1U, "Response", m_buffer, m_length); - - if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { - LogError("Received a NAK to the SET_FM_PARAMS2 command from the modem"); - return false; - } - - return true; -} - -bool CModem::setFMMiscParams() -{ - assert(m_serial != NULL); - - unsigned char buffer[20U]; - - buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 15U; - buffer[2U] = MMDVM_FM_PARAMS3; - - buffer[3U] = m_fmTimeout / 5U; - buffer[4U] = (unsigned char)(m_fmTimeoutLevel * 2.55F + 0.5F); - - buffer[5U] = (unsigned char)m_fmCtcssFrequency; - buffer[6U] = m_fmCtcssHighThreshold; - buffer[7U] = m_fmCtcssLowThreshold; - buffer[8U] = (unsigned char)(m_fmCtcssLevel * 2.55F + 0.5F); - - buffer[9U] = m_fmKerchunkTime; - buffer[10U] = m_fmHangTime; - - buffer[11U] = m_fmAccessMode; - if (m_fmCOSInvert) - buffer[11U] |= 0x80U; - - buffer[12U] = m_fmRFAudioBoost; - - buffer[13U] = (unsigned char)(m_fmMaxDevLevel * 2.55F + 0.5F); - - buffer[14U] = (unsigned char)(m_rxLevel * 2.55F + 0.5F); - - // CUtils::dump(1U, "Written", buffer, 15U); - - int ret = m_serial->write(buffer, 15U); - if (ret != 15) - return false; - - unsigned int count = 0U; - RESP_TYPE_MMDVM resp; - do { - CThread::sleep(10U); - - resp = getResponse(); - if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { - count++; - if (count >= MAX_RESPONSES) { - LogError("The MMDVM is not responding to the SET_FM_PARAMS3 command"); - return false; - } - } - } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); - - // CUtils::dump(1U, "Response", m_buffer, m_length); - - if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { - LogError("Received a NAK to the SET_FM_PARAMS3 command from the modem"); - return false; - } - - return true; -} - -void CModem::printDebug() +IModem::~IModem() { - if (m_buffer[2U] == MMDVM_DEBUG1) { - LogMessage("Debug: %.*s", m_length - 3U, m_buffer + 3U); - } else if (m_buffer[2U] == MMDVM_DEBUG2) { - short val1 = (m_buffer[m_length - 2U] << 8) | m_buffer[m_length - 1U]; - LogMessage("Debug: %.*s %d", m_length - 5U, m_buffer + 3U, val1); - } else if (m_buffer[2U] == MMDVM_DEBUG3) { - short val1 = (m_buffer[m_length - 4U] << 8) | m_buffer[m_length - 3U]; - short val2 = (m_buffer[m_length - 2U] << 8) | m_buffer[m_length - 1U]; - LogMessage("Debug: %.*s %d %d", m_length - 7U, m_buffer + 3U, val1, val2); - } else if (m_buffer[2U] == MMDVM_DEBUG4) { - short val1 = (m_buffer[m_length - 6U] << 8) | m_buffer[m_length - 5U]; - short val2 = (m_buffer[m_length - 4U] << 8) | m_buffer[m_length - 3U]; - short val3 = (m_buffer[m_length - 2U] << 8) | m_buffer[m_length - 1U]; - LogMessage("Debug: %.*s %d %d %d", m_length - 9U, m_buffer + 3U, val1, val2, val3); - } else if (m_buffer[2U] == MMDVM_DEBUG5) { - short val1 = (m_buffer[m_length - 8U] << 8) | m_buffer[m_length - 7U]; - short val2 = (m_buffer[m_length - 6U] << 8) | m_buffer[m_length - 5U]; - short val3 = (m_buffer[m_length - 4U] << 8) | m_buffer[m_length - 3U]; - short val4 = (m_buffer[m_length - 2U] << 8) | m_buffer[m_length - 1U]; - LogMessage("Debug: %.*s %d %d %d %d", m_length - 11U, m_buffer + 3U, val1, val2, val3, val4); - } -} - -CModem* CModem::createModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug){ - if (port == "NullModem") - return new CNullModem(port, duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, useCOSAsLockout, trace, debug); - else - return new CModem(port, duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, useCOSAsLockout, trace, debug); } diff --git a/Modem.h b/Modem.h index e28aad4..faf0917 100644 --- a/Modem.h +++ b/Modem.h @@ -19,217 +19,103 @@ #ifndef MODEM_H #define MODEM_H -#include "SerialController.h" -#include "RingBuffer.h" #include "Defines.h" -#include "Timer.h" #include -enum RESP_TYPE_MMDVM { - RTM_OK, - RTM_TIMEOUT, - RTM_ERROR -}; - -class CModem { +class IModem { public: - CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug); - virtual ~CModem(); + virtual ~IModem() = 0; - virtual void setSerialParams(const std::string& protocol, unsigned int address); - virtual void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency); - virtual void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled, bool fmEnabled); - virtual void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagLevel, float fmTXLevel); - virtual void setDMRParams(unsigned int colorCode); - virtual void setYSFParams(bool loDev, unsigned int txHang); - virtual void setP25Params(unsigned int txHang); - virtual void setNXDNParams(unsigned int txHang); - virtual void setTransparentDataParams(unsigned int sendFrameType); + virtual void setSerialParams(const std::string& protocol, unsigned int address, unsigned int speed) = 0; + virtual void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency) = 0; + virtual void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool m17ENabled, bool pocsagEnabled, bool fmEnabled, bool ax25Enabled) = 0; + virtual void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float m17TXLevel, float pocsagLevel, float fmTXLevel, float ax25TXLevel) = 0; + virtual void setDMRParams(unsigned int colorCode) = 0; + virtual void setYSFParams(bool loDev, unsigned int txHang) = 0; + virtual void setP25Params(unsigned int txHang) = 0; + virtual void setNXDNParams(unsigned int txHang) = 0; + virtual void setM17Params(unsigned int txHang) = 0; + virtual void setAX25Params(int rxTwist, unsigned int txDelay, unsigned int slotTime, unsigned int pPersist) = 0; + virtual void setTransparentDataParams(unsigned int sendFrameType) = 0; - virtual void setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch); - virtual void setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, unsigned int ackFrequency, unsigned int ackMinTime, unsigned int ackDelay, float ackLevel); - virtual void setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssHighThreshold, unsigned int ctcssLowThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, unsigned int accessMode, bool cosInvert, unsigned int rfAudioBoost, float maxDevLevel); + virtual void setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch) = 0; + virtual void setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, unsigned int ackFrequency, unsigned int ackMinTime, unsigned int ackDelay, float ackLevel) = 0; + virtual void setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssHighThreshold, unsigned int ctcssLowThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, unsigned int accessMode, bool cosInvert, bool noiseSquelch, unsigned int squelchHighThreshold, unsigned int squelchLowThreshold, unsigned int rfAudioBoost, float maxDevLevel) = 0; + virtual void setFMExtParams(const std::string& ack, unsigned int audioBoost) = 0; - virtual bool open(); + virtual bool open() = 0; - virtual unsigned int readDStarData(unsigned char* data); - virtual unsigned int readDMRData1(unsigned char* data); - virtual unsigned int readDMRData2(unsigned char* data); - virtual unsigned int readYSFData(unsigned char* data); - virtual unsigned int readP25Data(unsigned char* data); - virtual unsigned int readNXDNData(unsigned char* data); - virtual unsigned int readTransparentData(unsigned char* data); + virtual unsigned int readDStarData(unsigned char* data) = 0; + virtual unsigned int readDMRData1(unsigned char* data) = 0; + virtual unsigned int readDMRData2(unsigned char* data) = 0; + virtual unsigned int readYSFData(unsigned char* data) = 0; + virtual unsigned int readP25Data(unsigned char* data) = 0; + virtual unsigned int readNXDNData(unsigned char* data) = 0; + virtual unsigned int readM17Data(unsigned char* data) = 0; + virtual unsigned int readFMData(unsigned char* data) = 0; + virtual unsigned int readAX25Data(unsigned char* data) = 0; - virtual unsigned int readSerial(unsigned char* data, unsigned int length); + virtual bool hasDStarSpace() const = 0; + virtual bool hasDMRSpace1() const = 0; + virtual bool hasDMRSpace2() const = 0; + virtual bool hasYSFSpace() const = 0; + virtual bool hasP25Space() const = 0; + virtual bool hasNXDNSpace() const = 0; + virtual bool hasM17Space() const = 0; + virtual bool hasPOCSAGSpace() const = 0; + virtual unsigned int getFMSpace() const = 0; + virtual bool hasAX25Space() const = 0; - virtual bool hasDStarSpace() const; - virtual bool hasDMRSpace1() const; - virtual bool hasDMRSpace2() const; - virtual bool hasYSFSpace() const; - virtual bool hasP25Space() const; - virtual bool hasNXDNSpace() const; - virtual bool hasPOCSAGSpace() const; + virtual bool hasTX() const = 0; + virtual bool hasCD() const = 0; - virtual bool hasTX() const; - virtual bool hasCD() const; + virtual bool hasLockout() const = 0; + virtual bool hasError() const = 0; - virtual bool hasLockout() const; - virtual bool hasError() const; + virtual bool writeConfig() = 0; + virtual bool writeDStarData(const unsigned char* data, unsigned int length) = 0; + virtual bool writeDMRData1(const unsigned char* data, unsigned int length) = 0; + virtual bool writeDMRData2(const unsigned char* data, unsigned int length) = 0; + virtual bool writeYSFData(const unsigned char* data, unsigned int length) = 0; + virtual bool writeP25Data(const unsigned char* data, unsigned int length) = 0; + virtual bool writeNXDNData(const unsigned char* data, unsigned int length) = 0; + virtual bool writeM17Data(const unsigned char* data, unsigned int length) = 0; + virtual bool writePOCSAGData(const unsigned char* data, unsigned int length) = 0; + virtual bool writeFMData(const unsigned char* data, unsigned int length) = 0; + virtual bool writeAX25Data(const unsigned char* data, unsigned int length) = 0; - virtual bool writeConfig(); - virtual bool writeDStarData(const unsigned char* data, unsigned int length); - virtual bool writeDMRData1(const unsigned char* data, unsigned int length); - virtual bool writeDMRData2(const unsigned char* data, unsigned int length); - virtual bool writeYSFData(const unsigned char* data, unsigned int length); - virtual bool writeP25Data(const unsigned char* data, unsigned int length); - virtual bool writeNXDNData(const unsigned char* data, unsigned int length); - virtual bool writePOCSAGData(const unsigned char* data, unsigned int length); + virtual bool writeDStarInfo(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) = 0; + virtual bool writeDMRInfo(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) = 0; + virtual bool writeYSFInfo(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) = 0; + virtual bool writeP25Info(const char* source, bool group, unsigned int dest, const char* type) = 0; + virtual bool writeNXDNInfo(const char* source, bool group, unsigned int dest, const char* type) = 0; + virtual bool writeM17Info(const char* source, const char* dest, const char* type) = 0; + virtual bool writePOCSAGInfo(unsigned int ric, const std::string& message) = 0; + virtual bool writeIPInfo(const std::string& address) = 0; - virtual bool writeTransparentData(const unsigned char* data, unsigned int length); + virtual bool writeDMRStart(bool tx) = 0; + virtual bool writeDMRShortLC(const unsigned char* lc) = 0; + virtual bool writeDMRAbort(unsigned int slotNo) = 0; - virtual bool writeDStarInfo(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); - virtual bool writeDMRInfo(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); - virtual bool writeYSFInfo(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin); - virtual bool writeP25Info(const char* source, bool group, unsigned int dest, const char* type); - virtual bool writeNXDNInfo(const char* source, bool group, unsigned int dest, const char* type); - virtual bool writePOCSAGInfo(unsigned int ric, const std::string& message); - virtual bool writeIPInfo(const std::string& address); + virtual bool writeTransparentData(const unsigned char* data, unsigned int length) = 0; + virtual unsigned int readTransparentData(unsigned char* data) = 0; - virtual bool writeDMRStart(bool tx); - virtual bool writeDMRShortLC(const unsigned char* lc); - virtual bool writeDMRAbort(unsigned int slotNo); + virtual bool writeSerial(const unsigned char* data, unsigned int length) = 0; + virtual unsigned int readSerial(unsigned char* data, unsigned int length) = 0; - virtual bool writeSerial(const unsigned char* data, unsigned int length); + virtual unsigned char getMode() const = 0; + virtual bool setMode(unsigned char mode) = 0; - virtual unsigned char getMode() const; - virtual bool setMode(unsigned char mode); + virtual bool sendCWId(const std::string& callsign) = 0; - virtual bool sendCWId(const std::string& callsign); + virtual HW_TYPE getHWType() const = 0; - virtual HW_TYPE getHWType() const; + virtual void clock(unsigned int ms) = 0; - virtual void clock(unsigned int ms); - - virtual void close(); - - static CModem* createModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug); + virtual void close() = 0; private: - std::string m_port; - unsigned int m_dmrColorCode; - bool m_ysfLoDev; - unsigned int m_ysfTXHang; - unsigned int m_p25TXHang; - unsigned int m_nxdnTXHang; - bool m_duplex; - bool m_rxInvert; - bool m_txInvert; - bool m_pttInvert; - unsigned int m_txDelay; - unsigned int m_dmrDelay; - float m_rxLevel; - float m_cwIdTXLevel; - float m_dstarTXLevel; - float m_dmrTXLevel; - float m_ysfTXLevel; - float m_p25TXLevel; - float m_nxdnTXLevel; - float m_pocsagTXLevel; - float m_fmTXLevel; - float m_rfLevel; - bool m_useCOSAsLockout; - bool m_trace; - bool m_debug; - unsigned int m_rxFrequency; - unsigned int m_txFrequency; - unsigned int m_pocsagFrequency; - bool m_dstarEnabled; - bool m_dmrEnabled; - bool m_ysfEnabled; - bool m_p25Enabled; - bool m_nxdnEnabled; - bool m_pocsagEnabled; - bool m_fmEnabled; - int m_rxDCOffset; - int m_txDCOffset; - CSerialController* m_serial; - unsigned char* m_buffer; - unsigned int m_length; - unsigned int m_offset; - CRingBuffer m_rxDStarData; - CRingBuffer m_txDStarData; - CRingBuffer m_rxDMRData1; - CRingBuffer m_rxDMRData2; - CRingBuffer m_txDMRData1; - CRingBuffer m_txDMRData2; - CRingBuffer m_rxYSFData; - CRingBuffer m_txYSFData; - CRingBuffer m_rxP25Data; - CRingBuffer m_txP25Data; - CRingBuffer m_rxNXDNData; - CRingBuffer m_txNXDNData; - CRingBuffer m_txPOCSAGData; - CRingBuffer m_rxTransparentData; - CRingBuffer m_txTransparentData; - unsigned int m_sendTransparentDataFrameType; - CTimer m_statusTimer; - CTimer m_inactivityTimer; - CTimer m_playoutTimer; - unsigned int m_dstarSpace; - unsigned int m_dmrSpace1; - unsigned int m_dmrSpace2; - unsigned int m_ysfSpace; - unsigned int m_p25Space; - unsigned int m_nxdnSpace; - unsigned int m_pocsagSpace; - bool m_tx; - bool m_cd; - bool m_lockout; - bool m_error; - unsigned char m_mode; - HW_TYPE m_hwType; - - std::string m_fmCallsign; - unsigned int m_fmCallsignSpeed; - unsigned int m_fmCallsignFrequency; - unsigned int m_fmCallsignTime; - unsigned int m_fmCallsignHoldoff; - float m_fmCallsignHighLevel; - float m_fmCallsignLowLevel; - bool m_fmCallsignAtStart; - bool m_fmCallsignAtEnd; - bool m_fmCallsignAtLatch; - std::string m_fmRfAck; - unsigned int m_fmAckSpeed; - unsigned int m_fmAckFrequency; - unsigned int m_fmAckMinTime; - unsigned int m_fmAckDelay; - float m_fmAckLevel; - unsigned int m_fmTimeout; - float m_fmTimeoutLevel; - float m_fmCtcssFrequency; - unsigned int m_fmCtcssHighThreshold; - unsigned int m_fmCtcssLowThreshold; - float m_fmCtcssLevel; - unsigned int m_fmKerchunkTime; - unsigned int m_fmHangTime; - unsigned int m_fmAccessMode; - bool m_fmCOSInvert; - unsigned int m_fmRFAudioBoost; - float m_fmMaxDevLevel; - - bool readVersion(); - bool readStatus(); - bool setConfig(); - bool setFrequency(); - bool setFMCallsignParams(); - bool setFMAckParams(); - bool setFMMiscParams(); - - void printDebug(); - - RESP_TYPE_MMDVM getResponse(); }; #endif diff --git a/ModemSerialPort.cpp b/ModemSerialPort.cpp index 51ba01d..c34cb3d 100644 --- a/ModemSerialPort.cpp +++ b/ModemSerialPort.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016 by Jonathan Naylor G4KLX +* Copyright (C) 2016,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 @@ -21,22 +21,22 @@ #include #include -CModemSerialPort::CModemSerialPort(CModem* modem) : +IModemSerialPort::IModemSerialPort(IModem* modem) : m_modem(modem) { assert(modem != NULL); } -CModemSerialPort::~CModemSerialPort() +IModemSerialPort::~IModemSerialPort() { } -bool CModemSerialPort::open() +bool IModemSerialPort::open() { return true; } -int CModemSerialPort::write(const unsigned char* data, unsigned int length) +int IModemSerialPort::write(const unsigned char* data, unsigned int length) { assert(data != NULL); assert(length > 0U); @@ -46,7 +46,7 @@ int CModemSerialPort::write(const unsigned char* data, unsigned int length) return ret ? int(length) : -1; } -int CModemSerialPort::read(unsigned char* data, unsigned int length) +int IModemSerialPort::read(unsigned char* data, unsigned int length) { assert(data != NULL); assert(length > 0U); @@ -54,6 +54,6 @@ int CModemSerialPort::read(unsigned char* data, unsigned int length) return m_modem->readSerial(data, length); } -void CModemSerialPort::close() +void IModemSerialPort::close() { } diff --git a/ModemSerialPort.h b/ModemSerialPort.h index d6761ce..6de56a7 100644 --- a/ModemSerialPort.h +++ b/ModemSerialPort.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016 by Jonathan Naylor G4KLX +* Copyright (C) 2016,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 @@ -22,10 +22,10 @@ #include "SerialPort.h" #include "Modem.h" -class CModemSerialPort : public ISerialPort { +class IModemSerialPort : public ISerialPort { public: - CModemSerialPort(CModem* modem); - virtual ~CModemSerialPort(); + IModemSerialPort(IModem* modem); + virtual ~IModemSerialPort(); virtual bool open(); @@ -36,7 +36,7 @@ public: virtual void close(); private: - CModem* m_modem; + IModem* m_modem; }; #endif diff --git a/NXDNControl.cpp b/NXDNControl.cpp index 1e200c4..119fad6 100644 --- a/NXDNControl.cpp +++ b/NXDNControl.cpp @@ -737,7 +737,7 @@ void CNXDNControl::writeEndNet() void CNXDNControl::writeNetwork() { - unsigned char netData[40U]; + unsigned char netData[100U]; bool exists = m_network->read(netData); if (!exists) return; diff --git a/Nextion.cpp b/Nextion.cpp index fa6851e..2029aeb 100644 --- a/Nextion.cpp +++ b/Nextion.cpp @@ -36,6 +36,8 @@ const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms const unsigned int P25_BER_COUNT = 7U; // 7 * 180ms = 1260ms const unsigned int NXDN_RSSI_COUNT = 28U; // 28 * 40ms = 1120ms const unsigned int NXDN_BER_COUNT = 28U; // 28 * 40ms = 1120ms +const unsigned int M17_RSSI_COUNT = 28U; // 28 * 40ms = 1120ms +const unsigned int M17_BER_COUNT = 28U; // 28 * 40ms = 1120ms #define LAYOUT_COMPAT_MASK (7 << 0) // compatibility for old setting #define LAYOUT_TA_ENABLE (1 << 4) // enable Talker Alias (TA) display @@ -822,6 +824,79 @@ void CNextion::clearNXDNInt() sendCommand("t3.txt=\"\""); } +void CNextion::writeM17Int(const char* source, const char* dest, const char* type) +{ + assert(source != NULL); + assert(dest != NULL); + assert(type != NULL); + + if (m_mode != MODE_M17) { + sendCommand("page M17"); + sendCommandAction(6U); + } + + char text[30U]; + if (m_brightness > 0) { + ::sprintf(text, "dim=%u", m_brightness); + sendCommand(text); + } + + ::sprintf(text, "t0.txt=\"%s %.10s\"", type, source); + sendCommand(text); + sendCommandAction(122U); + + ::sprintf(text, "t1.txt=\"%s\"", dest); + sendCommand(text); + sendCommandAction(123U); + + m_clockDisplayTimer.stop(); + + m_mode = MODE_M17; + m_rssiAccum1 = 0U; + m_berAccum1 = 0.0F; + m_rssiCount1 = 0U; + m_berCount1 = 0U; +} + +void CNextion::writeM17RSSIInt(unsigned char rssi) +{ + m_rssiAccum1 += rssi; + m_rssiCount1++; + + if (m_rssiCount1 == M17_RSSI_COUNT) { + char text[25U]; + ::sprintf(text, "t2.txt=\"-%udBm\"", m_rssiAccum1 / M17_RSSI_COUNT); + sendCommand(text); + sendCommandAction(124U); + m_rssiAccum1 = 0U; + m_rssiCount1 = 0U; + } +} + +void CNextion::writeM17BERInt(float ber) +{ + m_berAccum1 += ber; + m_berCount1++; + + if (m_berCount1 == M17_BER_COUNT) { + char text[25U]; + ::sprintf(text, "t3.txt=\"%.1f%%\"", m_berAccum1 / float(M17_BER_COUNT)); + sendCommand(text); + sendCommandAction(125U); + m_berAccum1 = 0.0F; + m_berCount1 = 0U; + } +} + +void CNextion::clearM17Int() +{ + sendCommand("t0.txt=\"Listening\""); + sendCommandAction(121U); + sendCommand("t1.txt=\"\""); + sendCommand("t2.txt=\"\""); + sendCommand("t3.txt=\"\""); +} + void CNextion::writePOCSAGInt(uint32_t ric, const std::string& message) { if (m_mode != MODE_POCSAG) { diff --git a/Nextion.h b/Nextion.h index 488372f..713fc90 100644 --- a/Nextion.h +++ b/Nextion.h @@ -70,6 +70,11 @@ protected: virtual void writeNXDNBERInt(float ber); virtual void clearNXDNInt(); + virtual void writeM17Int(const char* source, const char* dest, const char* type); + virtual void writeM17RSSIInt(unsigned char rssi); + virtual void writeM17BERInt(float ber); + virtual void clearM17Int(); + virtual void writePOCSAGInt(uint32_t ric, const std::string& message); virtual void clearPOCSAGInt(); diff --git a/Nextion_G4KLX/NX3224K024.HMI b/Nextion_G4KLX/NX3224K024.HMI index 61c8067..ef8698b 100644 Binary files a/Nextion_G4KLX/NX3224K024.HMI and b/Nextion_G4KLX/NX3224K024.HMI differ diff --git a/Nextion_G4KLX/NX3224K024.tft b/Nextion_G4KLX/NX3224K024.tft index 417aefd..b1a66da 100644 Binary files a/Nextion_G4KLX/NX3224K024.tft and b/Nextion_G4KLX/NX3224K024.tft differ diff --git a/Nextion_G4KLX/NX3224K028.HMI b/Nextion_G4KLX/NX3224K028.HMI index b4ebbc3..f6c1475 100644 Binary files a/Nextion_G4KLX/NX3224K028.HMI and b/Nextion_G4KLX/NX3224K028.HMI differ diff --git a/Nextion_G4KLX/NX3224K028.tft b/Nextion_G4KLX/NX3224K028.tft index 7880b8c..28b552d 100644 Binary files a/Nextion_G4KLX/NX3224K028.tft and b/Nextion_G4KLX/NX3224K028.tft differ diff --git a/Nextion_G4KLX/NX3224T024.HMI b/Nextion_G4KLX/NX3224T024.HMI index 44f1538..ae1204d 100644 Binary files a/Nextion_G4KLX/NX3224T024.HMI and b/Nextion_G4KLX/NX3224T024.HMI differ diff --git a/Nextion_G4KLX/NX3224T024.tft b/Nextion_G4KLX/NX3224T024.tft index 3699e85..06ad235 100644 Binary files a/Nextion_G4KLX/NX3224T024.tft and b/Nextion_G4KLX/NX3224T024.tft differ diff --git a/Nextion_G4KLX/NX3224T028.HMI b/Nextion_G4KLX/NX3224T028.HMI index 3864b34..744dd07 100644 Binary files a/Nextion_G4KLX/NX3224T028.HMI and b/Nextion_G4KLX/NX3224T028.HMI differ diff --git a/Nextion_G4KLX/NX3224T028.tft b/Nextion_G4KLX/NX3224T028.tft index 034441b..2209493 100644 Binary files a/Nextion_G4KLX/NX3224T028.tft and b/Nextion_G4KLX/NX3224T028.tft differ diff --git a/Nextion_G4KLX/NX4024K032.HMI b/Nextion_G4KLX/NX4024K032.HMI index 7df6343..85cdc48 100644 Binary files a/Nextion_G4KLX/NX4024K032.HMI and b/Nextion_G4KLX/NX4024K032.HMI differ diff --git a/Nextion_G4KLX/NX4024K032.tft b/Nextion_G4KLX/NX4024K032.tft index 92b2843..04da59a 100644 Binary files a/Nextion_G4KLX/NX4024K032.tft and b/Nextion_G4KLX/NX4024K032.tft differ diff --git a/Nextion_G4KLX/NX4024T032.HMI b/Nextion_G4KLX/NX4024T032.HMI index 03cd4a8..10dbda4 100644 Binary files a/Nextion_G4KLX/NX4024T032.HMI and b/Nextion_G4KLX/NX4024T032.HMI differ diff --git a/Nextion_G4KLX/NX4024T032.tft b/Nextion_G4KLX/NX4024T032.tft index 9405c5e..31ba971 100644 Binary files a/Nextion_G4KLX/NX4024T032.tft and b/Nextion_G4KLX/NX4024T032.tft differ diff --git a/Nextion_G4KLX/NX4832K035.HMI b/Nextion_G4KLX/NX4832K035.HMI index e1c2cf4..3693082 100644 Binary files a/Nextion_G4KLX/NX4832K035.HMI and b/Nextion_G4KLX/NX4832K035.HMI differ diff --git a/Nextion_G4KLX/NX4832K035.tft b/Nextion_G4KLX/NX4832K035.tft index 71fc6d8..ff3678d 100644 Binary files a/Nextion_G4KLX/NX4832K035.tft and b/Nextion_G4KLX/NX4832K035.tft differ diff --git a/Nextion_G4KLX/NX4832T035.HMI b/Nextion_G4KLX/NX4832T035.HMI index 3128178..cda3ced 100644 Binary files a/Nextion_G4KLX/NX4832T035.HMI and b/Nextion_G4KLX/NX4832T035.HMI differ diff --git a/Nextion_G4KLX/NX4832T035.tft b/Nextion_G4KLX/NX4832T035.tft index 5643caa..dd9c7a2 100644 Binary files a/Nextion_G4KLX/NX4832T035.tft and b/Nextion_G4KLX/NX4832T035.tft differ diff --git a/NullDisplay.cpp b/NullDisplay.cpp index 2471e93..5708028 100644 --- a/NullDisplay.cpp +++ b/NullDisplay.cpp @@ -134,6 +134,20 @@ void CNullDisplay::clearNXDNInt() #endif } +void CNullDisplay::writeM17Int(const char* source, const char* dest, const char* type) +{ +#if defined(RASPBERRY_PI) + ::digitalWrite(LED_STATUS, 1); +#endif +} + +void CNullDisplay::clearM17Int() +{ +#if defined(RASPBERRY_PI) + ::digitalWrite(LED_STATUS, 0); +#endif +} + void CNullDisplay::writePOCSAGInt(uint32_t ric, const std::string& message) { #if defined(RASPBERRY_PI) diff --git a/NullDisplay.h b/NullDisplay.h index a222959..c53dc08 100644 --- a/NullDisplay.h +++ b/NullDisplay.h @@ -55,6 +55,9 @@ protected: virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); virtual void clearNXDNInt(); + virtual void writeM17Int(const char* source, const char* dest, const char* type); + virtual void clearM17Int(); + virtual void writePOCSAGInt(uint32_t ric, const std::string& message); virtual void clearPOCSAGInt(); diff --git a/NullModem.cpp b/NullModem.cpp index f60e500..40e3fdd 100644 --- a/NullModem.cpp +++ b/NullModem.cpp @@ -19,9 +19,7 @@ #include "NullModem.h" #include "Log.h" -CNullModem::CNullModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug) : -CModem(port, duplex,rxInvert, txInvert,pttInvert,txDelay, dmrDelay, useCOSAsLockout, trace, debug), -m_hwType(HWT_MMDVM) +CNullModem::CNullModem() { } @@ -29,7 +27,9 @@ CNullModem::~CNullModem() { } -bool CNullModem::open(){ +bool CNullModem::open() +{ ::LogMessage("Opening the MMDVM Null Modem"); + return true; -} \ No newline at end of file +} diff --git a/NullModem.h b/NullModem.h index b6bd80f..83e60d6 100644 --- a/NullModem.h +++ b/NullModem.h @@ -24,82 +24,100 @@ #include - -class CNullModem : public CModem { +class CNullModem : public IModem { public: - CNullModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug); + CNullModem(); virtual ~CNullModem(); - virtual void setSerialParams(const std::string& protocol, unsigned int address){}; - virtual void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency){}; - virtual void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled, bool fmEnabled){}; - virtual void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagLevel, float fmTXLevel){}; - virtual void setDMRParams(unsigned int colorCode){}; - virtual void setYSFParams(bool loDev, unsigned int txHang){}; - virtual void setTransparentDataParams(unsigned int sendFrameType){}; + virtual void setSerialParams(const std::string& protocol, unsigned int address, unsigned int speed) {}; + virtual void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency) {}; + virtual void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool m17Enabled, bool pocsagEnabled, bool fmEnabled, bool ax25Enabled) {}; + virtual void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float m17TXLevel, float pocsagLevel, float fmTXLevel, float ax25TXLevel) {}; + virtual void setDMRParams(unsigned int colorCode) {}; + virtual void setYSFParams(bool loDev, unsigned int txHang) {}; + virtual void setP25Params(unsigned int txHang) {}; + virtual void setNXDNParams(unsigned int txHang) {}; + virtual void setM17Params(unsigned int txHang) {}; + virtual void setAX25Params(int rxTwist, unsigned int txDelay, unsigned int slotTime, unsigned int pPersist) {}; + virtual void setTransparentDataParams(unsigned int sendFrameType) {}; + + virtual void setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch) {}; + virtual void setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, unsigned int ackFrequency, unsigned int ackMinTime, unsigned int ackDelay, float ackLevel) {}; + virtual void setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssHighThreshold, unsigned int ctcssLowThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, unsigned int accessMode, bool cosInvert, bool noiseSquelch, unsigned int squelchHighThreshold, unsigned int squelchLowThreshold, unsigned int rfAudioBoost, float maxDevLevel) {}; + virtual void setFMExtParams(const std::string& ack, unsigned int audioBoost) {}; virtual bool open(); - virtual unsigned int readDStarData(unsigned char* data){return 0;}; - virtual unsigned int readDMRData1(unsigned char* data){return 0;}; - virtual unsigned int readDMRData2(unsigned char* data){return 0;}; - virtual unsigned int readYSFData(unsigned char* data){return 0;}; - virtual unsigned int readP25Data(unsigned char* data){return 0;}; - virtual unsigned int readNXDNData(unsigned char* data){return 0;}; - virtual unsigned int readTransparentData(unsigned char* data){return 0;}; + virtual unsigned int readDStarData(unsigned char* data) { return 0U; }; + virtual unsigned int readDMRData1(unsigned char* data) { return 0U; }; + virtual unsigned int readDMRData2(unsigned char* data) { return 0U; }; + virtual unsigned int readYSFData(unsigned char* data) { return 0U; }; + virtual unsigned int readP25Data(unsigned char* data) { return 0U; }; + virtual unsigned int readNXDNData(unsigned char* data) { return 0U; }; + virtual unsigned int readM17Data(unsigned char* data) { return 0U; }; + virtual unsigned int readFMData(unsigned char* data) { return 0U; }; + virtual unsigned int readAX25Data(unsigned char* data) { return 0U; }; - virtual unsigned int readSerial(unsigned char* data, unsigned int length){return 0;}; + virtual bool hasDStarSpace()const { return true; }; + virtual bool hasDMRSpace1() const { return true; }; + virtual bool hasDMRSpace2() const { return true; }; + virtual bool hasYSFSpace() const { return true; }; + virtual bool hasP25Space() const { return true; }; + virtual bool hasNXDNSpace() const { return true; }; + virtual bool hasM17Space() const { return true; }; + virtual bool hasPOCSAGSpace() const { return true; }; + virtual unsigned int getFMSpace() const { return true; }; + virtual bool hasAX25Space() const { return true; }; - virtual bool hasDStarSpace()const {return true;}; - virtual bool hasDMRSpace1() const {return true;}; - virtual bool hasDMRSpace2() const {return true;}; - virtual bool hasYSFSpace() const {return true;}; - virtual bool hasP25Space() const {return true;}; - virtual bool hasNXDNSpace() const {return true;}; - virtual bool hasPOCSAGSpace() const{return true;}; + virtual bool hasTX() const { return false; }; + virtual bool hasCD() const { return false; }; - virtual bool hasTX() const {return false;}; - virtual bool hasCD() const {return false;}; + virtual bool hasLockout() const { return false; }; + virtual bool hasError() const { return false; }; - virtual bool hasLockout() const {return false;}; - virtual bool hasError() const {return false;}; + virtual bool writeDStarData(const unsigned char* data, unsigned int length) { return true; }; + virtual bool writeDMRData1(const unsigned char* data, unsigned int length) { return true; }; + virtual bool writeDMRData2(const unsigned char* data, unsigned int length) { return true; }; + virtual bool writeYSFData(const unsigned char* data, unsigned int length) { return true; }; + virtual bool writeP25Data(const unsigned char* data, unsigned int length) { return true; }; + virtual bool writeNXDNData(const unsigned char* data, unsigned int length) { return true; }; + virtual bool writeM17Data(const unsigned char* data, unsigned int length) { return true; }; + virtual bool writePOCSAGData(const unsigned char* data, unsigned int length) { return true; }; + virtual bool writeFMData(const unsigned char* data, unsigned int length) { return true; }; + virtual bool writeAX25Data(const unsigned char* data, unsigned int length) { return true; }; - virtual bool writeDStarData(const unsigned char* data, unsigned int length){return true;}; - virtual bool writeDMRData1(const unsigned char* data, unsigned int length){return true;}; - virtual bool writeDMRData2(const unsigned char* data, unsigned int length){return true;}; - virtual bool writeYSFData(const unsigned char* data, unsigned int length){return true;}; - virtual bool writeP25Data(const unsigned char* data, unsigned int length){return true;}; - virtual bool writeNXDNData(const unsigned char* data, unsigned int length){return true;}; - virtual bool writePOCSAGData(const unsigned char* data, unsigned int length){return true;}; + virtual bool writeConfig() { return true; }; + virtual bool writeDStarInfo(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) { return true; }; + virtual bool writeDMRInfo(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) { return true; }; + virtual bool writeYSFInfo(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) { return true; }; + virtual bool writeP25Info(const char* source, bool group, unsigned int dest, const char* type) { return true; }; + virtual bool writeNXDNInfo(const char* source, bool group, unsigned int dest, const char* type) { return true; }; + virtual bool writeM17Info(const char* source, const char* dest, const char* type) { return true; }; + virtual bool writePOCSAGInfo(unsigned int ric, const std::string& message) { return true; }; + virtual bool writeIPInfo(const std::string& address) { return true; }; - virtual bool writeTransparentData(const unsigned char* data, unsigned int length){return true;}; + virtual bool writeDMRStart(bool tx) { return true; }; + virtual bool writeDMRShortLC(const unsigned char* lc) { return true; }; + virtual bool writeDMRAbort(unsigned int slotNo) { return true; }; - virtual bool writeDStarInfo(const char* my1, const char* my2, const char* your, const char* type, const char* reflector){return true;}; - virtual bool writeDMRInfo(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type){return true;}; - virtual bool writeYSFInfo(const char* source, const char* dest, const char* type, const char* origin){return true;}; - virtual bool writeP25Info(const char* source, bool group, unsigned int dest, const char* type){return true;}; - virtual bool writeNXDNInfo(const char* source, bool group, unsigned int dest, const char* type){return true;}; - virtual bool writePOCSAGInfo(unsigned int ric, const std::string& message){return true;}; - virtual bool writeIPInfo(const std::string& address){return true;}; + virtual bool writeTransparentData(const unsigned char* data, unsigned int length) { return true; }; + virtual unsigned int readTransparentData(unsigned char* data) { return 0U; }; - virtual bool writeDMRStart(bool tx){return true;}; - virtual bool writeDMRShortLC(const unsigned char* lc){return true;}; - virtual bool writeDMRAbort(unsigned int slotNo){return true;}; + virtual bool writeSerial(const unsigned char* data, unsigned int length) { return true; }; + virtual unsigned int readSerial(unsigned char* data, unsigned int length) { return 0U; }; - virtual bool writeSerial(const unsigned char* data, unsigned int length){return true;}; + virtual unsigned char getMode() const { return MODE_IDLE; }; + virtual bool setMode(unsigned char mode) { return true; }; - virtual bool setMode(unsigned char mode){return true;}; + virtual bool sendCWId(const std::string& callsign) { return true; }; - virtual bool sendCWId(const std::string& callsign){return true;}; + virtual HW_TYPE getHWType() const { return HWT_MMDVM; }; - virtual HW_TYPE getHWType() const {return m_hwType;}; + virtual void clock(unsigned int ms) {}; - virtual void clock(unsigned int ms){}; - - virtual void close(){}; + virtual void close() {}; private: - HW_TYPE m_hwType; }; #endif diff --git a/OLED.cpp b/OLED.cpp index 562afde..0b765d4 100644 --- a/OLED.cpp +++ b/OLED.cpp @@ -148,6 +148,28 @@ const unsigned char logo_NXDN_bmp [] = 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +// Logo M17_sm, 128x16px +// XXX FIXME This is the NXDN logo, it needs replacing with the M17 logo +const unsigned char logo_M17_bmp [] = +{ +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xf0, 0x1f, 0xf8, 0x0f, 0x00, 0xff, 0x80, 0x7c, 0x00, 0x0f, 0xff, 0x80, 0x7f, 0xe0, 0x7f, +0xff, 0xe0, 0x0f, 0xf0, 0x1f, 0x80, 0x7e, 0x01, 0xf8, 0x00, 0x00, 0x7f, 0x00, 0x3f, 0xc0, 0x7f, +0xff, 0xc0, 0x07, 0xe0, 0x3f, 0x80, 0x38, 0x07, 0xf0, 0x00, 0x00, 0x3e, 0x00, 0x3f, 0x80, 0xff, +0xff, 0x80, 0x03, 0xc0, 0x3f, 0xc0, 0x00, 0x3f, 0xe0, 0x1f, 0x80, 0x3e, 0x00, 0x1f, 0x01, 0xff, +0xff, 0x00, 0x03, 0x80, 0x7f, 0xe0, 0x00, 0xff, 0xc0, 0x3f, 0x80, 0x3c, 0x00, 0x0e, 0x03, 0xff, +0xfe, 0x00, 0x01, 0x00, 0xff, 0xe0, 0x03, 0xff, 0x80, 0x7f, 0x80, 0x78, 0x08, 0x04, 0x03, 0xff, +0xfc, 0x03, 0x00, 0x01, 0xff, 0x80, 0x01, 0xff, 0x00, 0xff, 0x00, 0xf0, 0x1c, 0x00, 0x07, 0xff, +0xfc, 0x07, 0x80, 0x03, 0xfc, 0x00, 0x01, 0xfe, 0x01, 0xfc, 0x01, 0xe0, 0x1e, 0x00, 0x0f, 0xff, +0xf8, 0x0f, 0xc0, 0x07, 0xf0, 0x0e, 0x00, 0xfc, 0x00, 0x00, 0x07, 0xc0, 0x3f, 0x00, 0x1f, 0xff, +0xf0, 0x1f, 0xe0, 0x0f, 0x80, 0x3f, 0x00, 0x7c, 0x00, 0x00, 0x3f, 0xc0, 0x7f, 0x80, 0x3f, 0xff, +0xe0, 0x3f, 0xf0, 0x0e, 0x01, 0xff, 0x80, 0x38, 0x00, 0x07, 0xff, 0x80, 0xff, 0x80, 0x7f, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + // Logo POCASG/DAPNET, 128x16px const unsigned char logo_POCSAG_bmp [] = { @@ -346,7 +368,6 @@ void COLED::writeDStarInt(const char* my1, const char* my2, const char* your, co OLED_statusbar(); m_display.display(); - } void COLED::clearDStarInt() @@ -500,7 +521,6 @@ void COLED::writeP25Int(const char* source, bool group, unsigned int dest, const OLED_statusbar(); m_display.display(); - } void COLED::clearP25Int() @@ -566,6 +586,36 @@ void COLED::clearNXDNInt() m_display.display(); } +void COLED::writeM17Int(const char* source, const char* dest, const char* type) +{ + m_mode = MODE_M17; + + m_display.clearDisplay(); + m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); + + m_display.setCursor(0,OLED_LINE3); + m_display.printf("%s %s", type, source); + + m_display.setCursor(0,OLED_LINE4); + m_display.printf(" %s", dest); + + OLED_statusbar(); + m_display.display(); +} + +void COLED::clearM17Int() +{ + m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); + + m_display.setCursor(40,OLED_LINE4); + m_display.print("Listening"); + + m_display.setCursor(0,OLED_LINE6); + m_display.printf("%s",m_ipaddress.c_str()); + + m_display.display(); +} + void COLED::writePOCSAGInt(uint32_t ric, const std::string& message) { m_mode = MODE_POCSAG; @@ -594,7 +644,6 @@ void COLED::writePOCSAGInt(uint32_t ric, const std::string& message) OLED_statusbar(); m_display.display(); - } void COLED::clearPOCSAGInt() @@ -669,6 +718,8 @@ void COLED::OLED_statusbar() m_display.drawBitmap(0, 0, logo_P25_bmp, 128, 16, WHITE); else if (m_mode == MODE_NXDN) m_display.drawBitmap(0, 0, logo_NXDN_bmp, 128, 16, WHITE); + else if (m_mode == MODE_M17) + m_display.drawBitmap(0, 0, logo_M17_bmp, 128, 16, WHITE); else if (m_mode == MODE_POCSAG) m_display.drawBitmap(0, 0, logo_POCSAG_bmp, 128, 16, WHITE); else if (m_displayLogoScreensaver) diff --git a/OLED.h b/OLED.h index 5f3ead0..a7ab811 100644 --- a/OLED.h +++ b/OLED.h @@ -66,10 +66,16 @@ public: virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type); virtual void clearP25Int(); + virtual void writeM17Int(const char* source, const char* dest, const char* type); + virtual void clearM17Int(); + virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); virtual int writeNXDNIntEx(const class CUserDBentry& source, bool group, unsigned int dest, const char* type); virtual void clearNXDNInt(); + virtual void writeM17Int(const char* source, const char* dest, const char* type); + virtual void clearM17Int(); + virtual void writePOCSAGInt(uint32_t ric, const std::string& message); virtual void clearPOCSAGInt(); diff --git a/PseudoTTYController.cpp b/PseudoTTYController.cpp new file mode 100644 index 0000000..52a4ac9 --- /dev/null +++ b/PseudoTTYController.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2002-2004,2007-2011,2013,2014-2017,2019,2020 by Jonathan Naylor G4KLX + * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX + * + * 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(_WIN32) && !defined(_WIN64) + +#include "PseudoTTYController.h" +#include "Log.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +CPseudoTTYController::CPseudoTTYController(const std::string& symlink, unsigned int speed, bool assertRTS) : +CSerialController(speed, assertRTS), +m_symlink(symlink) +{ +} + +CPseudoTTYController::~CPseudoTTYController() +{ +} + +bool CPseudoTTYController::open() +{ + assert(m_fd == -1); + + int slavefd; + char slave[300]; + int result = ::openpty(&m_fd, &slavefd, slave, NULL, NULL); + if (result < 0) { + LogError("Cannot open the pseudo tty - errno : %d", errno); + return false; + } + + // Remove any previous stale symlink + ::unlink(m_symlink.c_str()); + + int ret = ::symlink(slave, m_symlink.c_str()); + if (ret != 0) { + LogError("Cannot make symlink to %s with %s", slave, m_symlink.c_str()); + close(); + return false; + } + + LogMessage("Made symbolic link from %s to %s", slave, m_symlink.c_str()); + + m_device = std::string(::ttyname(m_fd)); + + return setRaw(); +} + +void CPseudoTTYController::close() +{ + CSerialController::close(); + + ::unlink(m_symlink.c_str()); +} + +#endif + diff --git a/PseudoTTYController.h b/PseudoTTYController.h new file mode 100644 index 0000000..ccf6ba1 --- /dev/null +++ b/PseudoTTYController.h @@ -0,0 +1,43 @@ +/* + * 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. + */ + +#ifndef PseudoTTYController_H +#define PseudoTTYController_H + +#if !defined(_WIN32) && !defined(_WIN64) + +#include + +#include "SerialController.h" + +class CPseudoTTYController : public CSerialController { +public: + CPseudoTTYController(const std::string& symlink, unsigned int speed, bool assertRTS = false); + virtual ~CPseudoTTYController(); + + virtual bool open(); + + virtual void close(); + +protected: + std::string m_symlink; +}; + +#endif + +#endif diff --git a/README.md b/README.md index fafc4bf..0b6fe0b 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,15 @@ 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, -POCSAG, and FM 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, M17, +POCSAG, FM, and AX.25 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, 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. It uses the DAPNET Gateway to access DAPNET to receive +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. @@ -26,8 +27,7 @@ these are: The Nextion displays can connect to the UART on the Raspberry Pi, or via a USB to TTL serial converter like the FT-232RL. It may also be connected to the UART -output of the MMDVM modem (Arduino Due, STM32, Teensy), or to the UART output -on the UMP. +output of the MMDVM modem (Arduino Due, STM32, Teensy). The HD44780 displays are integrated with wiringPi for Raspberry Pi based platforms. diff --git a/RemoteControl.cpp b/RemoteControl.cpp index 3c2d2a8..af58dc5 100644 --- a/RemoteControl.cpp +++ b/RemoteControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Jonathan Naylor G4KLX + * Copyright (C) 2019,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 @@ -89,6 +89,8 @@ REMOTE_COMMAND CRemoteControl::getCommand() m_command = RCD_MODE_P25; else if (m_args.at(1U) == "nxdn") m_command = RCD_MODE_NXDN; + else if (m_args.at(1U) == "m17") + m_command = RCD_MODE_M17; } else if (m_args.at(0U) == "enable" && m_args.size() >= ENABLE_ARGS) { if (m_args.at(1U) == "dstar") m_command = RCD_ENABLE_DSTAR; @@ -100,8 +102,12 @@ REMOTE_COMMAND CRemoteControl::getCommand() m_command = RCD_ENABLE_P25; else if (m_args.at(1U) == "nxdn") m_command = RCD_ENABLE_NXDN; + else if (m_args.at(1U) == "m17") + m_command = RCD_ENABLE_M17; else if (m_args.at(1U) == "fm") m_command = RCD_ENABLE_FM; + else if (m_args.at(1U) == "ax25") + m_command = RCD_ENABLE_AX25; } else if (m_args.at(0U) == "disable" && m_args.size() >= DISABLE_ARGS) { if (m_args.at(1U) == "dstar") m_command = RCD_DISABLE_DSTAR; @@ -113,8 +119,12 @@ REMOTE_COMMAND CRemoteControl::getCommand() m_command = RCD_DISABLE_P25; else if (m_args.at(1U) == "nxdn") m_command = RCD_DISABLE_NXDN; + else if (m_args.at(1U) == "m17") + m_command = RCD_DISABLE_M17; else if (m_args.at(1U) == "fm") m_command = RCD_DISABLE_FM; + else if (m_args.at(1U) == "ax25") + m_command = RCD_DISABLE_AX25; } else if (m_args.at(0U) == "page" && m_args.size() >= PAGE_ARGS) { // Page command is in the form of "page " m_command = RCD_PAGE; @@ -144,6 +154,7 @@ unsigned int CRemoteControl::getArgCount() const case RCD_MODE_YSF: case RCD_MODE_P25: case RCD_MODE_NXDN: + case RCD_MODE_M17: return m_args.size() - SET_MODE_ARGS; case RCD_PAGE: return m_args.size() - 1U; @@ -164,14 +175,15 @@ std::string CRemoteControl::getArgString(unsigned int n) const case RCD_MODE_YSF: case RCD_MODE_P25: case RCD_MODE_NXDN: + case RCD_MODE_M17: n += SET_MODE_ARGS; break; case RCD_PAGE: n += 1U; break; case RCD_CW: - n += 1U; - break; + n += 1U; + break; default: return ""; } diff --git a/RemoteControl.h b/RemoteControl.h index c8d060d..b4aea18 100644 --- a/RemoteControl.h +++ b/RemoteControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Jonathan Naylor G4KLX + * Copyright (C) 2019,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 @@ -33,19 +33,24 @@ enum REMOTE_COMMAND { RCD_MODE_YSF, RCD_MODE_P25, RCD_MODE_NXDN, + RCD_MODE_M17, RCD_MODE_FM, RCD_ENABLE_DSTAR, RCD_ENABLE_DMR, RCD_ENABLE_YSF, RCD_ENABLE_P25, RCD_ENABLE_NXDN, + RCD_ENABLE_M17, RCD_ENABLE_FM, + RCD_ENABLE_AX25, RCD_DISABLE_DSTAR, RCD_DISABLE_DMR, RCD_DISABLE_YSF, RCD_DISABLE_P25, RCD_DISABLE_NXDN, + RCD_DISABLE_M17, RCD_DISABLE_FM, + RCD_DISABLE_AX25, RCD_PAGE, RCD_CW }; diff --git a/SerialController.cpp b/SerialController.cpp index cfb448d..99fe576 100644 --- a/SerialController.cpp +++ b/SerialController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2004,2007-2011,2013,2014-2017,2019 by Jonathan Naylor G4KLX + * Copyright (C) 2002-2004,2007-2011,2013,2014-2017,2019,2020 by Jonathan Naylor G4KLX * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX * * This program is free software; you can redistribute it and/or modify @@ -23,12 +23,11 @@ #include #include -#include - #if defined(_WIN32) || defined(_WIN64) #include #include #else +#include #include #include #include @@ -40,7 +39,7 @@ #if defined(_WIN32) || defined(_WIN64) -CSerialController::CSerialController(const std::string& device, SERIAL_SPEED speed, bool assertRTS) : +CSerialController::CSerialController(const std::string& device, unsigned int speed, bool assertRTS) : m_device(device), m_speed(speed), m_assertRTS(assertRTS), @@ -49,6 +48,14 @@ m_handle(INVALID_HANDLE_VALUE) assert(!device.empty()); } +CSerialController::CSerialController(unsigned int speed, bool assertRTS) : +m_device(), +m_speed(speed), +m_assertRTS(assertRTS), +m_handle(INVALID_HANDLE_VALUE) +{ +} + CSerialController::~CSerialController() { } @@ -221,7 +228,7 @@ void CSerialController::close() #else -CSerialController::CSerialController(const std::string& device, SERIAL_SPEED speed, bool assertRTS) : +CSerialController::CSerialController(const std::string& device, unsigned int speed, bool assertRTS) : m_device(device), m_speed(speed), m_assertRTS(assertRTS), @@ -230,6 +237,14 @@ m_fd(-1) assert(!device.empty()); } +CSerialController::CSerialController(unsigned int speed, bool assertRTS) : +m_device(), +m_speed(speed), +m_assertRTS(assertRTS), +m_fd(-1) +{ +} + CSerialController::~CSerialController() { } @@ -248,97 +263,107 @@ bool CSerialController::open() return false; } - if (::isatty(m_fd)) { - termios termios; - if (::tcgetattr(m_fd, &termios) < 0) { - LogError("Cannot get the attributes for %s", m_device.c_str()); - ::close(m_fd); - return false; - } + if (::isatty(m_fd)) + return setRaw(); + + return true; +} - termios.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK); - termios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL); - termios.c_iflag &= ~(IXON | IXOFF | IXANY); - termios.c_oflag &= ~(OPOST); - termios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS); - termios.c_cflag |= (CS8 | CLOCAL | CREAD); - termios.c_lflag &= ~(ISIG | ICANON | IEXTEN); - termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); -#if defined(__APPLE__) - termios.c_cc[VMIN] = 1; - termios.c_cc[VTIME] = 1; -#else - termios.c_cc[VMIN] = 0; - termios.c_cc[VTIME] = 10; -#endif - - switch (m_speed) { - case SERIAL_1200: - ::cfsetospeed(&termios, B1200); - ::cfsetispeed(&termios, B1200); - break; - case SERIAL_2400: - ::cfsetospeed(&termios, B2400); - ::cfsetispeed(&termios, B2400); - break; - case SERIAL_4800: - ::cfsetospeed(&termios, B4800); - ::cfsetispeed(&termios, B4800); - break; - case SERIAL_9600: - ::cfsetospeed(&termios, B9600); - ::cfsetispeed(&termios, B9600); - break; - case SERIAL_19200: - ::cfsetospeed(&termios, B19200); - ::cfsetispeed(&termios, B19200); - break; - case SERIAL_38400: - ::cfsetospeed(&termios, B38400); - ::cfsetispeed(&termios, B38400); - break; - case SERIAL_115200: - ::cfsetospeed(&termios, B115200); - ::cfsetispeed(&termios, B115200); - break; - case SERIAL_230400: - ::cfsetospeed(&termios, B230400); - ::cfsetispeed(&termios, B230400); - break; - default: - LogError("Unsupported serial port speed - %d", int(m_speed)); - ::close(m_fd); - return false; - } - - if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) { - LogError("Cannot set the attributes for %s", m_device.c_str()); - ::close(m_fd); - return false; - } - - if (m_assertRTS) { - unsigned int y; - if (::ioctl(m_fd, TIOCMGET, &y) < 0) { - LogError("Cannot get the control attributes for %s", m_device.c_str()); - ::close(m_fd); - return false; - } - - y |= TIOCM_RTS; - - if (::ioctl(m_fd, TIOCMSET, &y) < 0) { - LogError("Cannot set the control attributes for %s", m_device.c_str()); - ::close(m_fd); - return false; - } - } - -#if defined(__APPLE__) - setNonblock(false); -#endif +bool CSerialController::setRaw() +{ + termios termios; + if (::tcgetattr(m_fd, &termios) < 0) { + LogError("Cannot get the attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; } + termios.c_iflag &= ~(IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK); + termios.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL); + termios.c_iflag &= ~(IXON | IXOFF | IXANY); + termios.c_oflag &= ~(OPOST); + termios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS); + termios.c_cflag |= (CS8 | CLOCAL | CREAD); + termios.c_lflag &= ~(ISIG | ICANON | IEXTEN); + termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); +#if defined(__APPLE__) + termios.c_cc[VMIN] = 1; + termios.c_cc[VTIME] = 1; +#else + termios.c_cc[VMIN] = 0; + termios.c_cc[VTIME] = 10; +#endif + + switch (m_speed) { + case 1200U: + ::cfsetospeed(&termios, B1200); + ::cfsetispeed(&termios, B1200); + break; + case 2400U: + ::cfsetospeed(&termios, B2400); + ::cfsetispeed(&termios, B2400); + break; + case 4800U: + ::cfsetospeed(&termios, B4800); + ::cfsetispeed(&termios, B4800); + break; + case 9600U: + ::cfsetospeed(&termios, B9600); + ::cfsetispeed(&termios, B9600); + break; + case 19200U: + ::cfsetospeed(&termios, B19200); + ::cfsetispeed(&termios, B19200); + break; + case 38400U: + ::cfsetospeed(&termios, B38400); + ::cfsetispeed(&termios, B38400); + break; + case 115200U: + ::cfsetospeed(&termios, B115200); + ::cfsetispeed(&termios, B115200); + break; + case 230400U: + ::cfsetospeed(&termios, B230400); + ::cfsetispeed(&termios, B230400); + break; + case 460800U: + ::cfsetospeed(&termios, B460800); + ::cfsetispeed(&termios, B460800); + break; + default: + LogError("Unsupported serial port speed - %u", m_speed); + ::close(m_fd); + return false; + } + + if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) { + LogError("Cannot set the attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + + if (m_assertRTS) { + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) { + LogError("Cannot get the control attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + + y |= TIOCM_RTS; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) { + LogError("Cannot set the control attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + } + +#if defined(__APPLE__) + setNonblock(false); +#endif + return true; } diff --git a/SerialController.h b/SerialController.h index 6dfc461..e4adb7d 100644 --- a/SerialController.h +++ b/SerialController.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2004,2007-2009,2011-2013,2015-2017 by Jonathan Naylor G4KLX + * Copyright (C) 2002-2004,2007-2009,2011-2013,2015-2017,2020 by Jonathan Naylor G4KLX * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX * * This program is free software; you can redistribute it and/or modify @@ -28,21 +28,9 @@ #include #endif -enum SERIAL_SPEED { - SERIAL_1200 = 1200, - SERIAL_2400 = 2400, - SERIAL_4800 = 4800, - SERIAL_9600 = 9600, - SERIAL_19200 = 19200, - SERIAL_38400 = 38400, - SERIAL_76800 = 76800, - SERIAL_115200 = 115200, - SERIAL_230400 = 230400 -}; - class CSerialController : public ISerialPort { public: - CSerialController(const std::string& device, SERIAL_SPEED speed, bool assertRTS = false); + CSerialController(const std::string& device, unsigned int speed, bool assertRTS = false); virtual ~CSerialController(); virtual bool open(); @@ -58,8 +46,10 @@ public: #endif protected: + CSerialController(unsigned int speed, bool assertRTS = false); + std::string m_device; - SERIAL_SPEED m_speed; + unsigned int m_speed; bool m_assertRTS; #if defined(_WIN32) || defined(_WIN64) HANDLE m_handle; @@ -71,6 +61,7 @@ protected: int readNonblock(unsigned char* buffer, unsigned int length); #else bool canWrite(); + bool setRaw(); #endif }; diff --git a/SerialModem.cpp b/SerialModem.cpp new file mode 100644 index 0000000..721c736 --- /dev/null +++ b/SerialModem.cpp @@ -0,0 +1,2869 @@ +/* + * Copyright (C) 2011-2018,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. + */ + +#include "SerialController.h" +#if defined(__linux__) +#include "I2CController.h" +#endif +#include "DStarDefines.h" +#include "DMRDefines.h" +#include "YSFDefines.h" +#include "P25Defines.h" +#include "NXDNDefines.h" +#include "AX25Defines.h" +#include "POCSAGDefines.h" +#include "M17Defines.h" +#include "Thread.h" +#include "SerialModem.h" +#include "NullModem.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include +#include +#include + +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#endif + +const unsigned char MMDVM_FRAME_START = 0xE0U; + +const unsigned char MMDVM_GET_VERSION = 0x00U; +const unsigned char MMDVM_GET_STATUS = 0x01U; +const unsigned char MMDVM_SET_CONFIG = 0x02U; +const unsigned char MMDVM_SET_MODE = 0x03U; +const unsigned char MMDVM_SET_FREQ = 0x04U; + +const unsigned char MMDVM_SEND_CWID = 0x0AU; + +const unsigned char MMDVM_DSTAR_HEADER = 0x10U; +const unsigned char MMDVM_DSTAR_DATA = 0x11U; +const unsigned char MMDVM_DSTAR_LOST = 0x12U; +const unsigned char MMDVM_DSTAR_EOT = 0x13U; + +const unsigned char MMDVM_DMR_DATA1 = 0x18U; +const unsigned char MMDVM_DMR_LOST1 = 0x19U; +const unsigned char MMDVM_DMR_DATA2 = 0x1AU; +const unsigned char MMDVM_DMR_LOST2 = 0x1BU; +const unsigned char MMDVM_DMR_SHORTLC = 0x1CU; +const unsigned char MMDVM_DMR_START = 0x1DU; +const unsigned char MMDVM_DMR_ABORT = 0x1EU; + +const unsigned char MMDVM_YSF_DATA = 0x20U; +const unsigned char MMDVM_YSF_LOST = 0x21U; + +const unsigned char MMDVM_P25_HDR = 0x30U; +const unsigned char MMDVM_P25_LDU = 0x31U; +const unsigned char MMDVM_P25_LOST = 0x32U; + +const unsigned char MMDVM_NXDN_DATA = 0x40U; +const unsigned char MMDVM_NXDN_LOST = 0x41U; + +const unsigned char MMDVM_M17_HEADER = 0x45U; +const unsigned char MMDVM_M17_DATA = 0x46U; +const unsigned char MMDVM_M17_LOST = 0x47U; + +const unsigned char MMDVM_POCSAG_DATA = 0x50U; + +const unsigned char MMDVM_AX25_DATA = 0x55U; + +const unsigned char MMDVM_FM_PARAMS1 = 0x60U; +const unsigned char MMDVM_FM_PARAMS2 = 0x61U; +const unsigned char MMDVM_FM_PARAMS3 = 0x62U; +const unsigned char MMDVM_FM_PARAMS4 = 0x63U; +const unsigned char MMDVM_FM_DATA = 0x65U; +const unsigned char MMDVM_FM_CONTROL = 0x66U; +const unsigned char MMDVM_FM_EOT = 0x67U; + +const unsigned char MMDVM_ACK = 0x70U; +const unsigned char MMDVM_NAK = 0x7FU; + +const unsigned char MMDVM_SERIAL_DATA = 0x80U; + +const unsigned char MMDVM_TRANSPARENT = 0x90U; +const unsigned char MMDVM_QSO_INFO = 0x91U; + +const unsigned char MMDVM_DEBUG1 = 0xF1U; +const unsigned char MMDVM_DEBUG2 = 0xF2U; +const unsigned char MMDVM_DEBUG3 = 0xF3U; +const unsigned char MMDVM_DEBUG4 = 0xF4U; +const unsigned char MMDVM_DEBUG5 = 0xF5U; + +const unsigned int MAX_RESPONSES = 30U; + +const unsigned int BUFFER_LENGTH = 2000U; + + +CSerialModem::CSerialModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug) : +m_port(port), +m_protocolVersion(0U), +m_dmrColorCode(0U), +m_ysfLoDev(false), +m_ysfTXHang(4U), +m_p25TXHang(5U), +m_nxdnTXHang(5U), +m_m17TXHang(5U), +m_duplex(duplex), +m_rxInvert(rxInvert), +m_txInvert(txInvert), +m_pttInvert(pttInvert), +m_txDelay(txDelay), +m_dmrDelay(dmrDelay), +m_rxLevel(0.0F), +m_cwIdTXLevel(0.0F), +m_dstarTXLevel(0.0F), +m_dmrTXLevel(0.0F), +m_ysfTXLevel(0.0F), +m_p25TXLevel(0.0F), +m_nxdnTXLevel(0.0F), +m_m17TXLevel(0.0F), +m_pocsagTXLevel(0.0F), +m_fmTXLevel(0.0F), +m_ax25TXLevel(0.0F), +m_rfLevel(0.0F), +m_useCOSAsLockout(useCOSAsLockout), +m_trace(trace), +m_debug(debug), +m_rxFrequency(0U), +m_txFrequency(0U), +m_pocsagFrequency(0U), +m_dstarEnabled(false), +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_rxDCOffset(0), +m_txDCOffset(0), +m_serial(NULL), +m_buffer(NULL), +m_length(0U), +m_offset(0U), +m_state(SS_START), +m_type(0U), +m_rxDStarData(1000U, "Modem RX D-Star"), +m_txDStarData(1000U, "Modem TX D-Star"), +m_rxDMRData1(1000U, "Modem RX DMR1"), +m_rxDMRData2(1000U, "Modem RX DMR2"), +m_txDMRData1(1000U, "Modem TX DMR1"), +m_txDMRData2(1000U, "Modem TX DMR2"), +m_rxYSFData(1000U, "Modem RX YSF"), +m_txYSFData(1000U, "Modem TX YSF"), +m_rxP25Data(1000U, "Modem RX P25"), +m_txP25Data(1000U, "Modem TX P25"), +m_rxNXDNData(1000U, "Modem RX NXDN"), +m_txNXDNData(1000U, "Modem TX NXDN"), +m_rxM17Data(1000U, "Modem RX M17"), +m_txM17Data(1000U, "Modem TX M17"), +m_txPOCSAGData(1000U, "Modem TX POCSAG"), +m_rxFMData(5000U, "Modem RX FM"), +m_txFMData(5000U, "Modem TX FM"), +m_rxAX25Data(1000U, "Modem RX AX.25"), +m_txAX25Data(1000U, "Modem TX AX.25"), +m_rxSerialData(1000U, "Modem RX Serial"), +m_txSerialData(1000U, "Modem TX Serial"), +m_rxTransparentData(1000U, "Modem RX Transparent"), +m_txTransparentData(1000U, "Modem TX Transparent"), +m_sendTransparentDataFrameType(0U), +m_statusTimer(1000U, 0U, 250U), +m_inactivityTimer(1000U, 2U), +m_playoutTimer(1000U, 0U, 10U), +m_dstarSpace(0U), +m_dmrSpace1(0U), +m_dmrSpace2(0U), +m_ysfSpace(0U), +m_p25Space(0U), +m_nxdnSpace(0U), +m_m17Space(0U), +m_pocsagSpace(0U), +m_fmSpace(0U), +m_ax25Space(0U), +m_tx(false), +m_cd(false), +m_lockout(false), +m_error(false), +m_mode(MODE_IDLE), +m_hwType(HWT_UNKNOWN), +m_ax25RXTwist(0), +m_ax25TXDelay(300U), +m_ax25SlotTime(30U), +m_ax25PPersist(128U), +m_fmCallsign(), +m_fmCallsignSpeed(20U), +m_fmCallsignFrequency(1000U), +m_fmCallsignTime(600U), +m_fmCallsignHoldoff(0U), +m_fmCallsignHighLevel(35.0F), +m_fmCallsignLowLevel(15.0F), +m_fmCallsignAtStart(true), +m_fmCallsignAtEnd(true), +m_fmCallsignAtLatch(true), +m_fmRfAck("K"), +m_fmExtAck("N"), +m_fmAckSpeed(20U), +m_fmAckFrequency(1750U), +m_fmAckMinTime(4U), +m_fmAckDelay(1000U), +m_fmAckLevel(80.0F), +m_fmTimeout(120U), +m_fmTimeoutLevel(80.0F), +m_fmCtcssFrequency(88.4F), +m_fmCtcssHighThreshold(30U), +m_fmCtcssLowThreshold(20U), +m_fmCtcssLevel(10.0F), +m_fmKerchunkTime(0U), +m_fmHangTime(5U), +m_fmAccessMode(1U), +m_fmCOSInvert(false), +m_fmNoiseSquelch(false), +m_fmSquelchHighThreshold(30U), +m_fmSquelchLowThreshold(20U), +m_fmRFAudioBoost(1U), +m_fmExtAudioBoost(1U), +m_fmMaxDevLevel(90.0F), +m_fmExtEnable(false) +{ + m_buffer = new unsigned char[BUFFER_LENGTH]; + + assert(!port.empty()); +} + +CSerialModem::~CSerialModem() +{ + delete m_serial; + delete[] m_buffer; +} + +void CSerialModem::setSerialParams(const std::string& protocol, unsigned int address, unsigned int speed) +{ + // Create the serial controller instance according the protocol specified in conf. +#if defined(__linux__) + if (protocol == "i2c") + m_serial = new CI2CController(m_port, address); + else +#endif + m_serial = new CSerialController(m_port, speed, true); +} + +void CSerialModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency) +{ + m_rxFrequency = rxFrequency + rxOffset; + m_txFrequency = txFrequency + txOffset; + m_txDCOffset = txDCOffset; + m_rxDCOffset = rxDCOffset; + m_rfLevel = rfLevel; + m_pocsagFrequency = pocsagFrequency + txOffset; +} + +void CSerialModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool m17Enabled, bool pocsagEnabled, bool fmEnabled, bool ax25Enabled) +{ + m_dstarEnabled = dstarEnabled; + m_dmrEnabled = dmrEnabled; + m_ysfEnabled = ysfEnabled; + m_p25Enabled = p25Enabled; + m_nxdnEnabled = nxdnEnabled; + m_m17Enabled = m17Enabled; + m_pocsagEnabled = pocsagEnabled; + m_fmEnabled = fmEnabled; + m_ax25Enabled = ax25Enabled; +} + +void CSerialModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float m17TXLevel, float pocsagTXLevel, float fmTXLevel, float ax25TXLevel) +{ + m_rxLevel = rxLevel; + m_cwIdTXLevel = cwIdTXLevel; + m_dstarTXLevel = dstarTXLevel; + m_dmrTXLevel = dmrTXLevel; + m_ysfTXLevel = ysfTXLevel; + m_p25TXLevel = p25TXLevel; + m_nxdnTXLevel = nxdnTXLevel; + m_m17TXLevel = m17TXLevel; + m_pocsagTXLevel = pocsagTXLevel; + m_fmTXLevel = fmTXLevel; + m_ax25TXLevel = ax25TXLevel; +} + +void CSerialModem::setDMRParams(unsigned int colorCode) +{ + assert(colorCode < 16U); + + m_dmrColorCode = colorCode; +} + +void CSerialModem::setYSFParams(bool loDev, unsigned int txHang) +{ + m_ysfLoDev = loDev; + m_ysfTXHang = txHang; +} + +void CSerialModem::setP25Params(unsigned int txHang) +{ + m_p25TXHang = txHang; +} + +void CSerialModem::setNXDNParams(unsigned int txHang) +{ + m_nxdnTXHang = txHang; +} + +void CSerialModem::setM17Params(unsigned int txHang) +{ + m_m17TXHang = txHang; +} + +void CSerialModem::setAX25Params(int rxTwist, unsigned int txDelay, unsigned int slotTime, unsigned int pPersist) +{ + m_ax25RXTwist = rxTwist; + m_ax25TXDelay = txDelay; + m_ax25SlotTime = slotTime; + m_ax25PPersist = pPersist; +} + +void CSerialModem::setTransparentDataParams(unsigned int sendFrameType) +{ + m_sendTransparentDataFrameType = sendFrameType; +} + +bool CSerialModem::open() +{ + ::LogMessage("Opening the MMDVM"); + + bool ret = m_serial->open(); + if (!ret) + return false; + + ret = readVersion(); + if (!ret) { + m_serial->close(); + delete m_serial; + m_serial = NULL; + return false; + } else { + /* Stopping the inactivity timer here when a firmware version has been + successfuly read prevents the death spiral of "no reply from modem..." */ + m_inactivityTimer.stop(); + } + + ret = setFrequency(); + if (!ret) { + m_serial->close(); + delete m_serial; + m_serial = NULL; + return false; + } + + ret = writeConfig(); + if (!ret) { + m_serial->close(); + delete m_serial; + m_serial = NULL; + return false; + } + + if (m_fmEnabled && m_duplex) { + ret = setFMCallsignParams(); + if (!ret) { + m_serial->close(); + delete m_serial; + m_serial = NULL; + return false; + } + + ret = setFMAckParams(); + if (!ret) { + m_serial->close(); + delete m_serial; + m_serial = NULL; + return false; + } + + ret = setFMMiscParams(); + if (!ret) { + m_serial->close(); + delete m_serial; + m_serial = NULL; + return false; + } + + if (m_fmExtEnable) { + ret = setFMExtParams(); + if (!ret) { + m_serial->close(); + delete m_serial; + m_serial = NULL; + return false; + } + } + } + + m_statusTimer.start(); + + m_error = false; + m_offset = 0U; + + return true; +} + +void CSerialModem::clock(unsigned int ms) +{ + assert(m_serial != NULL); + + // Poll the modem status every 250ms + m_statusTimer.clock(ms); + if (m_statusTimer.hasExpired()) { + readStatus(); + m_statusTimer.start(); + } + + m_inactivityTimer.clock(ms); + if (m_inactivityTimer.hasExpired()) { + LogError("No reply from the modem for some time, resetting it"); + m_error = true; + close(); + + CThread::sleep(2000U); // 2s + while (!open()) + CThread::sleep(5000U); // 5s + } + + RESP_TYPE_MMDVM type = getResponse(); + + if (type == RTM_TIMEOUT) { + // Nothing to do + } else if (type == RTM_ERROR) { + // Nothing to do + } else { + // type == RTM_OK + switch (m_type) { + case MMDVM_DSTAR_HEADER: { + if (m_trace) + CUtils::dump(1U, "RX D-Star Header", m_buffer, m_length); + + unsigned char data = m_length - m_offset + 1U; + m_rxDStarData.addData(&data, 1U); + + data = TAG_HEADER; + m_rxDStarData.addData(&data, 1U); + + m_rxDStarData.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_DSTAR_DATA: { + if (m_trace) + CUtils::dump(1U, "RX D-Star Data", m_buffer, m_length); + + unsigned char data = m_length - m_offset + 1U; + m_rxDStarData.addData(&data, 1U); + + data = TAG_DATA; + m_rxDStarData.addData(&data, 1U); + + m_rxDStarData.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_DSTAR_LOST: { + if (m_trace) + CUtils::dump(1U, "RX D-Star Lost", m_buffer, m_length); + + unsigned char data = 1U; + m_rxDStarData.addData(&data, 1U); + + data = TAG_LOST; + m_rxDStarData.addData(&data, 1U); + } + break; + + case MMDVM_DSTAR_EOT: { + if (m_trace) + CUtils::dump(1U, "RX D-Star EOT", m_buffer, m_length); + + unsigned char data = 1U; + m_rxDStarData.addData(&data, 1U); + + data = TAG_EOT; + m_rxDStarData.addData(&data, 1U); + } + break; + + case MMDVM_DMR_DATA1: { + if (m_trace) + CUtils::dump(1U, "RX DMR Data 1", m_buffer, m_length); + + unsigned char data = m_length - m_offset + 1U; + m_rxDMRData1.addData(&data, 1U); + + if (m_buffer[3U] == (DMR_SYNC_DATA | DT_TERMINATOR_WITH_LC)) + data = TAG_EOT; + else + data = TAG_DATA; + m_rxDMRData1.addData(&data, 1U); + + m_rxDMRData1.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_DMR_DATA2: { + if (m_trace) + CUtils::dump(1U, "RX DMR Data 2", m_buffer, m_length); + + unsigned char data = m_length - m_offset + 1U; + m_rxDMRData2.addData(&data, 1U); + + if (m_buffer[3U] == (DMR_SYNC_DATA | DT_TERMINATOR_WITH_LC)) + data = TAG_EOT; + else + data = TAG_DATA; + m_rxDMRData2.addData(&data, 1U); + + m_rxDMRData2.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_DMR_LOST1: { + if (m_trace) + CUtils::dump(1U, "RX DMR Lost 1", m_buffer, m_length); + + unsigned char data = 1U; + m_rxDMRData1.addData(&data, 1U); + + data = TAG_LOST; + m_rxDMRData1.addData(&data, 1U); + } + break; + + case MMDVM_DMR_LOST2: { + if (m_trace) + CUtils::dump(1U, "RX DMR Lost 2", m_buffer, m_length); + + unsigned char data = 1U; + m_rxDMRData2.addData(&data, 1U); + + data = TAG_LOST; + m_rxDMRData2.addData(&data, 1U); + } + break; + + case MMDVM_YSF_DATA: { + if (m_trace) + CUtils::dump(1U, "RX YSF Data", m_buffer, m_length); + + unsigned char data = m_length - m_offset + 1U; + m_rxYSFData.addData(&data, 1U); + + data = TAG_DATA; + m_rxYSFData.addData(&data, 1U); + + m_rxYSFData.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_YSF_LOST: { + if (m_trace) + CUtils::dump(1U, "RX YSF Lost", m_buffer, m_length); + + unsigned char data = 1U; + m_rxYSFData.addData(&data, 1U); + + data = TAG_LOST; + m_rxYSFData.addData(&data, 1U); + } + break; + + case MMDVM_P25_HDR: { + if (m_trace) + CUtils::dump(1U, "RX P25 Header", m_buffer, m_length); + + unsigned char data = m_length - m_offset + 1U; + m_rxP25Data.addData(&data, 1U); + + data = TAG_HEADER; + m_rxP25Data.addData(&data, 1U); + + m_rxP25Data.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_P25_LDU: { + if (m_trace) + CUtils::dump(1U, "RX P25 LDU", m_buffer, m_length); + + unsigned char data = m_length - m_offset + 1U; + m_rxP25Data.addData(&data, 1U); + + data = TAG_DATA; + m_rxP25Data.addData(&data, 1U); + + m_rxP25Data.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_P25_LOST: { + if (m_trace) + CUtils::dump(1U, "RX P25 Lost", m_buffer, m_length); + + unsigned char data = 1U; + m_rxP25Data.addData(&data, 1U); + + data = TAG_LOST; + m_rxP25Data.addData(&data, 1U); + } + break; + + case MMDVM_NXDN_DATA: { + if (m_trace) + CUtils::dump(1U, "RX NXDN Data", m_buffer, m_length); + + unsigned char data = m_length - m_offset + 1U; + m_rxNXDNData.addData(&data, 1U); + + data = TAG_DATA; + m_rxNXDNData.addData(&data, 1U); + + m_rxNXDNData.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_NXDN_LOST: { + if (m_trace) + CUtils::dump(1U, "RX NXDN Lost", m_buffer, m_length); + + unsigned char data = 1U; + m_rxNXDNData.addData(&data, 1U); + + data = TAG_LOST; + m_rxNXDNData.addData(&data, 1U); + } + break; + + case MMDVM_M17_HEADER: { + if (m_trace) + CUtils::dump(1U, "RX M17 Header", m_buffer, m_length); + + unsigned char data = m_length - 2U; + m_rxM17Data.addData(&data, 1U); + + data = TAG_HEADER; + m_rxM17Data.addData(&data, 1U); + + m_rxM17Data.addData(m_buffer + 3U, m_length - 3U); + } + break; + + case MMDVM_M17_DATA: { + if (m_trace) + CUtils::dump(1U, "RX M17 Data", m_buffer, m_length); + + unsigned char data = m_length - 2U; + m_rxM17Data.addData(&data, 1U); + + data = TAG_DATA; + m_rxM17Data.addData(&data, 1U); + + m_rxM17Data.addData(m_buffer + 3U, m_length - 3U); + } + break; + + case MMDVM_M17_LOST: { + if (m_trace) + CUtils::dump(1U, "RX M17 Lost", m_buffer, m_length); + + unsigned char data = 1U; + m_rxM17Data.addData(&data, 1U); + + data = TAG_LOST; + m_rxM17Data.addData(&data, 1U); + } + break; + + case MMDVM_FM_DATA: { + if (m_trace) + CUtils::dump(1U, "RX FM Data", m_buffer, m_length); + + unsigned int data1 = m_length - m_offset + 1U; + m_rxFMData.addData((unsigned char*)&data1, sizeof(unsigned int)); + + unsigned char data2 = TAG_DATA; + m_rxFMData.addData(&data2, 1U); + + m_rxFMData.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_FM_CONTROL: { + if (m_trace) + CUtils::dump(1U, "RX FM Control", m_buffer, m_length); + + unsigned int data1 = m_length - m_offset + 1U; + m_rxFMData.addData((unsigned char*)&data1, sizeof(unsigned int)); + + unsigned char data2= TAG_HEADER; + m_rxFMData.addData(&data2, 1U); + + m_rxFMData.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_FM_EOT: { + if(m_trace) + CUtils::dump(1U, "RX FM End of transmission", m_buffer, m_length); + + unsigned int data1 = m_length - m_offset + 1U; + m_rxFMData.addData((unsigned char*)&data1, sizeof(unsigned int)); + + unsigned char data2 = TAG_EOT; + m_rxFMData.addData(&data2, 1U); + + m_rxFMData.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_AX25_DATA: { + if (m_trace) + CUtils::dump(1U, "RX AX.25 Data", m_buffer, m_length); + + unsigned int data = m_length - m_offset; + m_rxAX25Data.addData((unsigned char*)&data, sizeof(unsigned int)); + + m_rxAX25Data.addData(m_buffer + m_offset, m_length - m_offset); + } + break; + + case MMDVM_GET_STATUS: + // if (m_trace) + // CUtils::dump(1U, "GET_STATUS", m_buffer, m_length); + + switch (m_protocolVersion) { + case 1U: { + m_mode = m_buffer[m_offset + 1U]; + + m_tx = (m_buffer[m_offset + 2U] & 0x01U) == 0x01U; + bool adcOverflow = (m_buffer[m_offset + 2U] & 0x02U) == 0x02U; + if (adcOverflow) + LogError("MMDVM ADC levels have overflowed"); + bool rxOverflow = (m_buffer[m_offset + 2U] & 0x04U) == 0x04U; + if (rxOverflow) + LogError("MMDVM RX buffer has overflowed"); + bool txOverflow = (m_buffer[m_offset + 2U] & 0x08U) == 0x08U; + if (txOverflow) + LogError("MMDVM TX buffer has overflowed"); + m_lockout = (m_buffer[m_offset + 2U] & 0x10U) == 0x10U; + bool dacOverflow = (m_buffer[m_offset + 2U] & 0x20U) == 0x20U; + if (dacOverflow) + LogError("MMDVM DAC levels have overflowed"); + m_cd = (m_buffer[m_offset + 2U] & 0x40U) == 0x40U; + + m_p25Space = 0U; + m_nxdnSpace = 0U; + m_m17Space = 0U; + m_pocsagSpace = 0U; + m_fmSpace = 0U; + m_ax25Space = 0U; + + m_dstarSpace = m_buffer[m_offset + 3U]; + m_dmrSpace1 = m_buffer[m_offset + 4U]; + m_dmrSpace2 = m_buffer[m_offset + 5U]; + m_ysfSpace = m_buffer[m_offset + 6U]; + + // The following depend on the version of the firmware + if (m_length > (m_offset + 7U)) + m_p25Space = m_buffer[m_offset + 7U]; + if (m_length > (m_offset + 8U)) + m_nxdnSpace = m_buffer[m_offset + 8U]; + if (m_length > (m_offset + 9U)) + m_pocsagSpace = m_buffer[m_offset + 9U]; + if (m_length > (m_offset + 10U)) + m_m17Space = m_buffer[m_offset + 10U]; + } + break; + + case 2U: { + m_mode = m_buffer[m_offset + 0U]; + + m_tx = (m_buffer[m_offset + 1U] & 0x01U) == 0x01U; + bool adcOverflow = (m_buffer[m_offset + 1U] & 0x02U) == 0x02U; + if (adcOverflow) + LogError("MMDVM ADC levels have overflowed"); + bool rxOverflow = (m_buffer[m_offset + 1U] & 0x04U) == 0x04U; + if (rxOverflow) + LogError("MMDVM RX buffer has overflowed"); + bool txOverflow = (m_buffer[m_offset + 1U] & 0x08U) == 0x08U; + if (txOverflow) + LogError("MMDVM TX buffer has overflowed"); + m_lockout = (m_buffer[m_offset + 1U] & 0x10U) == 0x10U; + bool dacOverflow = (m_buffer[m_offset + 1U] & 0x20U) == 0x20U; + if (dacOverflow) + LogError("MMDVM DAC levels have overflowed"); + m_cd = (m_buffer[m_offset + 1U] & 0x40U) == 0x40U; + + m_dstarSpace = m_buffer[m_offset + 3U]; + m_dmrSpace1 = m_buffer[m_offset + 4U]; + m_dmrSpace2 = m_buffer[m_offset + 5U]; + m_ysfSpace = m_buffer[m_offset + 6U]; + m_p25Space = m_buffer[m_offset + 7U]; + m_nxdnSpace = m_buffer[m_offset + 8U]; + m_m17Space = m_buffer[m_offset + 9U]; + m_fmSpace = m_buffer[m_offset + 10U]; + m_pocsagSpace = m_buffer[m_offset + 11U]; + m_ax25Space = m_buffer[m_offset + 12U]; + } + break; + + default: + m_dstarSpace = 0U; + m_dmrSpace1 = 0U; + m_dmrSpace2 = 0U; + m_ysfSpace = 0U; + m_p25Space = 0U; + m_nxdnSpace = 0U; + m_m17Space = 0U; + m_pocsagSpace = 0U; + m_fmSpace = 0U; + m_ax25Space = 0U; + break; + } + + 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)); + break; + + case MMDVM_TRANSPARENT: { + if (m_trace) + CUtils::dump(1U, "RX Transparent Data", m_buffer, m_length); + + unsigned char offset = m_sendTransparentDataFrameType; + if (offset > 1U) offset = 1U; + unsigned char data = m_length - m_offset + offset; + m_rxTransparentData.addData(&data, 1U); + + m_rxTransparentData.addData(m_buffer + m_offset - offset, m_length - m_offset + offset); + } + break; + + // These should not be received, but don't complain if we do + case MMDVM_GET_VERSION: + case MMDVM_ACK: + break; + + case MMDVM_NAK: + LogWarning("Received a NAK from the MMDVM, command = 0x%02X, reason = %u", m_buffer[m_offset], m_buffer[m_offset + 1U]); + break; + + case MMDVM_DEBUG1: + case MMDVM_DEBUG2: + case MMDVM_DEBUG3: + case MMDVM_DEBUG4: + case MMDVM_DEBUG5: + printDebug(); + break; + + case MMDVM_SERIAL_DATA: + if (m_trace) + CUtils::dump(1U, "RX Serial Data", m_buffer, m_length); + m_rxSerialData.addData(m_buffer + m_offset, m_length - m_offset); + break; + + default: + LogMessage("Unknown message, type: %02X", m_type); + CUtils::dump("Buffer dump", m_buffer, m_length); + break; + } + } + + // Only feed data to the modem if the playout timer has expired + m_playoutTimer.clock(ms); + if (!m_playoutTimer.hasExpired()) + return; + + if (m_dstarSpace > 1U && !m_txDStarData.isEmpty()) { + unsigned char buffer[4U]; + m_txDStarData.peek(buffer, 4U); + + if ((buffer[3U] == MMDVM_DSTAR_HEADER && m_dstarSpace > 4U) || + (buffer[3U] == MMDVM_DSTAR_DATA && m_dstarSpace > 1U) || + (buffer[3U] == MMDVM_DSTAR_EOT && m_dstarSpace > 1U)) { + unsigned char len = 0U; + m_txDStarData.getData(&len, 1U); + m_txDStarData.getData(m_buffer, len); + + switch (buffer[3U]) { + case MMDVM_DSTAR_HEADER: + if (m_trace) + CUtils::dump(1U, "TX D-Star Header", m_buffer, len); + m_dstarSpace -= 4U; + break; + case MMDVM_DSTAR_DATA: + if (m_trace) + CUtils::dump(1U, "TX D-Star Data", m_buffer, len); + m_dstarSpace -= 1U; + break; + default: + if (m_trace) + CUtils::dump(1U, "TX D-Star EOT", m_buffer, len); + m_dstarSpace -= 1U; + break; + } + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing D-Star data to the MMDVM"); + + m_playoutTimer.start(); + } + } + + if (m_dmrSpace1 > 1U && !m_txDMRData1.isEmpty()) { + unsigned char len = 0U; + m_txDMRData1.getData(&len, 1U); + m_txDMRData1.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX DMR Data 1", m_buffer, len); + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing DMR data to the MMDVM"); + + m_playoutTimer.start(); + + m_dmrSpace1--; + } + + if (m_dmrSpace2 > 1U && !m_txDMRData2.isEmpty()) { + unsigned char len = 0U; + m_txDMRData2.getData(&len, 1U); + m_txDMRData2.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX DMR Data 2", m_buffer, len); + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing DMR data to the MMDVM"); + + m_playoutTimer.start(); + + m_dmrSpace2--; + } + + if (m_ysfSpace > 1U && !m_txYSFData.isEmpty()) { + unsigned char len = 0U; + m_txYSFData.getData(&len, 1U); + m_txYSFData.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX YSF Data", m_buffer, len); + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing YSF data to the MMDVM"); + + m_playoutTimer.start(); + + m_ysfSpace--; + } + + if (m_p25Space > 1U && !m_txP25Data.isEmpty()) { + unsigned char len = 0U; + m_txP25Data.getData(&len, 1U); + m_txP25Data.getData(m_buffer, len); + + if (m_trace) { + if (m_buffer[2U] == MMDVM_P25_HDR) + CUtils::dump(1U, "TX P25 HDR", m_buffer, len); + else + CUtils::dump(1U, "TX P25 LDU", m_buffer, len); + } + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing P25 data to the MMDVM"); + + m_playoutTimer.start(); + + m_p25Space--; + } + + if (m_nxdnSpace > 1U && !m_txNXDNData.isEmpty()) { + unsigned char len = 0U; + m_txNXDNData.getData(&len, 1U); + m_txNXDNData.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX NXDN Data", m_buffer, len); + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing NXDN data to the MMDVM"); + + m_playoutTimer.start(); + + m_nxdnSpace--; + } + + if (m_m17Space > 1U && !m_txM17Data.isEmpty()) { + unsigned char len = 0U; + m_txM17Data.getData(&len, 1U); + m_txM17Data.getData(m_buffer, len); + + if (m_trace) { + if (m_buffer[2U] == MMDVM_M17_HEADER) + CUtils::dump(1U, "TX M17 Header", m_buffer, len); + else + CUtils::dump(1U, "TX M17 Data", m_buffer, len); + } + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing M17 data to the MMDVM"); + + m_playoutTimer.start(); + + m_m17Space--; + } + + if (m_pocsagSpace > 1U && !m_txPOCSAGData.isEmpty()) { + unsigned char len = 0U; + m_txPOCSAGData.getData(&len, 1U); + m_txPOCSAGData.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX POCSAG Data", m_buffer, len); + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing POCSAG data to the MMDVM"); + + m_playoutTimer.start(); + + m_pocsagSpace--; + } + + if (m_fmSpace > 1U && !m_txFMData.isEmpty()) { + unsigned int len = 0U; + m_txFMData.getData((unsigned char*)&len, sizeof(unsigned int)); + m_txFMData.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX FM Data", m_buffer, len); + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing FM data to the MMDVM"); + + m_playoutTimer.start(); + + m_fmSpace--; + } + + if (m_ax25Space > 0U && !m_txAX25Data.isEmpty()) { + unsigned int len = 0U; + m_txAX25Data.getData((unsigned char*)&len, sizeof(unsigned int)); + m_txAX25Data.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX AX.25 Data", m_buffer, len); + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing AX.25 data to the MMDVM"); + + m_playoutTimer.start(); + + m_ax25Space = 0U; + } + + if (!m_txTransparentData.isEmpty()) { + unsigned char len = 0U; + m_txTransparentData.getData(&len, 1U); + m_txTransparentData.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX Transparent Data", m_buffer, len); + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing Transparent data to the MMDVM"); + } + + if (!m_txSerialData.isEmpty()) { + unsigned char len = 0U; + m_txSerialData.getData(&len, 1U); + m_txSerialData.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX Serial Data", m_buffer, len); + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing Serial data to the MMDVM"); + } +} + +void CSerialModem::close() +{ + assert(m_serial != NULL); + + ::LogMessage("Closing the MMDVM"); + + m_serial->close(); +} + +unsigned int CSerialModem::readDStarData(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxDStarData.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxDStarData.getData(&len, 1U); + m_rxDStarData.getData(data, len); + + return len; +} + +unsigned int CSerialModem::readDMRData1(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxDMRData1.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxDMRData1.getData(&len, 1U); + m_rxDMRData1.getData(data, len); + + return len; +} + +unsigned int CSerialModem::readDMRData2(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxDMRData2.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxDMRData2.getData(&len, 1U); + m_rxDMRData2.getData(data, len); + + return len; +} + +unsigned int CSerialModem::readYSFData(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxYSFData.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxYSFData.getData(&len, 1U); + m_rxYSFData.getData(data, len); + + return len; +} + +unsigned int CSerialModem::readP25Data(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxP25Data.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxP25Data.getData(&len, 1U); + m_rxP25Data.getData(data, len); + + return len; +} + +unsigned int CSerialModem::readNXDNData(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxNXDNData.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxNXDNData.getData(&len, 1U); + m_rxNXDNData.getData(data, len); + + return len; +} + +unsigned int CSerialModem::readM17Data(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxM17Data.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxM17Data.getData(&len, 1U); + m_rxM17Data.getData(data, len); + + return len; +} + +unsigned int CSerialModem::readFMData(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxFMData.isEmpty()) + return 0U; + + unsigned int len = 0U; + m_rxFMData.getData((unsigned char*)&len, sizeof(unsigned int)); + m_rxFMData.getData(data, len); + + return len; +} + +unsigned int CSerialModem::readAX25Data(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxAX25Data.isEmpty()) + return 0U; + + unsigned int len = 0U; + m_rxAX25Data.getData((unsigned char*)&len, sizeof(unsigned int)); + m_rxAX25Data.getData(data, len); + + return len; +} + +unsigned int CSerialModem::readTransparentData(unsigned char* data) +{ + assert(data != NULL); + + if (m_rxTransparentData.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_rxTransparentData.getData(&len, 1U); + m_rxTransparentData.getData(data, len); + + return len; +} + +unsigned int CSerialModem::readSerial(unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned int n = 0U; + while (!m_rxSerialData.isEmpty() && n < length) { + m_rxSerialData.getData(data + n, 1U); + n++; + } + + return n; +} + +bool CSerialModem::hasDStarSpace() const +{ + unsigned int space = m_txDStarData.freeSpace() / (DSTAR_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CSerialModem::writeDStarData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned char buffer[50U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + + switch (data[0U]) { + case TAG_HEADER: + buffer[2U] = MMDVM_DSTAR_HEADER; + break; + case TAG_DATA: + buffer[2U] = MMDVM_DSTAR_DATA; + break; + case TAG_EOT: + buffer[2U] = MMDVM_DSTAR_EOT; + break; + default: + CUtils::dump(2U, "Unknown D-Star packet type", data, length); + return false; + } + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txDStarData.addData(&len, 1U); + m_txDStarData.addData(buffer, len); + + return true; +} + +bool CSerialModem::hasDMRSpace1() const +{ + unsigned int space = m_txDMRData1.freeSpace() / (DMR_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CSerialModem::hasDMRSpace2() const +{ + unsigned int space = m_txDMRData2.freeSpace() / (DMR_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CSerialModem::writeDMRData1(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[40U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = MMDVM_DMR_DATA1; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txDMRData1.addData(&len, 1U); + m_txDMRData1.addData(buffer, len); + + return true; +} + +bool CSerialModem::writeDMRData2(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[40U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = MMDVM_DMR_DATA2; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txDMRData2.addData(&len, 1U); + m_txDMRData2.addData(buffer, len); + + return true; +} + +bool CSerialModem::hasYSFSpace() const +{ + unsigned int space = m_txYSFData.freeSpace() / (YSF_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CSerialModem::writeYSFData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[130U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = MMDVM_YSF_DATA; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txYSFData.addData(&len, 1U); + m_txYSFData.addData(buffer, len); + + return true; +} + +bool CSerialModem::hasP25Space() const +{ + unsigned int space = m_txP25Data.freeSpace() / (P25_LDU_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CSerialModem::writeP25Data(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_HEADER && data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[250U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = (data[0U] == TAG_HEADER) ? MMDVM_P25_HDR : MMDVM_P25_LDU; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txP25Data.addData(&len, 1U); + m_txP25Data.addData(buffer, len); + + return true; +} + +bool CSerialModem::hasNXDNSpace() const +{ + unsigned int space = m_txNXDNData.freeSpace() / (NXDN_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CSerialModem::writeNXDNData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[130U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + buffer[2U] = MMDVM_NXDN_DATA; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txNXDNData.addData(&len, 1U); + m_txNXDNData.addData(buffer, len); + + return true; +} + +bool CSerialModem::hasM17Space() const +{ + unsigned int space = m_txM17Data.freeSpace() / (M17_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CSerialModem::writeM17Data(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + if (data[0U] != TAG_HEADER && data[0U] != TAG_DATA && data[0U] != TAG_EOT) + return false; + + unsigned char buffer[130U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 2U; + + if (data[0U] == TAG_HEADER) + buffer[2U] = MMDVM_M17_HEADER; + else + buffer[2U] = MMDVM_M17_DATA; + + ::memcpy(buffer + 3U, data + 1U, length - 1U); + + unsigned char len = length + 2U; + m_txM17Data.addData(&len, 1U); + m_txM17Data.addData(buffer, len); + + return true; +} + +bool CSerialModem::hasPOCSAGSpace() const +{ + unsigned int space = m_txPOCSAGData.freeSpace() / (POCSAG_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CSerialModem::writePOCSAGData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned char buffer[130U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 3U; + buffer[2U] = MMDVM_POCSAG_DATA; + + ::memcpy(buffer + 3U, data, length); + + unsigned char len = length + 3U; + m_txPOCSAGData.addData(&len, 1U); + m_txPOCSAGData.addData(buffer, len); + + return true; +} + +unsigned int CSerialModem::getFMSpace() const +{ + return m_txFMData.freeSpace(); +} + +bool CSerialModem::writeFMData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned char buffer[500U]; + + unsigned int len; + if (length > 252U) { + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 0U; + buffer[2U] = (length + 4U) - 255U; + buffer[3U] = MMDVM_FM_DATA; + ::memcpy(buffer + 4U, data, length); + len = length + 4U; + } else { + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 3U; + buffer[2U] = MMDVM_FM_DATA; + ::memcpy(buffer + 3U, data, length); + len = length + 3U; + } + + m_txFMData.addData((unsigned char*)&len, sizeof(unsigned int)); + m_txFMData.addData(buffer, len); + + return true; +} + +bool CSerialModem::hasAX25Space() const +{ + unsigned int space = m_txAX25Data.freeSpace() / (AX25_MAX_FRAME_LENGTH_BYTES + 5U); + + return space > 1U; +} + +bool CSerialModem::writeAX25Data(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned char buffer[500U]; + + unsigned int len; + if (length > 252U) { + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 0U; + buffer[2U] = (length + 4U) - 255U; + buffer[3U] = MMDVM_AX25_DATA; + ::memcpy(buffer + 4U, data, length); + len = length + 4U; + } else { + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 3U; + buffer[2U] = MMDVM_AX25_DATA; + ::memcpy(buffer + 3U, data, length); + len = length + 3U; + } + + m_txAX25Data.addData((unsigned char*)&len, sizeof(unsigned int)); + m_txAX25Data.addData(buffer, len); + + return true; +} + +bool CSerialModem::writeTransparentData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned char buffer[250U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 3U; + buffer[2U] = MMDVM_TRANSPARENT; + + if (m_sendTransparentDataFrameType > 0U) { + ::memcpy(buffer + 2U, data, length); + length--; + buffer[1U]--; + + //when sendFrameType==1 , only 0x80 and 0x90 (MMDVM_SERIAL and MMDVM_TRANSPARENT) are allowed + // and reverted to default (MMDVM_TRANSPARENT) for any other value + //when >1, frame type is not checked + if (m_sendTransparentDataFrameType == 1U) { + if ((buffer[2U] & 0xE0) != 0x80) + buffer[2U] = MMDVM_TRANSPARENT; + } + } else { + ::memcpy(buffer + 3U, data, length); + } + + unsigned char len = length + 3U; + m_txTransparentData.addData(&len, 1U); + m_txTransparentData.addData(buffer, len); + + return true; +} + +bool CSerialModem::writeDStarInfo(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) +{ + assert(m_serial != NULL); + assert(my1 != NULL); + assert(my2 != NULL); + assert(your != NULL); + assert(type != NULL); + assert(reflector != NULL); + + unsigned char buffer[50U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 33U; + buffer[2U] = MMDVM_QSO_INFO; + + buffer[3U] = MODE_DSTAR; + + ::memcpy(buffer + 4U, my1, DSTAR_LONG_CALLSIGN_LENGTH); + ::memcpy(buffer + 12U, my2, DSTAR_SHORT_CALLSIGN_LENGTH); + + ::memcpy(buffer + 16U, your, DSTAR_LONG_CALLSIGN_LENGTH); + + ::memcpy(buffer + 24U, type, 1U); + + ::memcpy(buffer + 25U, reflector, DSTAR_LONG_CALLSIGN_LENGTH); + + return m_serial->write(buffer, 33U) != 33; +} + +bool CSerialModem::writeDMRInfo(unsigned int slotNo, const std::string& src, bool group, const std::string& dest, const char* type) +{ + assert(m_serial != NULL); + assert(type != NULL); + + unsigned char buffer[50U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 47U; + buffer[2U] = MMDVM_QSO_INFO; + + buffer[3U] = MODE_DMR; + + buffer[4U] = slotNo; + + ::sprintf((char*)(buffer + 5U), "%20.20s", src.c_str()); + + buffer[25U] = group ? 'G' : 'I'; + + ::sprintf((char*)(buffer + 26U), "%20.20s", dest.c_str()); + + ::memcpy(buffer + 46U, type, 1U); + + return m_serial->write(buffer, 47U) != 47; +} + +bool CSerialModem::writeYSFInfo(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) +{ + assert(m_serial != NULL); + assert(source != NULL); + assert(dest != NULL); + assert(type != NULL); + assert(origin != NULL); + + unsigned char buffer[40U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 36U; + buffer[2U] = MMDVM_QSO_INFO; + + buffer[3U] = MODE_YSF; + + ::memcpy(buffer + 4U, source, YSF_CALLSIGN_LENGTH); + ::memcpy(buffer + 14U, dest, YSF_CALLSIGN_LENGTH); + + ::memcpy(buffer + 24U, type, 1U); + + ::memcpy(buffer + 25U, origin, YSF_CALLSIGN_LENGTH); + + buffer[35U] = dgid; + + return m_serial->write(buffer, 36U) != 36; +} + +bool CSerialModem::writeP25Info(const char* source, bool group, unsigned int dest, const char* type) +{ + assert(m_serial != NULL); + assert(source != NULL); + assert(type != NULL); + + unsigned char buffer[40U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 31U; + buffer[2U] = MMDVM_QSO_INFO; + + buffer[3U] = MODE_DMR; + + ::sprintf((char*)(buffer + 4U), "%20.20s", source); + + buffer[24U] = group ? 'G' : 'I'; + + ::sprintf((char*)(buffer + 25U), "%05u", dest); // 16-bits + + ::memcpy(buffer + 30U, type, 1U); + + return m_serial->write(buffer, 31U) != 31; +} + +bool CSerialModem::writeNXDNInfo(const char* source, bool group, unsigned int dest, const char* type) +{ + assert(m_serial != NULL); + assert(source != NULL); + assert(type != NULL); + + unsigned char buffer[40U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 31U; + buffer[2U] = MMDVM_QSO_INFO; + + buffer[3U] = MODE_NXDN; + + ::sprintf((char*)(buffer + 4U), "%20.20s", source); + + buffer[24U] = group ? 'G' : 'I'; + + ::sprintf((char*)(buffer + 25U), "%05u", dest); // 16-bits + + ::memcpy(buffer + 30U, type, 1U); + + return m_serial->write(buffer, 31U) != 31; +} + +bool CSerialModem::writeM17Info(const char* source, const char* dest, const char* type) +{ + assert(m_serial != NULL); + assert(source != NULL); + assert(dest != NULL); + assert(type != NULL); + + unsigned char buffer[40U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 31U; + buffer[2U] = MMDVM_QSO_INFO; + + buffer[3U] = MODE_M17; + + ::sprintf((char*)(buffer + 4U), "%9.9s", source); + + ::sprintf((char*)(buffer + 13U), "%9.9s", dest); + + ::memcpy(buffer + 22U, type, 1U); + + return m_serial->write(buffer, 23U) != 23; +} + +bool CSerialModem::writePOCSAGInfo(unsigned int ric, const std::string& message) +{ + assert(m_serial != NULL); + + size_t length = message.size(); + + unsigned char buffer[250U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 11U; + buffer[2U] = MMDVM_QSO_INFO; + + buffer[3U] = MODE_POCSAG; + + ::sprintf((char*)(buffer + 4U), "%07u", ric); // 21-bits + + ::memcpy(buffer + 11U, message.c_str(), length); + + int ret = m_serial->write(buffer, length + 11U); + + return ret != int(length + 11U); +} + +bool CSerialModem::writeIPInfo(const std::string& address) +{ + assert(m_serial != NULL); + + size_t length = address.size(); + + unsigned char buffer[25U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 4U; + buffer[2U] = MMDVM_QSO_INFO; + + buffer[3U] = 250U; + + ::memcpy(buffer + 4U, address.c_str(), length); + + int ret = m_serial->write(buffer, length + 4U); + + return ret != int(length + 4U); +} + +bool CSerialModem::writeSerial(const unsigned char* data, unsigned int length) +{ + assert(m_serial != NULL); + assert(data != NULL); + assert(length > 0U); + + unsigned char buffer[255U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 3U; + buffer[2U] = MMDVM_SERIAL_DATA; + + ::memcpy(buffer + 3U, data, length); + + unsigned char len = length + 3U; + m_txSerialData.addData(&len, 1U); + m_txSerialData.addData(buffer, len); + + return true; +} + +bool CSerialModem::hasTX() const +{ + return m_tx; +} + +bool CSerialModem::hasCD() const +{ + return m_cd; +} + +bool CSerialModem::hasLockout() const +{ + return m_lockout; +} + +bool CSerialModem::hasError() const +{ + return m_error; +} + +bool CSerialModem::readVersion() +{ + assert(m_serial != NULL); + + CThread::sleep(2000U); // 2s + + for (unsigned int i = 0U; i < 6U; i++) { + unsigned char buffer[3U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 3U; + buffer[2U] = MMDVM_GET_VERSION; + + // CUtils::dump(1U, "Written", buffer, 3U); + + int ret = m_serial->write(buffer, 3U); + if (ret != 3) + return false; + +#if defined(__APPLE__) + m_serial->setNonblock(true); +#endif + + for (unsigned int count = 0U; count < MAX_RESPONSES; count++) { + CThread::sleep(10U); + RESP_TYPE_MMDVM resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] == MMDVM_GET_VERSION) { + if (::memcmp(m_buffer + 4U, "MMDVM ", 6U) == 0) + m_hwType = HWT_MMDVM; + else if (::memcmp(m_buffer + 4U, "DVMEGA", 6U) == 0) + m_hwType = HWT_DVMEGA; + else if (::memcmp(m_buffer + 4U, "ZUMspot", 7U) == 0) + m_hwType = HWT_MMDVM_ZUMSPOT; + else if (::memcmp(m_buffer + 4U, "MMDVM_HS_Hat", 12U) == 0) + m_hwType = HWT_MMDVM_HS_HAT; + else if (::memcmp(m_buffer + 4U, "MMDVM_HS_Dual_Hat", 17U) == 0) + m_hwType = HWT_MMDVM_HS_DUAL_HAT; + else if (::memcmp(m_buffer + 4U, "Nano_hotSPOT", 12U) == 0) + m_hwType = HWT_NANO_HOTSPOT; + else if (::memcmp(m_buffer + 4U, "Nano_DV", 7U) == 0) + m_hwType = HWT_NANO_DV; + else if (::memcmp(m_buffer + 4U, "D2RG_MMDVM_HS", 13U) == 0) + m_hwType = HWT_D2RG_MMDVM_HS; + else if (::memcmp(m_buffer + 4U, "MMDVM_HS-", 9U) == 0) + m_hwType = HWT_MMDVM_HS; + else if (::memcmp(m_buffer + 4U, "OpenGD77_HS", 11U) == 0) + m_hwType = HWT_OPENGD77_HS; + else if (::memcmp(m_buffer + 4U, "SkyBridge", 9U) == 0) + m_hwType = HWT_SKYBRIDGE; + + m_protocolVersion = m_buffer[3U]; + + switch (m_protocolVersion) { + case 1U: + LogInfo("MMDVM protocol version: %u, description: %.*s", m_protocolVersion, m_length - 4U, m_buffer + 4U); + return true; + + case 2U: + LogInfo("MMDVM protocol version: %u, description: %.*s", m_protocolVersion, m_length - 23U, m_buffer + 23U); + switch (m_buffer[6U]) { + case 0U: + LogInfo("CPU: Atmel ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U], m_buffer[17U], m_buffer[18U], m_buffer[19U], m_buffer[20U], m_buffer[21U], m_buffer[22U]); + break; + case 1U: + LogInfo("CPU: NXP ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U], m_buffer[17U], m_buffer[18U], m_buffer[19U], m_buffer[20U], m_buffer[21U], m_buffer[22U]); + break; + case 2U: + LogInfo("CPU: ST-Micro ARM, UDID: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", m_buffer[7U], m_buffer[8U], m_buffer[9U], m_buffer[10U], m_buffer[11U], m_buffer[12U], m_buffer[13U], m_buffer[14U], m_buffer[15U], m_buffer[16U], m_buffer[17U], m_buffer[18U]); + break; + default: + LogInfo("CPU: Unknown type: %u", m_buffer[6U]); + break; + } + char modeText[100U]; + ::strcpy(modeText, "Modes:"); + if ((m_buffer[4U] & 0x01U) == 0x01U) + ::strcat(modeText, " D-Star"); + if ((m_buffer[4U] & 0x02U) == 0x02U) + ::strcat(modeText, " DMR"); + if ((m_buffer[4U] & 0x04U) == 0x04U) + ::strcat(modeText, " YSF"); + if ((m_buffer[4U] & 0x08U) == 0x08U) + ::strcat(modeText, " P25"); + if ((m_buffer[4U] & 0x10U) == 0x10U) + ::strcat(modeText, " NXDN"); + if ((m_buffer[4U] & 0x20U) == 0x20U) + ::strcat(modeText, " M17"); + if ((m_buffer[4U] & 0x40U) == 0x40U) + ::strcat(modeText, " FM"); + if ((m_buffer[5U] & 0x01U) == 0x01U) + ::strcat(modeText, " POCSAG"); + if ((m_buffer[5U] & 0x02U) == 0x02U) + ::strcat(modeText, " AX.25"); + LogInfo(modeText); + return true; + + default: + LogError("MMDVM protocol version: %u, unsupported by this version of the MMDVM Host", m_protocolVersion); + return false; + } + + return true; + } + } + + CThread::sleep(1500U); + } + + LogError("Unable to read the firmware version after six attempts"); + + return false; +} + +bool CSerialModem::readStatus() +{ + assert(m_serial != NULL); + + unsigned char buffer[3U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 3U; + buffer[2U] = MMDVM_GET_STATUS; + + // CUtils::dump(1U, "Written", buffer, 3U); + + return m_serial->write(buffer, 3U) == 3; +} + +bool CSerialModem::writeConfig() +{ + switch (m_protocolVersion) { + case 1U: + return setConfig1(); + case 2U: + return setConfig2(); + default: + return false; + } +} + +bool CSerialModem::setConfig1() +{ + assert(m_serial != NULL); + + unsigned char buffer[30U]; + + buffer[0U] = MMDVM_FRAME_START; + + buffer[1U] = 26U; + + buffer[2U] = MMDVM_SET_CONFIG; + + buffer[3U] = 0x00U; + if (m_rxInvert) + buffer[3U] |= 0x01U; + if (m_txInvert) + buffer[3U] |= 0x02U; + if (m_pttInvert) + buffer[3U] |= 0x04U; + if (m_ysfLoDev) + buffer[3U] |= 0x08U; + if (m_debug) + buffer[3U] |= 0x10U; + if (m_useCOSAsLockout) + buffer[3U] |= 0x20U; + if (!m_duplex) + buffer[3U] |= 0x80U; + + buffer[4U] = 0x00U; + if (m_dstarEnabled) + buffer[4U] |= 0x01U; + if (m_dmrEnabled) + buffer[4U] |= 0x02U; + if (m_ysfEnabled) + buffer[4U] |= 0x04U; + if (m_p25Enabled) + buffer[4U] |= 0x08U; + if (m_nxdnEnabled) + buffer[4U] |= 0x10U; + if (m_pocsagEnabled) + buffer[4U] |= 0x20U; + if (m_m17Enabled) + buffer[4U] |= 0x40U; + + buffer[5U] = m_txDelay / 10U; // In 10ms units + + buffer[6U] = MODE_IDLE; + + buffer[7U] = (unsigned char)(m_rxLevel * 2.55F + 0.5F); + + buffer[8U] = (unsigned char)(m_cwIdTXLevel * 2.55F + 0.5F); + + buffer[9U] = m_dmrColorCode; + + buffer[10U] = m_dmrDelay; + + buffer[11U] = 128U; // Was OscOffset + + buffer[12U] = (unsigned char)(m_dstarTXLevel * 2.55F + 0.5F); + buffer[13U] = (unsigned char)(m_dmrTXLevel * 2.55F + 0.5F); + buffer[14U] = (unsigned char)(m_ysfTXLevel * 2.55F + 0.5F); + buffer[15U] = (unsigned char)(m_p25TXLevel * 2.55F + 0.5F); + + buffer[16U] = (unsigned char)(m_txDCOffset + 128); + buffer[17U] = (unsigned char)(m_rxDCOffset + 128); + + buffer[18U] = (unsigned char)(m_nxdnTXLevel * 2.55F + 0.5F); + + buffer[19U] = (unsigned char)m_ysfTXHang; + + buffer[20U] = (unsigned char)(m_pocsagTXLevel * 2.55F + 0.5F); + + buffer[21U] = (unsigned char)(m_fmTXLevel * 2.55F + 0.5F); + + buffer[22U] = (unsigned char)m_p25TXHang; + + buffer[23U] = (unsigned char)m_nxdnTXHang; + + buffer[24U] = (unsigned char)(m_m17TXLevel * 2.55F + 0.5F); + + buffer[25U] = (unsigned char)m_m17TXHang; + + // CUtils::dump(1U, "Written", buffer, 26U); + + int ret = m_serial->write(buffer, 26U); + if (ret != 26) + return false; + + unsigned int count = 0U; + RESP_TYPE_MMDVM resp; + do { + CThread::sleep(10U); + + resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { + count++; + if (count >= MAX_RESPONSES) { + LogError("The MMDVM is not responding to the SET_CONFIG command"); + return false; + } + } + } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); + + // CUtils::dump(1U, "Response", m_buffer, m_length); + + if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { + LogError("Received a NAK to the SET_CONFIG command from the modem"); + return false; + } + + m_playoutTimer.start(); + + return true; +} + +bool CSerialModem::setConfig2() +{ + assert(m_serial != NULL); + + unsigned char buffer[50U]; + + buffer[0U] = MMDVM_FRAME_START; + + buffer[1U] = 40U; + + buffer[2U] = MMDVM_SET_CONFIG; + + buffer[3U] = 0x00U; + if (m_rxInvert) + buffer[3U] |= 0x01U; + if (m_txInvert) + buffer[3U] |= 0x02U; + if (m_pttInvert) + buffer[3U] |= 0x04U; + if (m_ysfLoDev) + buffer[3U] |= 0x08U; + if (m_debug) + buffer[3U] |= 0x10U; + if (m_useCOSAsLockout) + buffer[3U] |= 0x20U; + if (!m_duplex) + buffer[3U] |= 0x80U; + + buffer[4U] = 0x00U; + if (m_dstarEnabled) + buffer[4U] |= 0x01U; + if (m_dmrEnabled) + buffer[4U] |= 0x02U; + if (m_ysfEnabled) + buffer[4U] |= 0x04U; + if (m_p25Enabled) + buffer[4U] |= 0x08U; + if (m_nxdnEnabled) + buffer[4U] |= 0x10U; + if (m_fmEnabled) + buffer[4U] |= 0x20U; + if (m_m17Enabled) + buffer[4U] |= 0x40U; + + buffer[5U] = 0x00U; + if (m_pocsagEnabled) + buffer[5U] |= 0x01U; + if (m_ax25Enabled) + buffer[5U] |= 0x02U; + + buffer[6U] = m_txDelay / 10U; // In 10ms units + + buffer[7U] = MODE_IDLE; + + buffer[8U] = (unsigned char)(m_txDCOffset + 128); + buffer[9U] = (unsigned char)(m_rxDCOffset + 128); + + buffer[10U] = (unsigned char)(m_rxLevel * 2.55F + 0.5F); + + buffer[11U] = (unsigned char)(m_cwIdTXLevel * 2.55F + 0.5F); + buffer[12U] = (unsigned char)(m_dstarTXLevel * 2.55F + 0.5F); + buffer[13U] = (unsigned char)(m_dmrTXLevel * 2.55F + 0.5F); + buffer[14U] = (unsigned char)(m_ysfTXLevel * 2.55F + 0.5F); + buffer[15U] = (unsigned char)(m_p25TXLevel * 2.55F + 0.5F); + buffer[16U] = (unsigned char)(m_nxdnTXLevel * 2.55F + 0.5F); + buffer[17U] = (unsigned char)(m_m17TXLevel * 2.55F + 0.5F); + buffer[18U] = (unsigned char)(m_pocsagTXLevel * 2.55F + 0.5F); + buffer[19U] = (unsigned char)(m_fmTXLevel * 2.55F + 0.5F); + buffer[20U] = (unsigned char)(m_ax25TXLevel * 2.55F + 0.5F); + buffer[21U] = 0x00U; + buffer[22U] = 0x00U; + + buffer[23U] = (unsigned char)m_ysfTXHang; + buffer[24U] = (unsigned char)m_p25TXHang; + buffer[25U] = (unsigned char)m_nxdnTXHang; + buffer[26U] = (unsigned char)m_m17TXHang; + buffer[27U] = 0x00U; + buffer[28U] = 0x00U; + + buffer[29U] = m_dmrColorCode; + buffer[30U] = m_dmrDelay; + + buffer[31U] = (unsigned char)(m_ax25RXTwist + 128); + buffer[32U] = m_ax25TXDelay / 10U; // In 10ms units + buffer[33U] = m_ax25SlotTime / 10U; // In 10ms units + buffer[34U] = m_ax25PPersist; + + buffer[35U] = 0x00U; + buffer[36U] = 0x00U; + buffer[37U] = 0x00U; + buffer[38U] = 0x00U; + buffer[39U] = 0x00U; + + // CUtils::dump(1U, "Written", buffer, 40U); + + int ret = m_serial->write(buffer, 40U); + if (ret != 40) + return false; + + unsigned int count = 0U; + RESP_TYPE_MMDVM resp; + do { + CThread::sleep(10U); + + resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { + count++; + if (count >= MAX_RESPONSES) { + LogError("The MMDVM is not responding to the SET_CONFIG command"); + return false; + } + } + } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); + + // CUtils::dump(1U, "Response", m_buffer, m_length); + + if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { + LogError("Received a NAK to the SET_CONFIG command from the modem"); + return false; + } + + m_playoutTimer.start(); + + return true; +} + +bool CSerialModem::setFrequency() +{ + assert(m_serial != NULL); + + unsigned char buffer[20U]; + unsigned char len; + unsigned int pocsagFrequency = 433000000U; + + if (m_pocsagEnabled) + pocsagFrequency = m_pocsagFrequency; + + if (m_hwType == HWT_DVMEGA) + len = 12U; + else { + buffer[12U] = (unsigned char)(m_rfLevel * 2.55F + 0.5F); + + buffer[13U] = (pocsagFrequency >> 0) & 0xFFU; + buffer[14U] = (pocsagFrequency >> 8) & 0xFFU; + buffer[15U] = (pocsagFrequency >> 16) & 0xFFU; + buffer[16U] = (pocsagFrequency >> 24) & 0xFFU; + + len = 17U; + } + + buffer[0U] = MMDVM_FRAME_START; + + buffer[1U] = len; + + buffer[2U] = MMDVM_SET_FREQ; + + buffer[3U] = 0x00U; + + buffer[4U] = (m_rxFrequency >> 0) & 0xFFU; + buffer[5U] = (m_rxFrequency >> 8) & 0xFFU; + buffer[6U] = (m_rxFrequency >> 16) & 0xFFU; + buffer[7U] = (m_rxFrequency >> 24) & 0xFFU; + + buffer[8U] = (m_txFrequency >> 0) & 0xFFU; + buffer[9U] = (m_txFrequency >> 8) & 0xFFU; + buffer[10U] = (m_txFrequency >> 16) & 0xFFU; + buffer[11U] = (m_txFrequency >> 24) & 0xFFU; + + // CUtils::dump(1U, "Written", buffer, len); + + int ret = m_serial->write(buffer, len); + if (ret != len) + return false; + + unsigned int count = 0U; + RESP_TYPE_MMDVM resp; + do { + CThread::sleep(10U); + + resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { + count++; + if (count >= MAX_RESPONSES) { + LogError("The MMDVM is not responding to the SET_FREQ command"); + return false; + } + } + } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); + + // CUtils::dump(1U, "Response", m_buffer, m_length); + + if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { + LogError("Received a NAK to the SET_FREQ command from the modem"); + return false; + } + + return true; +} + +RESP_TYPE_MMDVM CSerialModem::getResponse() +{ + assert(m_serial != NULL); + + if (m_state == SS_START) { + // Get the start of the frame or nothing at all + int ret = m_serial->read(m_buffer + 0U, 1U); + if (ret < 0) { + LogError("Error when reading from the modem"); + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + if (m_buffer[0U] != MMDVM_FRAME_START) + return RTM_TIMEOUT; + + m_state = SS_LENGTH1; + m_length = 1U; + } + + if (m_state == SS_LENGTH1) { + // Get the length of the frame, 1/2 + int ret = m_serial->read(m_buffer + 1U, 1U); + if (ret < 0) { + LogError("Error when reading from the modem"); + m_state = SS_START; + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + m_length = m_buffer[1U]; + m_offset = 2U; + + if (m_length == 0U) + m_state = SS_LENGTH2; + else + m_state = SS_TYPE; + } + + if (m_state == SS_LENGTH2) { + // Get the length of the frane, 2/2 + int ret = m_serial->read(m_buffer + 2U, 1U); + if (ret < 0) { + LogError("Error when reading from the modem"); + m_state = SS_START; + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + m_length = m_buffer[2U] + 255U; + m_offset = 3U; + m_state = SS_TYPE; + } + + if (m_state == SS_TYPE) { + // Get the frame type + int ret = m_serial->read(&m_type, 1U); + if (ret < 0) { + LogError("Error when reading from the modem"); + m_state = SS_START; + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + m_buffer[m_offset++] = m_type; + + m_state = SS_DATA; + } + + if (m_state == SS_DATA) { + while (m_offset < m_length) { + int ret = m_serial->read(m_buffer + m_offset, m_length - m_offset); + if (ret < 0) { + LogError("Error when reading from the modem"); + m_state = SS_START; + return RTM_ERROR; + } + + if (ret == 0) + return RTM_TIMEOUT; + + if (ret > 0) + m_offset += ret; + } + } + + // CUtils::dump(1U, "Received", m_buffer, m_length); + + m_offset = m_length > 255U ? 4U : 3U; + m_state = SS_START; + + return RTM_OK; +} + +HW_TYPE CSerialModem::getHWType() const +{ + return m_hwType; +} + +unsigned char CSerialModem::getMode() const +{ + return m_mode; +} + +bool CSerialModem::setMode(unsigned char mode) +{ + assert(m_serial != NULL); + + unsigned char buffer[4U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 4U; + buffer[2U] = MMDVM_SET_MODE; + buffer[3U] = mode; + + // CUtils::dump(1U, "Written", buffer, 4U); + + return m_serial->write(buffer, 4U) == 4; +} + +bool CSerialModem::sendCWId(const std::string& callsign) +{ + assert(m_serial != NULL); + + unsigned int length = callsign.length(); + if (length > 200U) + length = 200U; + + unsigned char buffer[205U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 3U; + buffer[2U] = MMDVM_SEND_CWID; + + for (unsigned int i = 0U; i < length; i++) + buffer[i + 3U] = callsign.at(i); + + // CUtils::dump(1U, "Written", buffer, length + 3U); + + return m_serial->write(buffer, length + 3U) == int(length + 3U); +} + +bool CSerialModem::writeDMRStart(bool tx) +{ + assert(m_serial != NULL); + + if (tx && m_tx) + return true; + if (!tx && !m_tx) + return true; + + unsigned char buffer[4U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 4U; + buffer[2U] = MMDVM_DMR_START; + buffer[3U] = tx ? 0x01U : 0x00U; + + // CUtils::dump(1U, "Written", buffer, 4U); + + return m_serial->write(buffer, 4U) == 4; +} + +bool CSerialModem::writeDMRAbort(unsigned int slotNo) +{ + assert(m_serial != NULL); + + if (slotNo == 1U) + m_txDMRData1.clear(); + else + m_txDMRData2.clear(); + + unsigned char buffer[4U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 4U; + buffer[2U] = MMDVM_DMR_ABORT; + buffer[3U] = slotNo; + + // CUtils::dump(1U, "Written", buffer, 4U); + + return m_serial->write(buffer, 4U) == 4; +} + +bool CSerialModem::writeDMRShortLC(const unsigned char* lc) +{ + assert(m_serial != NULL); + assert(lc != NULL); + + unsigned char buffer[12U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 12U; + buffer[2U] = MMDVM_DMR_SHORTLC; + buffer[3U] = lc[0U]; + buffer[4U] = lc[1U]; + buffer[5U] = lc[2U]; + buffer[6U] = lc[3U]; + buffer[7U] = lc[4U]; + buffer[8U] = lc[5U]; + buffer[9U] = lc[6U]; + buffer[10U] = lc[7U]; + buffer[11U] = lc[8U]; + + // CUtils::dump(1U, "Written", buffer, 12U); + + return m_serial->write(buffer, 12U) == 12; +} + +void CSerialModem::setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch) +{ + m_fmCallsign = callsign; + m_fmCallsignSpeed = callsignSpeed; + m_fmCallsignFrequency = callsignFrequency; + m_fmCallsignTime = callsignTime; + m_fmCallsignHoldoff = callsignHoldoff; + m_fmCallsignHighLevel = callsignHighLevel; + m_fmCallsignLowLevel = callsignLowLevel; + m_fmCallsignAtStart = callsignAtStart; + m_fmCallsignAtEnd = callsignAtEnd; + m_fmCallsignAtLatch = callsignAtLatch; +} + +void CSerialModem::setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, unsigned int ackFrequency, unsigned int ackMinTime, unsigned int ackDelay, float ackLevel) +{ + m_fmRfAck = rfAck; + m_fmAckSpeed = ackSpeed; + m_fmAckFrequency = ackFrequency; + m_fmAckMinTime = ackMinTime; + m_fmAckDelay = ackDelay; + m_fmAckLevel = ackLevel; +} + +void CSerialModem::setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssHighThreshold, unsigned int ctcssLowThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, unsigned int accessMode, bool cosInvert, bool noiseSquelch, unsigned int squelchHighThreshold, unsigned int squelchLowThreshold, unsigned int rfAudioBoost, float maxDevLevel) +{ + m_fmTimeout = timeout; + m_fmTimeoutLevel = timeoutLevel; + + m_fmCtcssFrequency = ctcssFrequency; + m_fmCtcssHighThreshold = ctcssHighThreshold; + m_fmCtcssLowThreshold = ctcssLowThreshold; + m_fmCtcssLevel = ctcssLevel; + + m_fmKerchunkTime = kerchunkTime; + + m_fmHangTime = hangTime; + + m_fmAccessMode = accessMode; + m_fmCOSInvert = cosInvert; + + m_fmNoiseSquelch = noiseSquelch; + m_fmSquelchHighThreshold = squelchHighThreshold; + m_fmSquelchLowThreshold = squelchLowThreshold; + + m_fmRFAudioBoost = rfAudioBoost; + m_fmMaxDevLevel = maxDevLevel; +} + +void CSerialModem::setFMExtParams(const std::string& ack, unsigned int audioBoost) +{ + m_fmExtAck = ack; + m_fmExtAudioBoost = audioBoost; + m_fmExtEnable = true; +} + +bool CSerialModem::setFMCallsignParams() +{ + assert(m_serial != NULL); + + unsigned char buffer[80U]; + unsigned char len = 10U + m_fmCallsign.size(); + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = len; + buffer[2U] = MMDVM_FM_PARAMS1; + + buffer[3U] = m_fmCallsignSpeed; + buffer[4U] = m_fmCallsignFrequency / 10U; + buffer[5U] = m_fmCallsignTime; + buffer[6U] = m_fmCallsignHoldoff; + + buffer[7U] = (unsigned char)(m_fmCallsignHighLevel * 2.55F + 0.5F); + buffer[8U] = (unsigned char)(m_fmCallsignLowLevel * 2.55F + 0.5F); + + buffer[9U] = 0x00U; + if (m_fmCallsignAtStart) + buffer[9U] |= 0x01U; + if (m_fmCallsignAtEnd) + buffer[9U] |= 0x02U; + if (m_fmCallsignAtLatch) + buffer[9U] |= 0x04U; + + for (unsigned int i = 0U; i < m_fmCallsign.size(); i++) + buffer[10U + i] = m_fmCallsign.at(i); + + // CUtils::dump(1U, "Written", buffer, len); + + int ret = m_serial->write(buffer, len); + if (ret != len) + return false; + + unsigned int count = 0U; + RESP_TYPE_MMDVM resp; + do { + CThread::sleep(10U); + + resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { + count++; + if (count >= MAX_RESPONSES) { + LogError("The MMDVM is not responding to the SET_FM_PARAMS1 command"); + return false; + } + } + } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); + + // CUtils::dump(1U, "Response", m_buffer, m_length); + + if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { + LogError("Received a NAK to the SET_FM_PARAMS1 command from the modem"); + return false; + } + + return true; +} + +bool CSerialModem::setFMAckParams() +{ + assert(m_serial != NULL); + + unsigned char buffer[80U]; + unsigned char len = 8U + m_fmRfAck.size(); + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = len; + buffer[2U] = MMDVM_FM_PARAMS2; + + buffer[3U] = m_fmAckSpeed; + buffer[4U] = m_fmAckFrequency / 10U; + buffer[5U] = m_fmAckMinTime; + buffer[6U] = m_fmAckDelay / 10U; + + buffer[7U] = (unsigned char)(m_fmAckLevel * 2.55F + 0.5F); + + for (unsigned int i = 0U; i < m_fmRfAck.size(); i++) + buffer[8U + i] = m_fmRfAck.at(i); + + // CUtils::dump(1U, "Written", buffer, len); + + int ret = m_serial->write(buffer, len); + if (ret != len) + return false; + + unsigned int count = 0U; + RESP_TYPE_MMDVM resp; + do { + CThread::sleep(10U); + + resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { + count++; + if (count >= MAX_RESPONSES) { + LogError("The MMDVM is not responding to the SET_FM_PARAMS2 command"); + return false; + } + } + } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); + + // CUtils::dump(1U, "Response", m_buffer, m_length); + + if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { + LogError("Received a NAK to the SET_FM_PARAMS2 command from the modem"); + return false; + } + + return true; +} + +bool CSerialModem::setFMMiscParams() +{ + assert(m_serial != NULL); + + unsigned char buffer[20U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = 17U; + buffer[2U] = MMDVM_FM_PARAMS3; + + buffer[3U] = m_fmTimeout / 5U; + buffer[4U] = (unsigned char)(m_fmTimeoutLevel * 2.55F + 0.5F); + + buffer[5U] = (unsigned char)m_fmCtcssFrequency; + buffer[6U] = m_fmCtcssHighThreshold; + buffer[7U] = m_fmCtcssLowThreshold; + buffer[8U] = (unsigned char)(m_fmCtcssLevel * 2.55F + 0.5F); + + buffer[9U] = m_fmKerchunkTime; + buffer[10U] = m_fmHangTime; + + buffer[11U] = m_fmAccessMode & 0x0FU; + if (m_fmNoiseSquelch) + buffer[11U] |= 0x40U; + if (m_fmCOSInvert) + buffer[11U] |= 0x80U; + + buffer[12U] = m_fmRFAudioBoost; + + buffer[13U] = (unsigned char)(m_fmMaxDevLevel * 2.55F + 0.5F); + + buffer[14U] = (unsigned char)(m_rxLevel * 2.55F + 0.5F); + + buffer[15U] = m_fmSquelchHighThreshold; + buffer[16U] = m_fmSquelchLowThreshold; + + // CUtils::dump(1U, "Written", buffer, 17U); + + int ret = m_serial->write(buffer, 17U); + if (ret != 17) + return false; + + unsigned int count = 0U; + RESP_TYPE_MMDVM resp; + do { + CThread::sleep(10U); + + resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { + count++; + if (count >= MAX_RESPONSES) { + LogError("The MMDVM is not responding to the SET_FM_PARAMS3 command"); + return false; + } + } + } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); + + // CUtils::dump(1U, "Response", m_buffer, m_length); + + if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { + LogError("Received a NAK to the SET_FM_PARAMS3 command from the modem"); + return false; + } + + return true; +} + +bool CSerialModem::setFMExtParams() +{ + assert(m_serial != NULL); + + unsigned char buffer[80U]; + unsigned char len = 7U + m_fmExtAck.size(); + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = len; + buffer[2U] = MMDVM_FM_PARAMS4; + + buffer[3U] = m_fmExtAudioBoost; + buffer[4U] = m_fmAckSpeed; + buffer[5U] = m_fmAckFrequency / 10U; + + buffer[6U] = (unsigned char)(m_fmAckLevel * 2.55F + 0.5F); + + for (unsigned int i = 0U; i < m_fmExtAck.size(); i++) + buffer[7U + i] = m_fmExtAck.at(i); + + // CUtils::dump(1U, "Written", buffer, len); + + int ret = m_serial->write(buffer, len); + if (ret != len) + return false; + + unsigned int count = 0U; + RESP_TYPE_MMDVM resp; + do { + CThread::sleep(10U); + + resp = getResponse(); + if (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK) { + count++; + if (count >= MAX_RESPONSES) { + LogError("The MMDVM is not responding to the SET_FM_PARAMS4 command"); + return false; + } + } + } while (resp == RTM_OK && m_buffer[2U] != MMDVM_ACK && m_buffer[2U] != MMDVM_NAK); + + // CUtils::dump(1U, "Response", m_buffer, m_length); + + if (resp == RTM_OK && m_buffer[2U] == MMDVM_NAK) { + LogError("Received a NAK to the SET_FM_PARAMS4 command from the modem"); + return false; + } + + return true; +} + +void CSerialModem::printDebug() +{ + if (m_buffer[2U] == MMDVM_DEBUG1) { + LogMessage("Debug: %.*s", m_length - m_offset - 0U, m_buffer + m_offset); + } else if (m_buffer[2U] == MMDVM_DEBUG2) { + short val1 = (m_buffer[m_length - 2U] << 8) | m_buffer[m_length - 1U]; + LogMessage("Debug: %.*s %d", m_length - m_offset - 2U, m_buffer + m_offset, val1); + } else if (m_buffer[2U] == MMDVM_DEBUG3) { + short val1 = (m_buffer[m_length - 4U] << 8) | m_buffer[m_length - 3U]; + short val2 = (m_buffer[m_length - 2U] << 8) | m_buffer[m_length - 1U]; + LogMessage("Debug: %.*s %d %d", m_length - m_offset - 4U, m_buffer + m_offset, val1, val2); + } else if (m_buffer[2U] == MMDVM_DEBUG4) { + short val1 = (m_buffer[m_length - 6U] << 8) | m_buffer[m_length - 5U]; + short val2 = (m_buffer[m_length - 4U] << 8) | m_buffer[m_length - 3U]; + short val3 = (m_buffer[m_length - 2U] << 8) | m_buffer[m_length - 1U]; + LogMessage("Debug: %.*s %d %d %d", m_length - m_offset - 6U, m_buffer + m_offset, val1, val2, val3); + } else if (m_buffer[2U] == MMDVM_DEBUG5) { + short val1 = (m_buffer[m_length - 8U] << 8) | m_buffer[m_length - 7U]; + short val2 = (m_buffer[m_length - 6U] << 8) | m_buffer[m_length - 5U]; + short val3 = (m_buffer[m_length - 4U] << 8) | m_buffer[m_length - 3U]; + short val4 = (m_buffer[m_length - 2U] << 8) | m_buffer[m_length - 1U]; + LogMessage("Debug: %.*s %d %d %d %d", m_length - m_offset - 8U, m_buffer + m_offset, val1, val2, val3, val4); + } +} diff --git a/SerialModem.h b/SerialModem.h new file mode 100644 index 0000000..4b56077 --- /dev/null +++ b/SerialModem.h @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2011-2018,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. + */ + +#ifndef SERIALMODEM_H +#define SERIALMODEM_H + +#include "Modem.h" + +#include "SerialPort.h" +#include "RingBuffer.h" +#include "Defines.h" +#include "Timer.h" + +#include + +enum RESP_TYPE_MMDVM { + RTM_OK, + RTM_TIMEOUT, + RTM_ERROR +}; + +enum SERIAL_STATE { + SS_START, + SS_LENGTH1, + SS_LENGTH2, + SS_TYPE, + SS_DATA +}; + +class CSerialModem : public IModem { +public: + CSerialModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug); + virtual ~CSerialModem(); + + virtual void setSerialParams(const std::string& protocol, unsigned int address, unsigned int speed); + virtual void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency); + virtual void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool m17Enabled, bool pocsagEnabled, bool fmEnabled, bool ax25Enabled); + virtual void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float m17TXLevel, float pocsagLevel, float fmTXLevel, float ax25TXLevel); + virtual void setDMRParams(unsigned int colorCode); + virtual void setYSFParams(bool loDev, unsigned int txHang); + virtual void setP25Params(unsigned int txHang); + virtual void setNXDNParams(unsigned int txHang); + virtual void setM17Params(unsigned int txHang); + virtual void setAX25Params(int rxTwist, unsigned int txDelay, unsigned int slotTime, unsigned int pPersist); + virtual void setTransparentDataParams(unsigned int sendFrameType); + + virtual void setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch); + virtual void setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, unsigned int ackFrequency, unsigned int ackMinTime, unsigned int ackDelay, float ackLevel); + virtual void setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssHighThreshold, unsigned int ctcssLowThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, unsigned int accessMode, bool cosInvert, bool noiseSquelch, unsigned int squelchHighThreshold, unsigned int squelchLowThreshold, unsigned int rfAudioBoost, float maxDevLevel); + virtual void setFMExtParams(const std::string& ack, unsigned int audioBoost); + + virtual bool open(); + + virtual unsigned int readDStarData(unsigned char* data); + virtual unsigned int readDMRData1(unsigned char* data); + virtual unsigned int readDMRData2(unsigned char* data); + virtual unsigned int readYSFData(unsigned char* data); + virtual unsigned int readP25Data(unsigned char* data); + virtual unsigned int readNXDNData(unsigned char* data); + virtual unsigned int readM17Data(unsigned char* data); + virtual unsigned int readFMData(unsigned char* data); + virtual unsigned int readAX25Data(unsigned char* data); + + virtual bool hasDStarSpace() const; + virtual bool hasDMRSpace1() const; + virtual bool hasDMRSpace2() const; + virtual bool hasYSFSpace() const; + virtual bool hasP25Space() const; + virtual bool hasNXDNSpace() const; + virtual bool hasM17Space() const; + virtual bool hasPOCSAGSpace() const; + virtual unsigned int getFMSpace() const; + virtual bool hasAX25Space() const; + + virtual bool hasTX() const; + virtual bool hasCD() const; + + virtual bool hasLockout() const; + virtual bool hasError() const; + + virtual bool writeConfig(); + virtual bool writeDStarData(const unsigned char* data, unsigned int length); + virtual bool writeDMRData1(const unsigned char* data, unsigned int length); + virtual bool writeDMRData2(const unsigned char* data, unsigned int length); + virtual bool writeYSFData(const unsigned char* data, unsigned int length); + virtual bool writeP25Data(const unsigned char* data, unsigned int length); + virtual bool writeNXDNData(const unsigned char* data, unsigned int length); + virtual bool writeM17Data(const unsigned char* data, unsigned int length); + virtual bool writePOCSAGData(const unsigned char* data, unsigned int length); + virtual bool writeFMData(const unsigned char* data, unsigned int length); + virtual bool writeAX25Data(const unsigned char* data, unsigned int length); + + virtual bool writeDStarInfo(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); + virtual bool writeDMRInfo(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); + virtual bool writeYSFInfo(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin); + virtual bool writeP25Info(const char* source, bool group, unsigned int dest, const char* type); + virtual bool writeNXDNInfo(const char* source, bool group, unsigned int dest, const char* type); + virtual bool writeM17Info(const char* source, const char* dest, const char* type); + virtual bool writePOCSAGInfo(unsigned int ric, const std::string& message); + virtual bool writeIPInfo(const std::string& address); + + virtual bool writeDMRStart(bool tx); + virtual bool writeDMRShortLC(const unsigned char* lc); + virtual bool writeDMRAbort(unsigned int slotNo); + + virtual bool writeTransparentData(const unsigned char* data, unsigned int length); + virtual unsigned int readTransparentData(unsigned char* data); + + virtual bool writeSerial(const unsigned char* data, unsigned int length); + virtual unsigned int readSerial(unsigned char* data, unsigned int length); + + virtual unsigned char getMode() const; + virtual bool setMode(unsigned char mode); + + virtual bool sendCWId(const std::string& callsign); + + virtual HW_TYPE getHWType() const; + + virtual void clock(unsigned int ms); + + virtual void close(); + +private: + std::string m_port; + unsigned int m_protocolVersion; + unsigned int m_dmrColorCode; + bool m_ysfLoDev; + unsigned int m_ysfTXHang; + unsigned int m_p25TXHang; + unsigned int m_nxdnTXHang; + unsigned int m_m17TXHang; + bool m_duplex; + bool m_rxInvert; + bool m_txInvert; + bool m_pttInvert; + unsigned int m_txDelay; + unsigned int m_dmrDelay; + float m_rxLevel; + float m_cwIdTXLevel; + float m_dstarTXLevel; + float m_dmrTXLevel; + float m_ysfTXLevel; + float m_p25TXLevel; + float m_nxdnTXLevel; + float m_m17TXLevel; + float m_pocsagTXLevel; + float m_fmTXLevel; + float m_ax25TXLevel; + float m_rfLevel; + bool m_useCOSAsLockout; + bool m_trace; + bool m_debug; + unsigned int m_rxFrequency; + unsigned int m_txFrequency; + unsigned int m_pocsagFrequency; + bool m_dstarEnabled; + bool m_dmrEnabled; + bool m_ysfEnabled; + bool m_p25Enabled; + bool m_nxdnEnabled; + bool m_m17Enabled; + bool m_pocsagEnabled; + bool m_fmEnabled; + bool m_ax25Enabled; + int m_rxDCOffset; + int m_txDCOffset; + ISerialPort* m_serial; + unsigned char* m_buffer; + unsigned int m_length; + unsigned int m_offset; + SERIAL_STATE m_state; + unsigned char m_type; + CRingBuffer m_rxDStarData; + CRingBuffer m_txDStarData; + CRingBuffer m_rxDMRData1; + CRingBuffer m_rxDMRData2; + CRingBuffer m_txDMRData1; + CRingBuffer m_txDMRData2; + CRingBuffer m_rxYSFData; + CRingBuffer m_txYSFData; + CRingBuffer m_rxP25Data; + CRingBuffer m_txP25Data; + CRingBuffer m_rxNXDNData; + CRingBuffer m_txNXDNData; + CRingBuffer m_rxM17Data; + CRingBuffer m_txM17Data; + CRingBuffer m_txPOCSAGData; + CRingBuffer m_rxFMData; + CRingBuffer m_txFMData; + CRingBuffer m_rxAX25Data; + CRingBuffer m_txAX25Data; + CRingBuffer m_rxSerialData; + CRingBuffer m_txSerialData; + CRingBuffer m_rxTransparentData; + CRingBuffer m_txTransparentData; + unsigned int m_sendTransparentDataFrameType; + CTimer m_statusTimer; + CTimer m_inactivityTimer; + CTimer m_playoutTimer; + unsigned int m_dstarSpace; + unsigned int m_dmrSpace1; + unsigned int m_dmrSpace2; + unsigned int m_ysfSpace; + unsigned int m_p25Space; + unsigned int m_nxdnSpace; + unsigned int m_m17Space; + unsigned int m_pocsagSpace; + unsigned int m_fmSpace; + unsigned int m_ax25Space; + bool m_tx; + bool m_cd; + bool m_lockout; + bool m_error; + unsigned char m_mode; + HW_TYPE m_hwType; + int m_ax25RXTwist; + unsigned int m_ax25TXDelay; + unsigned int m_ax25SlotTime; + unsigned int m_ax25PPersist; + + std::string m_fmCallsign; + unsigned int m_fmCallsignSpeed; + unsigned int m_fmCallsignFrequency; + unsigned int m_fmCallsignTime; + unsigned int m_fmCallsignHoldoff; + float m_fmCallsignHighLevel; + float m_fmCallsignLowLevel; + bool m_fmCallsignAtStart; + bool m_fmCallsignAtEnd; + bool m_fmCallsignAtLatch; + std::string m_fmRfAck; + std::string m_fmExtAck; + unsigned int m_fmAckSpeed; + unsigned int m_fmAckFrequency; + unsigned int m_fmAckMinTime; + unsigned int m_fmAckDelay; + float m_fmAckLevel; + unsigned int m_fmTimeout; + float m_fmTimeoutLevel; + float m_fmCtcssFrequency; + unsigned int m_fmCtcssHighThreshold; + unsigned int m_fmCtcssLowThreshold; + float m_fmCtcssLevel; + unsigned int m_fmKerchunkTime; + unsigned int m_fmHangTime; + unsigned int m_fmAccessMode; + bool m_fmCOSInvert; + bool m_fmNoiseSquelch; + unsigned int m_fmSquelchHighThreshold; + unsigned int m_fmSquelchLowThreshold; + unsigned int m_fmRFAudioBoost; + unsigned int m_fmExtAudioBoost; + float m_fmMaxDevLevel; + bool m_fmExtEnable; + + bool readVersion(); + bool readStatus(); + bool setConfig1(); + bool setConfig2(); + bool setFrequency(); + bool setFMCallsignParams(); + bool setFMAckParams(); + bool setFMMiscParams(); + bool setFMExtParams(); + + void printDebug(); + + RESP_TYPE_MMDVM getResponse(); +}; + +#endif diff --git a/Sync.cpp b/Sync.cpp index 75156af..3e53f64 100644 --- a/Sync.cpp +++ b/Sync.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2018,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 @@ -23,6 +23,7 @@ #include "YSFDefines.h" #include "P25Defines.h" #include "NXDNDefines.h" +#include "M17Defines.h" #include #include @@ -83,3 +84,17 @@ void CSync::addNXDNSync(unsigned char* data) for (unsigned int i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++) data[i] = (data[i] & ~NXDN_FSW_BYTES_MASK[i]) | NXDN_FSW_BYTES[i]; } + +void CSync::addM17HeaderSync(unsigned char* data) +{ + assert(data != NULL); + + ::memcpy(data, M17_HEADER_SYNC_BYTES, M17_SYNC_LENGTH_BYTES); +} + +void CSync::addM17DataSync(unsigned char* data) +{ + assert(data != NULL); + + ::memcpy(data, M17_DATA_SYNC_BYTES, M17_SYNC_LENGTH_BYTES); +} diff --git a/Sync.h b/Sync.h index 3ff325c..20ee2dd 100644 --- a/Sync.h +++ b/Sync.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2018,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 @@ -33,6 +33,9 @@ public: static void addNXDNSync(unsigned char* data); + static void addM17HeaderSync(unsigned char* data); + static void addM17DataSync(unsigned char* data); + private: }; diff --git a/TFTSerial.cpp b/TFTSerial.cpp deleted file mode 100644 index dab532e..0000000 --- a/TFTSerial.cpp +++ /dev/null @@ -1,588 +0,0 @@ -/* - * Copyright (C) 2015,2016,2018,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. - */ - -#include "TFTSerial.h" -#include "Log.h" - -#include -#include -#include - -const unsigned char ROTATION_PORTRAIT_LEFT = 0U; -const unsigned char ROTATION_LANDSCAPE_UD = 1U; -const unsigned char ROTATION_PORTRAIT_RIGHT = 2U; -const unsigned char ROTATION_LANDSCAPE = 3U; - -const unsigned char COLOUR_BLACK = 0U; -const unsigned char COLOUR_BLUE = 1U; -const unsigned char COLOUR_RED = 2U; -const unsigned char COLOUR_GREEN = 3U; -const unsigned char COLOUR_CYAN = 4U; -const unsigned char COLOUR_MAGENTA = 5U; -const unsigned char COLOUR_YELLOW = 6U; -const unsigned char COLOUR_WHITE = 7U; - -const unsigned char FONT_SMALL = 1U; -const unsigned char FONT_MEDIUM = 2U; -const unsigned char FONT_LARGE = 3U; - -// x = 0 to 159, y = 0 to 127 - Landscape -// x = 0 to 127, y = 0 to 159 - Portrait - -CTFTSerial::CTFTSerial(const std::string& callsign, unsigned int dmrid, ISerialPort* serial, unsigned int brightness) : -CDisplay(), -m_callsign(callsign), -m_dmrid(dmrid), -m_serial(serial), -m_brightness(brightness), -m_mode(MODE_IDLE) -{ - assert(serial != NULL); - assert(brightness >= 0U && brightness <= 100U); -} - -CTFTSerial::~CTFTSerial() -{ -} - -bool CTFTSerial::open() -{ - bool ret = m_serial->open(); - if (!ret) { - LogError("Cannot open the port for the TFT Serial"); - delete m_serial; - return false; - } - - setRotation(ROTATION_LANDSCAPE); - - setBrightness(m_brightness); - - setBackground(COLOUR_WHITE); - - setForeground(COLOUR_BLACK); - - setIdle(); - - return true; -} - -void CTFTSerial::setIdleInt() -{ - // Clear the screen - clearScreen(); - - setFontSize(FONT_LARGE); - - // Draw MMDVM logo - displayBitmap(0U, 0U, "MMDVM_sm.bmp"); - - char text[30]; - ::sprintf(text, "%-6s / %u", m_callsign.c_str(), m_dmrid); - - gotoPosPixel(18U, 55U); - displayText(text); - - gotoPosPixel(45U, 90U); - displayText("IDLE"); - - m_mode = MODE_IDLE; -} - -void CTFTSerial::setErrorInt(const char* text) -{ - assert(text != NULL); - - // Clear the screen - clearScreen(); - - setFontSize(FONT_MEDIUM); - - // Draw MMDVM logo - displayBitmap(0U, 0U, "MMDVM_sm.bmp"); - - setForeground(COLOUR_RED); - - gotoPosPixel(18U, 55U); - displayText(text); - - gotoPosPixel(18U, 90U); - displayText("ERROR"); - - setForeground(COLOUR_BLACK); - - m_mode = MODE_ERROR; -} - -void CTFTSerial::setLockoutInt() -{ - // Clear the screen - clearScreen(); - - setFontSize(FONT_LARGE); - - // Draw MMDVM logo - displayBitmap(0U, 0U, "MMDVM_sm.bmp"); - - gotoPosPixel(20U, 60U); - displayText("LOCKOUT"); - - m_mode = MODE_LOCKOUT; -} - -void CTFTSerial::setQuitInt() -{ - // Clear the screen - clearScreen(); - - setFontSize(FONT_LARGE); - - // Draw MMDVM logo - displayBitmap(0U, 0U, "MMDVM_sm.bmp"); - - gotoPosPixel(20U, 60U); - displayText("STOPPED"); - - m_mode = MODE_QUIT; -} - -void CTFTSerial::setFMInt() -{ - // Clear the screen - clearScreen(); - - setFontSize(FONT_LARGE); - - // Draw MMDVM logo - displayBitmap(0U, 0U, "MMDVM_sm.bmp"); - - gotoPosPixel(20U, 60U); - displayText("FM"); - - m_mode = MODE_FM; -} - -void CTFTSerial::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) -{ - assert(my1 != NULL); - assert(my2 != NULL); - assert(your != NULL); - assert(type != NULL); - assert(reflector != NULL); - - if (m_mode != MODE_DSTAR) { - // Clear the screen - clearScreen(); - - setFontSize(FONT_MEDIUM); - - // Draw D-Star insignia - displayBitmap(0U, 0U, "DStar_sm.bmp"); - } - - char text[30U]; - - ::sprintf(text, "%s %.8s/%4.4s", type, my1, my2); - gotoPosPixel(5U, 70U); - displayText(text); - - ::sprintf(text, "%.8s", your); - gotoPosPixel(5U, 90U); - displayText(text); - - if (::strcmp(reflector, " ") != 0) { - ::sprintf(text, "via %.8s", reflector); - gotoPosPixel(5U, 110U); - displayText(text); - } else { - gotoPosPixel(5U, 110U); - displayText(" "); - } - - m_mode = MODE_DSTAR; -} - -void CTFTSerial::clearDStarInt() -{ - gotoPosPixel(5U, 70U); - displayText(" Listening "); - - gotoPosPixel(5U, 90U); - displayText(" "); - - gotoPosPixel(5U, 110U); - displayText(" "); -} - -void CTFTSerial::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) -{ - assert(type != NULL); - - if (m_mode != MODE_DMR) { - // Clear the screen - clearScreen(); - - setFontSize(FONT_MEDIUM); - - // Draw DMR insignia - displayBitmap(0U, 0U, "DMR_sm.bmp"); - - if (slotNo == 1U) { - gotoPosPixel(5U, 90U); - displayText("2 Listening"); - } else { - gotoPosPixel(5U, 55U); - displayText("1 Listening"); - } - } - - if (slotNo == 1U) { - char text[30U]; - - ::sprintf(text, "1 %s %s", type, src.c_str()); - gotoPosPixel(5U, 55U); - displayText(text); - - ::sprintf(text, "%s%s", group ? "TG" : "", dst.c_str()); - gotoPosPixel(65U, 72U); - displayText(text); - } else { - char text[30U]; - - ::sprintf(text, "2 %s %s", type, src.c_str()); - gotoPosPixel(5U, 90U); - displayText(text); - - ::sprintf(text, "%s%s", group ? "TG" : "", dst.c_str()); - gotoPosPixel(65U, 107U); - displayText(text); - } - - m_mode = MODE_DMR; -} - -void CTFTSerial::clearDMRInt(unsigned int slotNo) -{ - if (slotNo == 1U) { - gotoPosPixel(5U, 55U); - displayText("1 Listening "); - - gotoPosPixel(65U, 72U); - displayText(" "); - } else { - gotoPosPixel(5U, 90U); - displayText("2 Listening "); - - gotoPosPixel(65U, 107U); - displayText(" "); - } -} - -void CTFTSerial::writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) -{ - assert(source != NULL); - assert(dest != NULL); - assert(type != NULL); - assert(origin != NULL); - - if (m_mode != MODE_YSF) { - // Clear the screen - clearScreen(); - - setFontSize(FONT_MEDIUM); - - // Draw the System Fusion insignia - displayBitmap(0U, 0U, "YSF_sm.bmp"); - } - - char text[30U]; - ::sprintf(text, "%s %.10s", type, source); - - gotoPosPixel(5U, 70U); - displayText(text); - - ::sprintf(text, " DG-ID %u", dgid); - - gotoPosPixel(5U, 90U); - displayText(text); - - if (::strcmp(origin, " ") != 0) { - ::sprintf(text, "at %.10s", origin); - gotoPosPixel(5U, 110U); - displayText(text); - } else { - gotoPosPixel(5U, 110U); - displayText(" "); - } - - m_mode = MODE_YSF; -} - -void CTFTSerial::clearFusionInt() -{ - gotoPosPixel(5U, 70U); - displayText(" Listening "); - - gotoPosPixel(5U, 90U); - displayText(" "); - - gotoPosPixel(5U, 110U); - displayText(" "); -} - -void CTFTSerial::writeP25Int(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(source != NULL); - assert(type != NULL); - - if (m_mode != MODE_P25) { - // Clear the screen - clearScreen(); - - setFontSize(FONT_MEDIUM); - - // Draw the P25 insignia - displayBitmap(0U, 0U, "P25_sm.bmp"); - } - - char text[30U]; - ::sprintf(text, "%s %.10s", type, source); - - gotoPosPixel(5U, 70U); - displayText(text); - - ::sprintf(text, " %s%u", group ? "TG" : "", dest); - - gotoPosPixel(5U, 90U); - displayText(text); - - m_mode = MODE_P25; -} - -void CTFTSerial::clearP25Int() -{ - gotoPosPixel(5U, 70U); - displayText(" Listening "); - - gotoPosPixel(5U, 90U); - displayText(" "); - - gotoPosPixel(5U, 110U); - displayText(" "); -} - -void CTFTSerial::writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(source != NULL); - assert(type != NULL); - - if (m_mode != MODE_NXDN) { - // Clear the screen - clearScreen(); - - setFontSize(FONT_MEDIUM); - - // Draw the P25 insignia - displayBitmap(0U, 0U, "NXDN_sm.bmp"); - } - - char text[30U]; - ::sprintf(text, "%s %.10s", type, source); - - gotoPosPixel(5U, 70U); - displayText(text); - - ::sprintf(text, " %s%u", group ? "TG" : "", dest); - - gotoPosPixel(5U, 90U); - displayText(text); - - m_mode = MODE_NXDN; -} - -void CTFTSerial::clearNXDNInt() -{ - gotoPosPixel(5U, 70U); - displayText(" Listening "); - - gotoPosPixel(5U, 90U); - displayText(" "); - - gotoPosPixel(5U, 110U); - displayText(" "); -} - -void CTFTSerial::writePOCSAGInt(uint32_t ric, const std::string& message) -{ - gotoPosPixel(15U, 90U); - displayText("POCSAG TX"); - - m_mode = MODE_CW; -} - -void CTFTSerial::clearPOCSAGInt() -{ - gotoPosPixel(45U, 90U); - displayText("IDLE"); -} - -void CTFTSerial::writeCWInt() -{ - gotoPosPixel(45U, 90U); - displayText("CW TX"); - - m_mode = MODE_CW; -} - -void CTFTSerial::clearCWInt() -{ - gotoPosPixel(45U, 90U); - displayText("IDLE"); -} - -void CTFTSerial::close() -{ - m_serial->close(); - delete m_serial; -} - -void CTFTSerial::clearScreen() -{ - m_serial->write((unsigned char*)"\x1B\x00\xFF", 3U); -} - -void CTFTSerial::setForeground(unsigned char colour) -{ - assert(colour >= 0U && colour <= 7U); - - m_serial->write((unsigned char*)"\x1B\x01", 2U); - m_serial->write(&colour, 1U); - m_serial->write((unsigned char*)"\xFF", 1U); -} - -void CTFTSerial::setBackground(unsigned char colour) -{ - assert(colour >= 0U && colour <= 7U); - - m_serial->write((unsigned char*)"\x1B\x02", 2U); - m_serial->write(&colour, 1U); - m_serial->write((unsigned char*)"\xFF", 1U); -} - -void CTFTSerial::setRotation(unsigned char rotation) -{ - assert(rotation >= 0U && rotation <= 3U); - - m_serial->write((unsigned char*)"\x1B\x03", 2U); - m_serial->write(&rotation, 1U); - m_serial->write((unsigned char*)"\xFF", 1U); -} - -void CTFTSerial::setFontSize(unsigned char size) -{ - assert(size >= 1U && size <= 3U); - - m_serial->write((unsigned char*)"\x1B\x04", 2U); - m_serial->write(&size, 1U); - m_serial->write((unsigned char*)"\xFF", 1U); -} - -void CTFTSerial::gotoBegOfLine() -{ - m_serial->write((unsigned char*)"\x1B\x05\xFF", 3U); -} - -void CTFTSerial::gotoPosText(unsigned char x, unsigned char y) -{ - m_serial->write((unsigned char*)"\x1B\x06", 2U); - m_serial->write(&x, 1U); - m_serial->write(&y, 1U); - m_serial->write((unsigned char*)"\xFF", 1U); -} - -void CTFTSerial::gotoPosPixel(unsigned char x, unsigned char y) -{ - m_serial->write((unsigned char*)"\x1B\x07", 2U); - m_serial->write(&x, 1U); - m_serial->write(&y, 1U); - m_serial->write((unsigned char*)"\xFF", 1U); -} - -void CTFTSerial::drawLine(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2) -{ - m_serial->write((unsigned char*)"\x1B\x08", 2U); - m_serial->write(&x1, 1U); - m_serial->write(&y1, 1U); - m_serial->write(&x2, 1U); - m_serial->write(&y2, 1U); - m_serial->write((unsigned char*)"\xFF", 1U); -} - -void CTFTSerial::drawBox(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2, bool filled) -{ - if (filled) - m_serial->write((unsigned char*)"\x1B\x0A", 2U); - else - m_serial->write((unsigned char*)"\x1B\x09", 2U); - - m_serial->write(&x1, 1U); - m_serial->write(&y1, 1U); - m_serial->write(&x2, 1U); - m_serial->write(&y2, 1U); - m_serial->write((unsigned char*)"\xFF", 1U); -} - -void CTFTSerial::drawCircle(unsigned char x, unsigned char y, unsigned char radius, bool filled) -{ - if (filled) - m_serial->write((unsigned char*)"\x1B\x0C", 2U); - else - m_serial->write((unsigned char*)"\x1B\x0B", 2U); - - m_serial->write(&x, 1U); - m_serial->write(&y, 1U); - m_serial->write(&radius, 1U); - m_serial->write((unsigned char*)"\xFF", 1U); -} - -void CTFTSerial::displayBitmap(unsigned char x, unsigned char y, const char* filename) -{ - assert(filename != NULL); - - m_serial->write((unsigned char*)"\x1B\x0D", 2U); - m_serial->write(&x, 1U); - m_serial->write(&y, 1U); - m_serial->write((unsigned char*)filename, (unsigned int)::strlen(filename)); - m_serial->write((unsigned char*)"\xFF", 1U); -} - -void CTFTSerial::setBrightness(unsigned char brightness) -{ - assert(brightness >= 0U && brightness <= 100U); - - m_serial->write((unsigned char*)"\x1B\x0E", 2U); - m_serial->write(&brightness, 1U); - m_serial->write((unsigned char*)"\xFF", 1U); -} - -void CTFTSerial::displayText(const char* text) -{ - assert(text != NULL); - - m_serial->write((unsigned char*)text, (unsigned int)::strlen(text)); -} diff --git a/TFTSerial.h b/TFTSerial.h deleted file mode 100644 index d34e9da..0000000 --- a/TFTSerial.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2015,2016,2018,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(TFTSERIAL_H) -#define TFTSERIAL_H - -#include "Display.h" -#include "Defines.h" -#include "SerialPort.h" - -#include - -class CTFTSerial : public CDisplay -{ -public: - CTFTSerial(const std::string& callsign, unsigned int dmrid, ISerialPort* serial, unsigned int brightness); - virtual ~CTFTSerial(); - - virtual bool open(); - - virtual void close(); - -protected: - virtual void setIdleInt(); - virtual void setErrorInt(const char* text); - virtual void setLockoutInt(); - virtual void setQuitInt(); - virtual void setFMInt(); - - virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); - virtual void clearDStarInt(); - - virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); - virtual void clearDMRInt(unsigned int slotNo); - - virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin); - virtual void clearFusionInt(); - - virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type); - virtual void clearP25Int(); - - virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); - virtual void clearNXDNInt(); - - virtual void writePOCSAGInt(uint32_t ric, const std::string& message); - virtual void clearPOCSAGInt(); - - virtual void writeCWInt(); - virtual void clearCWInt(); - -private: - std::string m_callsign; - unsigned int m_dmrid; - ISerialPort* m_serial; - unsigned int m_brightness; - unsigned char m_mode; - - void clearScreen(); - void setBackground(unsigned char colour); - void setForeground(unsigned char colour); - void setRotation(unsigned char rotation); - void setFontSize(unsigned char size); - void gotoBegOfLine(); - void gotoPosText(unsigned char x, unsigned char y); - void gotoPosPixel(unsigned char x, unsigned char y); - void drawLine(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2); - void drawBox(unsigned char x1, unsigned char y1, unsigned char x2, unsigned char y2, bool filled); - void drawCircle(unsigned char x, unsigned char y, unsigned char radius, bool filled); - void displayBitmap(unsigned char x, unsigned char y, const char* filename); - void setBrightness(unsigned char brightness); - void displayText(const char* text); -}; - -#endif diff --git a/TFTSerial/DMR_sm.bmp b/TFTSerial/DMR_sm.bmp deleted file mode 100644 index 2fae46a..0000000 Binary files a/TFTSerial/DMR_sm.bmp and /dev/null differ diff --git a/TFTSerial/DStar_sm.bmp b/TFTSerial/DStar_sm.bmp deleted file mode 100644 index f4e2afc..0000000 Binary files a/TFTSerial/DStar_sm.bmp and /dev/null differ diff --git a/TFTSerial/MMDVM_sm.bmp b/TFTSerial/MMDVM_sm.bmp deleted file mode 100644 index 674d1c6..0000000 Binary files a/TFTSerial/MMDVM_sm.bmp and /dev/null differ diff --git a/TFTSerial/NXDN_sm.bmp b/TFTSerial/NXDN_sm.bmp deleted file mode 100644 index 30420d8..0000000 Binary files a/TFTSerial/NXDN_sm.bmp and /dev/null differ diff --git a/TFTSerial/P25_sm.bmp b/TFTSerial/P25_sm.bmp deleted file mode 100644 index 5ba24f6..0000000 Binary files a/TFTSerial/P25_sm.bmp and /dev/null differ diff --git a/TFTSerial/YSF_sm.bmp b/TFTSerial/YSF_sm.bmp deleted file mode 100644 index 34cfb19..0000000 Binary files a/TFTSerial/YSF_sm.bmp and /dev/null differ diff --git a/TFTSurenoo.cpp b/TFTSurenoo.cpp index fc61ca9..c9498b6 100644 --- a/TFTSurenoo.cpp +++ b/TFTSurenoo.cpp @@ -77,6 +77,7 @@ enum LcdColour { #define STR_DSTAR "D-STAR" #define STR_MMDVM "MMDVM" #define STR_NXDN "NXDN" +#define STR_M17 "M17" #define STR_P25 "P25" #define STR_YSF "SystemFusion" @@ -358,6 +359,29 @@ void CTFTSurenoo::clearNXDNInt() clearDStarInt(); } +void CTFTSurenoo::writeM17Int(const char* source, const char* dest, const char* type) +{ + assert(source != NULL); + assert(dest != NULL); + assert(type != NULL); + + if (m_mode != MODE_M17) + setModeLine(STR_M17); + + ::snprintf(m_temp, sizeof(m_temp), "%s %s", type, source); + setStatusLine(statusLineNo(0), m_temp); + + ::snprintf(m_temp, sizeof(m_temp), "%s", dest); + setStatusLine(statusLineNo(1), m_temp); + + m_mode = MODE_M17; +} + +void CTFTSurenoo::clearM17Int() +{ + clearDStarInt(); +} + void CTFTSurenoo::writePOCSAGInt(uint32_t ric, const std::string& message) { setStatusLine(statusLineNo(1), "POCSAG TX"); diff --git a/TFTSurenoo.h b/TFTSurenoo.h index e21a17e..2e9abdb 100644 --- a/TFTSurenoo.h +++ b/TFTSurenoo.h @@ -61,6 +61,9 @@ protected: virtual int writeNXDNIntEx(const class CUserDBentry& source, bool group, unsigned int dest, const char* type); virtual void clearNXDNInt(); + virtual void writeM17Int(const char* source, const char* dest, const char* type); + virtual void clearM17Int(); + virtual void writePOCSAGInt(uint32_t ric, const std::string& message); virtual void clearPOCSAGInt(); diff --git a/Tools/DeEmphasis.py b/Tools/DeEmphasis.py new file mode 100644 index 0000000..44320c9 --- /dev/null +++ b/Tools/DeEmphasis.py @@ -0,0 +1,43 @@ +#based on https://github.com/gnuradio/gnuradio/blob/master/gr-analog/python/analog/fm_emph.py + +import math +import cmath +import numpy as np +import scipy.signal as signal +import pylab as pl + +tau = 750e-6 +fs = 8000 +fh = 2700 + +# Digital corner frequency +w_c = 1.0 / tau + +# Prewarped analog corner frequency +w_ca = 2.0 * fs * math.tan(w_c / (2.0 * fs)) + +# Resulting digital pole, zero, and gain term from the bilinear +# transformation of H(s) = w_ca / (s + w_ca) to +# H(z) = b0 (1 - z1 z^-1)/(1 - p1 z^-1) +k = -w_ca / (2.0 * fs) +z1 = -1.0 +p1 = (1.0 + k) / (1.0 - k) +b0 = -k / (1.0 - k) + +btaps = [ b0 * 1.0, b0 * -z1, 0 ] +ataps = [ 1.0, -p1, 0 ] + +# Since H(s = 0) = 1.0, then H(z = 1) = 1.0 and has 0 dB gain at DC + + +taps = np.concatenate((btaps, ataps), axis=0) +print("Taps") +print(*taps, "", sep=",", end="\n") + +f,h = signal.freqz(btaps,ataps, fs=fs) +pl.plot(f, 20*np.log10(np.abs(h))) +pl.xlabel('frequency/Hz') +pl.ylabel('gain/dB') +pl.ylim(top=0,bottom=-30) +pl.xlim(left=0, right=fh*2.5) +pl.show() \ No newline at end of file diff --git a/Tools/PreEmphasis.py b/Tools/PreEmphasis.py new file mode 100644 index 0000000..22c81f6 --- /dev/null +++ b/Tools/PreEmphasis.py @@ -0,0 +1,51 @@ +#based on https://github.com/gnuradio/gnuradio/blob/master/gr-analog/python/analog/fm_emph.py + +import math +import cmath +import numpy as np +import scipy.signal as signal +import pylab as pl + +tau = 750e-6 +fs = 8000 +fh = 2700 + +# Digital corner frequencies +w_cl = 1.0 / tau +w_ch = 2.0 * math.pi * fh + +# Prewarped analog corner frequencies +w_cla = 2.0 * fs * math.tan(w_cl / (2.0 * fs)) +w_cha = 2.0 * fs * math.tan(w_ch / (2.0 * fs)) + +# Resulting digital pole, zero, and gain term from the bilinear +# transformation of H(s) = (s + w_cla) / (s + w_cha) to +# H(z) = b0 (1 - z1 z^-1)/(1 - p1 z^-1) +kl = -w_cla / (2.0 * fs) +kh = -w_cha / (2.0 * fs) +z1 = (1.0 + kl) / (1.0 - kl) +p1 = (1.0 + kh) / (1.0 - kh) +b0 = (1.0 - kl) / (1.0 - kh) + +# Since H(s = infinity) = 1.0, then H(z = -1) = 1.0 and +# this filter has 0 dB gain at fs/2.0. +# That isn't what users are going to expect, so adjust with a +# gain, g, so that H(z = 1) = 1.0 for 0 dB gain at DC. +w_0dB = 2.0 * math.pi * 0.0 +g = abs(1.0 - p1 * cmath.rect(1.0, -w_0dB)) \ +/ (b0 * abs(1.0 - z1 * cmath.rect(1.0, -w_0dB))) + +btaps = [ g * b0 * 1.0, g * b0 * -z1, 0] +ataps = [ 1.0, -p1, 0] + +taps = np.concatenate((btaps, ataps), axis=0) +print("Taps") +print(*taps, "", sep=",", end="\n") + +f,h = signal.freqz(btaps,ataps, fs=fs) +pl.plot(f, 20*np.log10(np.abs(h))) +pl.xlabel('frequency/Hz') +pl.ylabel('gain/dB') +pl.ylim(top=30,bottom=0) +pl.xlim(left=0, right=fh*2.5) +pl.show() \ No newline at end of file diff --git a/UMP.cpp b/UMP.cpp deleted file mode 100644 index f843322..0000000 --- a/UMP.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/* -* Copyright (C) 2016 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 "Defines.h" -#include "Utils.h" -#include "Log.h" -#include "UMP.h" - -#include -#include -#include - -const unsigned char UMP_FRAME_START = 0xF0U; - -const unsigned char UMP_HELLO = 0x00U; - -const unsigned char UMP_SET_MODE = 0x01U; -const unsigned char UMP_SET_TX = 0x02U; -const unsigned char UMP_SET_CD = 0x03U; - -const unsigned char UMP_WRITE_SERIAL = 0x10U; -const unsigned char UMP_READ_SERIAL = 0x11U; - -const unsigned char UMP_STATUS = 0x50U; - -const unsigned int BUFFER_LENGTH = 255U; - -CUMP::CUMP(const std::string& port) : -m_serial(port, SERIAL_115200), -m_open(false), -m_buffer(NULL), -m_length(0U), -m_offset(0U), -m_lockout(false), -m_mode(MODE_IDLE), -m_tx(false), -m_cd(false) -{ - m_buffer = new unsigned char[BUFFER_LENGTH]; -} - -CUMP::~CUMP() -{ - delete[] m_buffer; -} - -bool CUMP::open() -{ - if (m_open) - return true; - - LogMessage("Opening the UMP"); - - bool ret = m_serial.open(); - if (!ret) - return false; - - unsigned char buffer[3U]; - - buffer[0U] = UMP_FRAME_START; - buffer[1U] = 3U; - buffer[2U] = UMP_HELLO; - - // CUtils::dump(1U, "Transmitted", buffer, 3U); - - int n = m_serial.write(buffer, 3U); - if (n != 3) { - m_serial.close(); - return false; - } - - m_open = true; - - return true; -} - -bool CUMP::setMode(unsigned char mode) -{ - if (mode == m_mode) - return true; - - m_mode = mode; - - unsigned char buffer[4U]; - - buffer[0U] = UMP_FRAME_START; - buffer[1U] = 4U; - buffer[2U] = UMP_SET_MODE; - buffer[3U] = mode; - - // CUtils::dump(1U, "Transmitted", buffer, 4U); - - return m_serial.write(buffer, 4U) == 4; -} - -bool CUMP::setTX(bool on) -{ - if (on == m_tx) - return true; - - m_tx = on; - - unsigned char buffer[4U]; - - buffer[0U] = UMP_FRAME_START; - buffer[1U] = 4U; - buffer[2U] = UMP_SET_TX; - buffer[3U] = on ? 0x01U : 0x00U; - - // CUtils::dump(1U, "Transmitted", buffer, 4U); - - return m_serial.write(buffer, 4U) == 4; -} - -bool CUMP::setCD(bool on) -{ - if (on == m_cd) - return true; - - m_cd = on; - - unsigned char buffer[4U]; - - buffer[0U] = UMP_FRAME_START; - buffer[1U] = 4U; - buffer[2U] = UMP_SET_CD; - buffer[3U] = on ? 0x01U : 0x00U; - - // CUtils::dump(1U, "Transmitted", buffer, 4U); - - return m_serial.write(buffer, 4U) == 4; -} - -bool CUMP::getLockout() const -{ - return m_lockout; -} - -int CUMP::write(const unsigned char* data, unsigned int length) -{ - assert(data != NULL); - assert(length > 0U); - - unsigned char buffer[250U]; - - buffer[0U] = UMP_FRAME_START; - buffer[1U] = length + 3U; - buffer[2U] = UMP_WRITE_SERIAL; - - ::memcpy(buffer + 3U, data, length); - - // CUtils::dump(1U, "Transmitted", buffer, length + 3U); - - return m_serial.write(buffer, length + 3U); -} - -// To be implemented later if needed -int CUMP::read(unsigned char* data, unsigned int length) -{ - assert(data != NULL); - assert(length > 0U); - - return 0; -} - -void CUMP::clock(unsigned int ms) -{ - if (m_offset == 0U) { - // Get the start of the frame or nothing at all - int ret = m_serial.read(m_buffer + 0U, 1U); - if (ret < 0) { - LogError("Error when reading from the UMP"); - return; - } - - if (ret == 0) - return; - - if (m_buffer[0U] != UMP_FRAME_START) - return; - - m_offset = 1U; - } - - if (m_offset == 1U) { - // Get the length of the frame - int ret = m_serial.read(m_buffer + 1U, 1U); - if (ret < 0) { - LogError("Error when reading from the UMP"); - m_offset = 0U; - return; - } - - if (ret == 0) - return; - - if (m_buffer[1U] >= 250U) { - LogError("Invalid length received from the UMP - %u", m_buffer[1U]); - m_offset = 0U; - return; - } - - m_length = m_buffer[1U]; - m_offset = 2U; - } - - if (m_offset == 2U) { - // Get the frame type - int ret = m_serial.read(m_buffer + 2U, 1U); - if (ret < 0) { - LogError("Error when reading from the UMP"); - m_offset = 0U; - return; - } - - if (ret == 0) - return; - - switch (m_buffer[2U]) { - case UMP_STATUS: - case UMP_READ_SERIAL: - break; - - default: - LogError("Unknown message, type: %02X", m_buffer[2U]); - m_offset = 0U; - return; - } - - m_offset = 3U; - } - - if (m_offset >= 3U) { - while (m_offset < m_length) { - int ret = m_serial.read(m_buffer + m_offset, m_length - m_offset); - if (ret < 0) { - LogError("Error when reading from the UMP"); - m_offset = 0U; - return; - } - - if (ret == 0) - return; - - if (ret > 0) - m_offset += ret; - } - } - - m_offset = 0U; - - // CUtils::dump(1U, "Received", m_buffer, m_length); - - if (m_buffer[2U] == UMP_STATUS) - m_lockout = (m_buffer[3U] & 0x01U) == 0x01U; -} - -void CUMP::close() -{ - if (!m_open) - return; - - LogMessage("Closing the UMP"); - - m_serial.close(); - - m_open = false; -} diff --git a/UMP.h b/UMP.h deleted file mode 100644 index 1acc3c1..0000000 --- a/UMP.h +++ /dev/null @@ -1,63 +0,0 @@ -/* -* Copyright (C) 2016 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(UMP_H) -#define UMP_H - -#include "SerialController.h" -#include "SerialPort.h" - -#include - -class CUMP : public ISerialPort -{ -public: - CUMP(const std::string& port); - virtual ~CUMP(); - - virtual bool open(); - - bool setMode(unsigned char mode); - - bool setTX(bool on); - - bool setCD(bool on); - - bool getLockout() const; - - virtual int read(unsigned char* buffer, unsigned int length); - - virtual int write(const unsigned char* buffer, unsigned int length); - - void clock(unsigned int ms); - - virtual void close(); - -private: - CSerialController m_serial; - bool m_open; - unsigned char* m_buffer; - unsigned int m_length; - unsigned int m_offset; - bool m_lockout; - unsigned char m_mode; - bool m_tx; - bool m_cd; -}; - -#endif diff --git a/UMP/UMP.ino b/UMP/UMP.ino deleted file mode 100644 index 4cf98e3..0000000 --- a/UMP/UMP.ino +++ /dev/null @@ -1,203 +0,0 @@ -/* -* Copyright (C) 2016,2018 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(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) && !defined(__AVR_ATmega32U4__) && !defined(__SAM3X8E__) && !defined(__MK20DX256__) -#include -#endif - -#if !defined(PIN_LED) -#define PIN_LED 13 -#endif - -#define PIN_DSTAR 3 -#define PIN_DMR 4 -#define PIN_YSF 5 -#define PIN_P25 6 -#define PIN_NXDN 7 -#define PIN_POCSAG 8 - -#define PIN_TX 10 -#define PIN_CD 11 - -#define PIN_LOCKOUT 12 - -#if defined(__MK20DX256__) -#define FLASH_DELAY 200000U -#else -#define FLASH_DELAY 3200U -#endif - -#if !defined(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) && !defined(__AVR_ATmega32U4__) && !defined(__SAM3X8E__) && !defined(__MK20DX256__) -AltSoftSerial mySerial; -#endif - -// Use the LOCKOUT function on the UMP -// #define USE_LOCKOUT - -void setup() -{ - Serial.begin(115200); - -#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) || defined(__MK20DX256__) - Serial1.begin(9600); -#else - mySerial.begin(9600); -#endif - - pinMode(PIN_LED, OUTPUT); - pinMode(PIN_DSTAR, OUTPUT); - pinMode(PIN_DMR, OUTPUT); - pinMode(PIN_YSF, OUTPUT); - pinMode(PIN_P25, OUTPUT); - pinMode(PIN_NXDN, OUTPUT); - pinMode(PIN_POCSAG, OUTPUT); - pinMode(PIN_TX, OUTPUT); - pinMode(PIN_CD, OUTPUT); - pinMode(PIN_LOCKOUT, INPUT); - - digitalWrite(PIN_DSTAR, LOW); - digitalWrite(PIN_DMR, LOW); - digitalWrite(PIN_YSF, LOW); - digitalWrite(PIN_P25, LOW); - digitalWrite(PIN_NXDN, LOW); - digitalWrite(PIN_POCSAG, LOW); - digitalWrite(PIN_TX, LOW); - digitalWrite(PIN_CD, LOW); -} - -#define UMP_FRAME_START 0xF0U - -#define UMP_HELLO 0x00U - -#define UMP_SET_MODE 0x01U -#define UMP_SET_TX 0x02U -#define UMP_SET_CD 0x03U - -#define UMP_WRITE_SERIAL 0x10U - -#define UMP_STATUS 0x50U - -#define MODE_IDLE 0U -#define MODE_DSTAR 1U -#define MODE_DMR 2U -#define MODE_YSF 3U -#define MODE_P25 4U -#define MODE_NXDN 5U -#define MODE_POCSAG 6U - -bool m_started = false; -uint32_t m_count = 0U; -bool m_led = false; - -uint8_t m_buffer[256U]; -uint8_t m_offset = 0U; -uint8_t m_length = 0U; - -bool m_lockout = false; - -void loop() -{ - while (Serial.available()) { - uint8_t c = Serial.read(); - - if (m_offset == 0U) { - if (c == UMP_FRAME_START) { - m_buffer[m_offset] = c; - m_offset = 1U; - } - } else if (m_offset == 1U) { - m_length = m_buffer[m_offset] = c; - m_offset = 2U; - } else { - m_buffer[m_offset] = c; - m_offset++; - - if (m_length == m_offset) { - switch (m_buffer[2U]) { - case UMP_HELLO: - m_started = true; - break; - case UMP_SET_MODE: - digitalWrite(PIN_DSTAR, m_buffer[3U] == MODE_DSTAR ? HIGH : LOW); - digitalWrite(PIN_DMR, m_buffer[3U] == MODE_DMR ? HIGH : LOW); - digitalWrite(PIN_YSF, m_buffer[3U] == MODE_YSF ? HIGH : LOW); - digitalWrite(PIN_P25, m_buffer[3U] == MODE_P25 ? HIGH : LOW); - digitalWrite(PIN_NXDN, m_buffer[3U] == MODE_NXDN ? HIGH : LOW); - digitalWrite(PIN_POCSAG, m_buffer[3U] == MODE_POCSAG ? HIGH : LOW); - break; - case UMP_SET_TX: - digitalWrite(PIN_TX, m_buffer[3U] == 0x01U ? HIGH : LOW); - break; - case UMP_SET_CD: - digitalWrite(PIN_CD, m_buffer[3U] == 0x01U ? HIGH : LOW); - break; - case UMP_WRITE_SERIAL: -#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) || defined(__MK20DX256__) - Serial1.write(m_buffer + 3U, m_length - 3U); -#else - mySerial.write(m_buffer + 3U, m_length - 3U); -#endif - break; - default: - break; - } - - m_length = 0U; - m_offset = 0U; - } - } - } - - bool lockout = false; -#if defined(USE_LOCKOUT) - lockout = digitalRead(PIN_LOCKOUT) == HIGH; -#endif - if (lockout != m_lockout) { - uint8_t data[4U]; - data[0U] = UMP_FRAME_START; - data[1U] = 4U; - data[2U] = UMP_STATUS; - data[3U] = lockout ? 0x01U : 0x00U; - Serial.write(data, 4U); - - m_lockout = lockout; - } - -#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) || defined(__MK20DX256__) - while (Serial1.available()) - Serial1.read(); -#else - while (mySerial.available()) - mySerial.read(); -#endif - - m_count++; - if (m_started) { - if (m_count > FLASH_DELAY) { - digitalWrite(PIN_LED, m_led ? LOW : HIGH); - m_led = !m_led; - m_count = 0U; - } - } else { - if (m_count > (FLASH_DELAY * 3U)) { - digitalWrite(PIN_LED, m_led ? LOW : HIGH); - m_led = !m_led; - m_count = 0U; - } - } -} diff --git a/YSFControl.cpp b/YSFControl.cpp index 63d23ca..8855160 100644 --- a/YSFControl.cpp +++ b/YSFControl.cpp @@ -1,1229 +1,1229 @@ -/* - * Copyright (C) 2015-2020 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 "YSFControl.h" -#include "Utils.h" -#include "Sync.h" -#include "Log.h" - -#include -#include -#include -#include - -// #define DUMP_YSF - -CYSFControl::CYSFControl(const std::string& callsign, bool selfOnly, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool lowDeviation, bool remoteGateway, CRSSIInterpolator* rssiMapper) : -m_callsign(NULL), -m_selfCallsign(NULL), -m_selfOnly(selfOnly), -m_network(network), -m_display(display), -m_duplex(duplex), -m_lowDeviation(lowDeviation), -m_remoteGateway(remoteGateway), -m_queue(5000U, "YSF Control"), -m_rfState(RS_RF_LISTENING), -m_netState(RS_NET_IDLE), -m_rfTimeoutTimer(1000U, timeout), -m_netTimeoutTimer(1000U, timeout), -m_packetTimer(1000U, 0U, 200U), -m_networkWatchdog(1000U, 0U, 1500U), -m_elapsed(), -m_rfFrames(0U), -m_netFrames(0U), -m_netLost(0U), -m_rfErrs(0U), -m_rfBits(1U), -m_netErrs(0U), -m_netBits(1U), -m_rfSource(NULL), -m_rfDest(NULL), -m_netSource(NULL), -m_netDest(NULL), -m_lastFICH(), -m_netN(0U), -m_rfPayload(), -m_netPayload(), -m_rssiMapper(rssiMapper), -m_rssi(0U), -m_maxRSSI(0U), -m_minRSSI(0U), -m_aveRSSI(0U), -m_rssiCount(0U), -m_enabled(true), -m_fp(NULL) -{ - assert(display != NULL); - assert(rssiMapper != NULL); - - m_rfPayload.setUplink(callsign); - m_rfPayload.setDownlink(callsign); - - m_netPayload.setDownlink(callsign); - - m_netSource = new unsigned char[YSF_CALLSIGN_LENGTH]; - m_netDest = new unsigned char[YSF_CALLSIGN_LENGTH]; - - m_callsign = new unsigned char[YSF_CALLSIGN_LENGTH]; - - std::string node = callsign; - node.resize(YSF_CALLSIGN_LENGTH, ' '); - - for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) - m_callsign[i] = node.at(i); - - m_selfCallsign = new unsigned char[YSF_CALLSIGN_LENGTH]; - ::memset(m_selfCallsign, 0x00U, YSF_CALLSIGN_LENGTH); - - for (unsigned int i = 0U; i < callsign.length(); i++) - m_selfCallsign[i] = callsign.at(i); -} - -CYSFControl::~CYSFControl() -{ - delete[] m_netSource; - delete[] m_netDest; - delete[] m_callsign; - delete[] m_selfCallsign; -} - -bool CYSFControl::writeModem(unsigned char *data, unsigned int len) -{ - assert(data != NULL); - - if (!m_enabled) - return false; - - unsigned char type = data[0U]; - - if (type == TAG_LOST && m_rfState == RS_RF_AUDIO) { - if (m_rssi != 0U) - LogMessage("YSF, transmission lost from %10.10s to %10.10s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_rfSource, m_rfDest, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else - LogMessage("YSF, transmission lost from %10.10s to %10.10s, %.1f seconds, BER: %.1f%%", m_rfSource, m_rfDest, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); - writeEndRF(); - return false; - } - - if (type == TAG_LOST && m_rfState == RS_RF_REJECTED) { - m_rfPayload.reset(); - m_rfSource = NULL; - m_rfDest = NULL; - m_rfState = RS_RF_LISTENING; - return false; - } - - if (type == TAG_LOST) { - m_rfPayload.reset(); - m_rfState = RS_RF_LISTENING; - return false; - } - - // Have we got RSSI bytes on the end? - if (len == (YSF_FRAME_LENGTH_BYTES + 4U)) { - uint16_t raw = 0U; - raw |= (data[122U] << 8) & 0xFF00U; - raw |= (data[123U] << 0) & 0x00FFU; - - // Convert the raw RSSI to dBm - int rssi = m_rssiMapper->interpolate(raw); - if (rssi != 0) - LogDebug("YSF, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi); - - // RSSI is always reported as positive - m_rssi = (rssi >= 0) ? rssi : -rssi; - - if (m_rssi > m_minRSSI) - m_minRSSI = m_rssi; - if (m_rssi < m_maxRSSI) - m_maxRSSI = m_rssi; - - m_aveRSSI += m_rssi; - m_rssiCount++; - } - - CYSFFICH fich; - bool valid = fich.decode(data + 2U); - if (!valid) { - unsigned char fi = m_lastFICH.getFI(); - unsigned char ft = m_lastFICH.getFT(); - unsigned char fn = m_lastFICH.getFN(); - unsigned char bt = m_lastFICH.getBT(); - unsigned char bn = m_lastFICH.getBN(); - - if (fi == YSF_FI_COMMUNICATIONS && ft > 0U) { - fn++; - if (fn > ft) { - fn = 0U; - if (bt > 0U) - bn++; - } - } - - m_lastFICH.setFI(YSF_FI_COMMUNICATIONS); - m_lastFICH.setFN(fn); - m_lastFICH.setBN(bn); - } else { - m_lastFICH = fich; - } - -#ifdef notdef - // Stop repeater packets coming through, unless we're acting as a remote gateway - if (m_remoteGateway) { - unsigned char mr = m_lastFICH.getMR(); - if (mr != YSF_MR_BUSY) - return false; - } else { - unsigned char mr = m_lastFICH.getMR(); - if (mr == YSF_MR_BUSY) - return false; - } -#endif - - unsigned char dt = m_lastFICH.getDT(); - - bool ret = false; - switch (dt) { - case YSF_DT_VOICE_FR_MODE: - ret = processVWData(valid, data); - break; - - case YSF_DT_VD_MODE1: - case YSF_DT_VD_MODE2: - ret = processDNData(valid, data); - break; - - case YSF_DT_DATA_FR_MODE: - ret = processFRData(valid, data); - break; - - default: - break; - } - - return ret; -} - -bool CYSFControl::processVWData(bool valid, unsigned char *data) -{ - unsigned char fi = m_lastFICH.getFI(); - unsigned char dgid = m_lastFICH.getDGId(); - - if (valid && fi == YSF_FI_HEADER) { - if (m_rfState == RS_RF_LISTENING) { - bool valid = m_rfPayload.processHeaderData(data + 2U); - if (!valid) - return false; - - m_rfSource = m_rfPayload.getSource(); - - if (m_selfOnly) { - bool ret = checkCallsign(m_rfSource); - if (!ret) { - LogMessage("YSF, invalid access attempt from %10.10s to DG-ID %u", m_rfSource, dgid); - m_rfState = RS_RF_REJECTED; - return false; - } - } - - unsigned char cm = m_lastFICH.getCM(); - if (cm == YSF_CM_GROUP1 || cm == YSF_CM_GROUP2) - m_rfDest = (unsigned char*)"ALL "; - else - m_rfDest = m_rfPayload.getDest(); - - m_rfFrames = 0U; - m_rfErrs = 0U; - m_rfBits = 1U; - m_rfTimeoutTimer.start(); - m_rfState = RS_RF_AUDIO; - - m_minRSSI = m_rssi; - m_maxRSSI = m_rssi; - m_aveRSSI = m_rssi; - m_rssiCount = 1U; -#if defined(DUMP_YSF) - openFile(); -#endif - m_display->writeFusion((char*)m_rfSource, (char*)m_rfDest, dgid, "R", " "); - LogMessage("YSF, received RF header from %10.10s to DG-ID %u", m_rfSource, dgid); - - CSync::addYSFSync(data + 2U); - - CYSFFICH fich = m_lastFICH; - - fich.encode(data + 2U); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - writeNetwork(data, m_rfFrames % 128U); - -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif - if (m_duplex) { - fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); - fich.setDev(m_lowDeviation); - fich.encode(data + 2U); - writeQueueRF(data); - } - - m_rfFrames++; - - m_display->writeFusionRSSI(m_rssi); - - return true; - } - } else if (valid && fi == YSF_FI_TERMINATOR) { - if (m_rfState == RS_RF_REJECTED) { - m_rfPayload.reset(); - m_rfSource = NULL; - m_rfDest = NULL; - m_rfState = RS_RF_LISTENING; - } else if (m_rfState == RS_RF_AUDIO) { - m_rfPayload.processHeaderData(data + 2U); - - CSync::addYSFSync(data + 2U); - - CYSFFICH fich = m_lastFICH; - - fich.encode(data + 2U); - - data[0U] = TAG_EOT; - data[1U] = 0x00U; - - writeNetwork(data, m_rfFrames % 128U); -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif - if (m_duplex) { - fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); - fich.setDev(m_lowDeviation); - fich.encode(data + 2U); - writeQueueRF(data); - } - - m_rfFrames++; - - if (m_rssi != 0U) - LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else - LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); - - writeEndRF(); - } - } else { - if (m_rfState == RS_RF_AUDIO) { - // If valid is false, update the m_lastFICH for this transmission - if (!valid) { - // XXX Check these values - m_lastFICH.setFT(0U); - m_lastFICH.setFN(0U); - } - - CSync::addYSFSync(data + 2U); - - CYSFFICH fich = m_lastFICH; - - unsigned char fn = fich.getFN(); - unsigned char ft = fich.getFT(); - - if (fn == 0U && ft == 1U) { - // The first packet after the header is odd - m_rfPayload.processVoiceFRModeData(data + 2U); - unsigned int errors = m_rfPayload.processVoiceFRModeAudio2(data + 2U); - m_rfErrs += errors; - m_rfBits += 288U; - m_display->writeFusionBER(float(errors) / 2.88F); - LogDebug("YSF, V Mode 3, seq %u, AMBE FEC %u/288 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 2.88F); - } else { - unsigned int errors = m_rfPayload.processVoiceFRModeAudio5(data + 2U); - m_rfErrs += errors; - m_rfBits += 720U; - m_display->writeFusionBER(float(errors) / 7.2F); - LogDebug("YSF, V Mode 3, seq %u, AMBE FEC %u/720 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 7.2F); - } - - fich.encode(data + 2U); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - writeNetwork(data, m_rfFrames % 128U); - - if (m_duplex) { - fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); - fich.setDev(m_lowDeviation); - fich.encode(data + 2U); - writeQueueRF(data); - } - -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif - m_rfFrames++; - - m_display->writeFusionRSSI(m_rssi); - - return true; - } - } - - return false; -} - -bool CYSFControl::processDNData(bool valid, unsigned char *data) -{ - unsigned char fi = m_lastFICH.getFI(); - unsigned char dgid = m_lastFICH.getDGId(); - - if (valid && fi == YSF_FI_HEADER) { - if (m_rfState == RS_RF_LISTENING) { - bool valid = m_rfPayload.processHeaderData(data + 2U); - if (!valid) - return false; - - m_rfSource = m_rfPayload.getSource(); - - if (m_selfOnly) { - bool ret = checkCallsign(m_rfSource); - if (!ret) { - LogMessage("YSF, invalid access attempt from %10.10s to DG-ID %u", m_rfSource, dgid); - m_rfState = RS_RF_REJECTED; - return false; - } - } - - unsigned char cm = m_lastFICH.getCM(); - if (cm == YSF_CM_GROUP1 || cm == YSF_CM_GROUP2) - m_rfDest = (unsigned char*)"ALL "; - else - m_rfDest = m_rfPayload.getDest(); - - m_rfFrames = 0U; - m_rfErrs = 0U; - m_rfBits = 1U; - m_rfTimeoutTimer.start(); - m_rfState = RS_RF_AUDIO; - - m_minRSSI = m_rssi; - m_maxRSSI = m_rssi; - m_aveRSSI = m_rssi; - m_rssiCount = 1U; -#if defined(DUMP_YSF) - openFile(); -#endif - m_display->writeFusion((char*)m_rfSource, (char*)m_rfDest, dgid, "R", " "); - LogMessage("YSF, received RF header from %10.10s to DG-ID %u", m_rfSource, dgid); - - CSync::addYSFSync(data + 2U); - - CYSFFICH fich = m_lastFICH; - - fich.encode(data + 2U); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - writeNetwork(data, m_rfFrames % 128U); - -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif - if (m_duplex) { - fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); - fich.setDev(m_lowDeviation); - fich.encode(data + 2U); - writeQueueRF(data); - } - - m_rfFrames++; - - m_display->writeFusionRSSI(m_rssi); - - return true; - } - } else if (valid && fi == YSF_FI_TERMINATOR) { - if (m_rfState == RS_RF_REJECTED) { - m_rfPayload.reset(); - m_rfSource = NULL; - m_rfDest = NULL; - m_rfState = RS_RF_LISTENING; - } else if (m_rfState == RS_RF_AUDIO) { - m_rfPayload.processHeaderData(data + 2U); - - CSync::addYSFSync(data + 2U); - - CYSFFICH fich = m_lastFICH; - - fich.encode(data + 2U); - - data[0U] = TAG_EOT; - data[1U] = 0x00U; - - writeNetwork(data, m_rfFrames % 128U); -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif - if (m_duplex) { - fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); - fich.setDev(m_lowDeviation); - fich.encode(data + 2U); - writeQueueRF(data); - } - - m_rfFrames++; - - if (m_rssi != 0U) - LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else - LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); - - writeEndRF(); - } - } else { - if (m_rfState == RS_RF_AUDIO) { - // If valid is false, update the m_lastFICH for this transmission - if (!valid) { - unsigned char ft = m_lastFICH.getFT(); - unsigned char fn = m_lastFICH.getFN() + 1U; - - if (fn > ft) - fn = 0U; - - m_lastFICH.setFN(fn); - } - - CSync::addYSFSync(data + 2U); - - unsigned char fn = m_lastFICH.getFN(); - unsigned char dt = m_lastFICH.getDT(); - - switch (dt) { - case YSF_DT_VD_MODE1: { - m_rfPayload.processVDMode1Data(data + 2U, fn); - unsigned int errors = m_rfPayload.processVDMode1Audio(data + 2U); - m_rfErrs += errors; - m_rfBits += 235U; - m_display->writeFusionBER(float(errors) / 2.35F); - LogDebug("YSF, V/D Mode 1, seq %u, AMBE FEC %u/235 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 2.35F); - } - break; - - case YSF_DT_VD_MODE2: { - m_rfPayload.processVDMode2Data(data + 2U, fn); - unsigned int errors = m_rfPayload.processVDMode2Audio(data + 2U); - m_rfErrs += errors; - m_rfBits += 405U; - m_display->writeFusionBER(float(errors) / 4.05F); - LogDebug("YSF, V/D Mode 2, seq %u, Repetition FEC %u/405 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 4.05F); - } - break; - - default: - break; - } - - CYSFFICH fich = m_lastFICH; - - fich.encode(data + 2U); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - writeNetwork(data, m_rfFrames % 128U); - - if (m_duplex) { - fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); - fich.setDev(m_lowDeviation); - fich.encode(data + 2U); - writeQueueRF(data); - } - -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif - m_rfFrames++; - - m_display->writeFusionRSSI(m_rssi); - - return true; - } else if (valid && m_rfState == RS_RF_LISTENING) { - // Only use clean frames for late entry. - unsigned char fn = m_lastFICH.getFN(); - unsigned char dt = m_lastFICH.getDT(); - - switch (dt) { - case YSF_DT_VD_MODE1: - valid = m_rfPayload.processVDMode1Data(data + 2U, fn); - break; - - case YSF_DT_VD_MODE2: - valid = m_rfPayload.processVDMode2Data(data + 2U, fn); - break; - - default: - valid = false; - break; - } - - if (!valid) - return false; - - unsigned char cm = m_lastFICH.getCM(); - if (cm == YSF_CM_GROUP1 || cm == YSF_CM_GROUP2) - m_rfDest = (unsigned char*)"ALL "; - else - m_rfDest = m_rfPayload.getDest(); - - m_rfSource = m_rfPayload.getSource(); - - if (m_rfSource == NULL || m_rfDest == NULL) - return false; - - if (m_selfOnly) { - bool ret = checkCallsign(m_rfSource); - if (!ret) { - LogMessage("YSF, invalid access attempt from %10.10s to DG-ID %u", m_rfSource, dgid); - m_rfState = RS_RF_REJECTED; - return false; - } - } - - m_rfFrames = 0U; - m_rfErrs = 0U; - m_rfBits = 1U; - m_rfTimeoutTimer.start(); - m_rfState = RS_RF_AUDIO; - - m_minRSSI = m_rssi; - m_maxRSSI = m_rssi; - m_aveRSSI = m_rssi; - m_rssiCount = 1U; -#if defined(DUMP_YSF) - openFile(); -#endif - // Build a new header and transmit it - unsigned char buffer[YSF_FRAME_LENGTH_BYTES + 2U]; - - CSync::addYSFSync(buffer + 2U); - - CYSFFICH fich = m_lastFICH; - fich.setFI(YSF_FI_HEADER); - fich.encode(buffer + 2U); - - unsigned char csd1[20U], csd2[20U]; - memcpy(csd1 + YSF_CALLSIGN_LENGTH, m_rfSource, YSF_CALLSIGN_LENGTH); - memset(csd2, ' ', YSF_CALLSIGN_LENGTH + YSF_CALLSIGN_LENGTH); - - if (cm == YSF_CM_GROUP1 || cm == YSF_CM_GROUP2) - memset(csd1 + 0U, '*', YSF_CALLSIGN_LENGTH); - else - memcpy(csd1 + 0U, m_rfDest, YSF_CALLSIGN_LENGTH); - - CYSFPayload payload; - payload.writeHeader(buffer + 2U, csd1, csd2); - - buffer[0U] = TAG_DATA; - buffer[1U] = 0x00U; - - writeNetwork(buffer, m_rfFrames % 128U); - - if (m_duplex) { - fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); - fich.setDev(m_lowDeviation); - fich.encode(buffer + 2U); - writeQueueRF(buffer); - } - -#if defined(DUMP_YSF) - writeFile(buffer + 2U); -#endif - m_display->writeFusion((char*)m_rfSource, (char*)m_rfDest, dgid, "R", " "); - LogMessage("YSF, received RF late entry from %10.10s to DG-ID %u", m_rfSource, dgid); - - CSync::addYSFSync(data + 2U); - - fich = m_lastFICH; - - fich.encode(data + 2U); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - writeNetwork(data, m_rfFrames % 128U); - - if (m_duplex) { - fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); - fich.setDev(m_lowDeviation); - fich.encode(data + 2U); - writeQueueRF(data); - } - -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif - m_rfFrames++; - - m_display->writeFusionRSSI(m_rssi); - - return true; - } - } - - return false; -} - -bool CYSFControl::processFRData(bool valid, unsigned char *data) -{ - unsigned char fi = m_lastFICH.getFI(); - unsigned char dgid = m_lastFICH.getDGId(); - - if (valid && fi == YSF_FI_HEADER) { - if (m_rfState == RS_RF_LISTENING) { - valid = m_rfPayload.processHeaderData(data + 2U); - if (!valid) - return false; - - m_rfSource = m_rfPayload.getSource(); - - if (m_selfOnly) { - bool ret = checkCallsign(m_rfSource); - if (!ret) { - LogMessage("YSF, invalid access attempt from %10.10s to DG-ID %u", m_rfSource, dgid); - m_rfState = RS_RF_REJECTED; - return false; - } - } - - unsigned char cm = m_lastFICH.getCM(); - if (cm == YSF_CM_GROUP1 || cm == YSF_CM_GROUP2) - m_rfDest = (unsigned char*)"ALL "; - else - m_rfDest = m_rfPayload.getDest(); - - m_rfFrames = 0U; - m_rfState = RS_RF_DATA; - - m_minRSSI = m_rssi; - m_maxRSSI = m_rssi; - m_aveRSSI = m_rssi; - m_rssiCount = 1U; -#if defined(DUMP_YSF) - openFile(); -#endif - m_display->writeFusion((char*)m_rfSource, (char*)m_rfDest, dgid, "R", " "); - LogMessage("YSF, received RF header from %10.10s to DG-ID %u", m_rfSource, dgid); - - CSync::addYSFSync(data + 2U); - - CYSFFICH fich = m_lastFICH; - - fich.encode(data + 2U); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - writeNetwork(data, m_rfFrames % 128U); -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif - if (m_duplex) { - fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); - fich.setDev(m_lowDeviation); - fich.encode(data + 2U); - writeQueueRF(data); - } - - m_rfFrames++; - - m_display->writeFusionRSSI(m_rssi); - - return true; - } - } else if (valid && fi == YSF_FI_TERMINATOR) { - if (m_rfState == RS_RF_REJECTED) { - m_rfPayload.reset(); - m_rfSource = NULL; - m_rfDest = NULL; - m_rfState = RS_RF_LISTENING; - } else if (m_rfState == RS_RF_DATA) { - m_rfPayload.processHeaderData(data + 2U); - - CSync::addYSFSync(data + 2U); - - CYSFFICH fich = m_lastFICH; - - fich.encode(data + 2U); - - data[0U] = TAG_EOT; - data[1U] = 0x00U; - - writeNetwork(data, m_rfFrames % 128U); -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif - if (m_duplex) { - fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); - fich.setDev(m_lowDeviation); - fich.encode(data + 2U); - writeQueueRF(data); - } - - m_rfFrames++; - - if (m_rssi != 0U) - LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, RSSI: -%u/-%u/-%u dBm", m_rfSource, dgid, float(m_rfFrames) / 10.0F, m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else - LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds", m_rfSource, dgid, float(m_rfFrames) / 10.0F); - - writeEndRF(); - } - } else { - if (m_rfState == RS_RF_DATA) { - // If valid is false, update the m_lastFICH for this transmission - if (!valid) { - unsigned char ft = m_lastFICH.getFT(); - unsigned char fn = m_lastFICH.getFN() + 1U; - - if (fn > ft) - fn = 0U; - - m_lastFICH.setFN(fn); - } - - CSync::addYSFSync(data + 2U); - - unsigned char fn = m_lastFICH.getFN(); - - m_rfPayload.processDataFRModeData(data + 2U, fn); - - CYSFFICH fich = m_lastFICH; - - fich.encode(data + 2U); - - data[0U] = TAG_DATA; - data[1U] = 0x00U; - - writeNetwork(data, m_rfFrames % 128U); - - if (m_duplex) { - fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); - fich.setDev(m_lowDeviation); - fich.encode(data + 2U); - writeQueueRF(data); - } - -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif - m_rfFrames++; - - m_display->writeFusionRSSI(m_rssi); - - return true; - } - } - - return false; -} - -unsigned int CYSFControl::readModem(unsigned char* data) -{ - assert(data != NULL); - - if (m_queue.isEmpty()) - return 0U; - - unsigned char len = 0U; - m_queue.getData(&len, 1U); - - m_queue.getData(data, len); - - return len; -} - -void CYSFControl::writeEndRF() -{ - m_rfState = RS_RF_LISTENING; - - m_rfTimeoutTimer.stop(); - m_rfPayload.reset(); - - // These variables are free'd by YSFPayload - m_rfSource = NULL; - m_rfDest = NULL; - - if (m_netState == RS_NET_IDLE) { - m_display->clearFusion(); - - if (m_network != NULL) - m_network->reset(); - } - -#if defined(DUMP_YSF) - closeFile(); -#endif -} - -void CYSFControl::writeEndNet() -{ - m_netState = RS_NET_IDLE; - - m_netTimeoutTimer.stop(); - m_networkWatchdog.stop(); - m_packetTimer.stop(); - - m_netPayload.reset(); - - m_display->clearFusion(); - - if (m_network != NULL) - m_network->reset(); -} - -void CYSFControl::writeNetwork() -{ - unsigned char data[200U]; - unsigned int length = m_network->read(data); - if (length == 0U) - return; - - if (!m_enabled) - return; - - if (m_rfState != RS_RF_LISTENING && m_netState == RS_NET_IDLE) - return; - - m_networkWatchdog.start(); - - bool gateway = ::memcmp(data + 4U, m_callsign, YSF_CALLSIGN_LENGTH) == 0; - - unsigned char n = (data[34U] & 0xFEU) >> 1; - bool end = (data[34U] & 0x01U) == 0x01U; - - CYSFFICH fich; - bool valid = fich.decode(data + 35U); - - unsigned char dgid = 0U; - if (valid) - dgid = fich.getDGId(); - - if (!m_netTimeoutTimer.isRunning()) { - if (end) - return; - - ::memcpy(m_netSource, data + 14U, YSF_CALLSIGN_LENGTH); - ::memcpy(m_netDest, data + 24U, YSF_CALLSIGN_LENGTH); - - if (::memcmp(m_netSource, " ", 10U) != 0 && ::memcmp(m_netDest, " ", 10U) != 0) { - m_display->writeFusion((char*)m_netSource, (char*)m_netDest, dgid, "N", (char*)(data + 4U)); - LogMessage("YSF, received network data from %10.10s to DG-ID %u at %10.10s", m_netSource, dgid, data + 4U); - } - - m_netTimeoutTimer.start(); - m_netPayload.reset(); - m_packetTimer.start(); - m_elapsed.start(); - m_netState = RS_NET_AUDIO; - m_netFrames = 0U; - m_netLost = 0U; - m_netErrs = 0U; - m_netBits = 1U; - m_netN = 0U; - } else { - // Check for duplicate frames, if we can - if (m_netN == n) - return; - } - - data[33U] = end ? TAG_EOT : TAG_DATA; - data[34U] = 0x00U; - - if (valid) { - unsigned char dt = fich.getDT(); - unsigned char fn = fich.getFN(); - unsigned char ft = fich.getFT(); - unsigned char fi = fich.getFI(); - unsigned char cm = fich.getCM(); - - if (::memcmp(m_netDest, " ", YSF_CALLSIGN_LENGTH) == 0) { - if (cm == YSF_CM_GROUP1 || cm == YSF_CM_GROUP2) - ::memcpy(m_netDest, "ALL ", YSF_CALLSIGN_LENGTH); - } - - if (m_remoteGateway) { - fich.setVoIP(false); - fich.setMR(YSF_MR_DIRECT); - } else { - fich.setVoIP(true); - fich.setMR(YSF_MR_BUSY); - } - - fich.setDev(m_lowDeviation); - fich.encode(data + 35U); - - // Set the downlink callsign - switch (fi) { - case YSF_FI_HEADER: { - bool ok = m_netPayload.processHeaderData(data + 35U); - if (ok) - processNetCallsigns(data, dgid); - } - break; - - case YSF_FI_TERMINATOR: - m_netPayload.processHeaderData(data + 35U); - break; - - case YSF_FI_COMMUNICATIONS: - switch (dt) { - case YSF_DT_VD_MODE1: { - bool ok = m_netPayload.processVDMode1Data(data + 35U, fn, gateway); - if (ok) - processNetCallsigns(data, dgid); - - unsigned int errors = m_netPayload.processVDMode1Audio(data + 35U); - m_netErrs += errors; - m_netBits += 235U; - } - break; - - case YSF_DT_VD_MODE2: { - bool ok = m_netPayload.processVDMode2Data(data + 35U, fn, gateway); - if (ok) - processNetCallsigns(data, dgid); - - unsigned int errors = m_netPayload.processVDMode2Audio(data + 35U); - m_netErrs += errors; - m_netBits += 135U; - } - break; - - case YSF_DT_DATA_FR_MODE: - m_netPayload.processDataFRModeData(data + 35U, fn, gateway); - break; - - case YSF_DT_VOICE_FR_MODE: - if (fn == 0U && ft == 1U) { - // The first packet after the header is odd - m_netPayload.processVoiceFRModeData(data + 35U); - unsigned int errors = m_netPayload.processVoiceFRModeAudio2(data + 35U); - m_netErrs += errors; - m_netBits += 288U; - } else { - unsigned int errors = m_netPayload.processVoiceFRModeAudio5(data + 35U); - m_netErrs += errors; - m_netBits += 720U; - } - break; - - default: - break; - } - break; - - default: - break; - } - } - - writeQueueNet(data + 33U); - - m_packetTimer.start(); - m_netFrames++; - m_netN = n; - - if (end) { - LogMessage("YSF, received network end of transmission from %10.10s to DG-ID %u, %.1f seconds, %u%% packet loss, BER: %.1f%%", m_netSource, dgid, float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); - writeEndNet(); - } -} - -void CYSFControl::clock(unsigned int ms) -{ - if (m_network != NULL) - writeNetwork(); - - m_rfTimeoutTimer.clock(ms); - m_netTimeoutTimer.clock(ms); - - if (m_netState == RS_NET_AUDIO) { - m_networkWatchdog.clock(ms); - - if (m_networkWatchdog.hasExpired()) { - LogMessage("YSF, network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%", float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); - writeEndNet(); - } - } -} - -void CYSFControl::writeQueueRF(const unsigned char *data) -{ - assert(data != NULL); - - if (m_netState != RS_NET_IDLE) - return; - - if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) - return; - - unsigned char len = YSF_FRAME_LENGTH_BYTES + 2U; - - unsigned int space = m_queue.freeSpace(); - if (space < (len + 1U)) { - LogError("YSF, overflow in the System Fusion RF queue"); - return; - } - - m_queue.addData(&len, 1U); - - m_queue.addData(data, len); -} - -void CYSFControl::writeQueueNet(const unsigned char *data) -{ - assert(data != NULL); - - if (m_netTimeoutTimer.isRunning() && m_netTimeoutTimer.hasExpired()) - return; - - unsigned char len = YSF_FRAME_LENGTH_BYTES + 2U; - - unsigned int space = m_queue.freeSpace(); - if (space < (len + 1U)) { - LogError("YSF, overflow in the System Fusion RF queue"); - return; - } - - m_queue.addData(&len, 1U); - - m_queue.addData(data, len); -} - -void CYSFControl::writeNetwork(const unsigned char *data, unsigned int count) -{ - assert(data != NULL); - - if (m_network == NULL) - return; - - if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) - return; - - m_network->write(m_rfSource, m_rfDest, data + 2U, count, data[0U] == TAG_EOT); -} - -bool CYSFControl::openFile() -{ - if (m_fp != NULL) - return true; - - time_t t; - ::time(&t); - - struct tm* tm = ::localtime(&t); - - char name[100U]; - ::sprintf(name, "YSF_%04d%02d%02d_%02d%02d%02d.ambe", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - - m_fp = ::fopen(name, "wb"); - if (m_fp == NULL) - return false; - - ::fwrite("YSF", 1U, 3U, m_fp); - - return true; -} - -bool CYSFControl::writeFile(const unsigned char* data) -{ - if (m_fp == NULL) - return false; - - ::fwrite(data, 1U, YSF_FRAME_LENGTH_BYTES, m_fp); - - return true; -} - -void CYSFControl::closeFile() -{ - if (m_fp != NULL) { - ::fclose(m_fp); - m_fp = NULL; - } -} - -bool CYSFControl::checkCallsign(const unsigned char* callsign) const -{ - return ::memcmp(callsign, m_selfCallsign, ::strlen((char*)m_selfCallsign)) == 0; -} - -void CYSFControl::processNetCallsigns(const unsigned char* data, unsigned char dgid) -{ - assert(data != NULL); - - if (::memcmp(m_netSource, " ", 10U) == 0 || ::memcmp(m_netDest, " ", 10U) == 0) { - if (::memcmp(m_netSource, " ", YSF_CALLSIGN_LENGTH) == 0) { - unsigned char* source = m_netPayload.getSource(); - if (source != NULL) - ::memcpy(m_netSource, source, YSF_CALLSIGN_LENGTH); - } - - if (::memcmp(m_netDest, " ", YSF_CALLSIGN_LENGTH) == 0) { - unsigned char* dest = m_netPayload.getDest(); - if (dest != NULL) - ::memcpy(m_netDest, dest, YSF_CALLSIGN_LENGTH); - } - - if (::memcmp(m_netSource, " ", 10U) != 0 && ::memcmp(m_netDest, " ", 10U) != 0) { - m_display->writeFusion((char*)m_netSource, (char*)m_netDest, dgid, "N", (char*)(data + 4U)); - LogMessage("YSF, received network data from %10.10s to DG-ID %u at %10.10s", m_netSource, dgid, data + 4U); - } - } -} - -bool CYSFControl::isBusy() const -{ - return m_rfState != RS_RF_LISTENING || m_netState != RS_NET_IDLE; -} - -void CYSFControl::enable(bool enabled) -{ - if (!enabled && m_enabled) { - m_queue.clear(); - - // Reset the RF section - m_rfState = RS_RF_LISTENING; - - m_rfTimeoutTimer.stop(); - m_rfPayload.reset(); - - // These variables are free'd by YSFPayload - m_rfSource = NULL; - m_rfDest = NULL; - - // Reset the networking section - m_netState = RS_NET_IDLE; - - m_netTimeoutTimer.stop(); - m_networkWatchdog.stop(); - m_packetTimer.stop(); - - m_netPayload.reset(); - } - - m_enabled = enabled; -} +/* + * Copyright (C) 2015-2020 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 "YSFControl.h" +#include "Utils.h" +#include "Sync.h" +#include "Log.h" + +#include +#include +#include +#include + +// #define DUMP_YSF + +CYSFControl::CYSFControl(const std::string& callsign, bool selfOnly, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool lowDeviation, bool remoteGateway, CRSSIInterpolator* rssiMapper) : +m_callsign(NULL), +m_selfCallsign(NULL), +m_selfOnly(selfOnly), +m_network(network), +m_display(display), +m_duplex(duplex), +m_lowDeviation(lowDeviation), +m_remoteGateway(remoteGateway), +m_queue(5000U, "YSF Control"), +m_rfState(RS_RF_LISTENING), +m_netState(RS_NET_IDLE), +m_rfTimeoutTimer(1000U, timeout), +m_netTimeoutTimer(1000U, timeout), +m_packetTimer(1000U, 0U, 200U), +m_networkWatchdog(1000U, 0U, 1500U), +m_elapsed(), +m_rfFrames(0U), +m_netFrames(0U), +m_netLost(0U), +m_rfErrs(0U), +m_rfBits(1U), +m_netErrs(0U), +m_netBits(1U), +m_rfSource(NULL), +m_rfDest(NULL), +m_netSource(NULL), +m_netDest(NULL), +m_lastFICH(), +m_netN(0U), +m_rfPayload(), +m_netPayload(), +m_rssiMapper(rssiMapper), +m_rssi(0U), +m_maxRSSI(0U), +m_minRSSI(0U), +m_aveRSSI(0U), +m_rssiCount(0U), +m_enabled(true), +m_fp(NULL) +{ + assert(display != NULL); + assert(rssiMapper != NULL); + + m_rfPayload.setUplink(callsign); + m_rfPayload.setDownlink(callsign); + + m_netPayload.setDownlink(callsign); + + m_netSource = new unsigned char[YSF_CALLSIGN_LENGTH]; + m_netDest = new unsigned char[YSF_CALLSIGN_LENGTH]; + + m_callsign = new unsigned char[YSF_CALLSIGN_LENGTH]; + + std::string node = callsign; + node.resize(YSF_CALLSIGN_LENGTH, ' '); + + for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) + m_callsign[i] = node.at(i); + + m_selfCallsign = new unsigned char[YSF_CALLSIGN_LENGTH]; + ::memset(m_selfCallsign, 0x00U, YSF_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < callsign.length(); i++) + m_selfCallsign[i] = callsign.at(i); +} + +CYSFControl::~CYSFControl() +{ + delete[] m_netSource; + delete[] m_netDest; + delete[] m_callsign; + delete[] m_selfCallsign; +} + +bool CYSFControl::writeModem(unsigned char *data, unsigned int len) +{ + assert(data != NULL); + + if (!m_enabled) + return false; + + unsigned char type = data[0U]; + + if (type == TAG_LOST && m_rfState == RS_RF_AUDIO) { + if (m_rssi != 0U) + LogMessage("YSF, transmission lost from %10.10s to %10.10s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_rfSource, m_rfDest, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + else + LogMessage("YSF, transmission lost from %10.10s to %10.10s, %.1f seconds, BER: %.1f%%", m_rfSource, m_rfDest, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeEndRF(); + return false; + } + + if (type == TAG_LOST && m_rfState == RS_RF_REJECTED) { + m_rfPayload.reset(); + m_rfSource = NULL; + m_rfDest = NULL; + m_rfState = RS_RF_LISTENING; + return false; + } + + if (type == TAG_LOST) { + m_rfPayload.reset(); + m_rfState = RS_RF_LISTENING; + return false; + } + + // Have we got RSSI bytes on the end? + if (len == (YSF_FRAME_LENGTH_BYTES + 4U)) { + uint16_t raw = 0U; + raw |= (data[122U] << 8) & 0xFF00U; + raw |= (data[123U] << 0) & 0x00FFU; + + // Convert the raw RSSI to dBm + int rssi = m_rssiMapper->interpolate(raw); + if (rssi != 0) + LogDebug("YSF, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi); + + // RSSI is always reported as positive + m_rssi = (rssi >= 0) ? rssi : -rssi; + + if (m_rssi > m_minRSSI) + m_minRSSI = m_rssi; + if (m_rssi < m_maxRSSI) + m_maxRSSI = m_rssi; + + m_aveRSSI += m_rssi; + m_rssiCount++; + } + + CYSFFICH fich; + bool valid = fich.decode(data + 2U); + if (!valid) { + unsigned char fi = m_lastFICH.getFI(); + unsigned char ft = m_lastFICH.getFT(); + unsigned char fn = m_lastFICH.getFN(); + unsigned char bt = m_lastFICH.getBT(); + unsigned char bn = m_lastFICH.getBN(); + + if (fi == YSF_FI_COMMUNICATIONS && ft > 0U) { + fn++; + if (fn > ft) { + fn = 0U; + if (bt > 0U) + bn++; + } + } + + m_lastFICH.setFI(YSF_FI_COMMUNICATIONS); + m_lastFICH.setFN(fn); + m_lastFICH.setBN(bn); + } else { + m_lastFICH = fich; + } + +#ifdef notdef + // Stop repeater packets coming through, unless we're acting as a remote gateway + if (m_remoteGateway) { + unsigned char mr = m_lastFICH.getMR(); + if (mr != YSF_MR_BUSY) + return false; + } else { + unsigned char mr = m_lastFICH.getMR(); + if (mr == YSF_MR_BUSY) + return false; + } +#endif + + unsigned char dt = m_lastFICH.getDT(); + + bool ret = false; + switch (dt) { + case YSF_DT_VOICE_FR_MODE: + ret = processVWData(valid, data); + break; + + case YSF_DT_VD_MODE1: + case YSF_DT_VD_MODE2: + ret = processDNData(valid, data); + break; + + case YSF_DT_DATA_FR_MODE: + ret = processFRData(valid, data); + break; + + default: + break; + } + + return ret; +} + +bool CYSFControl::processVWData(bool valid, unsigned char *data) +{ + unsigned char fi = m_lastFICH.getFI(); + unsigned char dgid = m_lastFICH.getDGId(); + + if (valid && fi == YSF_FI_HEADER) { + if (m_rfState == RS_RF_LISTENING) { + bool valid = m_rfPayload.processHeaderData(data + 2U); + if (!valid) + return false; + + m_rfSource = m_rfPayload.getSource(); + + if (m_selfOnly) { + bool ret = checkCallsign(m_rfSource); + if (!ret) { + LogMessage("YSF, invalid access attempt from %10.10s to DG-ID %u", m_rfSource, dgid); + m_rfState = RS_RF_REJECTED; + return false; + } + } + + unsigned char cm = m_lastFICH.getCM(); + if (cm == YSF_CM_GROUP1 || cm == YSF_CM_GROUP2) + m_rfDest = (unsigned char*)"ALL "; + else + m_rfDest = m_rfPayload.getDest(); + + m_rfFrames = 0U; + m_rfErrs = 0U; + m_rfBits = 1U; + m_rfTimeoutTimer.start(); + m_rfState = RS_RF_AUDIO; + + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; + m_rssiCount = 1U; +#if defined(DUMP_YSF) + openFile(); +#endif + m_display->writeFusion((char*)m_rfSource, (char*)m_rfDest, dgid, "R", " "); + LogMessage("YSF, received RF header from %10.10s to DG-ID %u", m_rfSource, dgid); + + CSync::addYSFSync(data + 2U); + + CYSFFICH fich = m_lastFICH; + + fich.encode(data + 2U); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetwork(data, m_rfFrames % 128U); + +#if defined(DUMP_YSF) + writeFile(data + 2U); +#endif + if (m_duplex) { + fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); + fich.setDev(m_lowDeviation); + fich.encode(data + 2U); + writeQueueRF(data); + } + + m_rfFrames++; + + m_display->writeFusionRSSI(m_rssi); + + return true; + } + } else if (valid && fi == YSF_FI_TERMINATOR) { + if (m_rfState == RS_RF_REJECTED) { + m_rfPayload.reset(); + m_rfSource = NULL; + m_rfDest = NULL; + m_rfState = RS_RF_LISTENING; + } else if (m_rfState == RS_RF_AUDIO) { + m_rfPayload.processHeaderData(data + 2U); + + CSync::addYSFSync(data + 2U); + + CYSFFICH fich = m_lastFICH; + + fich.encode(data + 2U); + + data[0U] = TAG_EOT; + data[1U] = 0x00U; + + writeNetwork(data, m_rfFrames % 128U); +#if defined(DUMP_YSF) + writeFile(data + 2U); +#endif + if (m_duplex) { + fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); + fich.setDev(m_lowDeviation); + fich.encode(data + 2U); + writeQueueRF(data); + } + + m_rfFrames++; + + if (m_rssi != 0U) + LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + else + LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + + writeEndRF(); + } + } else { + if (m_rfState == RS_RF_AUDIO) { + // If valid is false, update the m_lastFICH for this transmission + if (!valid) { + // XXX Check these values + m_lastFICH.setFT(0U); + m_lastFICH.setFN(0U); + } + + CSync::addYSFSync(data + 2U); + + CYSFFICH fich = m_lastFICH; + + unsigned char fn = fich.getFN(); + unsigned char ft = fich.getFT(); + + if (fn == 0U && ft == 1U) { + // The first packet after the header is odd + m_rfPayload.processVoiceFRModeData(data + 2U); + unsigned int errors = m_rfPayload.processVoiceFRModeAudio2(data + 2U); + m_rfErrs += errors; + m_rfBits += 288U; + m_display->writeFusionBER(float(errors) / 2.88F); + LogDebug("YSF, V Mode 3, seq %u, AMBE FEC %u/288 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 2.88F); + } else { + unsigned int errors = m_rfPayload.processVoiceFRModeAudio5(data + 2U); + m_rfErrs += errors; + m_rfBits += 720U; + m_display->writeFusionBER(float(errors) / 7.2F); + LogDebug("YSF, V Mode 3, seq %u, AMBE FEC %u/720 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 7.2F); + } + + fich.encode(data + 2U); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetwork(data, m_rfFrames % 128U); + + if (m_duplex) { + fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); + fich.setDev(m_lowDeviation); + fich.encode(data + 2U); + writeQueueRF(data); + } + +#if defined(DUMP_YSF) + writeFile(data + 2U); +#endif + m_rfFrames++; + + m_display->writeFusionRSSI(m_rssi); + + return true; + } + } + + return false; +} + +bool CYSFControl::processDNData(bool valid, unsigned char *data) +{ + unsigned char fi = m_lastFICH.getFI(); + unsigned char dgid = m_lastFICH.getDGId(); + + if (valid && fi == YSF_FI_HEADER) { + if (m_rfState == RS_RF_LISTENING) { + bool valid = m_rfPayload.processHeaderData(data + 2U); + if (!valid) + return false; + + m_rfSource = m_rfPayload.getSource(); + + if (m_selfOnly) { + bool ret = checkCallsign(m_rfSource); + if (!ret) { + LogMessage("YSF, invalid access attempt from %10.10s to DG-ID %u", m_rfSource, dgid); + m_rfState = RS_RF_REJECTED; + return false; + } + } + + unsigned char cm = m_lastFICH.getCM(); + if (cm == YSF_CM_GROUP1 || cm == YSF_CM_GROUP2) + m_rfDest = (unsigned char*)"ALL "; + else + m_rfDest = m_rfPayload.getDest(); + + m_rfFrames = 0U; + m_rfErrs = 0U; + m_rfBits = 1U; + m_rfTimeoutTimer.start(); + m_rfState = RS_RF_AUDIO; + + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; + m_rssiCount = 1U; +#if defined(DUMP_YSF) + openFile(); +#endif + m_display->writeFusion((char*)m_rfSource, (char*)m_rfDest, dgid, "R", " "); + LogMessage("YSF, received RF header from %10.10s to DG-ID %u", m_rfSource, dgid); + + CSync::addYSFSync(data + 2U); + + CYSFFICH fich = m_lastFICH; + + fich.encode(data + 2U); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetwork(data, m_rfFrames % 128U); + +#if defined(DUMP_YSF) + writeFile(data + 2U); +#endif + if (m_duplex) { + fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); + fich.setDev(m_lowDeviation); + fich.encode(data + 2U); + writeQueueRF(data); + } + + m_rfFrames++; + + m_display->writeFusionRSSI(m_rssi); + + return true; + } + } else if (valid && fi == YSF_FI_TERMINATOR) { + if (m_rfState == RS_RF_REJECTED) { + m_rfPayload.reset(); + m_rfSource = NULL; + m_rfDest = NULL; + m_rfState = RS_RF_LISTENING; + } else if (m_rfState == RS_RF_AUDIO) { + m_rfPayload.processHeaderData(data + 2U); + + CSync::addYSFSync(data + 2U); + + CYSFFICH fich = m_lastFICH; + + fich.encode(data + 2U); + + data[0U] = TAG_EOT; + data[1U] = 0x00U; + + writeNetwork(data, m_rfFrames % 128U); +#if defined(DUMP_YSF) + writeFile(data + 2U); +#endif + if (m_duplex) { + fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); + fich.setDev(m_lowDeviation); + fich.encode(data + 2U); + writeQueueRF(data); + } + + m_rfFrames++; + + if (m_rssi != 0U) + LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + else + LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + + writeEndRF(); + } + } else { + if (m_rfState == RS_RF_AUDIO) { + // If valid is false, update the m_lastFICH for this transmission + if (!valid) { + unsigned char ft = m_lastFICH.getFT(); + unsigned char fn = m_lastFICH.getFN() + 1U; + + if (fn > ft) + fn = 0U; + + m_lastFICH.setFN(fn); + } + + CSync::addYSFSync(data + 2U); + + unsigned char fn = m_lastFICH.getFN(); + unsigned char dt = m_lastFICH.getDT(); + + switch (dt) { + case YSF_DT_VD_MODE1: { + m_rfPayload.processVDMode1Data(data + 2U, fn); + unsigned int errors = m_rfPayload.processVDMode1Audio(data + 2U); + m_rfErrs += errors; + m_rfBits += 235U; + m_display->writeFusionBER(float(errors) / 2.35F); + LogDebug("YSF, V/D Mode 1, seq %u, AMBE FEC %u/235 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 2.35F); + } + break; + + case YSF_DT_VD_MODE2: { + m_rfPayload.processVDMode2Data(data + 2U, fn); + unsigned int errors = m_rfPayload.processVDMode2Audio(data + 2U); + m_rfErrs += errors; + m_rfBits += 405U; + m_display->writeFusionBER(float(errors) / 4.05F); + LogDebug("YSF, V/D Mode 2, seq %u, Repetition FEC %u/405 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 4.05F); + } + break; + + default: + break; + } + + CYSFFICH fich = m_lastFICH; + + fich.encode(data + 2U); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetwork(data, m_rfFrames % 128U); + + if (m_duplex) { + fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); + fich.setDev(m_lowDeviation); + fich.encode(data + 2U); + writeQueueRF(data); + } + +#if defined(DUMP_YSF) + writeFile(data + 2U); +#endif + m_rfFrames++; + + m_display->writeFusionRSSI(m_rssi); + + return true; + } else if (valid && m_rfState == RS_RF_LISTENING) { + // Only use clean frames for late entry. + unsigned char fn = m_lastFICH.getFN(); + unsigned char dt = m_lastFICH.getDT(); + + switch (dt) { + case YSF_DT_VD_MODE1: + valid = m_rfPayload.processVDMode1Data(data + 2U, fn); + break; + + case YSF_DT_VD_MODE2: + valid = m_rfPayload.processVDMode2Data(data + 2U, fn); + break; + + default: + valid = false; + break; + } + + if (!valid) + return false; + + unsigned char cm = m_lastFICH.getCM(); + if (cm == YSF_CM_GROUP1 || cm == YSF_CM_GROUP2) + m_rfDest = (unsigned char*)"ALL "; + else + m_rfDest = m_rfPayload.getDest(); + + m_rfSource = m_rfPayload.getSource(); + + if (m_rfSource == NULL || m_rfDest == NULL) + return false; + + if (m_selfOnly) { + bool ret = checkCallsign(m_rfSource); + if (!ret) { + LogMessage("YSF, invalid access attempt from %10.10s to DG-ID %u", m_rfSource, dgid); + m_rfState = RS_RF_REJECTED; + return false; + } + } + + m_rfFrames = 0U; + m_rfErrs = 0U; + m_rfBits = 1U; + m_rfTimeoutTimer.start(); + m_rfState = RS_RF_AUDIO; + + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; + m_rssiCount = 1U; +#if defined(DUMP_YSF) + openFile(); +#endif + // Build a new header and transmit it + unsigned char buffer[YSF_FRAME_LENGTH_BYTES + 2U]; + + CSync::addYSFSync(buffer + 2U); + + CYSFFICH fich = m_lastFICH; + fich.setFI(YSF_FI_HEADER); + fich.encode(buffer + 2U); + + unsigned char csd1[20U], csd2[20U]; + memcpy(csd1 + YSF_CALLSIGN_LENGTH, m_rfSource, YSF_CALLSIGN_LENGTH); + memset(csd2, ' ', YSF_CALLSIGN_LENGTH + YSF_CALLSIGN_LENGTH); + + if (cm == YSF_CM_GROUP1 || cm == YSF_CM_GROUP2) + memset(csd1 + 0U, '*', YSF_CALLSIGN_LENGTH); + else + memcpy(csd1 + 0U, m_rfDest, YSF_CALLSIGN_LENGTH); + + CYSFPayload payload; + payload.writeHeader(buffer + 2U, csd1, csd2); + + buffer[0U] = TAG_DATA; + buffer[1U] = 0x00U; + + writeNetwork(buffer, m_rfFrames % 128U); + + if (m_duplex) { + fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); + fich.setDev(m_lowDeviation); + fich.encode(buffer + 2U); + writeQueueRF(buffer); + } + +#if defined(DUMP_YSF) + writeFile(buffer + 2U); +#endif + m_display->writeFusion((char*)m_rfSource, (char*)m_rfDest, dgid, "R", " "); + LogMessage("YSF, received RF late entry from %10.10s to DG-ID %u", m_rfSource, dgid); + + CSync::addYSFSync(data + 2U); + + fich = m_lastFICH; + + fich.encode(data + 2U); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetwork(data, m_rfFrames % 128U); + + if (m_duplex) { + fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); + fich.setDev(m_lowDeviation); + fich.encode(data + 2U); + writeQueueRF(data); + } + +#if defined(DUMP_YSF) + writeFile(data + 2U); +#endif + m_rfFrames++; + + m_display->writeFusionRSSI(m_rssi); + + return true; + } + } + + return false; +} + +bool CYSFControl::processFRData(bool valid, unsigned char *data) +{ + unsigned char fi = m_lastFICH.getFI(); + unsigned char dgid = m_lastFICH.getDGId(); + + if (valid && fi == YSF_FI_HEADER) { + if (m_rfState == RS_RF_LISTENING) { + valid = m_rfPayload.processHeaderData(data + 2U); + if (!valid) + return false; + + m_rfSource = m_rfPayload.getSource(); + + if (m_selfOnly) { + bool ret = checkCallsign(m_rfSource); + if (!ret) { + LogMessage("YSF, invalid access attempt from %10.10s to DG-ID %u", m_rfSource, dgid); + m_rfState = RS_RF_REJECTED; + return false; + } + } + + unsigned char cm = m_lastFICH.getCM(); + if (cm == YSF_CM_GROUP1 || cm == YSF_CM_GROUP2) + m_rfDest = (unsigned char*)"ALL "; + else + m_rfDest = m_rfPayload.getDest(); + + m_rfFrames = 0U; + m_rfState = RS_RF_DATA; + + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; + m_rssiCount = 1U; +#if defined(DUMP_YSF) + openFile(); +#endif + m_display->writeFusion((char*)m_rfSource, (char*)m_rfDest, dgid, "R", " "); + LogMessage("YSF, received RF header from %10.10s to DG-ID %u", m_rfSource, dgid); + + CSync::addYSFSync(data + 2U); + + CYSFFICH fich = m_lastFICH; + + fich.encode(data + 2U); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetwork(data, m_rfFrames % 128U); +#if defined(DUMP_YSF) + writeFile(data + 2U); +#endif + if (m_duplex) { + fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); + fich.setDev(m_lowDeviation); + fich.encode(data + 2U); + writeQueueRF(data); + } + + m_rfFrames++; + + m_display->writeFusionRSSI(m_rssi); + + return true; + } + } else if (valid && fi == YSF_FI_TERMINATOR) { + if (m_rfState == RS_RF_REJECTED) { + m_rfPayload.reset(); + m_rfSource = NULL; + m_rfDest = NULL; + m_rfState = RS_RF_LISTENING; + } else if (m_rfState == RS_RF_DATA) { + m_rfPayload.processHeaderData(data + 2U); + + CSync::addYSFSync(data + 2U); + + CYSFFICH fich = m_lastFICH; + + fich.encode(data + 2U); + + data[0U] = TAG_EOT; + data[1U] = 0x00U; + + writeNetwork(data, m_rfFrames % 128U); +#if defined(DUMP_YSF) + writeFile(data + 2U); +#endif + if (m_duplex) { + fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); + fich.setDev(m_lowDeviation); + fich.encode(data + 2U); + writeQueueRF(data); + } + + m_rfFrames++; + + if (m_rssi != 0U) + LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, RSSI: -%u/-%u/-%u dBm", m_rfSource, dgid, float(m_rfFrames) / 10.0F, m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + else + LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds", m_rfSource, dgid, float(m_rfFrames) / 10.0F); + + writeEndRF(); + } + } else { + if (m_rfState == RS_RF_DATA) { + // If valid is false, update the m_lastFICH for this transmission + if (!valid) { + unsigned char ft = m_lastFICH.getFT(); + unsigned char fn = m_lastFICH.getFN() + 1U; + + if (fn > ft) + fn = 0U; + + m_lastFICH.setFN(fn); + } + + CSync::addYSFSync(data + 2U); + + unsigned char fn = m_lastFICH.getFN(); + + m_rfPayload.processDataFRModeData(data + 2U, fn); + + CYSFFICH fich = m_lastFICH; + + fich.encode(data + 2U); + + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeNetwork(data, m_rfFrames % 128U); + + if (m_duplex) { + fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); + fich.setDev(m_lowDeviation); + fich.encode(data + 2U); + writeQueueRF(data); + } + +#if defined(DUMP_YSF) + writeFile(data + 2U); +#endif + m_rfFrames++; + + m_display->writeFusionRSSI(m_rssi); + + return true; + } + } + + return false; +} + +unsigned int CYSFControl::readModem(unsigned char* data) +{ + assert(data != NULL); + + if (m_queue.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_queue.getData(&len, 1U); + + m_queue.getData(data, len); + + return len; +} + +void CYSFControl::writeEndRF() +{ + m_rfState = RS_RF_LISTENING; + + m_rfTimeoutTimer.stop(); + m_rfPayload.reset(); + + // These variables are free'd by YSFPayload + m_rfSource = NULL; + m_rfDest = NULL; + + if (m_netState == RS_NET_IDLE) { + m_display->clearFusion(); + + if (m_network != NULL) + m_network->reset(); + } + +#if defined(DUMP_YSF) + closeFile(); +#endif +} + +void CYSFControl::writeEndNet() +{ + m_netState = RS_NET_IDLE; + + m_netTimeoutTimer.stop(); + m_networkWatchdog.stop(); + m_packetTimer.stop(); + + m_netPayload.reset(); + + m_display->clearFusion(); + + if (m_network != NULL) + m_network->reset(); +} + +void CYSFControl::writeNetwork() +{ + unsigned char data[200U]; + unsigned int length = m_network->read(data); + if (length == 0U) + return; + + if (!m_enabled) + return; + + if (m_rfState != RS_RF_LISTENING && m_netState == RS_NET_IDLE) + return; + + m_networkWatchdog.start(); + + bool gateway = ::memcmp(data + 4U, m_callsign, YSF_CALLSIGN_LENGTH) == 0; + + unsigned char n = (data[34U] & 0xFEU) >> 1; + bool end = (data[34U] & 0x01U) == 0x01U; + + CYSFFICH fich; + bool valid = fich.decode(data + 35U); + + unsigned char dgid = 0U; + if (valid) + dgid = fich.getDGId(); + + if (!m_netTimeoutTimer.isRunning()) { + if (end) + return; + + ::memcpy(m_netSource, data + 14U, YSF_CALLSIGN_LENGTH); + ::memcpy(m_netDest, data + 24U, YSF_CALLSIGN_LENGTH); + + if (::memcmp(m_netSource, " ", 10U) != 0 && ::memcmp(m_netDest, " ", 10U) != 0) { + m_display->writeFusion((char*)m_netSource, (char*)m_netDest, dgid, "N", (char*)(data + 4U)); + LogMessage("YSF, received network data from %10.10s to DG-ID %u at %10.10s", m_netSource, dgid, data + 4U); + } + + m_netTimeoutTimer.start(); + m_netPayload.reset(); + m_packetTimer.start(); + m_elapsed.start(); + m_netState = RS_NET_AUDIO; + m_netFrames = 0U; + m_netLost = 0U; + m_netErrs = 0U; + m_netBits = 1U; + m_netN = 0U; + } else { + // Check for duplicate frames, if we can + if (m_netN == n) + return; + } + + data[33U] = end ? TAG_EOT : TAG_DATA; + data[34U] = 0x00U; + + if (valid) { + unsigned char dt = fich.getDT(); + unsigned char fn = fich.getFN(); + unsigned char ft = fich.getFT(); + unsigned char fi = fich.getFI(); + unsigned char cm = fich.getCM(); + + if (::memcmp(m_netDest, " ", YSF_CALLSIGN_LENGTH) == 0) { + if (cm == YSF_CM_GROUP1 || cm == YSF_CM_GROUP2) + ::memcpy(m_netDest, "ALL ", YSF_CALLSIGN_LENGTH); + } + + if (m_remoteGateway) { + fich.setVoIP(false); + fich.setMR(YSF_MR_DIRECT); + } else { + fich.setVoIP(true); + fich.setMR(YSF_MR_BUSY); + } + + fich.setDev(m_lowDeviation); + fich.encode(data + 35U); + + // Set the downlink callsign + switch (fi) { + case YSF_FI_HEADER: { + bool ok = m_netPayload.processHeaderData(data + 35U); + if (ok) + processNetCallsigns(data, dgid); + } + break; + + case YSF_FI_TERMINATOR: + m_netPayload.processHeaderData(data + 35U); + break; + + case YSF_FI_COMMUNICATIONS: + switch (dt) { + case YSF_DT_VD_MODE1: { + bool ok = m_netPayload.processVDMode1Data(data + 35U, fn, gateway); + if (ok) + processNetCallsigns(data, dgid); + + unsigned int errors = m_netPayload.processVDMode1Audio(data + 35U); + m_netErrs += errors; + m_netBits += 235U; + } + break; + + case YSF_DT_VD_MODE2: { + bool ok = m_netPayload.processVDMode2Data(data + 35U, fn, gateway); + if (ok) + processNetCallsigns(data, dgid); + + unsigned int errors = m_netPayload.processVDMode2Audio(data + 35U); + m_netErrs += errors; + m_netBits += 135U; + } + break; + + case YSF_DT_DATA_FR_MODE: + m_netPayload.processDataFRModeData(data + 35U, fn, gateway); + break; + + case YSF_DT_VOICE_FR_MODE: + if (fn == 0U && ft == 1U) { + // The first packet after the header is odd + m_netPayload.processVoiceFRModeData(data + 35U); + unsigned int errors = m_netPayload.processVoiceFRModeAudio2(data + 35U); + m_netErrs += errors; + m_netBits += 288U; + } else { + unsigned int errors = m_netPayload.processVoiceFRModeAudio5(data + 35U); + m_netErrs += errors; + m_netBits += 720U; + } + break; + + default: + break; + } + break; + + default: + break; + } + } + + writeQueueNet(data + 33U); + + m_packetTimer.start(); + m_netFrames++; + m_netN = n; + + if (end) { + LogMessage("YSF, received network end of transmission from %10.10s to DG-ID %u, %.1f seconds, %u%% packet loss, BER: %.1f%%", m_netSource, dgid, float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); + writeEndNet(); + } +} + +void CYSFControl::clock(unsigned int ms) +{ + if (m_network != NULL) + writeNetwork(); + + m_rfTimeoutTimer.clock(ms); + m_netTimeoutTimer.clock(ms); + + if (m_netState == RS_NET_AUDIO) { + m_networkWatchdog.clock(ms); + + if (m_networkWatchdog.hasExpired()) { + LogMessage("YSF, network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%", float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); + writeEndNet(); + } + } +} + +void CYSFControl::writeQueueRF(const unsigned char *data) +{ + assert(data != NULL); + + if (m_netState != RS_NET_IDLE) + return; + + if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) + return; + + unsigned char len = YSF_FRAME_LENGTH_BYTES + 2U; + + unsigned int space = m_queue.freeSpace(); + if (space < (len + 1U)) { + LogError("YSF, overflow in the System Fusion RF queue"); + return; + } + + m_queue.addData(&len, 1U); + + m_queue.addData(data, len); +} + +void CYSFControl::writeQueueNet(const unsigned char *data) +{ + assert(data != NULL); + + if (m_netTimeoutTimer.isRunning() && m_netTimeoutTimer.hasExpired()) + return; + + unsigned char len = YSF_FRAME_LENGTH_BYTES + 2U; + + unsigned int space = m_queue.freeSpace(); + if (space < (len + 1U)) { + LogError("YSF, overflow in the System Fusion RF queue"); + return; + } + + m_queue.addData(&len, 1U); + + m_queue.addData(data, len); +} + +void CYSFControl::writeNetwork(const unsigned char *data, unsigned int count) +{ + assert(data != NULL); + + if (m_network == NULL) + return; + + if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) + return; + + m_network->write(m_rfSource, m_rfDest, data + 2U, count, data[0U] == TAG_EOT); +} + +bool CYSFControl::openFile() +{ + if (m_fp != NULL) + return true; + + time_t t; + ::time(&t); + + struct tm* tm = ::localtime(&t); + + char name[100U]; + ::sprintf(name, "YSF_%04d%02d%02d_%02d%02d%02d.ambe", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + m_fp = ::fopen(name, "wb"); + if (m_fp == NULL) + return false; + + ::fwrite("YSF", 1U, 3U, m_fp); + + return true; +} + +bool CYSFControl::writeFile(const unsigned char* data) +{ + if (m_fp == NULL) + return false; + + ::fwrite(data, 1U, YSF_FRAME_LENGTH_BYTES, m_fp); + + return true; +} + +void CYSFControl::closeFile() +{ + if (m_fp != NULL) { + ::fclose(m_fp); + m_fp = NULL; + } +} + +bool CYSFControl::checkCallsign(const unsigned char* callsign) const +{ + return ::memcmp(callsign, m_selfCallsign, ::strlen((char*)m_selfCallsign)) == 0; +} + +void CYSFControl::processNetCallsigns(const unsigned char* data, unsigned char dgid) +{ + assert(data != NULL); + + if (::memcmp(m_netSource, " ", 10U) == 0 || ::memcmp(m_netDest, " ", 10U) == 0) { + if (::memcmp(m_netSource, " ", YSF_CALLSIGN_LENGTH) == 0) { + unsigned char* source = m_netPayload.getSource(); + if (source != NULL) + ::memcpy(m_netSource, source, YSF_CALLSIGN_LENGTH); + } + + if (::memcmp(m_netDest, " ", YSF_CALLSIGN_LENGTH) == 0) { + unsigned char* dest = m_netPayload.getDest(); + if (dest != NULL) + ::memcpy(m_netDest, dest, YSF_CALLSIGN_LENGTH); + } + + if (::memcmp(m_netSource, " ", 10U) != 0 && ::memcmp(m_netDest, " ", 10U) != 0) { + m_display->writeFusion((char*)m_netSource, (char*)m_netDest, dgid, "N", (char*)(data + 4U)); + LogMessage("YSF, received network data from %10.10s to DG-ID %u at %10.10s", m_netSource, dgid, data + 4U); + } + } +} + +bool CYSFControl::isBusy() const +{ + return m_rfState != RS_RF_LISTENING || m_netState != RS_NET_IDLE; +} + +void CYSFControl::enable(bool enabled) +{ + if (!enabled && m_enabled) { + m_queue.clear(); + + // Reset the RF section + m_rfState = RS_RF_LISTENING; + + m_rfTimeoutTimer.stop(); + m_rfPayload.reset(); + + // These variables are free'd by YSFPayload + m_rfSource = NULL; + m_rfDest = NULL; + + // Reset the networking section + m_netState = RS_NET_IDLE; + + m_netTimeoutTimer.stop(); + m_networkWatchdog.stop(); + m_packetTimer.stop(); + + m_netPayload.reset(); + } + + m_enabled = enabled; +} diff --git a/YSFControl.h b/YSFControl.h index a18222d..e1076f3 100644 --- a/YSFControl.h +++ b/YSFControl.h @@ -1,112 +1,112 @@ -/* - * Copyright (C) 2015-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(YSFControl_H) -#define YSFControl_H - -#include "RSSIInterpolator.h" -#include "YSFNetwork.h" -#include "YSFDefines.h" -#include "YSFPayload.h" -#include "RingBuffer.h" -#include "StopWatch.h" -#include "YSFFICH.h" -#include "Display.h" -#include "Defines.h" -#include "Timer.h" -#include "Modem.h" - -#include - -class CYSFControl { -public: - CYSFControl(const std::string& callsign, bool selfOnly, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool lowDeviation, bool remoteGateway, CRSSIInterpolator* rssiMapper); - ~CYSFControl(); - - 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: - unsigned char* m_callsign; - unsigned char* m_selfCallsign; - bool m_selfOnly; - CYSFNetwork* m_network; - CDisplay* m_display; - bool m_duplex; - bool m_lowDeviation; - bool m_remoteGateway; - CRingBuffer m_queue; - RPT_RF_STATE m_rfState; - RPT_NET_STATE m_netState; - CTimer m_rfTimeoutTimer; - CTimer m_netTimeoutTimer; - CTimer m_packetTimer; - CTimer m_networkWatchdog; - CStopWatch m_elapsed; - unsigned int m_rfFrames; - unsigned int m_netFrames; - unsigned int m_netLost; - unsigned int m_rfErrs; - unsigned int m_rfBits; - unsigned int m_netErrs; - unsigned int m_netBits; - unsigned char* m_rfSource; - unsigned char* m_rfDest; - unsigned char* m_netSource; - unsigned char* m_netDest; - CYSFFICH m_lastFICH; - unsigned char m_netN; - CYSFPayload m_rfPayload; - CYSFPayload m_netPayload; - CRSSIInterpolator* m_rssiMapper; - unsigned char m_rssi; - unsigned char m_maxRSSI; - unsigned char m_minRSSI; - unsigned int m_aveRSSI; - unsigned int m_rssiCount; - bool m_enabled; - FILE* m_fp; - - bool processVWData(bool valid, unsigned char *data); - bool processDNData(bool valid, unsigned char *data); - bool processFRData(bool valid, unsigned char *data); - - void writeQueueRF(const unsigned char* data); - void writeQueueNet(const unsigned char* data); - void writeNetwork(const unsigned char* data, unsigned int count); - void writeNetwork(); - - void writeEndRF(); - void writeEndNet(); - - bool openFile(); - bool writeFile(const unsigned char* data); - void closeFile(); - - bool checkCallsign(const unsigned char* callsign) const; - void processNetCallsigns(const unsigned char* data, unsigned char dgid); -}; - -#endif +/* + * Copyright (C) 2015-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(YSFControl_H) +#define YSFControl_H + +#include "RSSIInterpolator.h" +#include "YSFNetwork.h" +#include "YSFDefines.h" +#include "YSFPayload.h" +#include "RingBuffer.h" +#include "StopWatch.h" +#include "YSFFICH.h" +#include "Display.h" +#include "Defines.h" +#include "Timer.h" +#include "Modem.h" + +#include + +class CYSFControl { +public: + CYSFControl(const std::string& callsign, bool selfOnly, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool lowDeviation, bool remoteGateway, CRSSIInterpolator* rssiMapper); + ~CYSFControl(); + + 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: + unsigned char* m_callsign; + unsigned char* m_selfCallsign; + bool m_selfOnly; + CYSFNetwork* m_network; + CDisplay* m_display; + bool m_duplex; + bool m_lowDeviation; + bool m_remoteGateway; + CRingBuffer m_queue; + RPT_RF_STATE m_rfState; + RPT_NET_STATE m_netState; + CTimer m_rfTimeoutTimer; + CTimer m_netTimeoutTimer; + CTimer m_packetTimer; + CTimer m_networkWatchdog; + CStopWatch m_elapsed; + unsigned int m_rfFrames; + unsigned int m_netFrames; + unsigned int m_netLost; + unsigned int m_rfErrs; + unsigned int m_rfBits; + unsigned int m_netErrs; + unsigned int m_netBits; + unsigned char* m_rfSource; + unsigned char* m_rfDest; + unsigned char* m_netSource; + unsigned char* m_netDest; + CYSFFICH m_lastFICH; + unsigned char m_netN; + CYSFPayload m_rfPayload; + CYSFPayload m_netPayload; + CRSSIInterpolator* m_rssiMapper; + unsigned char m_rssi; + unsigned char m_maxRSSI; + unsigned char m_minRSSI; + unsigned int m_aveRSSI; + unsigned int m_rssiCount; + bool m_enabled; + FILE* m_fp; + + bool processVWData(bool valid, unsigned char *data); + bool processDNData(bool valid, unsigned char *data); + bool processFRData(bool valid, unsigned char *data); + + void writeQueueRF(const unsigned char* data); + void writeQueueNet(const unsigned char* data); + void writeNetwork(const unsigned char* data, unsigned int count); + void writeNetwork(); + + void writeEndRF(); + void writeEndNet(); + + bool openFile(); + bool writeFile(const unsigned char* data); + void closeFile(); + + bool checkCallsign(const unsigned char* callsign) const; + void processNetCallsigns(const unsigned char* data, unsigned char dgid); +}; + +#endif diff --git a/YSFConvolution.cpp b/YSFConvolution.cpp index 23b117e..f1f8a7a 100644 --- a/YSFConvolution.cpp +++ b/YSFConvolution.cpp @@ -1,141 +1,141 @@ -/* - * Copyright (C) 2009-2016 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 "YSFConvolution.h" - -#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]) - -const uint8_t BRANCH_TABLE1[] = {0U, 0U, 0U, 0U, 1U, 1U, 1U, 1U}; -const uint8_t BRANCH_TABLE2[] = {0U, 1U, 1U, 0U, 0U, 1U, 1U, 0U}; - -const unsigned int NUM_OF_STATES_D2 = 8U; -const unsigned int NUM_OF_STATES = 16U; -const uint32_t M = 2U; -const unsigned int K = 5U; - -CYSFConvolution::CYSFConvolution() : -m_metrics1(NULL), -m_metrics2(NULL), -m_oldMetrics(NULL), -m_newMetrics(NULL), -m_decisions(NULL), -m_dp(NULL) -{ - m_metrics1 = new uint16_t[16U]; - m_metrics2 = new uint16_t[16U]; - m_decisions = new uint64_t[180U]; -} - -CYSFConvolution::~CYSFConvolution() -{ - delete[] m_metrics1; - delete[] m_metrics2; - delete[] m_decisions; -} - -void CYSFConvolution::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 CYSFConvolution::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 = (BRANCH_TABLE1[i] ^ s0) + (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) <= 180); - - uint16_t* tmp = m_oldMetrics; - m_oldMetrics = m_newMetrics; - m_newMetrics = tmp; -} - -void CYSFConvolution::chainback(unsigned char* out, unsigned int nBits) -{ - assert(out != NULL); - - 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); - } -} - -void CYSFConvolution::encode(const unsigned char* in, unsigned char* out, unsigned int nBits) const -{ - assert(in != NULL); - assert(out != NULL); - 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++; - } -} +/* + * Copyright (C) 2009-2016 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 "YSFConvolution.h" + +#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]) + +const uint8_t BRANCH_TABLE1[] = {0U, 0U, 0U, 0U, 1U, 1U, 1U, 1U}; +const uint8_t BRANCH_TABLE2[] = {0U, 1U, 1U, 0U, 0U, 1U, 1U, 0U}; + +const unsigned int NUM_OF_STATES_D2 = 8U; +const unsigned int NUM_OF_STATES = 16U; +const uint32_t M = 2U; +const unsigned int K = 5U; + +CYSFConvolution::CYSFConvolution() : +m_metrics1(NULL), +m_metrics2(NULL), +m_oldMetrics(NULL), +m_newMetrics(NULL), +m_decisions(NULL), +m_dp(NULL) +{ + m_metrics1 = new uint16_t[16U]; + m_metrics2 = new uint16_t[16U]; + m_decisions = new uint64_t[180U]; +} + +CYSFConvolution::~CYSFConvolution() +{ + delete[] m_metrics1; + delete[] m_metrics2; + delete[] m_decisions; +} + +void CYSFConvolution::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 CYSFConvolution::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 = (BRANCH_TABLE1[i] ^ s0) + (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) <= 180); + + uint16_t* tmp = m_oldMetrics; + m_oldMetrics = m_newMetrics; + m_newMetrics = tmp; +} + +void CYSFConvolution::chainback(unsigned char* out, unsigned int nBits) +{ + assert(out != NULL); + + 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); + } +} + +void CYSFConvolution::encode(const unsigned char* in, unsigned char* out, unsigned int nBits) const +{ + assert(in != NULL); + assert(out != NULL); + 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++; + } +} diff --git a/YSFConvolution.h b/YSFConvolution.h index 457103c..23127e9 100644 --- a/YSFConvolution.h +++ b/YSFConvolution.h @@ -1,45 +1,45 @@ -/* - * Copyright (C) 2015,2016 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(YSFConvolution_H) -#define YSFConvolution_H - -#include - -class CYSFConvolution { -public: - CYSFConvolution(); - ~CYSFConvolution(); - - void start(); - void decode(uint8_t s0, uint8_t s1); - void chainback(unsigned char* out, unsigned int nBits); - - void encode(const unsigned char* in, unsigned char* out, unsigned int nBits) 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; -}; - -#endif - +/* + * Copyright (C) 2015,2016 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(YSFConvolution_H) +#define YSFConvolution_H + +#include + +class CYSFConvolution { +public: + CYSFConvolution(); + ~CYSFConvolution(); + + void start(); + void decode(uint8_t s0, uint8_t s1); + void chainback(unsigned char* out, unsigned int nBits); + + void encode(const unsigned char* in, unsigned char* out, unsigned int nBits) 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; +}; + +#endif + diff --git a/YSFDefines.h b/YSFDefines.h index afbaf83..0efccc8 100644 --- a/YSFDefines.h +++ b/YSFDefines.h @@ -1,53 +1,53 @@ -/* - * Copyright (C) 2015,2016,2017 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(YSFDefines_H) -#define YSFDefines_H - -const unsigned int YSF_FRAME_LENGTH_BYTES = 120U; - -const unsigned char YSF_SYNC_BYTES[] = {0xD4U, 0x71U, 0xC9U, 0x63U, 0x4DU}; -const unsigned int YSF_SYNC_LENGTH_BYTES = 5U; - -const unsigned int YSF_FICH_LENGTH_BYTES = 25U; - -const unsigned char YSF_SYNC_OK = 0x01U; - -const unsigned int YSF_CALLSIGN_LENGTH = 10U; - -const unsigned int YSF_FRAME_TIME = 100U; - -const unsigned char YSF_FI_HEADER = 0x00U; -const unsigned char YSF_FI_COMMUNICATIONS = 0x01U; -const unsigned char YSF_FI_TERMINATOR = 0x02U; -const unsigned char YSF_FI_TEST = 0x03U; - -const unsigned char YSF_DT_VD_MODE1 = 0x00U; -const unsigned char YSF_DT_DATA_FR_MODE = 0x01U; -const unsigned char YSF_DT_VD_MODE2 = 0x02U; -const unsigned char YSF_DT_VOICE_FR_MODE = 0x03U; - -const unsigned char YSF_CM_GROUP1 = 0x00U; -const unsigned char YSF_CM_GROUP2 = 0x01U; -const unsigned char YSF_CM_INDIVIDUAL = 0x03U; - -const unsigned char YSF_MR_DIRECT = 0x00U; -const unsigned char YSF_MR_NOT_BUSY = 0x01U; -const unsigned char YSF_MR_BUSY = 0x02U; - -#endif +/* + * Copyright (C) 2015,2016,2017 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(YSFDefines_H) +#define YSFDefines_H + +const unsigned int YSF_FRAME_LENGTH_BYTES = 120U; + +const unsigned char YSF_SYNC_BYTES[] = {0xD4U, 0x71U, 0xC9U, 0x63U, 0x4DU}; +const unsigned int YSF_SYNC_LENGTH_BYTES = 5U; + +const unsigned int YSF_FICH_LENGTH_BYTES = 25U; + +const unsigned char YSF_SYNC_OK = 0x01U; + +const unsigned int YSF_CALLSIGN_LENGTH = 10U; + +const unsigned int YSF_FRAME_TIME = 100U; + +const unsigned char YSF_FI_HEADER = 0x00U; +const unsigned char YSF_FI_COMMUNICATIONS = 0x01U; +const unsigned char YSF_FI_TERMINATOR = 0x02U; +const unsigned char YSF_FI_TEST = 0x03U; + +const unsigned char YSF_DT_VD_MODE1 = 0x00U; +const unsigned char YSF_DT_DATA_FR_MODE = 0x01U; +const unsigned char YSF_DT_VD_MODE2 = 0x02U; +const unsigned char YSF_DT_VOICE_FR_MODE = 0x03U; + +const unsigned char YSF_CM_GROUP1 = 0x00U; +const unsigned char YSF_CM_GROUP2 = 0x01U; +const unsigned char YSF_CM_INDIVIDUAL = 0x03U; + +const unsigned char YSF_MR_DIRECT = 0x00U; +const unsigned char YSF_MR_NOT_BUSY = 0x01U; +const unsigned char YSF_MR_BUSY = 0x02U; + +#endif diff --git a/YSFFICH.cpp b/YSFFICH.cpp index c1824fc..7227014 100644 --- a/YSFFICH.cpp +++ b/YSFFICH.cpp @@ -1,286 +1,286 @@ -/* - * Copyright (C) 2016,2017,2019,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. - */ - -#include "YSFConvolution.h" -#include "YSFDefines.h" -#include "Golay24128.h" -#include "YSFFICH.h" -#include "CRC.h" -#include "Log.h" - -#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]) - -const unsigned int INTERLEAVE_TABLE[] = { - 0U, 40U, 80U, 120U, 160U, - 2U, 42U, 82U, 122U, 162U, - 4U, 44U, 84U, 124U, 164U, - 6U, 46U, 86U, 126U, 166U, - 8U, 48U, 88U, 128U, 168U, - 10U, 50U, 90U, 130U, 170U, - 12U, 52U, 92U, 132U, 172U, - 14U, 54U, 94U, 134U, 174U, - 16U, 56U, 96U, 136U, 176U, - 18U, 58U, 98U, 138U, 178U, - 20U, 60U, 100U, 140U, 180U, - 22U, 62U, 102U, 142U, 182U, - 24U, 64U, 104U, 144U, 184U, - 26U, 66U, 106U, 146U, 186U, - 28U, 68U, 108U, 148U, 188U, - 30U, 70U, 110U, 150U, 190U, - 32U, 72U, 112U, 152U, 192U, - 34U, 74U, 114U, 154U, 194U, - 36U, 76U, 116U, 156U, 196U, - 38U, 78U, 118U, 158U, 198U}; - -CYSFFICH::CYSFFICH(const CYSFFICH& fich) : -m_fich(NULL) -{ - m_fich = new unsigned char[6U]; - - ::memcpy(m_fich, fich.m_fich, 6U); -} - -CYSFFICH::CYSFFICH() : -m_fich(NULL) -{ - m_fich = new unsigned char[6U]; - - memset(m_fich, 0x00U, 6U); -} - -CYSFFICH::~CYSFFICH() -{ - delete[] m_fich; -} - -bool CYSFFICH::decode(const unsigned char* bytes) -{ - assert(bytes != NULL); - - // Skip the sync bytes - bytes += YSF_SYNC_LENGTH_BYTES; - - CYSFConvolution viterbi; - viterbi.start(); - - // Deinterleave the FICH and send bits to the Viterbi decoder - for (unsigned int i = 0U; i < 100U; i++) { - unsigned int n = INTERLEAVE_TABLE[i]; - uint8_t s0 = READ_BIT1(bytes, n) ? 1U : 0U; - - n++; - uint8_t s1 = READ_BIT1(bytes, n) ? 1U : 0U; - - viterbi.decode(s0, s1); - } - - unsigned char output[13U]; - viterbi.chainback(output, 96U); - - unsigned int b0 = CGolay24128::decode24128(output + 0U); - unsigned int b1 = CGolay24128::decode24128(output + 3U); - unsigned int b2 = CGolay24128::decode24128(output + 6U); - unsigned int b3 = CGolay24128::decode24128(output + 9U); - - m_fich[0U] = (b0 >> 4) & 0xFFU; - m_fich[1U] = ((b0 << 4) & 0xF0U) | ((b1 >> 8) & 0x0FU); - m_fich[2U] = (b1 >> 0) & 0xFFU; - m_fich[3U] = (b2 >> 4) & 0xFFU; - m_fich[4U] = ((b2 << 4) & 0xF0U) | ((b3 >> 8) & 0x0FU); - m_fich[5U] = (b3 >> 0) & 0xFFU; - - return CCRC::checkCCITT162(m_fich, 6U); -} - -void CYSFFICH::encode(unsigned char* bytes) -{ - assert(bytes != NULL); - - // Skip the sync bytes - bytes += YSF_SYNC_LENGTH_BYTES; - - CCRC::addCCITT162(m_fich, 6U); - - unsigned int b0 = ((m_fich[0U] << 4) & 0xFF0U) | ((m_fich[1U] >> 4) & 0x00FU); - unsigned int b1 = ((m_fich[1U] << 8) & 0xF00U) | ((m_fich[2U] >> 0) & 0x0FFU); - unsigned int b2 = ((m_fich[3U] << 4) & 0xFF0U) | ((m_fich[4U] >> 4) & 0x00FU); - unsigned int b3 = ((m_fich[4U] << 8) & 0xF00U) | ((m_fich[5U] >> 0) & 0x0FFU); - - unsigned int c0 = CGolay24128::encode24128(b0); - unsigned int c1 = CGolay24128::encode24128(b1); - unsigned int c2 = CGolay24128::encode24128(b2); - unsigned int c3 = CGolay24128::encode24128(b3); - - unsigned char conv[13U]; - conv[0U] = (c0 >> 16) & 0xFFU; - conv[1U] = (c0 >> 8) & 0xFFU; - conv[2U] = (c0 >> 0) & 0xFFU; - conv[3U] = (c1 >> 16) & 0xFFU; - conv[4U] = (c1 >> 8) & 0xFFU; - conv[5U] = (c1 >> 0) & 0xFFU; - conv[6U] = (c2 >> 16) & 0xFFU; - conv[7U] = (c2 >> 8) & 0xFFU; - conv[8U] = (c2 >> 0) & 0xFFU; - conv[9U] = (c3 >> 16) & 0xFFU; - conv[10U] = (c3 >> 8) & 0xFFU; - conv[11U] = (c3 >> 0) & 0xFFU; - conv[12U] = 0x00U; - - CYSFConvolution convolution; - unsigned char convolved[25U]; - convolution.encode(conv, convolved, 100U); - - unsigned int j = 0U; - for (unsigned int i = 0U; i < 100U; i++) { - unsigned int n = INTERLEAVE_TABLE[i]; - - bool s0 = READ_BIT1(convolved, j) != 0U; - j++; - - bool s1 = READ_BIT1(convolved, j) != 0U; - j++; - - WRITE_BIT1(bytes, n, s0); - - n++; - WRITE_BIT1(bytes, n, s1); - } -} - -unsigned char CYSFFICH::getFI() const -{ - return (m_fich[0U] >> 6) & 0x03U; -} - -unsigned char CYSFFICH::getCM() const -{ - return (m_fich[0U] >> 2) & 0x03U; -} - -unsigned char CYSFFICH::getBN() const -{ - return m_fich[0U] & 0x03U; -} - -unsigned char CYSFFICH::getBT() const -{ - return (m_fich[1U] >> 6) & 0x03U; -} - -unsigned char CYSFFICH::getFN() const -{ - return (m_fich[1U] >> 3) & 0x07U; -} - -unsigned char CYSFFICH::getFT() const -{ - return m_fich[1U] & 0x07U; -} - -unsigned char CYSFFICH::getDT() const -{ - return m_fich[2U] & 0x03U; -} - -unsigned char CYSFFICH::getMR() const -{ - return (m_fich[2U] >> 3) & 0x03U; -} - -bool CYSFFICH::getDev() const -{ - return (m_fich[2U] & 0x40U) == 0x40U; -} - -unsigned char CYSFFICH::getDGId() const -{ - return m_fich[3U] & 0x7FU; -} - -void CYSFFICH::setFI(unsigned char fi) -{ - m_fich[0U] &= 0x3FU; - m_fich[0U] |= (fi << 6) & 0xC0U; -} - -void CYSFFICH::setBN(unsigned char bn) -{ - m_fich[0U] &= 0xFCU; - m_fich[0U] |= bn & 0x03U; -} - -void CYSFFICH::setBT(unsigned char bt) -{ - m_fich[1U] &= 0x3FU; - m_fich[1U] |= (bt << 6) & 0xC0U; -} - -void CYSFFICH::setFN(unsigned char fn) -{ - m_fich[1U] &= 0xC7U; - m_fich[1U] |= (fn << 3) & 0x38U; -} - -void CYSFFICH::setFT(unsigned char ft) -{ - m_fich[1U] &= 0xF8U; - m_fich[1U] |= ft & 0x07U; -} - -void CYSFFICH::setMR(unsigned char mr) -{ - m_fich[2U] &= 0xC7U; - m_fich[2U] |= (mr << 3) & 0x38U; -} - -void CYSFFICH::setVoIP(bool on) -{ - if (on) - m_fich[2U] |= 0x04U; - else - m_fich[2U] &= 0xFBU; -} - -void CYSFFICH::setDev(bool on) -{ - if (on) - m_fich[2U] |= 0x40U; - else - m_fich[2U] &= 0xBFU; -} - -void CYSFFICH::setDGId(unsigned char id) -{ - m_fich[3U] &= 0x80U; - m_fich[3U] |= id & 0x7FU; -} - -CYSFFICH& CYSFFICH::operator=(const CYSFFICH& fich) -{ - if (&fich != this) - ::memcpy(m_fich, fich.m_fich, 6U); - - return *this; -} +/* + * Copyright (C) 2016,2017,2019,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. + */ + +#include "YSFConvolution.h" +#include "YSFDefines.h" +#include "Golay24128.h" +#include "YSFFICH.h" +#include "CRC.h" +#include "Log.h" + +#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]) + +const unsigned int INTERLEAVE_TABLE[] = { + 0U, 40U, 80U, 120U, 160U, + 2U, 42U, 82U, 122U, 162U, + 4U, 44U, 84U, 124U, 164U, + 6U, 46U, 86U, 126U, 166U, + 8U, 48U, 88U, 128U, 168U, + 10U, 50U, 90U, 130U, 170U, + 12U, 52U, 92U, 132U, 172U, + 14U, 54U, 94U, 134U, 174U, + 16U, 56U, 96U, 136U, 176U, + 18U, 58U, 98U, 138U, 178U, + 20U, 60U, 100U, 140U, 180U, + 22U, 62U, 102U, 142U, 182U, + 24U, 64U, 104U, 144U, 184U, + 26U, 66U, 106U, 146U, 186U, + 28U, 68U, 108U, 148U, 188U, + 30U, 70U, 110U, 150U, 190U, + 32U, 72U, 112U, 152U, 192U, + 34U, 74U, 114U, 154U, 194U, + 36U, 76U, 116U, 156U, 196U, + 38U, 78U, 118U, 158U, 198U}; + +CYSFFICH::CYSFFICH(const CYSFFICH& fich) : +m_fich(NULL) +{ + m_fich = new unsigned char[6U]; + + ::memcpy(m_fich, fich.m_fich, 6U); +} + +CYSFFICH::CYSFFICH() : +m_fich(NULL) +{ + m_fich = new unsigned char[6U]; + + memset(m_fich, 0x00U, 6U); +} + +CYSFFICH::~CYSFFICH() +{ + delete[] m_fich; +} + +bool CYSFFICH::decode(const unsigned char* bytes) +{ + assert(bytes != NULL); + + // Skip the sync bytes + bytes += YSF_SYNC_LENGTH_BYTES; + + CYSFConvolution viterbi; + viterbi.start(); + + // Deinterleave the FICH and send bits to the Viterbi decoder + for (unsigned int i = 0U; i < 100U; i++) { + unsigned int n = INTERLEAVE_TABLE[i]; + uint8_t s0 = READ_BIT1(bytes, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(bytes, n) ? 1U : 0U; + + viterbi.decode(s0, s1); + } + + unsigned char output[13U]; + viterbi.chainback(output, 96U); + + unsigned int b0 = CGolay24128::decode24128(output + 0U); + unsigned int b1 = CGolay24128::decode24128(output + 3U); + unsigned int b2 = CGolay24128::decode24128(output + 6U); + unsigned int b3 = CGolay24128::decode24128(output + 9U); + + m_fich[0U] = (b0 >> 4) & 0xFFU; + m_fich[1U] = ((b0 << 4) & 0xF0U) | ((b1 >> 8) & 0x0FU); + m_fich[2U] = (b1 >> 0) & 0xFFU; + m_fich[3U] = (b2 >> 4) & 0xFFU; + m_fich[4U] = ((b2 << 4) & 0xF0U) | ((b3 >> 8) & 0x0FU); + m_fich[5U] = (b3 >> 0) & 0xFFU; + + return CCRC::checkCCITT162(m_fich, 6U); +} + +void CYSFFICH::encode(unsigned char* bytes) +{ + assert(bytes != NULL); + + // Skip the sync bytes + bytes += YSF_SYNC_LENGTH_BYTES; + + CCRC::addCCITT162(m_fich, 6U); + + unsigned int b0 = ((m_fich[0U] << 4) & 0xFF0U) | ((m_fich[1U] >> 4) & 0x00FU); + unsigned int b1 = ((m_fich[1U] << 8) & 0xF00U) | ((m_fich[2U] >> 0) & 0x0FFU); + unsigned int b2 = ((m_fich[3U] << 4) & 0xFF0U) | ((m_fich[4U] >> 4) & 0x00FU); + unsigned int b3 = ((m_fich[4U] << 8) & 0xF00U) | ((m_fich[5U] >> 0) & 0x0FFU); + + unsigned int c0 = CGolay24128::encode24128(b0); + unsigned int c1 = CGolay24128::encode24128(b1); + unsigned int c2 = CGolay24128::encode24128(b2); + unsigned int c3 = CGolay24128::encode24128(b3); + + unsigned char conv[13U]; + conv[0U] = (c0 >> 16) & 0xFFU; + conv[1U] = (c0 >> 8) & 0xFFU; + conv[2U] = (c0 >> 0) & 0xFFU; + conv[3U] = (c1 >> 16) & 0xFFU; + conv[4U] = (c1 >> 8) & 0xFFU; + conv[5U] = (c1 >> 0) & 0xFFU; + conv[6U] = (c2 >> 16) & 0xFFU; + conv[7U] = (c2 >> 8) & 0xFFU; + conv[8U] = (c2 >> 0) & 0xFFU; + conv[9U] = (c3 >> 16) & 0xFFU; + conv[10U] = (c3 >> 8) & 0xFFU; + conv[11U] = (c3 >> 0) & 0xFFU; + conv[12U] = 0x00U; + + CYSFConvolution convolution; + unsigned char convolved[25U]; + convolution.encode(conv, convolved, 100U); + + unsigned int j = 0U; + for (unsigned int i = 0U; i < 100U; i++) { + unsigned int n = INTERLEAVE_TABLE[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } +} + +unsigned char CYSFFICH::getFI() const +{ + return (m_fich[0U] >> 6) & 0x03U; +} + +unsigned char CYSFFICH::getCM() const +{ + return (m_fich[0U] >> 2) & 0x03U; +} + +unsigned char CYSFFICH::getBN() const +{ + return m_fich[0U] & 0x03U; +} + +unsigned char CYSFFICH::getBT() const +{ + return (m_fich[1U] >> 6) & 0x03U; +} + +unsigned char CYSFFICH::getFN() const +{ + return (m_fich[1U] >> 3) & 0x07U; +} + +unsigned char CYSFFICH::getFT() const +{ + return m_fich[1U] & 0x07U; +} + +unsigned char CYSFFICH::getDT() const +{ + return m_fich[2U] & 0x03U; +} + +unsigned char CYSFFICH::getMR() const +{ + return (m_fich[2U] >> 3) & 0x03U; +} + +bool CYSFFICH::getDev() const +{ + return (m_fich[2U] & 0x40U) == 0x40U; +} + +unsigned char CYSFFICH::getDGId() const +{ + return m_fich[3U] & 0x7FU; +} + +void CYSFFICH::setFI(unsigned char fi) +{ + m_fich[0U] &= 0x3FU; + m_fich[0U] |= (fi << 6) & 0xC0U; +} + +void CYSFFICH::setBN(unsigned char bn) +{ + m_fich[0U] &= 0xFCU; + m_fich[0U] |= bn & 0x03U; +} + +void CYSFFICH::setBT(unsigned char bt) +{ + m_fich[1U] &= 0x3FU; + m_fich[1U] |= (bt << 6) & 0xC0U; +} + +void CYSFFICH::setFN(unsigned char fn) +{ + m_fich[1U] &= 0xC7U; + m_fich[1U] |= (fn << 3) & 0x38U; +} + +void CYSFFICH::setFT(unsigned char ft) +{ + m_fich[1U] &= 0xF8U; + m_fich[1U] |= ft & 0x07U; +} + +void CYSFFICH::setMR(unsigned char mr) +{ + m_fich[2U] &= 0xC7U; + m_fich[2U] |= (mr << 3) & 0x38U; +} + +void CYSFFICH::setVoIP(bool on) +{ + if (on) + m_fich[2U] |= 0x04U; + else + m_fich[2U] &= 0xFBU; +} + +void CYSFFICH::setDev(bool on) +{ + if (on) + m_fich[2U] |= 0x40U; + else + m_fich[2U] &= 0xBFU; +} + +void CYSFFICH::setDGId(unsigned char id) +{ + m_fich[3U] &= 0x80U; + m_fich[3U] |= id & 0x7FU; +} + +CYSFFICH& CYSFFICH::operator=(const CYSFFICH& fich) +{ + if (&fich != this) + ::memcpy(m_fich, fich.m_fich, 6U); + + return *this; +} diff --git a/YSFFICH.h b/YSFFICH.h index 5d27ac0..a6e8851 100644 --- a/YSFFICH.h +++ b/YSFFICH.h @@ -1,59 +1,59 @@ -/* - * Copyright (C) 2016,2017,2019,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(YSFFICH_H) -#define YSFFICH_H - -class CYSFFICH { -public: - CYSFFICH(const CYSFFICH& fich); - CYSFFICH(); - ~CYSFFICH(); - - bool decode(const unsigned char* bytes); - - void encode(unsigned char* bytes); - - unsigned char getFI() const; - unsigned char getCM() const; - unsigned char getBN() const; - unsigned char getBT() const; - unsigned char getFN() const; - unsigned char getFT() const; - unsigned char getDT() const; - unsigned char getMR() const; - bool getDev() const; - unsigned char getDGId() const; - - void setFI(unsigned char fi); - void setBN(unsigned char bn); - void setBT(unsigned char bt); - void setFN(unsigned char fn); - void setFT(unsigned char ft); - void setMR(unsigned char mr); - void setVoIP(bool set); - void setDev(bool set); - void setDGId(unsigned char id); - - CYSFFICH& operator=(const CYSFFICH& fich); - -private: - unsigned char* m_fich; -}; - -#endif +/* + * Copyright (C) 2016,2017,2019,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(YSFFICH_H) +#define YSFFICH_H + +class CYSFFICH { +public: + CYSFFICH(const CYSFFICH& fich); + CYSFFICH(); + ~CYSFFICH(); + + bool decode(const unsigned char* bytes); + + void encode(unsigned char* bytes); + + unsigned char getFI() const; + unsigned char getCM() const; + unsigned char getBN() const; + unsigned char getBT() const; + unsigned char getFN() const; + unsigned char getFT() const; + unsigned char getDT() const; + unsigned char getMR() const; + bool getDev() const; + unsigned char getDGId() const; + + void setFI(unsigned char fi); + void setBN(unsigned char bn); + void setBT(unsigned char bt); + void setFN(unsigned char fn); + void setFT(unsigned char ft); + void setMR(unsigned char mr); + void setVoIP(bool set); + void setDev(bool set); + void setDGId(unsigned char id); + + CYSFFICH& operator=(const CYSFFICH& fich); + +private: + unsigned char* m_fich; +}; + +#endif diff --git a/YSFNetwork.cpp b/YSFNetwork.cpp index 089a932..9452ff7 100644 --- a/YSFNetwork.cpp +++ b/YSFNetwork.cpp @@ -1,201 +1,201 @@ -/* - * Copyright (C) 2009-2014,2016,2019,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. - */ - -#include "YSFDefines.h" -#include "YSFNetwork.h" -#include "Defines.h" -#include "Utils.h" -#include "Log.h" - -#include -#include -#include - -const unsigned int BUFFER_LENGTH = 200U; - -CYSFNetwork::CYSFNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, const std::string& callsign, bool debug) : -m_socket(myAddress, myPort), -m_addr(), -m_addrLen(0U), -m_callsign(), -m_debug(debug), -m_enabled(false), -m_buffer(1000U, "YSF Network"), -m_pollTimer(1000U, 5U), -m_tag(NULL) -{ - m_callsign = callsign; - m_callsign.resize(YSF_CALLSIGN_LENGTH, ' '); - - if (CUDPSocket::lookup(gatewayAddress, gatewayPort, m_addr, m_addrLen) != 0) - m_addrLen = 0U; - - m_tag = new unsigned char[YSF_CALLSIGN_LENGTH]; - ::memset(m_tag, ' ', YSF_CALLSIGN_LENGTH); -} - -CYSFNetwork::~CYSFNetwork() -{ - delete[] m_tag; -} - -bool CYSFNetwork::open() -{ - if (m_addrLen == 0U) { - LogError("Unable to resolve the address of the YSF Gateway"); - return false; - } - - LogMessage("Opening YSF network connection"); - - m_pollTimer.start(); - - return m_socket.open(m_addr); -} - -bool CYSFNetwork::write(const unsigned char* src, const unsigned char* dest, const unsigned char* data, unsigned int count, bool end) -{ - assert(data != NULL); - - unsigned char buffer[200U]; - - buffer[0] = 'Y'; - buffer[1] = 'S'; - buffer[2] = 'F'; - buffer[3] = 'D'; - - for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) - buffer[i + 4U] = m_callsign.at(i); - - if (src != NULL) - ::memcpy(buffer + 14U, src, YSF_CALLSIGN_LENGTH); - else - ::memset(buffer + 14U, ' ', YSF_CALLSIGN_LENGTH); - - if (dest != NULL) - ::memcpy(buffer + 24U, dest, YSF_CALLSIGN_LENGTH); - else - ::memset(buffer + 24U, ' ', YSF_CALLSIGN_LENGTH); - - buffer[34U] = end ? 0x01U : 0x00U; - buffer[34U] |= (count & 0x7FU) << 1; - - ::memcpy(buffer + 35U, data, YSF_FRAME_LENGTH_BYTES); - - if (m_debug) - CUtils::dump(1U, "YSF Network Data Sent", buffer, 155U); - - return m_socket.write(buffer, 155U, m_addr, m_addrLen); -} - -bool CYSFNetwork::writePoll() -{ - unsigned char buffer[20U]; - - buffer[0] = 'Y'; - buffer[1] = 'S'; - buffer[2] = 'F'; - buffer[3] = 'P'; - - for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) - buffer[i + 4U] = m_callsign.at(i); - - if (m_debug) - CUtils::dump(1U, "YSF Network Poll Sent", buffer, 14U); - - return m_socket.write(buffer, 14U, m_addr, m_addrLen); -} - -void CYSFNetwork::clock(unsigned int ms) -{ - m_pollTimer.clock(ms); - if (m_pollTimer.hasExpired()) { - writePoll(); - m_pollTimer.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("YSF, packet received from an invalid source"); - return; - } - - if (m_debug) - CUtils::dump(1U, "YSF Network Data Received", buffer, length); - - // Invalid packet type? - if (::memcmp(buffer, "YSFD", 4U) != 0) - return; - - if (!m_enabled) - return; - - if (::memcmp(m_tag, " ", YSF_CALLSIGN_LENGTH) == 0) { - ::memcpy(m_tag, buffer + 4U, YSF_CALLSIGN_LENGTH); - } else { - if (::memcmp(m_tag, buffer + 4U, YSF_CALLSIGN_LENGTH) != 0) - return; - } - - bool end = (buffer[34U] & 0x01U) == 0x01U; - if (end) - ::memset(m_tag, ' ', YSF_CALLSIGN_LENGTH); - - m_buffer.addData(buffer, 155U); -} - -unsigned int CYSFNetwork::read(unsigned char* data) -{ - assert(data != NULL); - - if (m_buffer.isEmpty()) - return 0U; - - m_buffer.getData(data, 155U); - - return 155U; -} - -void CYSFNetwork::reset() -{ - ::memset(m_tag, ' ', YSF_CALLSIGN_LENGTH); -} - -void CYSFNetwork::close() -{ - m_socket.close(); - - LogMessage("Closing YSF network connection"); -} - -void CYSFNetwork::enable(bool enabled) -{ - if (enabled && !m_enabled) - reset(); - else if (!enabled && m_enabled) - m_buffer.clear(); - - m_enabled = enabled; -} +/* + * Copyright (C) 2009-2014,2016,2019,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. + */ + +#include "YSFDefines.h" +#include "YSFNetwork.h" +#include "Defines.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 200U; + +CYSFNetwork::CYSFNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, const std::string& callsign, bool debug) : +m_socket(myAddress, myPort), +m_addr(), +m_addrLen(0U), +m_callsign(), +m_debug(debug), +m_enabled(false), +m_buffer(1000U, "YSF Network"), +m_pollTimer(1000U, 5U), +m_tag(NULL) +{ + m_callsign = callsign; + m_callsign.resize(YSF_CALLSIGN_LENGTH, ' '); + + if (CUDPSocket::lookup(gatewayAddress, gatewayPort, m_addr, m_addrLen) != 0) + m_addrLen = 0U; + + m_tag = new unsigned char[YSF_CALLSIGN_LENGTH]; + ::memset(m_tag, ' ', YSF_CALLSIGN_LENGTH); +} + +CYSFNetwork::~CYSFNetwork() +{ + delete[] m_tag; +} + +bool CYSFNetwork::open() +{ + if (m_addrLen == 0U) { + LogError("Unable to resolve the address of the YSF Gateway"); + return false; + } + + LogMessage("Opening YSF network connection"); + + m_pollTimer.start(); + + return m_socket.open(m_addr); +} + +bool CYSFNetwork::write(const unsigned char* src, const unsigned char* dest, const unsigned char* data, unsigned int count, bool end) +{ + assert(data != NULL); + + unsigned char buffer[200U]; + + buffer[0] = 'Y'; + buffer[1] = 'S'; + buffer[2] = 'F'; + buffer[3] = 'D'; + + for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) + buffer[i + 4U] = m_callsign.at(i); + + if (src != NULL) + ::memcpy(buffer + 14U, src, YSF_CALLSIGN_LENGTH); + else + ::memset(buffer + 14U, ' ', YSF_CALLSIGN_LENGTH); + + if (dest != NULL) + ::memcpy(buffer + 24U, dest, YSF_CALLSIGN_LENGTH); + else + ::memset(buffer + 24U, ' ', YSF_CALLSIGN_LENGTH); + + buffer[34U] = end ? 0x01U : 0x00U; + buffer[34U] |= (count & 0x7FU) << 1; + + ::memcpy(buffer + 35U, data, YSF_FRAME_LENGTH_BYTES); + + if (m_debug) + CUtils::dump(1U, "YSF Network Data Sent", buffer, 155U); + + return m_socket.write(buffer, 155U, m_addr, m_addrLen); +} + +bool CYSFNetwork::writePoll() +{ + unsigned char buffer[20U]; + + buffer[0] = 'Y'; + buffer[1] = 'S'; + buffer[2] = 'F'; + buffer[3] = 'P'; + + for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) + buffer[i + 4U] = m_callsign.at(i); + + if (m_debug) + CUtils::dump(1U, "YSF Network Poll Sent", buffer, 14U); + + return m_socket.write(buffer, 14U, m_addr, m_addrLen); +} + +void CYSFNetwork::clock(unsigned int ms) +{ + m_pollTimer.clock(ms); + if (m_pollTimer.hasExpired()) { + writePoll(); + m_pollTimer.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("YSF, packet received from an invalid source"); + return; + } + + if (m_debug) + CUtils::dump(1U, "YSF Network Data Received", buffer, length); + + // Invalid packet type? + if (::memcmp(buffer, "YSFD", 4U) != 0) + return; + + if (!m_enabled) + return; + + if (::memcmp(m_tag, " ", YSF_CALLSIGN_LENGTH) == 0) { + ::memcpy(m_tag, buffer + 4U, YSF_CALLSIGN_LENGTH); + } else { + if (::memcmp(m_tag, buffer + 4U, YSF_CALLSIGN_LENGTH) != 0) + return; + } + + bool end = (buffer[34U] & 0x01U) == 0x01U; + if (end) + ::memset(m_tag, ' ', YSF_CALLSIGN_LENGTH); + + m_buffer.addData(buffer, 155U); +} + +unsigned int CYSFNetwork::read(unsigned char* data) +{ + assert(data != NULL); + + if (m_buffer.isEmpty()) + return 0U; + + m_buffer.getData(data, 155U); + + return 155U; +} + +void CYSFNetwork::reset() +{ + ::memset(m_tag, ' ', YSF_CALLSIGN_LENGTH); +} + +void CYSFNetwork::close() +{ + m_socket.close(); + + LogMessage("Closing YSF network connection"); +} + +void CYSFNetwork::enable(bool enabled) +{ + if (enabled && !m_enabled) + reset(); + else if (!enabled && m_enabled) + m_buffer.clear(); + + m_enabled = enabled; +} diff --git a/YSFNetwork.h b/YSFNetwork.h index aef43d3..9639b86 100644 --- a/YSFNetwork.h +++ b/YSFNetwork.h @@ -1,63 +1,63 @@ -/* - * Copyright (C) 2009-2014,2016,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. - */ - -#ifndef YSFNetwork_H -#define YSFNetwork_H - -#include "YSFDefines.h" -#include "RingBuffer.h" -#include "UDPSocket.h" -#include "Timer.h" - -#include -#include - -class CYSFNetwork { -public: - CYSFNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, const std::string& callsign, bool debug); - ~CYSFNetwork(); - - bool open(); - - void enable(bool enabled); - - bool write(const unsigned char* src, const unsigned char* dest, const unsigned char* data, unsigned int count, bool end); - - unsigned int read(unsigned char* data); - - void reset(); - - void close(); - - void clock(unsigned int ms); - -private: - CUDPSocket m_socket; - sockaddr_storage m_addr; - unsigned int m_addrLen; - std::string m_callsign; - bool m_debug; - bool m_enabled; - CRingBuffer m_buffer; - CTimer m_pollTimer; - unsigned char* m_tag; - - bool writePoll(); -}; - -#endif +/* + * Copyright (C) 2009-2014,2016,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. + */ + +#ifndef YSFNetwork_H +#define YSFNetwork_H + +#include "YSFDefines.h" +#include "RingBuffer.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CYSFNetwork { +public: + CYSFNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, const std::string& callsign, bool debug); + ~CYSFNetwork(); + + bool open(); + + void enable(bool enabled); + + bool write(const unsigned char* src, const unsigned char* dest, const unsigned char* data, unsigned int count, bool end); + + unsigned int read(unsigned char* data); + + void reset(); + + void close(); + + void clock(unsigned int ms); + +private: + CUDPSocket m_socket; + sockaddr_storage m_addr; + unsigned int m_addrLen; + std::string m_callsign; + bool m_debug; + bool m_enabled; + CRingBuffer m_buffer; + CTimer m_pollTimer; + unsigned char* m_tag; + + bool writePoll(); +}; + +#endif diff --git a/YSFPayload.cpp b/YSFPayload.cpp index 898e321..f0fa17f 100644 --- a/YSFPayload.cpp +++ b/YSFPayload.cpp @@ -1,1027 +1,1027 @@ -/* -* Copyright (C) 2016,2017,2020 Jonathan Naylor, G4KLX -* Copyright (C) 2016 Mathias Weyland, HB9FRV -* -* 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 "YSFConvolution.h" -#include "YSFPayload.h" -#include "YSFDefines.h" -#include "Utils.h" -#include "CRC.h" -#include "Log.h" - -#include -#include -#include -#include - -const unsigned int INTERLEAVE_TABLE_9_20[] = { - 0U, 40U, 80U, 120U, 160U, 200U, 240U, 280U, 320U, - 2U, 42U, 82U, 122U, 162U, 202U, 242U, 282U, 322U, - 4U, 44U, 84U, 124U, 164U, 204U, 244U, 284U, 324U, - 6U, 46U, 86U, 126U, 166U, 206U, 246U, 286U, 326U, - 8U, 48U, 88U, 128U, 168U, 208U, 248U, 288U, 328U, - 10U, 50U, 90U, 130U, 170U, 210U, 250U, 290U, 330U, - 12U, 52U, 92U, 132U, 172U, 212U, 252U, 292U, 332U, - 14U, 54U, 94U, 134U, 174U, 214U, 254U, 294U, 334U, - 16U, 56U, 96U, 136U, 176U, 216U, 256U, 296U, 336U, - 18U, 58U, 98U, 138U, 178U, 218U, 258U, 298U, 338U, - 20U, 60U, 100U, 140U, 180U, 220U, 260U, 300U, 340U, - 22U, 62U, 102U, 142U, 182U, 222U, 262U, 302U, 342U, - 24U, 64U, 104U, 144U, 184U, 224U, 264U, 304U, 344U, - 26U, 66U, 106U, 146U, 186U, 226U, 266U, 306U, 346U, - 28U, 68U, 108U, 148U, 188U, 228U, 268U, 308U, 348U, - 30U, 70U, 110U, 150U, 190U, 230U, 270U, 310U, 350U, - 32U, 72U, 112U, 152U, 192U, 232U, 272U, 312U, 352U, - 34U, 74U, 114U, 154U, 194U, 234U, 274U, 314U, 354U, - 36U, 76U, 116U, 156U, 196U, 236U, 276U, 316U, 356U, - 38U, 78U, 118U, 158U, 198U, 238U, 278U, 318U, 358U}; - -const unsigned int INTERLEAVE_TABLE_5_20[] = { - 0U, 40U, 80U, 120U, 160U, - 2U, 42U, 82U, 122U, 162U, - 4U, 44U, 84U, 124U, 164U, - 6U, 46U, 86U, 126U, 166U, - 8U, 48U, 88U, 128U, 168U, - 10U, 50U, 90U, 130U, 170U, - 12U, 52U, 92U, 132U, 172U, - 14U, 54U, 94U, 134U, 174U, - 16U, 56U, 96U, 136U, 176U, - 18U, 58U, 98U, 138U, 178U, - 20U, 60U, 100U, 140U, 180U, - 22U, 62U, 102U, 142U, 182U, - 24U, 64U, 104U, 144U, 184U, - 26U, 66U, 106U, 146U, 186U, - 28U, 68U, 108U, 148U, 188U, - 30U, 70U, 110U, 150U, 190U, - 32U, 72U, 112U, 152U, 192U, - 34U, 74U, 114U, 154U, 194U, - 36U, 76U, 116U, 156U, 196U, - 38U, 78U, 118U, 158U, 198U}; - -// This one differs from the others in that it interleaves bits and not dibits -const unsigned int INTERLEAVE_TABLE_26_4[] = { - 0U, 4U, 8U, 12U, 16U, 20U, 24U, 28U, 32U, 36U, 40U, 44U, 48U, 52U, 56U, 60U, 64U, 68U, 72U, 76U, 80U, 84U, 88U, 92U, 96U, 100U, - 1U, 5U, 9U, 13U, 17U, 21U, 25U, 29U, 33U, 37U, 41U, 45U, 49U, 53U, 57U, 61U, 65U, 69U, 73U, 77U, 81U, 85U, 89U, 93U, 97U, 101U, - 2U, 6U, 10U, 14U, 18U, 22U, 26U, 30U, 34U, 38U, 42U, 46U, 50U, 54U, 58U, 62U, 66U, 70U, 74U, 78U, 82U, 86U, 90U, 94U, 98U, 102U, - 3U, 7U, 11U, 15U, 19U, 23U, 27U, 31U, 35U, 39U, 43U, 47U, 51U, 55U, 59U, 63U, 67U, 71U, 75U, 79U, 83U, 87U, 91U, 95U, 99U, 103U}; - -const unsigned char WHITENING_DATA[] = {0x93U, 0xD7U, 0x51U, 0x21U, 0x9CU, 0x2FU, 0x6CU, 0xD0U, 0xEFU, 0x0FU, - 0xF8U, 0x3DU, 0xF1U, 0x73U, 0x20U, 0x94U, 0xEDU, 0x1EU, 0x7CU, 0xD8U}; - -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]) - -CYSFPayload::CYSFPayload() : -m_uplink(NULL), -m_downlink(NULL), -m_source(NULL), -m_dest(NULL), -m_fec() -{ -} - -CYSFPayload::~CYSFPayload() -{ - delete[] m_uplink; - delete[] m_downlink; - delete[] m_source; - delete[] m_dest; -} - -bool CYSFPayload::processHeaderData(unsigned char* data) -{ - assert(data != NULL); - - data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; - - unsigned char dch[45U]; - - unsigned char* p1 = data; - unsigned char* p2 = dch; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p2, p1, 9U); - p1 += 18U; p2 += 9U; - } - - CYSFConvolution conv; - conv.start(); - - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; - - n++; - uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; - - conv.decode(s0, s1); - } - - unsigned char output[23U]; - conv.chainback(output, 176U); - - bool valid1 = CCRC::checkCCITT162(output, 22U); - if (valid1) { - for (unsigned int i = 0U; i < 20U; i++) - output[i] ^= WHITENING_DATA[i]; - - if (m_dest == NULL) { - m_dest = new unsigned char[YSF_CALLSIGN_LENGTH]; - ::memcpy(m_dest, output + 0U, YSF_CALLSIGN_LENGTH); - } - - if (m_source == NULL) { - m_source = new unsigned char[YSF_CALLSIGN_LENGTH]; - ::memcpy(m_source, output + YSF_CALLSIGN_LENGTH, YSF_CALLSIGN_LENGTH); - } - - for (unsigned int i = 0U; i < 20U; i++) - output[i] ^= WHITENING_DATA[i]; - - CCRC::addCCITT162(output, 22U); - output[22U] = 0x00U; - - unsigned char convolved[45U]; - conv.encode(output, convolved, 180U); - - unsigned char bytes[45U]; - unsigned int j = 0U; - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - - bool s0 = READ_BIT1(convolved, j) != 0U; - j++; - - bool s1 = READ_BIT1(convolved, j) != 0U; - j++; - - WRITE_BIT1(bytes, n, s0); - - n++; - WRITE_BIT1(bytes, n, s1); - } - - p1 = data; - p2 = bytes; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p1, p2, 9U); - p1 += 18U; p2 += 9U; - } - } - - p1 = data + 9U; - p2 = dch; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p2, p1, 9U); - p1 += 18U; p2 += 9U; - } - - conv.start(); - - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; - - n++; - uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; - - conv.decode(s0, s1); - } - - conv.chainback(output, 176U); - - bool valid2 = CCRC::checkCCITT162(output, 22U); - if (valid2) { - for (unsigned int i = 0U; i < 20U; i++) - output[i] ^= WHITENING_DATA[i]; - - if (m_downlink != NULL) - ::memcpy(output + 0U, m_downlink, YSF_CALLSIGN_LENGTH); - - if (m_uplink != NULL) - ::memcpy(output + YSF_CALLSIGN_LENGTH, m_uplink, YSF_CALLSIGN_LENGTH); - - for (unsigned int i = 0U; i < 20U; i++) - output[i] ^= WHITENING_DATA[i]; - - CCRC::addCCITT162(output, 22U); - output[22U] = 0x00U; - - unsigned char convolved[45U]; - conv.encode(output, convolved, 180U); - - unsigned char bytes[45U]; - unsigned int j = 0U; - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - - bool s0 = READ_BIT1(convolved, j) != 0U; - j++; - - bool s1 = READ_BIT1(convolved, j) != 0U; - j++; - - WRITE_BIT1(bytes, n, s0); - - n++; - WRITE_BIT1(bytes, n, s1); - } - - p1 = data + 9U; - p2 = bytes; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p1, p2, 9U); - p1 += 18U; p2 += 9U; - } - } - - return valid1; -} - -unsigned int CYSFPayload::processVDMode1Audio(unsigned char* data) -{ - assert(data != NULL); - - data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; - - // Regenerate the AMBE FEC - unsigned int errors = 0U; - errors += m_fec.regenerateYSFDN(data + 9U); - errors += m_fec.regenerateYSFDN(data + 27U); - errors += m_fec.regenerateYSFDN(data + 45U); - errors += m_fec.regenerateYSFDN(data + 63U); - errors += m_fec.regenerateYSFDN(data + 81U); - - return errors; -} - -bool CYSFPayload::processVDMode1Data(unsigned char* data, unsigned char fn, bool gateway) -{ - assert(data != NULL); - - data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; - - unsigned char dch[45U]; - - unsigned char* p1 = data; - unsigned char* p2 = dch; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p2, p1, 9U); - p1 += 18U; p2 += 9U; - } - - CYSFConvolution conv; - conv.start(); - - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; - - n++; - uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; - - conv.decode(s0, s1); - } - - unsigned char output[23U]; - conv.chainback(output, 176U); - - bool ret = CCRC::checkCCITT162(output, 22U); - if (ret) { - for (unsigned int i = 0U; i < 20U; i++) - output[i] ^= WHITENING_DATA[i]; - - switch (fn) { - case 0U: - if (m_dest == NULL) { - m_dest = new unsigned char[YSF_CALLSIGN_LENGTH]; - ::memcpy(m_dest, output + 0U, YSF_CALLSIGN_LENGTH); - } - - if (m_source == NULL) { - m_source = new unsigned char[YSF_CALLSIGN_LENGTH]; - ::memcpy(m_source, output + YSF_CALLSIGN_LENGTH, YSF_CALLSIGN_LENGTH); - } - - break; - - case 1U: - if (m_downlink != NULL && !gateway) - ::memcpy(output + 0U, m_downlink, YSF_CALLSIGN_LENGTH); - - if (m_uplink != NULL && !gateway) - ::memcpy(output + YSF_CALLSIGN_LENGTH, m_uplink, YSF_CALLSIGN_LENGTH); - - break; - - case 3U: - // CUtils::dump(1U, "V/D Mode 1 Data, DT1", output, 20U); - break; - - case 4U: - // CUtils::dump(1U, "V/D Mode 1 Data, DT2", output, 20U); - break; - - case 5U: - // CUtils::dump(1U, "V/D Mode 1 Data, DT3", output, 20U); - break; - - case 6U: - // CUtils::dump(1U, "V/D Mode 1 Data, DT4", output, 20U); - break; - - case 7U: - // CUtils::dump(1U, "V/D Mode 1 Data, DT5", output, 20U); - break; - - default: - break; - } - - for (unsigned int i = 0U; i < 20U; i++) - output[i] ^= WHITENING_DATA[i]; - - CCRC::addCCITT162(output, 22U); - output[22U] = 0x00U; - - unsigned char convolved[45U]; - conv.encode(output, convolved, 180U); - - unsigned char bytes[45U]; - unsigned int j = 0U; - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - - bool s0 = READ_BIT1(convolved, j) != 0U; - j++; - - bool s1 = READ_BIT1(convolved, j) != 0U; - j++; - - WRITE_BIT1(bytes, n, s0); - - n++; - WRITE_BIT1(bytes, n, s1); - } - - p1 = data; - p2 = bytes; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p1, p2, 9U); - p1 += 18U; p2 += 9U; - } - } - - return ret && (fn == 0U); -} - -unsigned int CYSFPayload::processVDMode2Audio(unsigned char* data) -{ - assert(data != NULL); - - data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; - - unsigned int errors = 0U; - unsigned int offset = 40U; // DCH(0) - - // We have a total of 5 VCH sections, iterate through each - for (unsigned int j = 0U; j < 5U; j++, offset += 144U) { - unsigned int errs = 0U; - - unsigned char vch[13U]; - - // Deinterleave - for (unsigned int i = 0U; i < 104U; i++) { - unsigned int n = INTERLEAVE_TABLE_26_4[i]; - bool s = READ_BIT1(data, offset + n); - WRITE_BIT1(vch, i, s); - } - - // "Un-whiten" (descramble) - for (unsigned int i = 0U; i < 13U; i++) - vch[i] ^= WHITENING_DATA[i]; - - // errors += READ_BIT1(vch, 103); // Padding bit must be zero but apparently it is not... - - for (unsigned int i = 0U; i < 81U; i += 3) { - uint8_t vote = 0U; - vote += READ_BIT1(vch, i + 0U) ? 1U : 0U; - vote += READ_BIT1(vch, i + 1U) ? 1U : 0U; - vote += READ_BIT1(vch, i + 2U) ? 1U : 0U; - - switch (vote) { - case 1U: // 1 0 0, or 0 1 0, or 0 0 1, convert to 0 0 0 - WRITE_BIT1(vch, i + 0U, false); - WRITE_BIT1(vch, i + 1U, false); - WRITE_BIT1(vch, i + 2U, false); - errs++; - break; - case 2U: // 1 1 0, or 0 1 1, or 1 0 1, convert to 1 1 1 - WRITE_BIT1(vch, i + 0U, true); - WRITE_BIT1(vch, i + 1U, true); - WRITE_BIT1(vch, i + 2U, true); - errs++; - break; - default: // 0U (0 0 0), or 3U (1 1 1), no errors - break; - } - } - - // Reconstruct only if we have bit errors. - if (errs > 0U) { - // Accumulate the total number of errors - errors += errs; - - // Scramble - for (unsigned int i = 0U; i < 13U; i++) - vch[i] ^= WHITENING_DATA[i]; - - // Interleave - for (unsigned int i = 0U; i < 104U; i++) { - unsigned int n = INTERLEAVE_TABLE_26_4[i]; - bool s = READ_BIT1(vch, i); - WRITE_BIT1(data, offset + n, s); - } - } - } - - // "errors" is the number of triplets that were recognized to be corrupted - // and that were corrected. There are 27 of those per VCH and 5 VCH per CC, - // yielding a total of 27*5 = 135. I believe the expected value of this - // error distribution to be Bin(1;3,BER)+Bin(2;3,BER) which entails 75% for - // BER = 0.5. - return errors; -} - -bool CYSFPayload::processVDMode2Data(unsigned char* data, unsigned char fn, bool gateway) -{ - assert(data != NULL); - - data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; - - unsigned char dch[25U]; - - unsigned char* p1 = data; - unsigned char* p2 = dch; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p2, p1, 5U); - p1 += 18U; p2 += 5U; - } - - CYSFConvolution conv; - conv.start(); - - for (unsigned int i = 0U; i < 100U; i++) { - unsigned int n = INTERLEAVE_TABLE_5_20[i]; - uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; - - n++; - uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; - - conv.decode(s0, s1); - } - - unsigned char output[13U]; - conv.chainback(output, 96U); - - bool ret = CCRC::checkCCITT162(output, 12U); - if (ret) { - for (unsigned int i = 0U; i < 10U; i++) - output[i] ^= WHITENING_DATA[i]; - - switch (fn) { - case 0U: - if (m_dest == NULL) { - m_dest = new unsigned char[YSF_CALLSIGN_LENGTH]; - ::memcpy(m_dest, output, YSF_CALLSIGN_LENGTH); - } - break; - - case 1U: - if (m_source == NULL) { - m_source = new unsigned char[YSF_CALLSIGN_LENGTH]; - ::memcpy(m_source, output, YSF_CALLSIGN_LENGTH); - } - break; - - case 2U: - if (m_downlink != NULL && !gateway) - ::memcpy(output, m_downlink, YSF_CALLSIGN_LENGTH); - break; - - case 3U: - if (m_uplink != NULL && !gateway) - ::memcpy(output, m_uplink, YSF_CALLSIGN_LENGTH); - break; - - case 6U: - // CUtils::dump(1U, "V/D Mode 2 Data, DT1", output, YSF_CALLSIGN_LENGTH); - break; - - case 7U: - // CUtils::dump(1U, "V/D Mode 2 Data, DT2", output, YSF_CALLSIGN_LENGTH); - break; - - default: - break; - } - - for (unsigned int i = 0U; i < 10U; i++) - output[i] ^= WHITENING_DATA[i]; - - CCRC::addCCITT162(output, 12U); - output[12U] = 0x00U; - - unsigned char convolved[25U]; - conv.encode(output, convolved, 100U); - - unsigned char bytes[25U]; - unsigned int j = 0U; - for (unsigned int i = 0U; i < 100U; i++) { - unsigned int n = INTERLEAVE_TABLE_5_20[i]; - - bool s0 = READ_BIT1(convolved, j) != 0U; - j++; - - bool s1 = READ_BIT1(convolved, j) != 0U; - j++; - - WRITE_BIT1(bytes, n, s0); - - n++; - WRITE_BIT1(bytes, n, s1); - } - - p1 = data; - p2 = bytes; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p1, p2, 5U); - p1 += 18U; p2 += 5U; - } - } - - return ret && (fn == 0U || fn == 1U); -} - -bool CYSFPayload::processDataFRModeData(unsigned char* data, unsigned char fn, bool gateway) -{ - assert(data != NULL); - - data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; - - unsigned char dch[45U]; - - unsigned char* p1 = data; - unsigned char* p2 = dch; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p2, p1, 9U); - p1 += 18U; p2 += 9U; - } - - CYSFConvolution conv; - conv.start(); - - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; - - n++; - uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; - - conv.decode(s0, s1); - } - - unsigned char output[23U]; - conv.chainback(output, 176U); - - bool ret1 = CCRC::checkCCITT162(output, 22U); - if (ret1) { - for (unsigned int i = 0U; i < 20U; i++) - output[i] ^= WHITENING_DATA[i]; - - switch (fn) { - case 0U: - // CUtils::dump(1U, "FR Mode Data, CSD1", output, 20U); - - if (m_dest == NULL) { - m_dest = new unsigned char[YSF_CALLSIGN_LENGTH]; - ::memcpy(m_dest, output + 0U, YSF_CALLSIGN_LENGTH); - } - - if (m_source == NULL) { - m_source = new unsigned char[YSF_CALLSIGN_LENGTH]; - ::memcpy(m_source, output + YSF_CALLSIGN_LENGTH, YSF_CALLSIGN_LENGTH); - } - - break; - - case 1U: - // CUtils::dump(1U, "FR Mode Data, CSD3", output, 20U); - break; - - case 2U: - // CUtils::dump(1U, "FR Mode Data, DT2", output, 20U); - break; - - case 3U: - // CUtils::dump(1U, "FR Mode Data, DT4", output, 20U); - break; - - case 4U: - // CUtils::dump(1U, "FR Mode Data, DT6", output, 20U); - break; - - case 5U: - // CUtils::dump(1U, "FR Mode Data, DT8", output, 20U); - break; - - case 6U: - // CUtils::dump(1U, "FR Mode Data, DT10", output, 20U); - break; - - case 7U: - // CUtils::dump(1U, "FR Mode Data, DT12", output, 20U); - break; - - default: - break; - } - - for (unsigned int i = 0U; i < 20U; i++) - output[i] ^= WHITENING_DATA[i]; - - CCRC::addCCITT162(output, 22U); - output[22U] = 0x00U; - - unsigned char convolved[45U]; - conv.encode(output, convolved, 180U); - - unsigned char bytes[45U]; - unsigned int j = 0U; - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - - bool s0 = READ_BIT1(convolved, j) != 0U; - j++; - - bool s1 = READ_BIT1(convolved, j) != 0U; - j++; - - WRITE_BIT1(bytes, n, s0); - - n++; - WRITE_BIT1(bytes, n, s1); - } - - p1 = data; - p2 = bytes; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p1, p2, 9U); - p1 += 18U; p2 += 9U; - } - } - - p1 = data + 9U; - p2 = dch; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p2, p1, 9U); - p1 += 18U; p2 += 9U; - } - - conv.start(); - - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; - - n++; - uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; - - conv.decode(s0, s1); - } - - conv.chainback(output, 176U); - - bool ret2 = CCRC::checkCCITT162(output, 22U); - if (ret2) { - for (unsigned int i = 0U; i < 20U; i++) - output[i] ^= WHITENING_DATA[i]; - - switch (fn) { - case 0U: - // CUtils::dump(1U, "FR Mode Data, CSD2", output, 20U); - - if (m_downlink != NULL && !gateway) - ::memcpy(output + 0U, m_downlink, YSF_CALLSIGN_LENGTH); - - if (m_uplink != NULL && !gateway) - ::memcpy(output + YSF_CALLSIGN_LENGTH, m_uplink, YSF_CALLSIGN_LENGTH); - - break; - - case 1U: - // CUtils::dump(1U, "FR Mode Data, DT1", output, 20U); - break; - - case 2U: - // CUtils::dump(1U, "FR Mode Data, DT3", output, 20U); - break; - - case 3U: - // CUtils::dump(1U, "FR Mode Data, DT5", output, 20U); - break; - - case 4U: - // CUtils::dump(1U, "FR Mode Data, DT7", output, 20U); - break; - - case 5U: - // CUtils::dump(1U, "FR Mode Data, DT9", output, 20U); - break; - - case 6U: - // CUtils::dump(1U, "FR Mode Data, DT11", output, 20U); - break; - - case 7U: - // CUtils::dump(1U, "FR Mode Data, DT13", output, 20U); - break; - - default: - break; - } - - for (unsigned int i = 0U; i < 20U; i++) - output[i] ^= WHITENING_DATA[i]; - - CCRC::addCCITT162(output, 22U); - output[22U] = 0x00U; - - unsigned char convolved[45U]; - conv.encode(output, convolved, 180U); - - unsigned char bytes[45U]; - unsigned int j = 0U; - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - - bool s0 = READ_BIT1(convolved, j) != 0U; - j++; - - bool s1 = READ_BIT1(convolved, j) != 0U; - j++; - - WRITE_BIT1(bytes, n, s0); - - n++; - WRITE_BIT1(bytes, n, s1); - } - - p1 = data + 9U; - p2 = bytes; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p1, p2, 9U); - p1 += 18U; p2 += 9U; - } - } - - return ret1 && (fn == 0U); -} - -unsigned int CYSFPayload::processVoiceFRModeAudio2(unsigned char* data) -{ - assert(data != NULL); - - data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; - - // Regenerate the IMBE FEC - unsigned int errors = 0U; - errors += m_fec.regenerateIMBE(data + 54U); - errors += m_fec.regenerateIMBE(data + 72U); - - return errors; -} - -unsigned int CYSFPayload::processVoiceFRModeAudio5(unsigned char* data) -{ - assert(data != NULL); - - data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; - - // Regenerate the IMBE FEC - unsigned int errors = 0U; - errors += m_fec.regenerateIMBE(data + 0U); - errors += m_fec.regenerateIMBE(data + 18U); - errors += m_fec.regenerateIMBE(data + 36U); - errors += m_fec.regenerateIMBE(data + 54U); - errors += m_fec.regenerateIMBE(data + 72U); - - return errors; -} - -bool CYSFPayload::processVoiceFRModeData(unsigned char* data) -{ - assert(data != NULL); - - data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; - - unsigned char dch[45U]; - ::memcpy(dch, data, 45U); - - CYSFConvolution conv; - conv.start(); - - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; - - n++; - uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; - - conv.decode(s0, s1); - } - - unsigned char output[23U]; - conv.chainback(output, 176U); - - bool ret = CCRC::checkCCITT162(output, 22U); - if (ret) { - CCRC::addCCITT162(output, 22U); - output[22U] = 0x00U; - - unsigned char convolved[45U]; - conv.encode(output, convolved, 180U); - - unsigned char bytes[45U]; - unsigned int j = 0U; - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - - bool s0 = READ_BIT1(convolved, j) != 0U; - j++; - - bool s1 = READ_BIT1(convolved, j) != 0U; - j++; - - WRITE_BIT1(bytes, n, s0); - - n++; - WRITE_BIT1(bytes, n, s1); - } - - ::memcpy(data, bytes, 45U); - } - - return ret; -} - -void CYSFPayload::writeHeader(unsigned char* data, const unsigned char* csd1, const unsigned char* csd2) -{ - assert(data != NULL); - assert(csd1 != NULL); - assert(csd2 != NULL); - - writeDataFRModeData1(csd1, data); - - writeDataFRModeData2(csd2, data); -} - -void CYSFPayload::writeDataFRModeData1(const unsigned char* dt, unsigned char* data) -{ - assert(dt != NULL); - assert(data != NULL); - - data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; - - unsigned char output[25U]; - for (unsigned int i = 0U; i < 20U; i++) - output[i] = dt[i] ^ WHITENING_DATA[i]; - - CCRC::addCCITT162(output, 22U); - output[22U] = 0x00U; - - unsigned char convolved[45U]; - - CYSFConvolution conv; - conv.encode(output, convolved, 180U); - - unsigned char bytes[45U]; - unsigned int j = 0U; - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - - bool s0 = READ_BIT1(convolved, j) != 0U; - j++; - - bool s1 = READ_BIT1(convolved, j) != 0U; - j++; - - WRITE_BIT1(bytes, n, s0); - - n++; - WRITE_BIT1(bytes, n, s1); - } - - unsigned char* p1 = data; - unsigned char* p2 = bytes; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p1, p2, 9U); - p1 += 18U; p2 += 9U; - } -} - -void CYSFPayload::writeDataFRModeData2(const unsigned char* dt, unsigned char* data) -{ - assert(dt != NULL); - assert(data != NULL); - - data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; - - unsigned char output[25U]; - for (unsigned int i = 0U; i < 20U; i++) - output[i] = dt[i] ^ WHITENING_DATA[i]; - - CCRC::addCCITT162(output, 22U); - output[22U] = 0x00U; - - unsigned char convolved[45U]; - - CYSFConvolution conv; - conv.encode(output, convolved, 180U); - - unsigned char bytes[45U]; - unsigned int j = 0U; - for (unsigned int i = 0U; i < 180U; i++) { - unsigned int n = INTERLEAVE_TABLE_9_20[i]; - - bool s0 = READ_BIT1(convolved, j) != 0U; - j++; - - bool s1 = READ_BIT1(convolved, j) != 0U; - j++; - - WRITE_BIT1(bytes, n, s0); - - n++; - WRITE_BIT1(bytes, n, s1); - } - - unsigned char* p1 = data + 9U; - unsigned char* p2 = bytes; - for (unsigned int i = 0U; i < 5U; i++) { - ::memcpy(p1, p2, 9U); - p1 += 18U; p2 += 9U; - } -} - -void CYSFPayload::setUplink(const std::string& callsign) -{ - m_uplink = new unsigned char[YSF_CALLSIGN_LENGTH]; - - std::string uplink = callsign; - uplink.resize(YSF_CALLSIGN_LENGTH, ' '); - - for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) - m_uplink[i] = uplink.at(i); -} - -void CYSFPayload::setDownlink(const std::string& callsign) -{ - m_downlink = new unsigned char[YSF_CALLSIGN_LENGTH]; - - std::string downlink = callsign; - downlink.resize(YSF_CALLSIGN_LENGTH, ' '); - - for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) - m_downlink[i] = downlink.at(i); -} - -unsigned char* CYSFPayload::getSource() -{ - return m_source; -} - -unsigned char* CYSFPayload::getDest() -{ - return m_dest; -} - -void CYSFPayload::reset() -{ - delete[] m_source; - delete[] m_dest; - - m_source = NULL; - m_dest = NULL; -} +/* +* Copyright (C) 2016,2017,2020 Jonathan Naylor, G4KLX +* Copyright (C) 2016 Mathias Weyland, HB9FRV +* +* 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 "YSFConvolution.h" +#include "YSFPayload.h" +#include "YSFDefines.h" +#include "Utils.h" +#include "CRC.h" +#include "Log.h" + +#include +#include +#include +#include + +const unsigned int INTERLEAVE_TABLE_9_20[] = { + 0U, 40U, 80U, 120U, 160U, 200U, 240U, 280U, 320U, + 2U, 42U, 82U, 122U, 162U, 202U, 242U, 282U, 322U, + 4U, 44U, 84U, 124U, 164U, 204U, 244U, 284U, 324U, + 6U, 46U, 86U, 126U, 166U, 206U, 246U, 286U, 326U, + 8U, 48U, 88U, 128U, 168U, 208U, 248U, 288U, 328U, + 10U, 50U, 90U, 130U, 170U, 210U, 250U, 290U, 330U, + 12U, 52U, 92U, 132U, 172U, 212U, 252U, 292U, 332U, + 14U, 54U, 94U, 134U, 174U, 214U, 254U, 294U, 334U, + 16U, 56U, 96U, 136U, 176U, 216U, 256U, 296U, 336U, + 18U, 58U, 98U, 138U, 178U, 218U, 258U, 298U, 338U, + 20U, 60U, 100U, 140U, 180U, 220U, 260U, 300U, 340U, + 22U, 62U, 102U, 142U, 182U, 222U, 262U, 302U, 342U, + 24U, 64U, 104U, 144U, 184U, 224U, 264U, 304U, 344U, + 26U, 66U, 106U, 146U, 186U, 226U, 266U, 306U, 346U, + 28U, 68U, 108U, 148U, 188U, 228U, 268U, 308U, 348U, + 30U, 70U, 110U, 150U, 190U, 230U, 270U, 310U, 350U, + 32U, 72U, 112U, 152U, 192U, 232U, 272U, 312U, 352U, + 34U, 74U, 114U, 154U, 194U, 234U, 274U, 314U, 354U, + 36U, 76U, 116U, 156U, 196U, 236U, 276U, 316U, 356U, + 38U, 78U, 118U, 158U, 198U, 238U, 278U, 318U, 358U}; + +const unsigned int INTERLEAVE_TABLE_5_20[] = { + 0U, 40U, 80U, 120U, 160U, + 2U, 42U, 82U, 122U, 162U, + 4U, 44U, 84U, 124U, 164U, + 6U, 46U, 86U, 126U, 166U, + 8U, 48U, 88U, 128U, 168U, + 10U, 50U, 90U, 130U, 170U, + 12U, 52U, 92U, 132U, 172U, + 14U, 54U, 94U, 134U, 174U, + 16U, 56U, 96U, 136U, 176U, + 18U, 58U, 98U, 138U, 178U, + 20U, 60U, 100U, 140U, 180U, + 22U, 62U, 102U, 142U, 182U, + 24U, 64U, 104U, 144U, 184U, + 26U, 66U, 106U, 146U, 186U, + 28U, 68U, 108U, 148U, 188U, + 30U, 70U, 110U, 150U, 190U, + 32U, 72U, 112U, 152U, 192U, + 34U, 74U, 114U, 154U, 194U, + 36U, 76U, 116U, 156U, 196U, + 38U, 78U, 118U, 158U, 198U}; + +// This one differs from the others in that it interleaves bits and not dibits +const unsigned int INTERLEAVE_TABLE_26_4[] = { + 0U, 4U, 8U, 12U, 16U, 20U, 24U, 28U, 32U, 36U, 40U, 44U, 48U, 52U, 56U, 60U, 64U, 68U, 72U, 76U, 80U, 84U, 88U, 92U, 96U, 100U, + 1U, 5U, 9U, 13U, 17U, 21U, 25U, 29U, 33U, 37U, 41U, 45U, 49U, 53U, 57U, 61U, 65U, 69U, 73U, 77U, 81U, 85U, 89U, 93U, 97U, 101U, + 2U, 6U, 10U, 14U, 18U, 22U, 26U, 30U, 34U, 38U, 42U, 46U, 50U, 54U, 58U, 62U, 66U, 70U, 74U, 78U, 82U, 86U, 90U, 94U, 98U, 102U, + 3U, 7U, 11U, 15U, 19U, 23U, 27U, 31U, 35U, 39U, 43U, 47U, 51U, 55U, 59U, 63U, 67U, 71U, 75U, 79U, 83U, 87U, 91U, 95U, 99U, 103U}; + +const unsigned char WHITENING_DATA[] = {0x93U, 0xD7U, 0x51U, 0x21U, 0x9CU, 0x2FU, 0x6CU, 0xD0U, 0xEFU, 0x0FU, + 0xF8U, 0x3DU, 0xF1U, 0x73U, 0x20U, 0x94U, 0xEDU, 0x1EU, 0x7CU, 0xD8U}; + +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]) + +CYSFPayload::CYSFPayload() : +m_uplink(NULL), +m_downlink(NULL), +m_source(NULL), +m_dest(NULL), +m_fec() +{ +} + +CYSFPayload::~CYSFPayload() +{ + delete[] m_uplink; + delete[] m_downlink; + delete[] m_source; + delete[] m_dest; +} + +bool CYSFPayload::processHeaderData(unsigned char* data) +{ + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char dch[45U]; + + unsigned char* p1 = data; + unsigned char* p2 = dch; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p2, p1, 9U); + p1 += 18U; p2 += 9U; + } + + CYSFConvolution conv; + conv.start(); + + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + unsigned char output[23U]; + conv.chainback(output, 176U); + + bool valid1 = CCRC::checkCCITT162(output, 22U); + if (valid1) { + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + if (m_dest == NULL) { + m_dest = new unsigned char[YSF_CALLSIGN_LENGTH]; + ::memcpy(m_dest, output + 0U, YSF_CALLSIGN_LENGTH); + } + + if (m_source == NULL) { + m_source = new unsigned char[YSF_CALLSIGN_LENGTH]; + ::memcpy(m_source, output + YSF_CALLSIGN_LENGTH, YSF_CALLSIGN_LENGTH); + } + + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + p1 = data; + p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p1, p2, 9U); + p1 += 18U; p2 += 9U; + } + } + + p1 = data + 9U; + p2 = dch; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p2, p1, 9U); + p1 += 18U; p2 += 9U; + } + + conv.start(); + + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + conv.chainback(output, 176U); + + bool valid2 = CCRC::checkCCITT162(output, 22U); + if (valid2) { + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + if (m_downlink != NULL) + ::memcpy(output + 0U, m_downlink, YSF_CALLSIGN_LENGTH); + + if (m_uplink != NULL) + ::memcpy(output + YSF_CALLSIGN_LENGTH, m_uplink, YSF_CALLSIGN_LENGTH); + + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + p1 = data + 9U; + p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p1, p2, 9U); + p1 += 18U; p2 += 9U; + } + } + + return valid1; +} + +unsigned int CYSFPayload::processVDMode1Audio(unsigned char* data) +{ + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + // Regenerate the AMBE FEC + unsigned int errors = 0U; + errors += m_fec.regenerateYSFDN(data + 9U); + errors += m_fec.regenerateYSFDN(data + 27U); + errors += m_fec.regenerateYSFDN(data + 45U); + errors += m_fec.regenerateYSFDN(data + 63U); + errors += m_fec.regenerateYSFDN(data + 81U); + + return errors; +} + +bool CYSFPayload::processVDMode1Data(unsigned char* data, unsigned char fn, bool gateway) +{ + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char dch[45U]; + + unsigned char* p1 = data; + unsigned char* p2 = dch; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p2, p1, 9U); + p1 += 18U; p2 += 9U; + } + + CYSFConvolution conv; + conv.start(); + + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + unsigned char output[23U]; + conv.chainback(output, 176U); + + bool ret = CCRC::checkCCITT162(output, 22U); + if (ret) { + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + switch (fn) { + case 0U: + if (m_dest == NULL) { + m_dest = new unsigned char[YSF_CALLSIGN_LENGTH]; + ::memcpy(m_dest, output + 0U, YSF_CALLSIGN_LENGTH); + } + + if (m_source == NULL) { + m_source = new unsigned char[YSF_CALLSIGN_LENGTH]; + ::memcpy(m_source, output + YSF_CALLSIGN_LENGTH, YSF_CALLSIGN_LENGTH); + } + + break; + + case 1U: + if (m_downlink != NULL && !gateway) + ::memcpy(output + 0U, m_downlink, YSF_CALLSIGN_LENGTH); + + if (m_uplink != NULL && !gateway) + ::memcpy(output + YSF_CALLSIGN_LENGTH, m_uplink, YSF_CALLSIGN_LENGTH); + + break; + + case 3U: + // CUtils::dump(1U, "V/D Mode 1 Data, DT1", output, 20U); + break; + + case 4U: + // CUtils::dump(1U, "V/D Mode 1 Data, DT2", output, 20U); + break; + + case 5U: + // CUtils::dump(1U, "V/D Mode 1 Data, DT3", output, 20U); + break; + + case 6U: + // CUtils::dump(1U, "V/D Mode 1 Data, DT4", output, 20U); + break; + + case 7U: + // CUtils::dump(1U, "V/D Mode 1 Data, DT5", output, 20U); + break; + + default: + break; + } + + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + p1 = data; + p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p1, p2, 9U); + p1 += 18U; p2 += 9U; + } + } + + return ret && (fn == 0U); +} + +unsigned int CYSFPayload::processVDMode2Audio(unsigned char* data) +{ + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned int errors = 0U; + unsigned int offset = 40U; // DCH(0) + + // We have a total of 5 VCH sections, iterate through each + for (unsigned int j = 0U; j < 5U; j++, offset += 144U) { + unsigned int errs = 0U; + + unsigned char vch[13U]; + + // Deinterleave + for (unsigned int i = 0U; i < 104U; i++) { + unsigned int n = INTERLEAVE_TABLE_26_4[i]; + bool s = READ_BIT1(data, offset + n); + WRITE_BIT1(vch, i, s); + } + + // "Un-whiten" (descramble) + for (unsigned int i = 0U; i < 13U; i++) + vch[i] ^= WHITENING_DATA[i]; + + // errors += READ_BIT1(vch, 103); // Padding bit must be zero but apparently it is not... + + for (unsigned int i = 0U; i < 81U; i += 3) { + uint8_t vote = 0U; + vote += READ_BIT1(vch, i + 0U) ? 1U : 0U; + vote += READ_BIT1(vch, i + 1U) ? 1U : 0U; + vote += READ_BIT1(vch, i + 2U) ? 1U : 0U; + + switch (vote) { + case 1U: // 1 0 0, or 0 1 0, or 0 0 1, convert to 0 0 0 + WRITE_BIT1(vch, i + 0U, false); + WRITE_BIT1(vch, i + 1U, false); + WRITE_BIT1(vch, i + 2U, false); + errs++; + break; + case 2U: // 1 1 0, or 0 1 1, or 1 0 1, convert to 1 1 1 + WRITE_BIT1(vch, i + 0U, true); + WRITE_BIT1(vch, i + 1U, true); + WRITE_BIT1(vch, i + 2U, true); + errs++; + break; + default: // 0U (0 0 0), or 3U (1 1 1), no errors + break; + } + } + + // Reconstruct only if we have bit errors. + if (errs > 0U) { + // Accumulate the total number of errors + errors += errs; + + // Scramble + for (unsigned int i = 0U; i < 13U; i++) + vch[i] ^= WHITENING_DATA[i]; + + // Interleave + for (unsigned int i = 0U; i < 104U; i++) { + unsigned int n = INTERLEAVE_TABLE_26_4[i]; + bool s = READ_BIT1(vch, i); + WRITE_BIT1(data, offset + n, s); + } + } + } + + // "errors" is the number of triplets that were recognized to be corrupted + // and that were corrected. There are 27 of those per VCH and 5 VCH per CC, + // yielding a total of 27*5 = 135. I believe the expected value of this + // error distribution to be Bin(1;3,BER)+Bin(2;3,BER) which entails 75% for + // BER = 0.5. + return errors; +} + +bool CYSFPayload::processVDMode2Data(unsigned char* data, unsigned char fn, bool gateway) +{ + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char dch[25U]; + + unsigned char* p1 = data; + unsigned char* p2 = dch; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p2, p1, 5U); + p1 += 18U; p2 += 5U; + } + + CYSFConvolution conv; + conv.start(); + + for (unsigned int i = 0U; i < 100U; i++) { + unsigned int n = INTERLEAVE_TABLE_5_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + unsigned char output[13U]; + conv.chainback(output, 96U); + + bool ret = CCRC::checkCCITT162(output, 12U); + if (ret) { + for (unsigned int i = 0U; i < 10U; i++) + output[i] ^= WHITENING_DATA[i]; + + switch (fn) { + case 0U: + if (m_dest == NULL) { + m_dest = new unsigned char[YSF_CALLSIGN_LENGTH]; + ::memcpy(m_dest, output, YSF_CALLSIGN_LENGTH); + } + break; + + case 1U: + if (m_source == NULL) { + m_source = new unsigned char[YSF_CALLSIGN_LENGTH]; + ::memcpy(m_source, output, YSF_CALLSIGN_LENGTH); + } + break; + + case 2U: + if (m_downlink != NULL && !gateway) + ::memcpy(output, m_downlink, YSF_CALLSIGN_LENGTH); + break; + + case 3U: + if (m_uplink != NULL && !gateway) + ::memcpy(output, m_uplink, YSF_CALLSIGN_LENGTH); + break; + + case 6U: + // CUtils::dump(1U, "V/D Mode 2 Data, DT1", output, YSF_CALLSIGN_LENGTH); + break; + + case 7U: + // CUtils::dump(1U, "V/D Mode 2 Data, DT2", output, YSF_CALLSIGN_LENGTH); + break; + + default: + break; + } + + for (unsigned int i = 0U; i < 10U; i++) + output[i] ^= WHITENING_DATA[i]; + + CCRC::addCCITT162(output, 12U); + output[12U] = 0x00U; + + unsigned char convolved[25U]; + conv.encode(output, convolved, 100U); + + unsigned char bytes[25U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 100U; i++) { + unsigned int n = INTERLEAVE_TABLE_5_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + p1 = data; + p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p1, p2, 5U); + p1 += 18U; p2 += 5U; + } + } + + return ret && (fn == 0U || fn == 1U); +} + +bool CYSFPayload::processDataFRModeData(unsigned char* data, unsigned char fn, bool gateway) +{ + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char dch[45U]; + + unsigned char* p1 = data; + unsigned char* p2 = dch; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p2, p1, 9U); + p1 += 18U; p2 += 9U; + } + + CYSFConvolution conv; + conv.start(); + + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + unsigned char output[23U]; + conv.chainback(output, 176U); + + bool ret1 = CCRC::checkCCITT162(output, 22U); + if (ret1) { + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + switch (fn) { + case 0U: + // CUtils::dump(1U, "FR Mode Data, CSD1", output, 20U); + + if (m_dest == NULL) { + m_dest = new unsigned char[YSF_CALLSIGN_LENGTH]; + ::memcpy(m_dest, output + 0U, YSF_CALLSIGN_LENGTH); + } + + if (m_source == NULL) { + m_source = new unsigned char[YSF_CALLSIGN_LENGTH]; + ::memcpy(m_source, output + YSF_CALLSIGN_LENGTH, YSF_CALLSIGN_LENGTH); + } + + break; + + case 1U: + // CUtils::dump(1U, "FR Mode Data, CSD3", output, 20U); + break; + + case 2U: + // CUtils::dump(1U, "FR Mode Data, DT2", output, 20U); + break; + + case 3U: + // CUtils::dump(1U, "FR Mode Data, DT4", output, 20U); + break; + + case 4U: + // CUtils::dump(1U, "FR Mode Data, DT6", output, 20U); + break; + + case 5U: + // CUtils::dump(1U, "FR Mode Data, DT8", output, 20U); + break; + + case 6U: + // CUtils::dump(1U, "FR Mode Data, DT10", output, 20U); + break; + + case 7U: + // CUtils::dump(1U, "FR Mode Data, DT12", output, 20U); + break; + + default: + break; + } + + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + p1 = data; + p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p1, p2, 9U); + p1 += 18U; p2 += 9U; + } + } + + p1 = data + 9U; + p2 = dch; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p2, p1, 9U); + p1 += 18U; p2 += 9U; + } + + conv.start(); + + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + conv.chainback(output, 176U); + + bool ret2 = CCRC::checkCCITT162(output, 22U); + if (ret2) { + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + switch (fn) { + case 0U: + // CUtils::dump(1U, "FR Mode Data, CSD2", output, 20U); + + if (m_downlink != NULL && !gateway) + ::memcpy(output + 0U, m_downlink, YSF_CALLSIGN_LENGTH); + + if (m_uplink != NULL && !gateway) + ::memcpy(output + YSF_CALLSIGN_LENGTH, m_uplink, YSF_CALLSIGN_LENGTH); + + break; + + case 1U: + // CUtils::dump(1U, "FR Mode Data, DT1", output, 20U); + break; + + case 2U: + // CUtils::dump(1U, "FR Mode Data, DT3", output, 20U); + break; + + case 3U: + // CUtils::dump(1U, "FR Mode Data, DT5", output, 20U); + break; + + case 4U: + // CUtils::dump(1U, "FR Mode Data, DT7", output, 20U); + break; + + case 5U: + // CUtils::dump(1U, "FR Mode Data, DT9", output, 20U); + break; + + case 6U: + // CUtils::dump(1U, "FR Mode Data, DT11", output, 20U); + break; + + case 7U: + // CUtils::dump(1U, "FR Mode Data, DT13", output, 20U); + break; + + default: + break; + } + + for (unsigned int i = 0U; i < 20U; i++) + output[i] ^= WHITENING_DATA[i]; + + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + p1 = data + 9U; + p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p1, p2, 9U); + p1 += 18U; p2 += 9U; + } + } + + return ret1 && (fn == 0U); +} + +unsigned int CYSFPayload::processVoiceFRModeAudio2(unsigned char* data) +{ + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + // Regenerate the IMBE FEC + unsigned int errors = 0U; + errors += m_fec.regenerateIMBE(data + 54U); + errors += m_fec.regenerateIMBE(data + 72U); + + return errors; +} + +unsigned int CYSFPayload::processVoiceFRModeAudio5(unsigned char* data) +{ + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + // Regenerate the IMBE FEC + unsigned int errors = 0U; + errors += m_fec.regenerateIMBE(data + 0U); + errors += m_fec.regenerateIMBE(data + 18U); + errors += m_fec.regenerateIMBE(data + 36U); + errors += m_fec.regenerateIMBE(data + 54U); + errors += m_fec.regenerateIMBE(data + 72U); + + return errors; +} + +bool CYSFPayload::processVoiceFRModeData(unsigned char* data) +{ + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char dch[45U]; + ::memcpy(dch, data, 45U); + + CYSFConvolution conv; + conv.start(); + + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + uint8_t s0 = READ_BIT1(dch, n) ? 1U : 0U; + + n++; + uint8_t s1 = READ_BIT1(dch, n) ? 1U : 0U; + + conv.decode(s0, s1); + } + + unsigned char output[23U]; + conv.chainback(output, 176U); + + bool ret = CCRC::checkCCITT162(output, 22U); + if (ret) { + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + ::memcpy(data, bytes, 45U); + } + + return ret; +} + +void CYSFPayload::writeHeader(unsigned char* data, const unsigned char* csd1, const unsigned char* csd2) +{ + assert(data != NULL); + assert(csd1 != NULL); + assert(csd2 != NULL); + + writeDataFRModeData1(csd1, data); + + writeDataFRModeData2(csd2, data); +} + +void CYSFPayload::writeDataFRModeData1(const unsigned char* dt, unsigned char* data) +{ + assert(dt != NULL); + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char output[25U]; + for (unsigned int i = 0U; i < 20U; i++) + output[i] = dt[i] ^ WHITENING_DATA[i]; + + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + + CYSFConvolution conv; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + unsigned char* p1 = data; + unsigned char* p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p1, p2, 9U); + p1 += 18U; p2 += 9U; + } +} + +void CYSFPayload::writeDataFRModeData2(const unsigned char* dt, unsigned char* data) +{ + assert(dt != NULL); + assert(data != NULL); + + data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES; + + unsigned char output[25U]; + for (unsigned int i = 0U; i < 20U; i++) + output[i] = dt[i] ^ WHITENING_DATA[i]; + + CCRC::addCCITT162(output, 22U); + output[22U] = 0x00U; + + unsigned char convolved[45U]; + + CYSFConvolution conv; + conv.encode(output, convolved, 180U); + + unsigned char bytes[45U]; + unsigned int j = 0U; + for (unsigned int i = 0U; i < 180U; i++) { + unsigned int n = INTERLEAVE_TABLE_9_20[i]; + + bool s0 = READ_BIT1(convolved, j) != 0U; + j++; + + bool s1 = READ_BIT1(convolved, j) != 0U; + j++; + + WRITE_BIT1(bytes, n, s0); + + n++; + WRITE_BIT1(bytes, n, s1); + } + + unsigned char* p1 = data + 9U; + unsigned char* p2 = bytes; + for (unsigned int i = 0U; i < 5U; i++) { + ::memcpy(p1, p2, 9U); + p1 += 18U; p2 += 9U; + } +} + +void CYSFPayload::setUplink(const std::string& callsign) +{ + m_uplink = new unsigned char[YSF_CALLSIGN_LENGTH]; + + std::string uplink = callsign; + uplink.resize(YSF_CALLSIGN_LENGTH, ' '); + + for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) + m_uplink[i] = uplink.at(i); +} + +void CYSFPayload::setDownlink(const std::string& callsign) +{ + m_downlink = new unsigned char[YSF_CALLSIGN_LENGTH]; + + std::string downlink = callsign; + downlink.resize(YSF_CALLSIGN_LENGTH, ' '); + + for (unsigned int i = 0U; i < YSF_CALLSIGN_LENGTH; i++) + m_downlink[i] = downlink.at(i); +} + +unsigned char* CYSFPayload::getSource() +{ + return m_source; +} + +unsigned char* CYSFPayload::getDest() +{ + return m_dest; +} + +void CYSFPayload::reset() +{ + delete[] m_source; + delete[] m_dest; + + m_source = NULL; + m_dest = NULL; +} diff --git a/YSFPayload.h b/YSFPayload.h index ab9e6ca..06b858f 100644 --- a/YSFPayload.h +++ b/YSFPayload.h @@ -1,67 +1,67 @@ -/* -* Copyright (C) 2016,2017,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(YSFPayload_H) -#define YSFPayload_H - -#include "AMBEFEC.h" - -#include - -class CYSFPayload { -public: - CYSFPayload(); - ~CYSFPayload(); - - bool processHeaderData(unsigned char* bytes); - - bool processVDMode1Data(unsigned char* bytes, unsigned char fn, bool gateway = false); - unsigned int processVDMode1Audio(unsigned char* bytes); - - bool processVDMode2Data(unsigned char* bytes, unsigned char fn, bool gateway = false); - unsigned int processVDMode2Audio(unsigned char* bytes); - - bool processDataFRModeData(unsigned char* bytes, unsigned char fn, bool gateway = false); - - bool processVoiceFRModeData(unsigned char* bytes); - - unsigned int processVoiceFRModeAudio2(unsigned char* bytes); - unsigned int processVoiceFRModeAudio5(unsigned char* bytes); - - void writeHeader(unsigned char* data, const unsigned char* csd1, const unsigned char* csd2); - - void writeDataFRModeData1(const unsigned char* dt, unsigned char* data); - void writeDataFRModeData2(const unsigned char* dt, unsigned char* data); - - unsigned char* getSource(); - unsigned char* getDest(); - - void setUplink(const std::string& callsign); - void setDownlink(const std::string& callsign); - - void reset(); - -private: - unsigned char* m_uplink; - unsigned char* m_downlink; - unsigned char* m_source; - unsigned char* m_dest; - CAMBEFEC m_fec; -}; - -#endif +/* +* Copyright (C) 2016,2017,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(YSFPayload_H) +#define YSFPayload_H + +#include "AMBEFEC.h" + +#include + +class CYSFPayload { +public: + CYSFPayload(); + ~CYSFPayload(); + + bool processHeaderData(unsigned char* bytes); + + bool processVDMode1Data(unsigned char* bytes, unsigned char fn, bool gateway = false); + unsigned int processVDMode1Audio(unsigned char* bytes); + + bool processVDMode2Data(unsigned char* bytes, unsigned char fn, bool gateway = false); + unsigned int processVDMode2Audio(unsigned char* bytes); + + bool processDataFRModeData(unsigned char* bytes, unsigned char fn, bool gateway = false); + + bool processVoiceFRModeData(unsigned char* bytes); + + unsigned int processVoiceFRModeAudio2(unsigned char* bytes); + unsigned int processVoiceFRModeAudio5(unsigned char* bytes); + + void writeHeader(unsigned char* data, const unsigned char* csd1, const unsigned char* csd2); + + void writeDataFRModeData1(const unsigned char* dt, unsigned char* data); + void writeDataFRModeData2(const unsigned char* dt, unsigned char* data); + + unsigned char* getSource(); + unsigned char* getDest(); + + void setUplink(const std::string& callsign); + void setDownlink(const std::string& callsign); + + void reset(); + +private: + unsigned char* m_uplink; + unsigned char* m_downlink; + unsigned char* m_source; + unsigned char* m_dest; + CAMBEFEC m_fec; +}; + +#endif diff --git a/linux/init/README.md b/linux/pi-star/init/README.md similarity index 100% rename from linux/init/README.md rename to linux/pi-star/init/README.md diff --git a/linux/init/mmdvmhost b/linux/pi-star/init/mmdvmhost similarity index 100% rename from linux/init/mmdvmhost rename to linux/pi-star/init/mmdvmhost diff --git a/linux/systemd/README.md b/linux/pi-star/systemd/README.md similarity index 100% rename from linux/systemd/README.md rename to linux/pi-star/systemd/README.md diff --git a/linux/pi-star/systemd/mmdvmhost.service b/linux/pi-star/systemd/mmdvmhost.service new file mode 100644 index 0000000..a4de9f3 --- /dev/null +++ b/linux/pi-star/systemd/mmdvmhost.service @@ -0,0 +1,12 @@ +[Unit] +Description=MMDVMHost Radio Servce +After=syslog.target network.target + +[Service] +Type=forking +ExecStart=/usr/local/sbin/mmdvmhost_service start +ExecStop=/usr/local/sbin/mmdvmhost_service stop +ExecReload=/usr/local/sbin/mmdvmhost_service restart + +[Install] +WantedBy=multi-user.target diff --git a/linux/systemd/mmdvmhost.timer b/linux/pi-star/systemd/mmdvmhost.timer similarity index 100% rename from linux/systemd/mmdvmhost.timer rename to linux/pi-star/systemd/mmdvmhost.timer diff --git a/linux/systemd/mmdvmhost_service b/linux/pi-star/systemd/mmdvmhost_service similarity index 100% rename from linux/systemd/mmdvmhost_service rename to linux/pi-star/systemd/mmdvmhost_service diff --git a/linux/systemd/mmdvmhost.service b/linux/systemd/mmdvmhost.service index a4de9f3..27ca4ee 100644 --- a/linux/systemd/mmdvmhost.service +++ b/linux/systemd/mmdvmhost.service @@ -1,12 +1,12 @@ [Unit] -Description=MMDVMHost Radio Servce +Description=MMDVMHost Radio Service After=syslog.target network.target [Service] +User=mmdvm Type=forking -ExecStart=/usr/local/sbin/mmdvmhost_service start -ExecStop=/usr/local/sbin/mmdvmhost_service stop -ExecReload=/usr/local/sbin/mmdvmhost_service restart +ExecStart=/usr/local/bin/MMDVMHost +Restart=on-abnormal [Install] WantedBy=multi-user.target