diff --git a/CASTInfo.cpp b/CASTInfo.cpp index 603ee20..46178c0 100644 --- a/CASTInfo.cpp +++ b/CASTInfo.cpp @@ -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..a369aaa 100644 --- a/CASTInfo.h +++ b/CASTInfo.h @@ -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(); diff --git a/Conf.cpp b/Conf.cpp index 4ec01c5..8dc04cb 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -43,6 +43,7 @@ enum SECTION { SECTION_FUSION, SECTION_P25, SECTION_NXDN, + SECTION_M17, SECTION_POCSAG, SECTION_FM, SECTION_DSTAR_NETWORK, @@ -50,6 +51,7 @@ enum SECTION { SECTION_FUSION_NETWORK, SECTION_P25_NETWORK, SECTION_NXDN_NETWORK, + SECTION_M17_NETWORK, SECTION_POCSAG_NETWORK, SECTION_TFTSERIAL, SECTION_HD44780, @@ -103,6 +105,7 @@ 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_modemRSSIMappingFile(), @@ -166,6 +169,11 @@ m_nxdnSelfOnly(false), m_nxdnRemoteGateway(false), m_nxdnTXHang(5U), m_nxdnModeHang(10U), +m_m17Enabled(false), +m_m17SelfOnly(false), +m_m17AllowEncryption(false), +m_m17TXHang(5U), +m_m17ModeHang(10U), m_pocsagEnabled(false), m_pocsagFrequency(0U), m_fmEnabled(false), @@ -237,6 +245,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), @@ -331,6 +345,8 @@ bool CConf::read() 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) @@ -345,6 +361,8 @@ bool CConf::read() 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, "[TFT Serial]", 12U) == 0) @@ -406,12 +424,12 @@ bool CConf::read() else if (::strcmp(key, "Duplex") == 0) m_duplex = ::atoi(value) == 1; else if (::strcmp(key, "ModeHang") == 0) - m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = - m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = (unsigned int)::atoi(value); + m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = m_nxdnNetworkModeHang = m_m17NetworkModeHang = + m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = m_nxdnModeHang = m_m17ModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "RFModeHang") == 0) - m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = (unsigned int)::atoi(value); + m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = m_nxdnModeHang = m_m17ModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "NetModeHang") == 0) - m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = (unsigned int)::atoi(value); + m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = m_nxdnNetworkModeHang = m_m17NetworkModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "Display") == 0) m_display = value; else if (::strcmp(key, "Daemon") == 0) @@ -485,7 +503,7 @@ bool CConf::read() 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)); + m_modemFMTXLevel = m_modemCWIdTXLevel = m_modemDStarTXLevel = m_modemDMRTXLevel = m_modemYSFTXLevel = m_modemP25TXLevel = m_modemNXDNTXLevel = m_modemM17TXLevel = float(::atof(value)); else if (::strcmp(key, "CWIdTXLevel") == 0) m_modemCWIdTXLevel = float(::atof(value)); else if (::strcmp(key, "D-StarTXLevel") == 0) @@ -498,6 +516,8 @@ bool CConf::read() 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) @@ -698,13 +718,23 @@ bool CConf::read() 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, "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_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) { @@ -859,6 +889,19 @@ bool CConf::read() 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; @@ -1172,6 +1215,11 @@ float CConf::getModemNXDNTXLevel() const return m_modemNXDNTXLevel; } +float CConf::getModemM17TXLevel() const +{ + return m_modemM17TXLevel; +} + float CConf::getModemPOCSAGTXLevel() const { return m_modemPOCSAGTXLevel; @@ -1487,6 +1535,31 @@ unsigned int CConf::getNXDNModeHang() const return m_nxdnModeHang; } +bool CConf::getM17Enabled() const +{ + return m_m17Enabled; +} + +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; @@ -1842,6 +1915,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; diff --git a/Conf.h b/Conf.h index cc29715..97cd45d 100644 --- a/Conf.h +++ b/Conf.h @@ -84,6 +84,7 @@ public: float getModemYSFTXLevel() const; float getModemP25TXLevel() const; float getModemNXDNTXLevel() const; + float getModemM17TXLevel() const; float getModemPOCSAGTXLevel() const; float getModemFMTXLevel() const; std::string getModemRSSIMappingFile() const; @@ -162,6 +163,13 @@ public: unsigned int getNXDNTXHang() const; unsigned int getNXDNModeHang() const; + // The M17 section + bool getM17Enabled() 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; @@ -247,6 +255,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; @@ -355,6 +371,7 @@ private: float m_modemYSFTXLevel; float m_modemP25TXLevel; float m_modemNXDNTXLevel; + float m_modemM17TXLevel; float m_modemPOCSAGTXLevel; float m_modemFMTXLevel; std::string m_modemRSSIMappingFile; @@ -426,6 +443,12 @@ private: unsigned int m_nxdnTXHang; unsigned int m_nxdnModeHang; + bool m_m17Enabled; + bool m_m17SelfOnly; + bool m_m17AllowEncryption; + unsigned int m_m17TXHang; + unsigned int m_m17ModeHang; + bool m_pocsagEnabled; unsigned int m_pocsagFrequency; @@ -504,6 +527,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; diff --git a/Defines.h b/Defines.h index 18c103e..81973ff 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; diff --git a/Display.cpp b/Display.cpp index 39b0a09..bacba26 100644 --- a/Display.cpp +++ b/Display.cpp @@ -335,6 +335,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 +426,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 +521,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() */ diff --git a/Display.h b/Display.h index e23b3ec..165d05c 100644 --- a/Display.h +++ b/Display.h @@ -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(); @@ -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/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..060f4ef --- /dev/null +++ b/M17Control.cpp @@ -0,0 +1,841 @@ +/* + * 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, bool selfOnly, bool allowEncryption, CM17Network* network, CDisplay* display, unsigned int timeout, bool duplex, CRSSIInterpolator* rssiMapper) : +m_callsign(callsign), +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_netLICH(), +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) { + 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_rfFN = 0U; + +#if defined(DUMP_M17) + openFile(); +#endif + m_rfLICH.setLinkSetup(frame); + + 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; + } + } + + if (!m_allowEncryption) { + bool ret = m_rfLICH.isNONCENull(); + 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 data transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_DATA; + break; + case 2U: + LogMessage("M17, received RF voice transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_AUDIO; + break; + case 3U: + LogMessage("M17, received RF voice + data transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_AUDIO; + break; + default: + LogMessage("M17, received RF unknown transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_DATA; + break; + } + + m_display->writeM17(source.c_str(), dest.c_str(), "R"); + +#if defined(DUMP_M17) + writeFile(data + 2U); +#endif + if (m_duplex) { + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + // Generate the sync + CSync::addM17Sync(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); + } + + return true; + } else { + m_rfState = RS_RF_LATE_ENTRY; + } + } + + if (m_rfState == RS_RF_LATE_ENTRY) { + 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); + + 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 char lich[M17_LICH_FRAGMENT_LENGTH_BYTES]; + CM17Utils::combineFragmentLICH(lich1, lich2, lich3, lich4, lich); + + m_rfLICH.setFragment(lich, m_rfFN); + + 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; + +#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 late entry data transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_DATA; + break; + case 2U: + LogMessage("M17, received RF late entry voice transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_AUDIO; + break; + case 3U: + LogMessage("M17, received RF late entry voice + data transmission from %s to %s", source.c_str(), dest.c_str()); + m_rfState = RS_RF_AUDIO; + break; + default: + LogMessage("M17, received RF late entry unknown transmission from %s to %s", 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_DATA; + data[1U] = 0x00U; + + // Generate the sync + CSync::addM17Sync(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) { +#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::addM17Sync(rfData + 2U); + + unsigned char lich[M17_LICH_FRAGMENT_LENGTH_BYTES]; + m_netLICH.getFragment(lich, m_rfFN); + + unsigned int frag1, frag2, frag3, frag4; + CM17Utils::splitFragmentLICH(lich, frag1, frag2, frag3, frag4); + + // Add Golay to the LICH fragment here + unsigned int lich1 = CGolay24128::encode24128(frag1); + unsigned int lich2 = CGolay24128::encode24128(frag2); + unsigned int lich3 = CGolay24128::encode24128(frag3); + unsigned int lich4 = CGolay24128::encode24128(frag4); + + CM17Utils::combineFragmentLICHFEC(lich1, lich2, lich3, lich4, rfData + 2U + M17_SYNC_LENGTH_BYTES); + + // Add the Convolution FEC + conv.encodeData(frame, rfData + 2U + M17_SYNC_LENGTH_BYTES + M17_LICH_FRAGMENT_FEC_LENGTH_BYTES); + + // 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++; + + // 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) { + 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(); + + 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_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; + + // Create a dummy start message + unsigned char start[M17_FRAME_LENGTH_BYTES + 2U]; + + start[0U] = TAG_DATA; + start[1U] = 0x00U; + + // Generate the sync + CSync::addM17Sync(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::addM17Sync(data + 2U); + + m_netFrames++; + + // Add the fragment LICH + uint16_t fn = (netData[28U] << 8) + (netData[29U] << 0); + + unsigned char lich[M17_LICH_FRAGMENT_LENGTH_BYTES]; + m_netLICH.getFragment(lich, fn); + + unsigned int frag1, frag2, frag3, frag4; + CM17Utils::splitFragmentLICH(lich, frag1, frag2, frag3, frag4); + + // Add Golay to the LICH fragment here + unsigned int lich1 = CGolay24128::encode24128(frag1); + unsigned int lich2 = CGolay24128::encode24128(frag2); + unsigned int lich3 = CGolay24128::encode24128(frag3); + unsigned int lich4 = CGolay24128::encode24128(frag4); + + CM17Utils::combineFragmentLICHFEC(lich1, lich2, lich3, lich4, data + 2U + M17_SYNC_LENGTH_BYTES); + + // Add the FN and the data/audio + unsigned char payload[M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + 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); + + // EOT handling + 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..bff2d19 --- /dev/null +++ b/M17Control.h @@ -0,0 +1,101 @@ +/* + * 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, 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; + 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; + CM17LICH m_netLICH; + 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..53b5f63 --- /dev/null +++ b/M17Defines.h @@ -0,0 +1,56 @@ +/* + * 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_SYNC_BYTES[] = {0x32U, 0x43U}; +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 / 5U; +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..0c0469a --- /dev/null +++ b/M17LICH.cpp @@ -0,0 +1,147 @@ +/* + * 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 short fn) const +{ + assert(data != NULL); + + CM17CRC::encodeCRC(m_lich, M17_LICH_LENGTH_BYTES); + + unsigned int n = (fn & 0x7FFFU) % 5U; + + ::memcpy(data, m_lich + (n * M17_LICH_FRAGMENT_LENGTH_BYTES), M17_LICH_FRAGMENT_LENGTH_BYTES); +} + +void CM17LICH::setFragment(const unsigned char* data, unsigned short fn) +{ + assert(data != NULL); + + unsigned int n = (fn & 0x7FFFU) % 5U; + + ::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..8842e22 --- /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 short fn) const; + void setFragment(const unsigned char* data, unsigned short fn); + +private: + unsigned char* m_lich; + bool m_valid; +}; + +#endif diff --git a/M17Network.cpp b/M17Network.cpp new file mode 100644 index 0000000..6fa65ae --- /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 026cf94..3fb7a23 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -59,6 +59,7 @@ RFLevel=100 # YSFTXLevel=50 # P25TXLevel=50 # NXDNTXLevel=50 +# M17TXLevel-50 # POCSAGTXLevel=50 # FMTXLevel=50 RSSIMappingFile=RSSI.dat @@ -133,6 +134,12 @@ RemoteGateway=0 TXHang=5 # ModeHang=10 +[M17] +Enable=1 +SelfOnly=0 +TXHang=5 +# ModeHang=10 + [POCSAG] Enable=1 Frequency=439987500 @@ -217,6 +224,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 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index 333b701..408e971 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -120,12 +120,14 @@ m_dmr(NULL), m_ysf(NULL), m_p25(NULL), m_nxdn(NULL), +m_m17(NULL), m_pocsag(NULL), m_dstarNetwork(NULL), m_dmrNetwork(NULL), m_ysfNetwork(NULL), m_p25Network(NULL), m_nxdnNetwork(NULL), +m_m17Network(NULL), m_pocsagNetwork(NULL), m_display(NULL), m_ump(NULL), @@ -135,11 +137,13 @@ m_dmrRFModeHang(10U), m_ysfRFModeHang(10U), m_p25RFModeHang(10U), m_nxdnRFModeHang(10U), +m_m17RFModeHang(10U), m_dstarNetModeHang(3U), m_dmrNetModeHang(3U), m_ysfNetModeHang(3U), m_p25NetModeHang(3U), m_nxdnNetModeHang(3U), +m_m17NetModeHang(3U), m_pocsagNetModeHang(3U), m_modeTimer(1000U), m_dmrTXTimer(1000U), @@ -151,6 +155,7 @@ m_dmrEnabled(false), m_ysfEnabled(false), m_p25Enabled(false), m_nxdnEnabled(false), +m_m17Enabled(false), m_pocsagEnabled(false), m_fmEnabled(false), m_cwIdTime(0U), @@ -317,6 +322,12 @@ 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) @@ -499,7 +510,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(); @@ -602,6 +612,21 @@ 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(); + 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(" Allow Encryption: %s", allowEncryption ? "yes" : "no"); + LogInfo(" TX Hang: %us", txHang); + LogInfo(" Mode Hang: %us", m_m17RFModeHang); + + m_m17 = new CM17Control(m_callsign, selfOnly, allowEncryption, m_m17Network, m_display, m_timeout, m_duplex, rssi); + } + CTimer pocsagTimer(1000U, 30U); if (m_pocsagEnabled) { @@ -810,6 +835,22 @@ 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->readTransparentData(data); if (transparentSocket != NULL && len > 0U) transparentSocket->write(data, len, transparentAddress, transparentAddrLen); @@ -941,6 +982,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) { @@ -990,6 +1050,8 @@ 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); @@ -1003,6 +1065,8 @@ 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); @@ -1117,6 +1181,11 @@ 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; @@ -1137,6 +1206,7 @@ int CMMDVMHost::run() delete m_ysf; delete m_p25; delete m_nxdn; + delete m_m17; delete m_pocsag; return 0; @@ -1159,6 +1229,7 @@ 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(); bool trace = m_conf.getModemTrace(); @@ -1168,6 +1239,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(); @@ -1200,6 +1272,7 @@ 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(" TX Frequency: %uHz (%uHz)", txFrequency, txFrequency + txOffset); @@ -1207,13 +1280,14 @@ bool CMMDVMHost::createModem() 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); + m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_m17Enabled, m_pocsagEnabled, m_fmEnabled); + m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, m17TXLevel, pocsagTXLevel, fmTXLevel); 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); if (m_fmEnabled) { std::string callsign = m_conf.getFMCallsign(); @@ -1468,6 +1542,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(); @@ -1505,6 +1606,7 @@ 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_duplex = m_conf.getDuplex(); @@ -1522,6 +1624,7 @@ 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"); } @@ -1543,6 +1646,8 @@ 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_dstar != NULL) @@ -1555,6 +1660,8 @@ 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); m_modem->setMode(MODE_DSTAR); @@ -1577,6 +1684,8 @@ 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_dstar != NULL) @@ -1589,6 +1698,8 @@ 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); m_modem->setMode(MODE_DMR); @@ -1615,6 +1726,8 @@ 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_dstar != NULL) @@ -1627,6 +1740,8 @@ 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); m_modem->setMode(MODE_YSF); @@ -1649,6 +1764,8 @@ 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_dstar != NULL) @@ -1661,6 +1778,8 @@ 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); m_modem->setMode(MODE_P25); @@ -1683,6 +1802,8 @@ 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_dstar != NULL) @@ -1695,6 +1816,8 @@ void CMMDVMHost::setMode(unsigned char mode) 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); m_modem->setMode(MODE_NXDN); @@ -1706,6 +1829,44 @@ void CMMDVMHost::setMode(unsigned char mode) 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) + 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(false); + if (m_m17 != NULL) + m_m17->enable(true); + if (m_pocsag != NULL) + m_pocsag->enable(false); + m_modem->setMode(MODE_M17); + if (m_ump != NULL) + m_ump->setMode(MODE_M17); + m_mode = MODE_M17; + m_modeTimer.start(); + m_cwIdTimer.stop(); + createLockFile("M17"); + break; + case MODE_POCSAG: if (m_dstarNetwork != NULL) m_dstarNetwork->enable(false); @@ -1717,6 +1878,8 @@ 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_dstar != NULL) @@ -1729,6 +1892,8 @@ 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); m_modem->setMode(MODE_POCSAG); @@ -1751,6 +1916,8 @@ 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_dstar != NULL) @@ -1763,6 +1930,8 @@ 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_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { @@ -1789,6 +1958,8 @@ 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_dstar != NULL) @@ -1801,6 +1972,8 @@ 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_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { @@ -1829,6 +2002,8 @@ 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_dstar != NULL) @@ -1841,6 +2016,8 @@ 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_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { @@ -1867,6 +2044,8 @@ 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_dstar != NULL) @@ -1879,6 +2058,8 @@ 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_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { @@ -1958,6 +2139,10 @@ 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) processModeCommand(MODE_FM, 0); @@ -1992,6 +2177,12 @@ void CMMDVMHost::remoteControl() 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) processEnableCommand(m_fmEnabled, true); @@ -2026,6 +2217,12 @@ void CMMDVMHost::remoteControl() 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) processEnableCommand(m_fmEnabled, false); @@ -2041,18 +2238,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; } @@ -2078,9 +2277,11 @@ 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); + 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_m17Enabled, m_pocsagEnabled, m_fmEnabled); if (!m_modem->writeConfig()) LogError("Cannot write Config to MMDVM"); } diff --git a/MMDVMHost.h b/MMDVMHost.h index 4c58f98..9fadcb9 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -29,10 +29,12 @@ #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 "DMRLookup.h" #include "Display.h" #include "Timer.h" @@ -59,12 +61,14 @@ private: CYSFControl* m_ysf; CP25Control* m_p25; CNXDNControl* m_nxdn; + CM17Control* m_m17; CPOCSAGControl* m_pocsag; CDStarNetwork* m_dstarNetwork; CDMRNetwork* m_dmrNetwork; CYSFNetwork* m_ysfNetwork; CP25Network* m_p25Network; INXDNNetwork* m_nxdnNetwork; + CM17Network* m_m17Network; CPOCSAGNetwork* m_pocsagNetwork; CDisplay* m_display; CUMP* m_ump; @@ -74,11 +78,13 @@ private: unsigned int m_ysfRFModeHang; unsigned int m_p25RFModeHang; unsigned int m_nxdnRFModeHang; + unsigned int m_m17RFModeHang; 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; CTimer m_modeTimer; CTimer m_dmrTXTimer; @@ -90,6 +96,7 @@ private: bool m_ysfEnabled; bool m_p25Enabled; bool m_nxdnEnabled; + bool m_m17Enabled; bool m_pocsagEnabled; bool m_fmEnabled; unsigned int m_cwIdTime; @@ -110,6 +117,7 @@ private: bool createYSFNetwork(); bool createP25Network(); bool createNXDNNetwork(); + bool createM17Network(); bool createPOCSAGNetwork(); void remoteControl(); diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index ef7577c..e190780 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -188,6 +188,13 @@ + + + + + + + @@ -283,6 +290,11 @@ + + + + + @@ -321,6 +333,7 @@ + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index ee1ff1b..92f9aca 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -299,6 +299,27 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -562,5 +583,23 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file diff --git a/Makefile b/Makefile index ed7568e..24323fe 100644 --- a/Makefile +++ b/Makefile @@ -8,13 +8,13 @@ LDFLAGS = -g OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o DMREmbeddedData.o DMRFullLC.o \ - DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o 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 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 + 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 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 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 all: MMDVMHost RemoteCommand diff --git a/Makefile.Pi b/Makefile.Pi index cd01588..22dca30 100644 --- a/Makefile.Pi +++ b/Makefile.Pi @@ -8,13 +8,13 @@ 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 DMREMB.o DMREmbeddedData.o DMRFullLC.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 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 + 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 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 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 all: MMDVMHost RemoteCommand diff --git a/Makefile.Pi.Adafruit b/Makefile.Pi.Adafruit index 54d2b28..cd37ada 100644 --- a/Makefile.Pi.Adafruit +++ b/Makefile.Pi.Adafruit @@ -9,13 +9,13 @@ 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 DMREMB.o DMREmbeddedData.o DMRFullLC.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 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 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 + 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 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 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 all: MMDVMHost RemoteCommand diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780 index 10e5dd3..004aac0 100644 --- a/Makefile.Pi.HD44780 +++ b/Makefile.Pi.HD44780 @@ -8,13 +8,13 @@ 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 DMREMB.o DMREmbeddedData.o DMRFullLC.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 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 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 + 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 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 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 all: MMDVMHost RemoteCommand diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED index 8206dc7..8164647 100644 --- a/Makefile.Pi.OLED +++ b/Makefile.Pi.OLED @@ -12,13 +12,13 @@ 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 DMREMB.o DMREmbeddedData.o DMRFullLC.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 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 + 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 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 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 all: MMDVMHost RemoteCommand diff --git a/Makefile.Pi.PCF8574 b/Makefile.Pi.PCF8574 index 192c86e..c105a58 100644 --- a/Makefile.Pi.PCF8574 +++ b/Makefile.Pi.PCF8574 @@ -9,13 +9,13 @@ 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 DMREMB.o DMREmbeddedData.o DMRFullLC.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 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 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 + 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 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 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 all: MMDVMHost RemoteCommand diff --git a/Modem.cpp b/Modem.cpp index 52a497d..2451c9b 100644 --- a/Modem.cpp +++ b/Modem.cpp @@ -23,6 +23,7 @@ #include "P25Defines.h" #include "NXDNDefines.h" #include "POCSAGDefines.h" +#include "M17Defines.h" #include "Thread.h" #include "Modem.h" #include "NullModem.h" @@ -74,6 +75,9 @@ 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_DATA = 0x45U; +const unsigned char MMDVM_M17_LOST = 0x46U; + const unsigned char MMDVM_POCSAG_DATA = 0x50U; const unsigned char MMDVM_FM_PARAMS1 = 0x60U; @@ -106,6 +110,7 @@ m_ysfLoDev(false), m_ysfTXHang(4U), m_p25TXHang(5U), m_nxdnTXHang(5U), +m_m17TXHang(5U), m_duplex(duplex), m_rxInvert(rxInvert), m_txInvert(txInvert), @@ -119,6 +124,7 @@ 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_rfLevel(0.0F), @@ -133,6 +139,7 @@ m_dmrEnabled(false), m_ysfEnabled(false), m_p25Enabled(false), m_nxdnEnabled(false), +m_m17Enabled(false), m_pocsagEnabled(false), m_fmEnabled(false), m_rxDCOffset(0), @@ -153,6 +160,8 @@ 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_rxTransparentData(1000U, "Modem RX Transparent"), m_txTransparentData(1000U, "Modem TX Transparent"), @@ -166,6 +175,7 @@ m_dmrSpace2(0U), m_ysfSpace(0U), m_p25Space(0U), m_nxdnSpace(0U), +m_m17Space(0U), m_pocsagSpace(0U), m_tx(false), m_cd(false), @@ -232,18 +242,19 @@ void CModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int tx m_pocsagFrequency = pocsagFrequency + txOffset; } -void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled, bool fmEnabled) +void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool m17Enabled, bool pocsagEnabled, bool fmEnabled) { m_dstarEnabled = dstarEnabled; m_dmrEnabled = dmrEnabled; m_ysfEnabled = ysfEnabled; m_p25Enabled = p25Enabled; m_nxdnEnabled = nxdnEnabled; + m_m17Enabled = m17Enabled; 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) +void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float m17TXLevel, float pocsagTXLevel, float fmTXLevel) { m_rxLevel = rxLevel; m_cwIdTXLevel = cwIdTXLevel; @@ -252,6 +263,7 @@ void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, flo m_ysfTXLevel = ysfTXLevel; m_p25TXLevel = p25TXLevel; m_nxdnTXLevel = nxdnTXLevel; + m_m17TXLevel = m17TXLevel; m_pocsagTXLevel = pocsagTXLevel; m_fmTXLevel = fmTXLevel; } @@ -279,6 +291,11 @@ void CModem::setNXDNParams(unsigned int txHang) m_nxdnTXHang = txHang; } +void CModem::setM17Params(unsigned int txHang) +{ + m_m17TXHang = txHang; +} + void CModem::setTransparentDataParams(unsigned int sendFrameType) { m_sendTransparentDataFrameType = sendFrameType; @@ -587,12 +604,39 @@ void CModem::clock(unsigned int ms) } 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_GET_STATUS: { // if (m_trace) // CUtils::dump(1U, "GET_STATUS", m_buffer, m_length); m_p25Space = 0U; m_nxdnSpace = 0U; + m_m17Space = 0U; m_pocsagSpace = 0U; m_mode = m_buffer[4U]; @@ -630,9 +674,11 @@ void CModem::clock(unsigned int ms) m_nxdnSpace = m_buffer[11U]; if (m_length > 12U) m_pocsagSpace = m_buffer[12U]; + if (m_length > 13U) + m_m17Space = m_buffer[13U]; 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)); + // LogMessage("status=%02X, tx=%d, space=%u,%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_m17Space, m_pocsagSpace, int(m_lockout), int(m_cd)); } break; @@ -819,6 +865,23 @@ void CModem::clock(unsigned int ms) 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) + 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); @@ -943,6 +1006,20 @@ unsigned int CModem::readNXDNData(unsigned char* data) return len; } +unsigned int CModem::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 CModem::readTransparentData(unsigned char* data) { assert(data != NULL); @@ -1157,6 +1234,36 @@ bool CModem::writeNXDNData(const unsigned char* data, unsigned int length) return true; } +bool CModem::hasM17Space() const +{ + unsigned int space = m_txM17Data.freeSpace() / (M17_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CModem::writeM17Data(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_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 CModem::hasPOCSAGSpace() const { unsigned int space = m_txPOCSAGData.freeSpace() / (POCSAG_FRAME_LENGTH_BYTES + 4U); @@ -1351,6 +1458,30 @@ bool CModem::writeNXDNInfo(const char* source, bool group, unsigned int dest, co return m_serial->write(buffer, 31U) != 31; } +bool CModem::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 CModem::writePOCSAGInfo(unsigned int ric, const std::string& message) { assert(m_serial != NULL); @@ -1523,7 +1654,7 @@ bool CModem::setConfig() buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 24U; + buffer[1U] = 26U; buffer[2U] = MMDVM_SET_CONFIG; @@ -1558,6 +1689,8 @@ bool CModem::setConfig() buffer[4U] |= 0x20U; if (m_fmEnabled && m_duplex) buffer[4U] |= 0x40U; + if (m_m17Enabled) + buffer[4U] |= 0x80U; buffer[5U] = m_txDelay / 10U; // In 10ms units @@ -1593,10 +1726,13 @@ bool CModem::setConfig() buffer[23U] = (unsigned char)m_nxdnTXHang; - // CUtils::dump(1U, "Written", buffer, 24U); + buffer[24U] = (unsigned char)(m_m17TXLevel * 2.55F + 0.5F); + buffer[25U] = (unsigned char)m_m17TXHang; - int ret = m_serial->write(buffer, 24U); - if (ret != 24) + // CUtils::dump(1U, "Written", buffer, 26U); + + int ret = m_serial->write(buffer, 26U); + if (ret != 26) return false; unsigned int count = 0U; diff --git a/Modem.h b/Modem.h index e28aad4..8c5e53a 100644 --- a/Modem.h +++ b/Modem.h @@ -39,12 +39,13 @@ public: 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 setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool m17Enabled, bool pocsagEnabled, bool fmEnabled); + virtual void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float m17TXLevel, 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 setM17Params(unsigned int txHang); 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); @@ -59,6 +60,7 @@ public: 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 readTransparentData(unsigned char* data); virtual unsigned int readSerial(unsigned char* data, unsigned int length); @@ -69,6 +71,7 @@ public: virtual bool hasYSFSpace() const; virtual bool hasP25Space() const; virtual bool hasNXDNSpace() const; + virtual bool hasM17Space() const; virtual bool hasPOCSAGSpace() const; virtual bool hasTX() const; @@ -84,6 +87,7 @@ public: 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 writeTransparentData(const unsigned char* data, unsigned int length); @@ -93,6 +97,7 @@ public: 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); @@ -122,6 +127,7 @@ private: 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; @@ -135,6 +141,7 @@ private: float m_ysfTXLevel; float m_p25TXLevel; float m_nxdnTXLevel; + float m_m17TXLevel; float m_pocsagTXLevel; float m_fmTXLevel; float m_rfLevel; @@ -149,6 +156,7 @@ private: bool m_ysfEnabled; bool m_p25Enabled; bool m_nxdnEnabled; + bool m_m17Enabled; bool m_pocsagEnabled; bool m_fmEnabled; int m_rxDCOffset; @@ -169,6 +177,8 @@ private: CRingBuffer m_txP25Data; CRingBuffer m_rxNXDNData; CRingBuffer m_txNXDNData; + CRingBuffer m_rxM17Data; + CRingBuffer m_txM17Data; CRingBuffer m_txPOCSAGData; CRingBuffer m_rxTransparentData; CRingBuffer m_txTransparentData; @@ -182,6 +192,7 @@ private: unsigned int m_ysfSpace; unsigned int m_p25Space; unsigned int m_nxdnSpace; + unsigned int m_m17Space; unsigned int m_pocsagSpace; bool m_tx; bool m_cd; 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/RemoteControl.cpp b/RemoteControl.cpp index 3c2d2a8..2e3bf8e 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,6 +102,8 @@ 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(0U) == "disable" && m_args.size() >= DISABLE_ARGS) { @@ -113,6 +117,8 @@ 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(0U) == "page" && m_args.size() >= PAGE_ARGS) { @@ -144,6 +150,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 +171,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..ace1425 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,18 +33,21 @@ 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_DISABLE_DSTAR, RCD_DISABLE_DMR, RCD_DISABLE_YSF, RCD_DISABLE_P25, RCD_DISABLE_NXDN, + RCD_DISABLE_M17, RCD_DISABLE_FM, RCD_PAGE, RCD_CW diff --git a/Sync.cpp b/Sync.cpp index 75156af..34dec37 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,10 @@ 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::addM17Sync(unsigned char* data) +{ + assert(data != NULL); + + ::memcpy(data, M17_SYNC_BYTES, M17_SYNC_LENGTH_BYTES); +} diff --git a/Sync.h b/Sync.h index 3ff325c..2118c96 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,8 @@ public: static void addNXDNSync(unsigned char* data); + static void addM17Sync(unsigned char* data); + private: }; diff --git a/TFTSerial.cpp b/TFTSerial.cpp index dab532e..2d0e9dc 100644 --- a/TFTSerial.cpp +++ b/TFTSerial.cpp @@ -397,7 +397,7 @@ void CTFTSerial::writeNXDNInt(const char* source, bool group, unsigned int dest, setFontSize(FONT_MEDIUM); - // Draw the P25 insignia + // Draw the NXDN insignia displayBitmap(0U, 0U, "NXDN_sm.bmp"); } @@ -427,6 +427,48 @@ void CTFTSerial::clearNXDNInt() displayText(" "); } +void CTFTSerial::writeM17Int(const char* source, const char* dest, const char* type) +{ + assert(source != NULL); + assert(dest != NULL); + assert(type != NULL); + + if (m_mode != MODE_M17) { + // Clear the screen + clearScreen(); + + setFontSize(FONT_MEDIUM); + + // Draw the M17 insignia + displayBitmap(0U, 0U, "M17_sm.bmp"); + } + + char text[30U]; + ::sprintf(text, "%s %.10s", type, source); + + gotoPosPixel(5U, 70U); + displayText(text); + + ::sprintf(text, " %s", dest); + + gotoPosPixel(5U, 90U); + displayText(text); + + m_mode = MODE_M17; +} + +void CTFTSerial::clearM17Int() +{ + 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); diff --git a/TFTSerial.h b/TFTSerial.h index d34e9da..86d4130 100644 --- a/TFTSerial.h +++ b/TFTSerial.h @@ -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(); diff --git a/TFTSerial/M17_sm.bmp b/TFTSerial/M17_sm.bmp new file mode 100644 index 0000000..656e410 Binary files /dev/null and b/TFTSerial/M17_sm.bmp 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/Version.h b/Version.h index b85724b..8d5b383 100644 --- a/Version.h +++ b/Version.h @@ -19,6 +19,6 @@ #if !defined(VERSION_H) #define VERSION_H -const char* VERSION = "20201031"; +const char* VERSION = "20201101"; #endif