diff --git a/Conf.cpp b/Conf.cpp index 41b6801..8629341 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -43,11 +43,13 @@ enum SECTION { SECTION_FUSION, SECTION_P25, SECTION_NXDN, + SECTION_POCSAG, SECTION_DSTAR_NETWORK, SECTION_DMR_NETWORK, SECTION_FUSION_NETWORK, SECTION_P25_NETWORK, SECTION_NXDN_NETWORK, + SECTION_POCSAG_NETWORK, SECTION_TFTSERIAL, SECTION_HD44780, SECTION_NEXTION, @@ -85,6 +87,8 @@ m_dmrIdLookupTime(0U), m_nxdnIdLookupFile(), m_nxdnIdLookupTime(0U), m_modemPort(), +m_modemProtocol("uart"), +m_modemAddress(0x22), m_modemRXInvert(false), m_modemTXInvert(false), m_modemPTTInvert(false), @@ -102,6 +106,7 @@ m_modemDMRTXLevel(50.0F), m_modemYSFTXLevel(50.0F), m_modemP25TXLevel(50.0F), m_modemNXDNTXLevel(50.0F), +m_modemPOCSAGTXLevel(50.0F), m_modemRSSIMappingFile(), m_modemTrace(false), m_modemDebug(false), @@ -109,6 +114,7 @@ m_transparentEnabled(false), m_transparentRemoteAddress(), m_transparentRemotePort(0U), m_transparentLocalPort(0U), +m_transparentSendFrameType(0U), m_umpEnabled(false), m_umpPort(), m_dstarEnabled(false), @@ -141,6 +147,7 @@ m_fusionEnabled(false), m_fusionLowDeviation(false), m_fusionRemoteGateway(false), m_fusionSelfOnly(false), +m_fusionTXHang(4U), m_fusionSQLEnabled(false), m_fusionSQL(0U), m_fusionModeHang(10U), @@ -157,6 +164,8 @@ m_nxdnRAN(1U), m_nxdnSelfOnly(false), m_nxdnRemoteGateway(false), m_nxdnModeHang(10U), +m_pocsagEnabled(false), +m_pocsagFrequency(0U), m_dstarNetworkEnabled(false), m_dstarGatewayAddress(), m_dstarGatewayPort(0U), @@ -170,7 +179,7 @@ m_dmrNetworkLocal(0U), m_dmrNetworkPassword(), m_dmrNetworkOptions(), m_dmrNetworkDebug(false), -m_dmrNetworkJitter(300U), +m_dmrNetworkJitter(360U), m_dmrNetworkSlot1(true), m_dmrNetworkSlot2(true), m_dmrNetworkModeHang(3U), @@ -194,6 +203,13 @@ m_nxdnLocalAddress(), m_nxdnLocalPort(0U), m_nxdnNetworkModeHang(3U), m_nxdnNetworkDebug(false), +m_pocsagNetworkEnabled(false), +m_pocsagGatewayAddress(), +m_pocsagGatewayPort(0U), +m_pocsagLocalAddress(), +m_pocsagLocalPort(0U), +m_pocsagNetworkModeHang(3U), +m_pocsagNetworkDebug(false), m_tftSerialPort("/dev/ttyAMA0"), m_tftSerialBrightness(50U), m_hd44780Rows(2U), @@ -272,6 +288,8 @@ bool CConf::read() section = SECTION_P25; else if (::strncmp(buffer, "[NXDN]", 6U) == 0) section = SECTION_NXDN; + else if (::strncmp(buffer, "[POCSAG]", 8U) == 0) + section = SECTION_POCSAG; else if (::strncmp(buffer, "[D-Star Network]", 16U) == 0) section = SECTION_DSTAR_NETWORK; else if (::strncmp(buffer, "[DMR Network]", 13U) == 0) @@ -282,6 +300,8 @@ bool CConf::read() section = SECTION_P25_NETWORK; else if (::strncmp(buffer, "[NXDN Network]", 14U) == 0) section = SECTION_NXDN_NETWORK; + else if (::strncmp(buffer, "[POCSAG Network]", 16U) == 0) + section = SECTION_POCSAG_NETWORK; else if (::strncmp(buffer, "[TFT Serial]", 12U) == 0) section = SECTION_TFTSERIAL; else if (::strncmp(buffer, "[HD44780]", 9U) == 0) @@ -338,7 +358,7 @@ bool CConf::read() m_daemon = ::atoi(value) == 1; } else if (section == SECTION_INFO) { if (::strcmp(key, "TXFrequency") == 0) - m_txFrequency = (unsigned int)::atoi(value); + m_pocsagFrequency = m_txFrequency = (unsigned int)::atoi(value); else if (::strcmp(key, "RXFrequency") == 0) m_rxFrequency = (unsigned int)::atoi(value); else if (::strcmp(key, "Power") == 0) @@ -388,6 +408,10 @@ bool CConf::read() } else if (section == SECTION_MODEM) { if (::strcmp(key, "Port") == 0) m_modemPort = value; + else if (::strcmp(key, "Protocol") == 0) + m_modemProtocol = value; + else if (::strcmp(key, "Address") == 0) + m_modemAddress = (unsigned int)::strtoul(value, NULL, 16); else if (::strcmp(key, "RXInvert") == 0) m_modemRXInvert = ::atoi(value) == 1; else if (::strcmp(key, "TXInvert") == 0) @@ -424,6 +448,8 @@ bool CConf::read() m_modemP25TXLevel = float(::atof(value)); else if (::strcmp(key, "NXDNTXLevel") == 0) m_modemNXDNTXLevel = float(::atof(value)); + else if (::strcmp(key, "POCSAGTXLevel") == 0) + m_modemPOCSAGTXLevel = float(::atof(value)); else if (::strcmp(key, "RSSIMappingFile") == 0) m_modemRSSIMappingFile = value; else if (::strcmp(key, "Trace") == 0) @@ -439,6 +465,8 @@ bool CConf::read() m_transparentRemotePort = (unsigned int)::atoi(value); else if (::strcmp(key, "LocalPort") == 0) m_transparentLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "SendFrameType") == 0) + m_transparentSendFrameType = (unsigned int)::atoi(value); } else if (section == SECTION_UMP) { if (::strcmp(key, "Enable") == 0) m_umpEnabled = ::atoi(value) == 1; @@ -553,6 +581,8 @@ bool CConf::read() m_fusionRemoteGateway = ::atoi(value) == 1; else if (::strcmp(key, "SelfOnly") == 0) m_fusionSelfOnly = ::atoi(value) == 1; + else if (::strcmp(key, "TXHang") == 0) + m_fusionTXHang = (unsigned int)::atoi(value); else if (::strcmp(key, "ModeHang") == 0) m_fusionModeHang = (unsigned int)::atoi(value); } else if (section == SECTION_P25) { @@ -583,6 +613,11 @@ bool CConf::read() m_nxdnRemoteGateway = ::atoi(value) == 1; else if (::strcmp(key, "ModeHang") == 0) m_nxdnModeHang = (unsigned int)::atoi(value); + } else if (section == SECTION_POCSAG) { + if (::strcmp(key, "Enable") == 0) + m_pocsagEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Frequency") == 0) + m_pocsagFrequency = (unsigned int)::atoi(value); } else if (section == SECTION_DSTAR_NETWORK) { if (::strcmp(key, "Enable") == 0) m_dstarNetworkEnabled = ::atoi(value) == 1; @@ -662,6 +697,21 @@ bool CConf::read() m_nxdnNetworkModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) m_nxdnNetworkDebug = ::atoi(value) == 1; + } else if (section == SECTION_POCSAG_NETWORK) { + if (::strcmp(key, "Enable") == 0) + m_pocsagNetworkEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "LocalAddress") == 0) + m_pocsagLocalAddress = value; + else if (::strcmp(key, "LocalPort") == 0) + m_pocsagLocalPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_pocsagGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_pocsagGatewayPort = (unsigned int)::atoi(value); + else if (::strcmp(key, "ModeHang") == 0) + m_pocsagNetworkModeHang = (unsigned int)::atoi(value); + else if (::strcmp(key, "Debug") == 0) + m_pocsagNetworkDebug = ::atoi(value) == 1; } else if (section == SECTION_TFTSERIAL) { if (::strcmp(key, "Port") == 0) m_tftSerialPort = value; @@ -872,6 +922,16 @@ std::string CConf::getModemPort() const return m_modemPort; } +std::string CConf::getModemProtocol() const +{ + return m_modemProtocol; +} + +unsigned int CConf::getModemAddress() const +{ + return m_modemAddress; +} + bool CConf::getModemRXInvert() const { return m_modemRXInvert; @@ -957,6 +1017,11 @@ float CConf::getModemNXDNTXLevel() const return m_modemNXDNTXLevel; } +float CConf::getModemPOCSAGTXLevel() const +{ + return m_modemPOCSAGTXLevel; +} + std::string CConf::getModemRSSIMappingFile () const { return m_modemRSSIMappingFile; @@ -992,6 +1057,11 @@ unsigned int CConf::getTransparentLocalPort() const return m_transparentLocalPort; } +unsigned int CConf::getTransparentSendFrameType() const +{ + return m_transparentSendFrameType; +} + bool CConf::getUMPEnabled() const { return m_umpEnabled; @@ -1147,6 +1217,11 @@ bool CConf::getFusionRemoteGateway() const return m_fusionRemoteGateway; } +unsigned int CConf::getFusionTXHang() const +{ + return m_fusionTXHang; +} + bool CConf::getFusionSelfOnly() const { return m_fusionSelfOnly; @@ -1232,6 +1307,16 @@ unsigned int CConf::getNXDNModeHang() const return m_nxdnModeHang; } +bool CConf::getPOCSAGEnabled() const +{ + return m_pocsagEnabled; +} + +unsigned int CConf::getPOCSAGFrequency() const +{ + return m_pocsagFrequency; +} + bool CConf::getDStarNetworkEnabled() const { return m_dstarNetworkEnabled; @@ -1417,6 +1502,41 @@ bool CConf::getNXDNNetworkDebug() const return m_nxdnNetworkDebug; } +bool CConf::getPOCSAGNetworkEnabled() const +{ + return m_pocsagNetworkEnabled; +} + +std::string CConf::getPOCSAGGatewayAddress() const +{ + return m_pocsagGatewayAddress; +} + +unsigned int CConf::getPOCSAGGatewayPort() const +{ + return m_pocsagGatewayPort; +} + +std::string CConf::getPOCSAGLocalAddress() const +{ + return m_pocsagLocalAddress; +} + +unsigned int CConf::getPOCSAGLocalPort() const +{ + return m_pocsagLocalPort; +} + +unsigned int CConf::getPOCSAGNetworkModeHang() const +{ + return m_pocsagNetworkModeHang; +} + +bool CConf::getPOCSAGNetworkDebug() const +{ + return m_pocsagNetworkDebug; +} + std::string CConf::getTFTSerialPort() const { return m_tftSerialPort; diff --git a/Conf.h b/Conf.h index 26d0aa2..8b7d16c 100644 --- a/Conf.h +++ b/Conf.h @@ -70,6 +70,8 @@ public: // The Modem section std::string getModemPort() const; + std::string getModemProtocol() const; + unsigned int getModemAddress() const; bool getModemRXInvert() const; bool getModemTXInvert() const; bool getModemPTTInvert() const; @@ -87,6 +89,7 @@ public: float getModemYSFTXLevel() const; float getModemP25TXLevel() const; float getModemNXDNTXLevel() const; + float getModemPOCSAGTXLevel() const; std::string getModemRSSIMappingFile() const; bool getModemTrace() const; bool getModemDebug() const; @@ -96,6 +99,7 @@ public: std::string getTransparentRemoteAddress() const; unsigned int getTransparentRemotePort() const; unsigned int getTransparentLocalPort() const; + unsigned int getTransparentSendFrameType() const; // The UMP section bool getUMPEnabled() const; @@ -136,6 +140,7 @@ public: bool getFusionLowDeviation() const; bool getFusionRemoteGateway() const; bool getFusionSelfOnly() const; + unsigned int getFusionTXHang() const; bool getFusionSQLEnabled() const; unsigned char getFusionSQL() const; unsigned int getFusionModeHang() const; @@ -157,6 +162,10 @@ public: bool getNXDNRemoteGateway() const; unsigned int getNXDNModeHang() const; + // The POCSAG section + bool getPOCSAGEnabled() const; + unsigned int getPOCSAGFrequency() const; + // The D-Star Network section bool getDStarNetworkEnabled() const; std::string getDStarGatewayAddress() const; @@ -204,6 +213,15 @@ public: unsigned int getNXDNNetworkModeHang() const; bool getNXDNNetworkDebug() const; + // The POCSAG Network section + bool getPOCSAGNetworkEnabled() const; + std::string getPOCSAGGatewayAddress() const; + unsigned int getPOCSAGGatewayPort() const; + std::string getPOCSAGLocalAddress() const; + unsigned int getPOCSAGLocalPort() const; + unsigned int getPOCSAGNetworkModeHang() const; + bool getPOCSAGNetworkDebug() const; + // The TFTSERIAL section std::string getTFTSerialPort() const; unsigned int getTFTSerialBrightness() const; @@ -277,6 +295,8 @@ private: unsigned int m_nxdnIdLookupTime; std::string m_modemPort; + std::string m_modemProtocol; + unsigned int m_modemAddress; bool m_modemRXInvert; bool m_modemTXInvert; bool m_modemPTTInvert; @@ -294,6 +314,7 @@ private: float m_modemYSFTXLevel; float m_modemP25TXLevel; float m_modemNXDNTXLevel; + float m_modemPOCSAGTXLevel; std::string m_modemRSSIMappingFile; bool m_modemTrace; bool m_modemDebug; @@ -302,6 +323,7 @@ private: std::string m_transparentRemoteAddress; unsigned int m_transparentRemotePort; unsigned int m_transparentLocalPort; + unsigned int m_transparentSendFrameType; bool m_umpEnabled; std::string m_umpPort; @@ -338,6 +360,7 @@ private: bool m_fusionLowDeviation; bool m_fusionRemoteGateway; bool m_fusionSelfOnly; + unsigned int m_fusionTXHang; bool m_fusionSQLEnabled; unsigned char m_fusionSQL; unsigned int m_fusionModeHang; @@ -357,6 +380,9 @@ private: bool m_nxdnRemoteGateway; unsigned int m_nxdnModeHang; + bool m_pocsagEnabled; + unsigned int m_pocsagFrequency; + bool m_dstarNetworkEnabled; std::string m_dstarGatewayAddress; unsigned int m_dstarGatewayPort; @@ -399,6 +425,14 @@ private: unsigned int m_nxdnNetworkModeHang; bool m_nxdnNetworkDebug; + bool m_pocsagNetworkEnabled; + std::string m_pocsagGatewayAddress; + unsigned int m_pocsagGatewayPort; + std::string m_pocsagLocalAddress; + unsigned int m_pocsagLocalPort; + unsigned int m_pocsagNetworkModeHang; + bool m_pocsagNetworkDebug; + std::string m_tftSerialPort; unsigned int m_tftSerialBrightness; diff --git a/DMRNetwork.cpp b/DMRNetwork.cpp index 251ddb3..86bc335 100644 --- a/DMRNetwork.cpp +++ b/DMRNetwork.cpp @@ -26,6 +26,7 @@ #include #include #include +#include const unsigned int BUFFER_LENGTH = 500U; @@ -33,6 +34,7 @@ const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U; CDMRNetwork::CDMRNetwork(const std::string& address, unsigned int port, unsigned int local, unsigned int id, const std::string& password, bool duplex, const char* version, bool debug, bool slot1, bool slot2, HW_TYPE hwType) : +m_addressStr(address), m_address(), m_port(port), m_id(NULL), @@ -122,6 +124,9 @@ bool CDMRNetwork::open() { LogMessage("DMR, Opening DMR Network"); + if (m_address.s_addr == INADDR_NONE) + m_address = CUDPSocket::lookup(m_addressStr); + m_status = WAITING_CONNECT; m_timeoutTimer.stop(); m_retryTimer.start(); @@ -544,6 +549,9 @@ bool CDMRNetwork::writeConfig() case HWT_MMDVM_HS_DUAL_HAT: software = "MMDVM_MMDVM_HS_Dual_Hat"; break; + case HWT_NANO_HOTSPOT: + software = "MMDVM_Nano_hotSPOT"; + break; default: software = "MMDVM_Unknown"; break; @@ -570,6 +578,9 @@ bool CDMRNetwork::writeConfig() case HWT_NANO_HOTSPOT: software = "MMDVM_Nano_hotSPOT"; break; + case HWT_NANO_DV: + software = "MMDVM_Nano_DV"; + break; case HWT_MMDVM_HS: software = "MMDVM_MMDVM_HS"; break; diff --git a/DMRNetwork.h b/DMRNetwork.h index d1d12e4..9dbb812 100644 --- a/DMRNetwork.h +++ b/DMRNetwork.h @@ -57,6 +57,7 @@ public: void close(); private: + std::string m_addressStr; in_addr m_address; unsigned int m_port; uint8_t* m_id; diff --git a/DMRSlot.cpp b/DMRSlot.cpp index f50a783..769a114 100644 --- a/DMRSlot.cpp +++ b/DMRSlot.cpp @@ -89,6 +89,7 @@ m_rfLC(NULL), m_netLC(NULL), m_rfSeqNo(0U), m_rfN(0U), +m_lastrfN(0U), m_netN(0U), m_networkWatchdog(1000U, 0U, 1500U), m_rfTimeoutTimer(1000U, timeout), @@ -529,6 +530,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) } } else if (audioSync) { if (m_rfState == RS_RF_AUDIO) { + m_lastrfN = 0; // Convert the Audio Sync to be from the BS or MS as needed CSync::addDMRAudioSync(data + 2U, m_duplex); @@ -574,6 +576,11 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) m_rfN = data[1U] & 0x0FU; if (m_rfN > 5U) return false; + if (m_rfN == m_lastrfN) + return false; + if (m_rfN != (m_lastrfN + 1U)) + return false; + m_lastrfN = m_rfN; unsigned int errors = 0U; unsigned char fid = m_rfLC->getFID(); @@ -816,6 +823,11 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) m_rfN = data[1U] & 0x0FU; if (m_rfN > 5U) return false; + if (m_rfN == m_lastrfN) + return false; + if (m_rfN != (m_lastrfN + 1U)) + return false; + m_lastrfN = m_rfN; // Regenerate the EMB emb.getData(data + 2U); diff --git a/DMRSlot.h b/DMRSlot.h index 4dfbde4..ff00617 100644 --- a/DMRSlot.h +++ b/DMRSlot.h @@ -79,6 +79,7 @@ private: CDMRLC* m_netLC; unsigned char m_rfSeqNo; unsigned char m_rfN; + unsigned char m_lastrfN; unsigned char m_netN; CTimer m_networkWatchdog; CTimer m_rfTimeoutTimer; diff --git a/DStarNetwork.cpp b/DStarNetwork.cpp index 443fcac..ebf971f 100644 --- a/DStarNetwork.cpp +++ b/DStarNetwork.cpp @@ -26,6 +26,7 @@ #include #include #include +#include const unsigned int BUFFER_LENGTH = 100U; diff --git a/Defines.h b/Defines.h index b7e036a..d4b9ff5 100644 --- a/Defines.h +++ b/Defines.h @@ -25,9 +25,11 @@ const unsigned char MODE_DMR = 2U; 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_CW = 98U; const unsigned char MODE_LOCKOUT = 99U; const unsigned char MODE_ERROR = 100U; +const unsigned char MODE_QUIT = 110U; const unsigned char TAG_HEADER = 0x00U; const unsigned char TAG_DATA = 0x01U; @@ -41,6 +43,7 @@ enum HW_TYPE { HWT_MMDVM_HS_HAT, HWT_MMDVM_HS_DUAL_HAT, HWT_NANO_HOTSPOT, + HWT_NANO_DV, HWT_MMDVM_HS, HWT_UNKNOWN }; diff --git a/Display.cpp b/Display.cpp index 6deffd1..c1faa16 100644 --- a/Display.cpp +++ b/Display.cpp @@ -18,8 +18,25 @@ #include "Display.h" #include "Defines.h" +#include "SerialController.h" +#include "ModemSerialPort.h" +#include "NullDisplay.h" +#include "TFTSerial.h" +#include "LCDproc.h" +#include "Nextion.h" +#include "Conf.h" +#include "Modem.h" +#include "UMP.h" #include "Log.h" +#if defined(HD44780) +#include "HD44780.h" +#endif + +#if defined(OLED) +#include "OLED.h" +#endif + #include #include #include @@ -71,6 +88,17 @@ void CDisplay::setError(const char* text) setErrorInt(text); } +void CDisplay::setQuit() +{ + m_timer1.stop(); + m_timer2.stop(); + + m_mode1 = MODE_QUIT; + m_mode2 = MODE_QUIT; + + setQuitInt(); +} + void CDisplay::writeDStar(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) { assert(my1 != NULL); @@ -298,6 +326,25 @@ void CDisplay::clearNXDN() } } +void CDisplay::writePOCSAG(uint32_t ric, const std::string& message) +{ + m_timer1.start(); + m_mode1 = MODE_POCSAG; + + writePOCSAGInt(ric, message); +} + +void CDisplay::clearPOCSAG() +{ + if (m_timer1.hasExpired()) { + clearPOCSAGInt(); + m_timer1.stop(); + m_mode1 = MODE_IDLE; + } else { + m_mode1 = MODE_POCSAG; + } +} + void CDisplay::writeCW() { m_timer1.start(); @@ -336,6 +383,11 @@ void CDisplay::clock(unsigned int ms) m_mode1 = MODE_IDLE; m_timer1.stop(); break; + case MODE_POCSAG: + clearPOCSAGInt(); + m_mode1 = MODE_IDLE; + m_timer1.stop(); + break; case MODE_CW: clearCWInt(); m_mode1 = MODE_IDLE; @@ -406,3 +458,159 @@ void CDisplay::writeNXDNRSSIInt(unsigned char rssi) void CDisplay::writeNXDNBERInt(float ber) { } + +/* Factory method extracted from MMDVMHost.cpp - BG5HHP */ +CDisplay* CDisplay::createDisplay(const CConf& conf, CUMP* ump, CModem* modem) +{ + CDisplay *display = NULL; + + std::string type = conf.getDisplay(); + unsigned int dmrid = conf.getDMRId(); + + LogInfo("Display Parameters"); + LogInfo(" Type: %s", type.c_str()); + + if (type == "TFT Serial") { + std::string port = conf.getTFTSerialPort(); + unsigned int brightness = conf.getTFTSerialBrightness(); + + LogInfo(" Port: %s", port.c_str()); + LogInfo(" Brightness: %u", brightness); + + ISerialPort* serial = NULL; + if (port == "modem") + serial = new CModemSerialPort(modem); + else + serial = new CSerialController(port, SERIAL_9600); + + display = new CTFTSerial(conf.getCallsign(), dmrid, serial, brightness); + } else if (type == "Nextion") { + std::string port = conf.getNextionPort(); + unsigned int brightness = conf.getNextionBrightness(); + bool displayClock = conf.getNextionDisplayClock(); + bool utc = conf.getNextionUTC(); + unsigned int idleBrightness = conf.getNextionIdleBrightness(); + unsigned int screenLayout = conf.getNextionScreenLayout(); + + LogInfo(" Port: %s", port.c_str()); + LogInfo(" Brightness: %u", brightness); + LogInfo(" Clock Display: %s", displayClock ? "yes" : "no"); + if (displayClock) + LogInfo(" Display UTC: %s", utc ? "yes" : "no"); + LogInfo(" Idle Brightness: %u", idleBrightness); + + switch (screenLayout) { + case 0U: + LogInfo(" Screen Layout: G4KLX (Default)"); + break; + case 2U: + LogInfo(" Screen Layout: ON7LDS"); + break; + case 3U: + LogInfo(" Screen Layout: DIY by ON7LDS"); + break; + case 4U: + LogInfo(" Screen Layout: DIY by ON7LDS (High speed)"); + break; + default: + LogInfo(" Screen Layout: %u (Unknown)", screenLayout); + break; + } + + if (port == "modem") { + ISerialPort* serial = new CModemSerialPort(modem); + display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout); + } else if (port == "ump") { + if (ump != NULL) + display = new CNextion(conf.getCallsign(), dmrid, ump, brightness, displayClock, utc, idleBrightness, screenLayout); + else + display = new CNullDisplay; + } else { + SERIAL_SPEED baudrate = SERIAL_9600; + if (screenLayout==4U) + baudrate = SERIAL_115200; + ISerialPort* serial = new CSerialController(port, baudrate); + display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout); + } + } else if (type == "LCDproc") { + std::string address = conf.getLCDprocAddress(); + unsigned int port = conf.getLCDprocPort(); + unsigned int localPort = conf.getLCDprocLocalPort(); + bool displayClock = conf.getLCDprocDisplayClock(); + bool utc = conf.getLCDprocUTC(); + bool dimOnIdle = conf.getLCDprocDimOnIdle(); + + LogInfo(" Address: %s", address.c_str()); + LogInfo(" Port: %u", port); + + if (localPort == 0 ) + LogInfo(" Local Port: random"); + else + LogInfo(" Local Port: %u", localPort); + + LogInfo(" Dim Display on Idle: %s", dimOnIdle ? "yes" : "no"); + LogInfo(" Clock Display: %s", displayClock ? "yes" : "no"); + + if (displayClock) + LogInfo(" Display UTC: %s", utc ? "yes" : "no"); + + display = new CLCDproc(address.c_str(), port, localPort, conf.getCallsign(), dmrid, displayClock, utc, conf.getDuplex(), dimOnIdle); +#if defined(HD44780) + } else if (type == "HD44780") { + unsigned int rows = conf.getHD44780Rows(); + unsigned int columns = conf.getHD44780Columns(); + std::vector pins = conf.getHD44780Pins(); + unsigned int i2cAddress = conf.getHD44780i2cAddress(); + bool pwm = conf.getHD44780PWM(); + unsigned int pwmPin = conf.getHD44780PWMPin(); + unsigned int pwmBright = conf.getHD44780PWMBright(); + unsigned int pwmDim = conf.getHD44780PWMDim(); + bool displayClock = conf.getHD44780DisplayClock(); + bool utc = conf.getHD44780UTC(); + + if (pins.size() == 6U) { + LogInfo(" Rows: %u", rows); + LogInfo(" Columns: %u", columns); + +#if defined(ADAFRUIT_DISPLAY) || defined(PCF8574_DISPLAY) + LogInfo(" Device Address: %#x", i2cAddress); +#else + LogInfo(" Pins: %u,%u,%u,%u,%u,%u", pins.at(0U), pins.at(1U), pins.at(2U), pins.at(3U), pins.at(4U), pins.at(5U)); +#endif + + LogInfo(" PWM Backlight: %s", pwm ? "yes" : "no"); + if (pwm) { + LogInfo(" PWM Pin: %u", pwmPin); + LogInfo(" PWM Bright: %u", pwmBright); + LogInfo(" PWM Dim: %u", pwmDim); + } + + LogInfo(" Clock Display: %s", displayClock ? "yes" : "no"); + if (displayClock) + LogInfo(" Display UTC: %s", utc ? "yes" : "no"); + + display = new CHD44780(rows, columns, conf.getCallsign(), dmrid, pins, i2cAddress, pwm, pwmPin, pwmBright, pwmDim, displayClock, utc, conf.getDuplex()); + } +#endif + +#if defined(OLED) + } else if (type == "OLED") { + unsigned char type = conf.getOLEDType(); + unsigned char brightness = conf.getOLEDBrightness(); + bool invert = conf.getOLEDInvert(); + bool scroll = conf.getOLEDScroll(); + display = new COLED(type, brightness, invert, scroll, conf.getDMRNetworkSlot1(), conf.getDMRNetworkSlot2()); +#endif + } else { + LogWarning("No valid display found, disabling"); + display = new CNullDisplay; + } + + bool ret = display->open(); + if (!ret) { + delete display; + display = new CNullDisplay; + } + + return display; +} diff --git a/Display.h b/Display.h index fe6360b..65515be 100644 --- a/Display.h +++ b/Display.h @@ -23,6 +23,12 @@ #include +#include + +class CConf; +class CModem; +class CUMP; + class CDisplay { public: @@ -34,6 +40,7 @@ public: void setIdle(); void setLockout(); void setError(const char* text); + void setQuit(); void writeDStar(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); void writeDStarRSSI(unsigned char rssi); @@ -61,17 +68,22 @@ public: void writeNXDNBER(float ber); void clearNXDN(); + void writePOCSAG(uint32_t ric, const std::string& message); + void clearPOCSAG(); + void writeCW(); - void clearCW(); virtual void close() = 0; void clock(unsigned int ms); + static CDisplay* createDisplay(const CConf& conf, CUMP* ump, CModem* modem); + protected: virtual void setIdleInt() = 0; virtual void setLockoutInt() = 0; virtual void setErrorInt(const char* text) = 0; + virtual void setQuitInt() = 0; virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) = 0; virtual void writeDStarRSSIInt(unsigned char rssi); @@ -99,6 +111,9 @@ protected: virtual void writeNXDNBERInt(float ber); virtual void clearNXDNInt() = 0; + virtual void writePOCSAGInt(uint32_t ric, const std::string& message) = 0; + virtual void clearPOCSAGInt() = 0; + virtual void writeCWInt() = 0; virtual void clearCWInt() = 0; diff --git a/HD44780.cpp b/HD44780.cpp index 47b6329..970f174 100644 --- a/HD44780.cpp +++ b/HD44780.cpp @@ -374,6 +374,31 @@ void CHD44780::setLockoutInt() m_dmr = false; } +void CHD44780::setQuitInt() +{ +#ifdef ADAFRUIT_DISPLAY + adafruitLCDColour(AC_RED); +#endif + + m_clockDisplayTimer.stop(); // Stop the clock display + ::lcdClear(m_fd); + + if (m_pwm) { + if (m_pwmPin != 1U) + ::softPwmWrite(m_pwmPin, m_pwmBright); + else + ::pwmWrite(m_pwmPin, (m_pwmBright / 100) * 1024); + } + + ::lcdPosition(m_fd, 0, 0); + ::lcdPuts(m_fd, "MMDVM"); + + ::lcdPosition(m_fd, 0, 1); + ::lcdPuts(m_fd, "STOPPED"); + + m_dmr = false; +} + void CHD44780::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) { assert(my1 != NULL); @@ -995,6 +1020,18 @@ void CHD44780::clearNXDNInt() } } +void CHD44780::writePOCSAGInt(uint32_t ric, const std::string& message) +{ + ::lcdPosition(m_fd, m_cols - 5, m_rows - 1); + ::lcdPuts(m_fd, "POCSAG TX"); +} + +void CHD44780::clearPOCSAGInt() +{ + ::lcdPosition(m_fd, m_cols - 5, m_rows - 1); + ::lcdPuts(m_fd, " Idle"); +} + void CHD44780::writeCWInt() { ::lcdPosition(m_fd, m_cols - 5, m_rows - 1); diff --git a/HD44780.h b/HD44780.h index 41b0e83..6160482 100644 --- a/HD44780.h +++ b/HD44780.h @@ -100,6 +100,7 @@ protected: virtual void setIdleInt(); virtual void setErrorInt(const char* text); virtual void setLockoutInt(); + virtual void setQuitInt(); virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); virtual void writeDStarRSSIInt(unsigned char rssi); @@ -121,6 +122,9 @@ protected: virtual void writeNXDNRSSIInt(unsigned char rssi); virtual void clearNXDNInt(); + virtual void writePOCSAGInt(uint32_t ric, const std::string& message); + virtual void clearPOCSAGInt(); + virtual void writeCWInt(); virtual void clearCWInt(); diff --git a/I2CController.cpp b/I2CController.cpp new file mode 100644 index 0000000..b44ab03 --- /dev/null +++ b/I2CController.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2002-2004,2007-2011,2013,2014-2017 by Jonathan Naylor G4KLX + * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "I2CController.h" +#include "Log.h" + +#include +#include + +#include + +#if defined(_WIN32) || defined(_WIN64) + +#include +#include + +CI2CController::CI2CController(const std::string& device, SERIAL_SPEED speed, unsigned int address, bool assertRTS) : +CSerialController(device, speed, assertRTS), +m_address(address) +{ +} + +CI2CController::~CI2CController() +{ +} + +bool CI2CController::open() +{ + return CSerialController::open(); +} + +int CI2CController::read(unsigned char* buffer, unsigned int length) +{ + return CSerialController::read(buffer, length); +} + +int CI2CController::write(const unsigned char* buffer, unsigned int length) +{ + return CSerialController::write(buffer, length); +} + +#else + +#include +#include +#include +#include +#include +#include +#if !defined(__APPLE__) +#include +#endif + +CI2CController::CI2CController(const std::string& device, SERIAL_SPEED speed, unsigned int address, bool assertRTS) : +CSerialController(device, speed, assertRTS), +m_address(address) +{ +} + +CI2CController::~CI2CController() +{ +} + +bool CI2CController::open() +{ + assert(m_fd == -1); + +#if !defined(__APPLE__) + m_fd = ::open(m_device.c_str(), O_RDWR); + if (m_fd < 0) { + LogError("Cannot open device - %s", m_device.c_str()); + return false; + } + + if (::ioctl(m_fd, I2C_TENBIT, 0) < 0) { + LogError("CI2C: failed to set 7bitaddress"); + ::close(m_fd); + return false; + } + + if (::ioctl(m_fd, I2C_SLAVE, m_address) < 0) { + LogError("CI2C: Failed to acquire bus access/talk to slave 0x%02X", m_address); + ::close(m_fd); + return false; + } +#else + #warning "I2C controller does not support OSX" +#endif + + return true; +} + +int CI2CController::read(unsigned char* buffer, unsigned int length) +{ + assert(buffer != NULL); + assert(m_fd != -1); + + if (length == 0U) + return 0; + + unsigned int offset = 0U; + + while (offset < length) { +#if !defined(__APPLE__) + ssize_t n = ::read(m_fd, buffer + offset, 1U); + if (n < 0) { + if (errno != EAGAIN) { + LogError("Error returned from read(), errno=%d", errno); + return -1; + } + } + + if (n > 0) + offset += n; +#endif + } + + return length; +} + +int CI2CController::write(const unsigned char* buffer, unsigned int length) +{ + assert(buffer != NULL); + assert(m_fd != -1); + + if (length == 0U) + return 0; + + unsigned int ptr = 0U; + while (ptr < length) { + ssize_t n = 0U; +#if !defined(__APPLE__) + n = ::write(m_fd, buffer + ptr, 1U); +#endif + if (n < 0) { + if (errno != EAGAIN) { + LogError("Error returned from write(), errno=%d", errno); + return -1; + } + } + + if (n > 0) + ptr += n; + } + + return length; +} + +#endif diff --git a/I2CController.h b/I2CController.h new file mode 100644 index 0000000..6e59672 --- /dev/null +++ b/I2CController.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2002-2004,2007-2009,2011-2013,2015-2017 by Jonathan Naylor G4KLX + * Copyright (C) 1999-2001 by Thomas Sailor HB9JNX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef I2CController_H +#define I2CController_H + +#include "SerialController.h" + +class CI2CController : public CSerialController { +public: + CI2CController(const std::string& device, SERIAL_SPEED speed, unsigned int address = 0x22U, bool assertRTS = false); + virtual ~CI2CController(); + + virtual bool open(); + + virtual int read(unsigned char* buffer, unsigned int length); + + virtual int write(const unsigned char* buffer, unsigned int length); + +private: + unsigned int m_address; +}; + +#endif diff --git a/Images/POCSAG.bmp b/Images/POCSAG.bmp new file mode 100644 index 0000000..bd80cdd Binary files /dev/null and b/Images/POCSAG.bmp differ diff --git a/Images/YSF.bmp b/Images/YSF.bmp index a753134..d1a8292 100644 Binary files a/Images/YSF.bmp and b/Images/YSF.bmp differ diff --git a/LCDproc.cpp b/LCDproc.cpp index d78a43a..8ef2721 100644 --- a/LCDproc.cpp +++ b/LCDproc.cpp @@ -224,6 +224,23 @@ void CLCDproc::setLockoutInt() // LED 4 Green 8 Red 128 Yellow 136 +void CLCDproc::setQuitInt() +{ + m_clockDisplayTimer.stop(); // Stop the clock display + + if (m_screensDefined) { + socketPrintf(m_socketfd, "screen_set DStar -priority hidden"); + socketPrintf(m_socketfd, "screen_set DMR -priority hidden"); + 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, "widget_set Status Status %u %u Stopped", m_cols - 6, m_rows); + socketPrintf(m_socketfd, "output 0"); // Clear all LEDs + } + + m_dmr = false; +} + void CLCDproc::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) { assert(my1 != NULL); @@ -514,6 +531,14 @@ void CLCDproc::clearNXDNInt() socketPrintf(m_socketfd, "output 16"); // Set LED5 color green } +void CLCDproc::writePOCSAGInt(uint32_t ric, const std::string& message) +{ +} + +void CLCDproc::clearPOCSAGInt() +{ +} + void CLCDproc::writeCWInt() { } diff --git a/LCDproc.h b/LCDproc.h index fcb1e59..2554d36 100644 --- a/LCDproc.h +++ b/LCDproc.h @@ -39,6 +39,8 @@ protected: virtual void setIdleInt(); virtual void setErrorInt(const char* text); virtual void setLockoutInt(); + virtual void setQuitInt(); + virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); virtual void writeDStarRSSIInt(unsigned char rssi); @@ -60,6 +62,9 @@ protected: virtual void writeNXDNRSSIInt(unsigned char rssi); virtual void clearNXDNInt(); + virtual void writePOCSAGInt(uint32_t ric, const std::string& message); + virtual void clearPOCSAGInt(); + virtual void writeCWInt(); virtual void clearCWInt(); diff --git a/MMDVM.ini b/MMDVM.ini index d62a355..661e86b 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -44,6 +44,8 @@ Time=24 # Port=/dev/ttyACM0 # Port=/dev/ttyAMA0 Port=\\.\COM3 +Protocol=uart +# Address=0x22 TXInvert=1 RXInvert=0 PTTInvert=0 @@ -62,6 +64,7 @@ RFLevel=100 # YSFTXLevel=50 # P25TXLevel=50 # NXDNTXLevel=50 +# POCSAGTXLevel=50 RSSIMappingFile=RSSI.dat Trace=0 Debug=0 @@ -71,6 +74,7 @@ Enable=0 RemoteAddress=127.0.0.1 RemotePort=40094 LocalPort=40095 +# SendFrameType=0 [UMP] Enable=0 @@ -107,6 +111,7 @@ TXHang=4 Enable=1 LowDeviation=0 SelfOnly=0 +TXHang=4 #DGID=1 RemoteGateway=0 # ModeHang=10 @@ -126,6 +131,10 @@ SelfOnly=0 RemoteGateway=0 # ModeHang=10 +[POCSAG] +Enable=1 +Frequency=439987500 + [D-Star Network] Enable=1 GatewayAddress=127.0.0.1 @@ -173,6 +182,15 @@ GatewayPort=14020 # ModeHang=3 Debug=0 +[POCSAG Network] +Enable=1 +LocalAddress=127.0.0.1 +LocalPort=3800 +GatewayAddress=127.0.0.1 +GatewayPort=4800 +# ModeHang=3 +Debug=0 + [TFT Serial] # Port=modem Port=/dev/ttyAMA0 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index 0cf02a9..3acb840 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -19,34 +19,24 @@ #include "MMDVMHost.h" #include "RSSIInterpolator.h" #include "SerialController.h" -#include "ModemSerialPort.h" #include "Version.h" #include "StopWatch.h" #include "Defines.h" #include "DStarControl.h" #include "DMRControl.h" -#include "TFTSerial.h" -#include "NullDisplay.h" #include "YSFControl.h" #include "P25Control.h" #include "NXDNControl.h" -#include "Nextion.h" -#include "LCDproc.h" +#include "POCSAGControl.h" #include "Thread.h" #include "Log.h" #include "GitVersion.h" -#if defined(HD44780) -#include "HD44780.h" -#endif - -#if defined(OLED) -#include "OLED.h" -#endif - #include #include +#include + #if !defined(_WIN32) && !defined(_WIN64) #include #include @@ -134,6 +124,7 @@ m_dmrNetwork(NULL), m_ysfNetwork(NULL), m_p25Network(NULL), m_nxdnNetwork(NULL), +m_pocsagNetwork(NULL), m_display(NULL), m_ump(NULL), m_mode(MODE_IDLE), @@ -147,6 +138,7 @@ m_dmrNetModeHang(3U), m_ysfNetModeHang(3U), m_p25NetModeHang(3U), m_nxdnNetModeHang(3U), +m_pocsagNetModeHang(3U), m_modeTimer(1000U), m_dmrTXTimer(1000U), m_cwIdTimer(1000U), @@ -157,6 +149,7 @@ m_dmrEnabled(false), m_ysfEnabled(false), m_p25Enabled(false), m_nxdnEnabled(false), +m_pocsagEnabled(false), m_cwIdTime(0U), m_dmrLookup(NULL), m_nxdnLookup(NULL), @@ -178,19 +171,13 @@ int CMMDVMHost::run() return 1; } - ret = ::LogInitialise(m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel()); - if (!ret) { - ::fprintf(stderr, "MMDVMHost: unable to open the log file\n"); - return 1; - } - #if !defined(_WIN32) && !defined(_WIN64) bool m_daemon = m_conf.getDaemon(); if (m_daemon) { // Create new process pid_t pid = ::fork(); if (pid == -1) { - LogWarning("Couldn't fork() , exiting"); + ::fprintf(stderr, "Couldn't fork() , exiting\n"); return -1; } else if (pid != 0) { exit(EXIT_SUCCESS); @@ -198,56 +185,66 @@ int CMMDVMHost::run() // Create new session and process group if (::setsid() == -1){ - LogWarning("Couldn't setsid(), exiting"); + ::fprintf(stderr, "Couldn't setsid(), exiting\n"); return -1; } // Set the working directory to the root directory if (::chdir("/") == -1){ - LogWarning("Couldn't cd /, exiting"); + ::fprintf(stderr, "Couldn't cd /, exiting\n"); return -1; } - ::close(STDIN_FILENO); - ::close(STDOUT_FILENO); - ::close(STDERR_FILENO); - -#if !defined(HD44780) && !defined(OLED) - //If we are currently root... +#if !defined(HD44780) && !defined(OLED) && !defined(_OPENWRT) + // If we are currently root... if (getuid() == 0) { struct passwd* user = ::getpwnam("mmdvm"); if (user == NULL) { - LogError("Could not get the mmdvm user, exiting"); + ::fprintf(stderr, "Could not get the mmdvm user, exiting\n"); return -1; } uid_t mmdvm_uid = user->pw_uid; gid_t mmdvm_gid = user->pw_gid; - //Set user and group ID's to mmdvm:mmdvm + // Set user and group ID's to mmdvm:mmdvm if (::setgid(mmdvm_gid) != 0) { - LogWarning("Could not set mmdvm GID, exiting"); + ::fprintf(stderr, "Could not set mmdvm GID, exiting\n"); return -1; } if (::setuid(mmdvm_uid) != 0) { - LogWarning("Could not set mmdvm UID, exiting"); + ::fprintf(stderr, "Could not set mmdvm UID, exiting\n"); return -1; } - //Double check it worked (AKA Paranoia) + // Double check it worked (AKA Paranoia) if (::setuid(0) != -1){ - LogWarning("It's possible to regain root - something is wrong!, exiting"); + ::fprintf(stderr, "It's possible to regain root - something is wrong!, exiting\n"); return -1; } } } #else - LogWarning("Dropping root permissions in daemon mode is disabled with HD44780 display"); + ::fprintf(stderr, "Dropping root permissions in daemon mode is disabled.\n"); } #endif #endif + ret = ::LogInitialise(m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel()); + if (!ret) { + ::fprintf(stderr, "MMDVMHost: unable to open the log file\n"); + return 1; + } + +#if !defined(_WIN32) && !defined(_WIN64) + if (m_daemon) { + ::close(STDIN_FILENO); + ::close(STDOUT_FILENO); + ::close(STDERR_FILENO); + } +#endif + LogInfo(HEADER1); LogInfo(HEADER2); LogInfo(HEADER3); @@ -276,7 +273,7 @@ int CMMDVMHost::run() } } - createDisplay(); + m_display = CDisplay::createDisplay(m_conf,m_ump,m_modem); if (m_dstarEnabled && m_conf.getDStarNetworkEnabled()) { ret = createDStarNetwork(); @@ -308,19 +305,28 @@ int CMMDVMHost::run() return 1; } + if (m_pocsagEnabled && m_conf.getPOCSAGNetworkEnabled()) { + ret = createPOCSAGNetwork(); + if (!ret) + return 1; + } + in_addr transparentAddress; unsigned int transparentPort = 0U; CUDPSocket* transparentSocket = NULL; + unsigned int sendFrameType = 0U; if (m_conf.getTransparentEnabled()) { std::string remoteAddress = m_conf.getTransparentRemoteAddress(); unsigned int remotePort = m_conf.getTransparentRemotePort(); unsigned int localPort = m_conf.getTransparentLocalPort(); + sendFrameType = m_conf.getTransparentSendFrameType(); LogInfo("Transparent Data"); LogInfo(" Remote Address: %s", remoteAddress.c_str()); LogInfo(" Remote Port: %u", remotePort); LogInfo(" Local Port: %u", localPort); + LogInfo(" Send Frame Type: %u", sendFrameType); transparentAddress = CUDPSocket::lookup(remoteAddress); transparentPort = remotePort; @@ -440,7 +446,7 @@ int CMMDVMHost::run() LogInfo(" Embedded LC Only: %s", embeddedLCOnly ? "yes" : "no"); LogInfo(" Dump Talker Alias Data: %s", dumpTAData ? "yes" : "no"); LogInfo(" Prefixes: %u", prefixes.size()); - + if (blackList.size() > 0U) LogInfo(" Source ID Black List: %u", blackList.size()); if (whiteList.size() > 0U) @@ -474,16 +480,18 @@ int CMMDVMHost::run() CYSFControl* ysf = NULL; if (m_ysfEnabled) { - bool lowDeviation = m_conf.getFusionLowDeviation(); - bool remoteGateway = m_conf.getFusionRemoteGateway(); - bool selfOnly = m_conf.getFusionSelfOnly(); - bool sqlEnabled = m_conf.getFusionSQLEnabled(); - unsigned char sql = m_conf.getFusionSQL(); - m_ysfRFModeHang = m_conf.getFusionModeHang(); + bool lowDeviation = m_conf.getFusionLowDeviation(); + bool remoteGateway = m_conf.getFusionRemoteGateway(); + unsigned int txHang = m_conf.getFusionTXHang(); + bool selfOnly = m_conf.getFusionSelfOnly(); + bool sqlEnabled = m_conf.getFusionSQLEnabled(); + unsigned char sql = m_conf.getFusionSQL(); + m_ysfRFModeHang = m_conf.getFusionModeHang(); LogInfo("YSF RF Parameters"); LogInfo(" Low Deviation: %s", lowDeviation ? "yes" : "no"); LogInfo(" Remote Gateway: %s", remoteGateway ? "yes" : "no"); + LogInfo(" TX Hang: %us", txHang); LogInfo(" Self Only: %s", selfOnly ? "yes" : "no"); LogInfo(" DSQ: %s", sqlEnabled ? "yes" : "no"); if (sqlEnabled) @@ -543,6 +551,20 @@ int CMMDVMHost::run() nxdn = new CNXDNControl(ran, id, selfOnly, m_nxdnNetwork, m_display, m_timeout, m_duplex, remoteGateway, m_nxdnLookup, rssi); } + CTimer pocsagTimer(1000U, 30U); + + CPOCSAGControl* pocsag = NULL; + if (m_pocsagEnabled) { + unsigned int frequency = m_conf.getPOCSAGFrequency(); + + LogInfo("POCSAG RF Parameters"); + LogInfo(" Frequency: %uHz", frequency); + + pocsag = new CPOCSAGControl(m_pocsagNetwork, m_display); + + pocsagTimer.start(); + } + setMode(MODE_IDLE); LogMessage("MMDVMHost-%s is running", VERSION); @@ -841,12 +863,31 @@ int CMMDVMHost::run() } } + if (pocsag != NULL) { + ret = m_modem->hasPOCSAGSpace(); + if (ret) { + len = pocsag->readModem(data); + if (len > 0U) { + if (m_mode == MODE_IDLE) { + m_modeTimer.setTimeout(m_pocsagNetModeHang); + setMode(MODE_POCSAG); + } + if (m_mode == MODE_POCSAG) { + m_modem->writePOCSAGData(data, len); + m_modeTimer.start(); + } else if (m_mode != MODE_LOCKOUT) { + LogWarning("POCSAG data received when in mode %u", m_mode); + } + } + } + } + if (transparentSocket != NULL) { in_addr address; unsigned int port = 0U; len = transparentSocket->read(data, 200U, address, port); if (len > 0U) - m_modem->writeTransparentData(data, len); + m_modem->writeTransparentData(data, len, sendFrameType); } unsigned int ms = stopWatch.elapsed(); @@ -867,6 +908,8 @@ int CMMDVMHost::run() p25->clock(ms); if (nxdn != NULL) nxdn->clock(ms); + if (pocsag != NULL) + pocsag->clock(ms); if (m_dstarNetwork != NULL) m_dstarNetwork->clock(ms); @@ -878,6 +921,8 @@ int CMMDVMHost::run() m_p25Network->clock(ms); if (m_nxdnNetwork != NULL) m_nxdnNetwork->clock(ms); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->clock(ms); m_cwIdTimer.clock(ms); if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) { @@ -912,6 +957,13 @@ int CMMDVMHost::run() m_dmrTXTimer.stop(); } + pocsagTimer.clock(ms); + if (pocsagTimer.isRunning() && pocsagTimer.hasExpired()) { + assert(m_pocsagNetwork != NULL); + m_pocsagNetwork->enable(m_mode == MODE_IDLE || m_mode == MODE_POCSAG); + pocsagTimer.start(); + } + if (m_ump != NULL) m_ump->clock(ms); @@ -919,7 +971,7 @@ int CMMDVMHost::run() CThread::sleep(5U); } - setMode(MODE_IDLE); + setMode(MODE_QUIT); m_modem->close(); delete m_modem; @@ -963,6 +1015,11 @@ int CMMDVMHost::run() delete m_nxdnNetwork; } + if (m_pocsagNetwork != NULL) { + m_pocsagNetwork->close(); + delete m_pocsagNetwork; + } + if (transparentSocket != NULL) { transparentSocket->close(); delete transparentSocket; @@ -973,39 +1030,48 @@ int CMMDVMHost::run() delete ysf; delete p25; delete nxdn; + delete pocsag; return 0; } bool CMMDVMHost::createModem() { - std::string port = m_conf.getModemPort(); - bool rxInvert = m_conf.getModemRXInvert(); - bool txInvert = m_conf.getModemTXInvert(); - bool pttInvert = m_conf.getModemPTTInvert(); - unsigned int txDelay = m_conf.getModemTXDelay(); - unsigned int dmrDelay = m_conf.getModemDMRDelay(); - float rxLevel = m_conf.getModemRXLevel(); - float cwIdTXLevel = m_conf.getModemCWIdTXLevel(); - float dstarTXLevel = m_conf.getModemDStarTXLevel(); - float dmrTXLevel = m_conf.getModemDMRTXLevel(); - float ysfTXLevel = m_conf.getModemYSFTXLevel(); - float p25TXLevel = m_conf.getModemP25TXLevel(); - float nxdnTXLevel = m_conf.getModemNXDNTXLevel(); - bool trace = m_conf.getModemTrace(); - bool debug = m_conf.getModemDebug(); - unsigned int colorCode = m_conf.getDMRColorCode(); - bool lowDeviation = m_conf.getFusionLowDeviation(); - unsigned int rxFrequency = m_conf.getRXFrequency(); - unsigned int txFrequency = m_conf.getTXFrequency(); - int rxOffset = m_conf.getModemRXOffset(); - int txOffset = m_conf.getModemTXOffset(); - int rxDCOffset = m_conf.getModemRXDCOffset(); - int txDCOffset = m_conf.getModemTXDCOffset(); - float rfLevel = m_conf.getModemRFLevel(); + std::string port = m_conf.getModemPort(); + std::string protocol = m_conf.getModemProtocol(); + unsigned int address = m_conf.getModemAddress(); + bool rxInvert = m_conf.getModemRXInvert(); + bool txInvert = m_conf.getModemTXInvert(); + bool pttInvert = m_conf.getModemPTTInvert(); + unsigned int txDelay = m_conf.getModemTXDelay(); + unsigned int dmrDelay = m_conf.getModemDMRDelay(); + float rxLevel = m_conf.getModemRXLevel(); + float cwIdTXLevel = m_conf.getModemCWIdTXLevel(); + float dstarTXLevel = m_conf.getModemDStarTXLevel(); + float dmrTXLevel = m_conf.getModemDMRTXLevel(); + float ysfTXLevel = m_conf.getModemYSFTXLevel(); + float p25TXLevel = m_conf.getModemP25TXLevel(); + float nxdnTXLevel = m_conf.getModemNXDNTXLevel(); + float pocsagTXLevel = m_conf.getModemPOCSAGTXLevel(); + bool trace = m_conf.getModemTrace(); + bool debug = m_conf.getModemDebug(); + unsigned int colorCode = m_conf.getDMRColorCode(); + bool lowDeviation = m_conf.getFusionLowDeviation(); + unsigned int txHang = m_conf.getFusionTXHang(); + unsigned int rxFrequency = m_conf.getRXFrequency(); + unsigned int txFrequency = m_conf.getTXFrequency(); + unsigned int pocsagFrequency = m_conf.getPOCSAGFrequency(); + int rxOffset = m_conf.getModemRXOffset(); + int txOffset = m_conf.getModemTXOffset(); + int rxDCOffset = m_conf.getModemRXDCOffset(); + int txDCOffset = m_conf.getModemTXDCOffset(); + float rfLevel = m_conf.getModemRFLevel(); LogInfo("Modem Parameters"); LogInfo(" Port: %s", port.c_str()); + LogInfo(" Protocol: %s", protocol.c_str()); + if (protocol == "i2c") + LogInfo(" i2c Address: %02X", address); LogInfo(" RX Invert: %s", rxInvert ? "yes" : "no"); LogInfo(" TX Invert: %s", txInvert ? "yes" : "no"); LogInfo(" PTT Invert: %s", pttInvert ? "yes" : "no"); @@ -1023,15 +1089,17 @@ bool CMMDVMHost::createModem() LogInfo(" YSF TX Level: %.1f%%", ysfTXLevel); LogInfo(" P25 TX Level: %.1f%%", p25TXLevel); LogInfo(" NXDN TX Level: %.1f%%", nxdnTXLevel); + LogInfo(" POCSAG TX Level: %.1f%%", pocsagTXLevel); LogInfo(" RX Frequency: %uHz (%uHz)", rxFrequency, rxFrequency + rxOffset); LogInfo(" TX Frequency: %uHz (%uHz)", txFrequency, txFrequency + txOffset); m_modem = new CModem(port, m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, trace, debug); - m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled); - m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel); - m_modem->setRFParams(rxFrequency, rxOffset, txFrequency, txOffset, txDCOffset, rxDCOffset, rfLevel); + m_modem->setSerialParams(protocol,address); + m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled); + m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel); + m_modem->setRFParams(rxFrequency, rxOffset, txFrequency, txOffset, txDCOffset, rxDCOffset, rfLevel, pocsagFrequency); m_modem->setDMRParams(colorCode); - m_modem->setYSFParams(lowDeviation); + m_modem->setYSFParams(lowDeviation, txHang); bool ret = m_modem->open(); if (!ret) { @@ -1230,17 +1298,48 @@ bool CMMDVMHost::createNXDNNetwork() return true; } +bool CMMDVMHost::createPOCSAGNetwork() +{ + std::string gatewayAddress = m_conf.getPOCSAGGatewayAddress(); + unsigned int gatewayPort = m_conf.getPOCSAGGatewayPort(); + std::string localAddress = m_conf.getPOCSAGLocalAddress(); + unsigned int localPort = m_conf.getPOCSAGLocalPort(); + m_pocsagNetModeHang = m_conf.getPOCSAGNetworkModeHang(); + bool debug = m_conf.getPOCSAGNetworkDebug(); + + LogInfo("POCSAG Network Parameters"); + LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); + LogInfo(" Gateway Port: %u", gatewayPort); + LogInfo(" Local Address: %s", localAddress.c_str()); + LogInfo(" Local Port: %u", localPort); + LogInfo(" Mode Hang: %us", m_pocsagNetModeHang); + + m_pocsagNetwork = new CPOCSAGNetwork(localAddress, localPort, gatewayAddress, gatewayPort, debug); + + bool ret = m_pocsagNetwork->open(); + if (!ret) { + delete m_pocsagNetwork; + m_pocsagNetwork = NULL; + return false; + } + + m_pocsagNetwork->enable(true); + + return true; +} + void CMMDVMHost::readParams() { - m_dstarEnabled = m_conf.getDStarEnabled(); - m_dmrEnabled = m_conf.getDMREnabled(); - m_ysfEnabled = m_conf.getFusionEnabled(); - m_p25Enabled = m_conf.getP25Enabled(); - m_nxdnEnabled = m_conf.getNXDNEnabled(); - m_duplex = m_conf.getDuplex(); - m_callsign = m_conf.getCallsign(); - m_id = m_conf.getId(); - m_timeout = m_conf.getTimeout(); + m_dstarEnabled = m_conf.getDStarEnabled(); + m_dmrEnabled = m_conf.getDMREnabled(); + m_ysfEnabled = m_conf.getFusionEnabled(); + m_p25Enabled = m_conf.getP25Enabled(); + m_nxdnEnabled = m_conf.getNXDNEnabled(); + m_pocsagEnabled = m_conf.getPOCSAGEnabled(); + m_duplex = m_conf.getDuplex(); + m_callsign = m_conf.getCallsign(); + m_id = m_conf.getId(); + m_timeout = m_conf.getTimeout(); LogInfo("General Parameters"); LogInfo(" Callsign: %s", m_callsign.c_str()); @@ -1252,160 +1351,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"); -} - -void CMMDVMHost::createDisplay() -{ - std::string type = m_conf.getDisplay(); - unsigned int dmrid = m_conf.getDMRId(); - - LogInfo("Display Parameters"); - LogInfo(" Type: %s", type.c_str()); - - if (type == "TFT Serial") { - std::string port = m_conf.getTFTSerialPort(); - unsigned int brightness = m_conf.getTFTSerialBrightness(); - - LogInfo(" Port: %s", port.c_str()); - LogInfo(" Brightness: %u", brightness); - - ISerialPort* serial = NULL; - if (port == "modem") - serial = new CModemSerialPort(m_modem); - else - serial = new CSerialController(port, SERIAL_9600); - - m_display = new CTFTSerial(m_callsign, dmrid, serial, brightness); - } else if (type == "Nextion") { - std::string port = m_conf.getNextionPort(); - unsigned int brightness = m_conf.getNextionBrightness(); - bool displayClock = m_conf.getNextionDisplayClock(); - bool utc = m_conf.getNextionUTC(); - unsigned int idleBrightness = m_conf.getNextionIdleBrightness(); - unsigned int screenLayout = m_conf.getNextionScreenLayout(); - - LogInfo(" Port: %s", port.c_str()); - LogInfo(" Brightness: %u", brightness); - LogInfo(" Clock Display: %s", displayClock ? "yes" : "no"); - if (displayClock) - LogInfo(" Display UTC: %s", utc ? "yes" : "no"); - LogInfo(" Idle Brightness: %u", idleBrightness); - - switch (screenLayout) { - case 0U: - LogInfo(" Screen Layout: G4KLX (Default)"); - break; - case 2U: - LogInfo(" Screen Layout: ON7LDS"); - break; - case 3U: - LogInfo(" Screen Layout: DIY by ON7LDS"); - break; - case 4U: - LogInfo(" Screen Layout: DIY by ON7LDS (High speed)"); - break; - default: - LogInfo(" Screen Layout: %u (Unknown)", screenLayout); - break; - } - - if (port == "modem") { - ISerialPort* serial = new CModemSerialPort(m_modem); - m_display = new CNextion(m_callsign, dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout); - } else if (port == "ump") { - if (m_ump != NULL) - m_display = new CNextion(m_callsign, dmrid, m_ump, brightness, displayClock, utc, idleBrightness, screenLayout); - } else { - SERIAL_SPEED baudrate = SERIAL_9600; - if (screenLayout==4U) - baudrate = SERIAL_115200; - ISerialPort* serial = new CSerialController(port, baudrate); - m_display = new CNextion(m_callsign, dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout); - } - } else if (type == "LCDproc") { - std::string address = m_conf.getLCDprocAddress(); - unsigned int port = m_conf.getLCDprocPort(); - unsigned int localPort = m_conf.getLCDprocLocalPort(); - bool displayClock = m_conf.getLCDprocDisplayClock(); - bool utc = m_conf.getLCDprocUTC(); - bool dimOnIdle = m_conf.getLCDprocDimOnIdle(); - - LogInfo(" Address: %s", address.c_str()); - LogInfo(" Port: %u", port); - - if (localPort == 0 ) - LogInfo(" Local Port: random"); - else - LogInfo(" Local Port: %u", localPort); - - LogInfo(" Dim Display on Idle: %s", dimOnIdle ? "yes" : "no"); - LogInfo(" Clock Display: %s", displayClock ? "yes" : "no"); - - if (displayClock) - LogInfo(" Display UTC: %s", utc ? "yes" : "no"); - - m_display = new CLCDproc(address.c_str(), port, localPort, m_callsign, dmrid, displayClock, utc, m_duplex, dimOnIdle); -#if defined(HD44780) - } else if (type == "HD44780") { - unsigned int rows = m_conf.getHD44780Rows(); - unsigned int columns = m_conf.getHD44780Columns(); - std::vector pins = m_conf.getHD44780Pins(); - unsigned int i2cAddress = m_conf.getHD44780i2cAddress(); - bool pwm = m_conf.getHD44780PWM(); - unsigned int pwmPin = m_conf.getHD44780PWMPin(); - unsigned int pwmBright = m_conf.getHD44780PWMBright(); - unsigned int pwmDim = m_conf.getHD44780PWMDim(); - bool displayClock = m_conf.getHD44780DisplayClock(); - bool utc = m_conf.getHD44780UTC(); - - if (pins.size() == 6U) { - LogInfo(" Rows: %u", rows); - LogInfo(" Columns: %u", columns); - -#if defined(ADAFRUIT_DISPLAY) || defined(PCF8574_DISPLAY) - LogInfo(" Device Address: %#x", i2cAddress); -#else - LogInfo(" Pins: %u,%u,%u,%u,%u,%u", pins.at(0U), pins.at(1U), pins.at(2U), pins.at(3U), pins.at(4U), pins.at(5U)); -#endif - - LogInfo(" PWM Backlight: %s", pwm ? "yes" : "no"); - if (pwm) { - LogInfo(" PWM Pin: %u", pwmPin); - LogInfo(" PWM Bright: %u", pwmBright); - LogInfo(" PWM Dim: %u", pwmDim); - } - - LogInfo(" Clock Display: %s", displayClock ? "yes" : "no"); - if (displayClock) - LogInfo(" Display UTC: %s", utc ? "yes" : "no"); - - m_display = new CHD44780(rows, columns, m_callsign, dmrid, pins, i2cAddress, pwm, pwmPin, pwmBright, pwmDim, displayClock, utc, m_duplex); - } -#endif - -#if defined(OLED) - } else if (type == "OLED") { - unsigned char type = m_conf.getOLEDType(); - unsigned char brightness = m_conf.getOLEDBrightness(); - bool invert = m_conf.getOLEDInvert(); - bool scroll = m_conf.getOLEDScroll(); - m_display = new COLED(type, brightness, invert, scroll, m_conf.getDMRNetworkSlot1(), m_conf.getDMRNetworkSlot2()); -#endif - } else { - m_display = new CNullDisplay; - } - - if (m_display == NULL) { - LogWarning("No valid display found, disabling"); - m_display = new CNullDisplay; - return; - } - - bool ret = m_display->open(); - if (!ret) { - delete m_display; - m_display = new CNullDisplay; - } + LogInfo(" POCSAG: %s", m_pocsagEnabled ? "enabled" : "disabled"); } void CMMDVMHost::setMode(unsigned char mode) @@ -1423,6 +1369,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); m_modem->setMode(MODE_DSTAR); if (m_ump != NULL) m_ump->setMode(MODE_DSTAR); @@ -1440,6 +1388,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); m_modem->setMode(MODE_DMR); if (m_ump != NULL) m_ump->setMode(MODE_DMR); @@ -1461,6 +1411,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); m_modem->setMode(MODE_YSF); if (m_ump != NULL) m_ump->setMode(MODE_YSF); @@ -1478,6 +1430,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_ysfNetwork->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); m_modem->setMode(MODE_P25); if (m_ump != NULL) m_ump->setMode(MODE_P25); @@ -1495,6 +1449,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_ysfNetwork->enable(false); if (m_p25Network != NULL) m_p25Network->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); m_modem->setMode(MODE_NXDN); if (m_ump != NULL) m_ump->setMode(MODE_NXDN); @@ -1503,6 +1459,25 @@ void CMMDVMHost::setMode(unsigned char mode) m_cwIdTimer.stop(); break; + case MODE_POCSAG: + 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); + m_modem->setMode(MODE_POCSAG); + if (m_ump != NULL) + m_ump->setMode(MODE_POCSAG); + m_mode = MODE_POCSAG; + m_modeTimer.start(); + m_cwIdTimer.stop(); + break; + case MODE_LOCKOUT: LogMessage("Mode set to Lockout"); if (m_dstarNetwork != NULL) @@ -1515,6 +1490,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); @@ -1540,6 +1517,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(false); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(false); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(false); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); @@ -1563,6 +1542,8 @@ void CMMDVMHost::setMode(unsigned char mode) m_p25Network->enable(true); if (m_nxdnNetwork != NULL) m_nxdnNetwork->enable(true); + if (m_pocsagNetwork != NULL) + m_pocsagNetwork->enable(true); if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); @@ -1579,6 +1560,9 @@ void CMMDVMHost::setMode(unsigned char mode) m_cwIdTimer.start(); } m_display->setIdle(); + if (mode==MODE_QUIT) { + m_display->setQuit(); + } m_mode = MODE_IDLE; m_modeTimer.stop(); break; diff --git a/MMDVMHost.h b/MMDVMHost.h index 787df39..889fe69 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -19,6 +19,7 @@ #if !defined(MMDVMHOST_H) #define MMDVMHOST_H +#include "POCSAGNetwork.h" #include "DStarNetwork.h" #include "NXDNNetwork.h" #include "NXDNLookup.h" @@ -43,42 +44,45 @@ public: int run(); private: - CConf m_conf; - CModem* m_modem; - CDStarNetwork* m_dstarNetwork; - CDMRNetwork* m_dmrNetwork; - CYSFNetwork* m_ysfNetwork; - CP25Network* m_p25Network; - CNXDNNetwork* m_nxdnNetwork; - CDisplay* m_display; - CUMP* m_ump; - unsigned char m_mode; - unsigned int m_dstarRFModeHang; - unsigned int m_dmrRFModeHang; - unsigned int m_ysfRFModeHang; - unsigned int m_p25RFModeHang; - unsigned int m_nxdnRFModeHang; - unsigned int m_dstarNetModeHang; - unsigned int m_dmrNetModeHang; - unsigned int m_ysfNetModeHang; - unsigned int m_p25NetModeHang; - unsigned int m_nxdnNetModeHang; - CTimer m_modeTimer; - CTimer m_dmrTXTimer; - CTimer m_cwIdTimer; - bool m_duplex; - unsigned int m_timeout; - bool m_dstarEnabled; - bool m_dmrEnabled; - bool m_ysfEnabled; - bool m_p25Enabled; - bool m_nxdnEnabled; - unsigned int m_cwIdTime; - CDMRLookup* m_dmrLookup; - CNXDNLookup* m_nxdnLookup; - std::string m_callsign; - unsigned int m_id; - std::string m_cwCallsign; + CConf m_conf; + CModem* m_modem; + CDStarNetwork* m_dstarNetwork; + CDMRNetwork* m_dmrNetwork; + CYSFNetwork* m_ysfNetwork; + CP25Network* m_p25Network; + CNXDNNetwork* m_nxdnNetwork; + CPOCSAGNetwork* m_pocsagNetwork; + CDisplay* m_display; + CUMP* m_ump; + unsigned char m_mode; + unsigned int m_dstarRFModeHang; + unsigned int m_dmrRFModeHang; + unsigned int m_ysfRFModeHang; + unsigned int m_p25RFModeHang; + unsigned int m_nxdnRFModeHang; + unsigned int m_dstarNetModeHang; + unsigned int m_dmrNetModeHang; + unsigned int m_ysfNetModeHang; + unsigned int m_p25NetModeHang; + unsigned int m_nxdnNetModeHang; + unsigned int m_pocsagNetModeHang; + CTimer m_modeTimer; + CTimer m_dmrTXTimer; + CTimer m_cwIdTimer; + bool m_duplex; + unsigned int m_timeout; + bool m_dstarEnabled; + bool m_dmrEnabled; + bool m_ysfEnabled; + bool m_p25Enabled; + bool m_nxdnEnabled; + bool m_pocsagEnabled; + unsigned int m_cwIdTime; + CDMRLookup* m_dmrLookup; + CNXDNLookup* m_nxdnLookup; + std::string m_callsign; + unsigned int m_id; + std::string m_cwCallsign; void readParams(); bool createModem(); @@ -87,7 +91,7 @@ private: bool createYSFNetwork(); bool createP25Network(); bool createNXDNNetwork(); - void createDisplay(); + bool createPOCSAGNetwork(); void setMode(unsigned char mode); }; diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index e63859c..7c565a0 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -183,6 +183,7 @@ + @@ -212,6 +213,9 @@ + + + @@ -266,6 +270,7 @@ + @@ -294,6 +299,8 @@ + + diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index c276fc7..328f121 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -263,6 +263,18 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -493,5 +505,14 @@ Source Files + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file diff --git a/Makefile b/Makefile index ae0175b..195652d 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,11 @@ LDFLAGS = -g OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.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 DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \ - NullDisplay.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ + Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi b/Makefile.Pi index 3ef9513..0c14f6c 100644 --- a/Makefile.Pi +++ b/Makefile.Pi @@ -9,11 +9,11 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.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 DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.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 NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \ + UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.Adafruit b/Makefile.Pi.Adafruit index ec80427..2fa1a02 100644 --- a/Makefile.Pi.Adafruit +++ b/Makefile.Pi.Adafruit @@ -9,11 +9,11 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.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 DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.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 NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ + Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780 index e35f8d3..918710a 100644 --- a/Makefile.Pi.HD44780 +++ b/Makefile.Pi.HD44780 @@ -9,11 +9,11 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.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 DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.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 NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ + Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED index 600e7db..6b305cb 100644 --- a/Makefile.Pi.OLED +++ b/Makefile.Pi.OLED @@ -9,11 +9,11 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.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 DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o OLED.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.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 NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ + Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Pi.PCF8574 b/Makefile.Pi.PCF8574 index 4fbc2a8..1075ea6 100644 --- a/Makefile.Pi.PCF8574 +++ b/Makefile.Pi.PCF8574 @@ -9,11 +9,11 @@ LDFLAGS = -g -L/usr/local/lib OBJECTS = \ AMBEFEC.o BCH.o BPTC19696.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 DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ - DStarSlowData.o Golay2087.o Golay24128.o Hamming.o HD44780.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.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 NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o \ + Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Makefile.Solaris b/Makefile.Solaris index 8f58787..a42b044 100644 --- a/Makefile.Solaris +++ b/Makefile.Solaris @@ -11,9 +11,9 @@ OBJECTS = \ DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRAccessControl.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o \ DStarSlowData.o Golay2087.o Golay24128.o Hamming.o LCDproc.o Log.o MMDVMHost.o Modem.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o \ NullDisplay.o NXDNAudio.o NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.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 QR1676.o RS129.o RS241213.o \ - RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o UMP.o Utils.o YSFControl.o \ - YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.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 RS129.o RS241213.o RSSIInterpolator.o SerialController.o SerialPort.o SHA256.o StopWatch.o Sync.o TFTSerial.o Thread.o Timer.o UDPSocket.o \ + UMP.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o all: MMDVMHost diff --git a/Modem.cpp b/Modem.cpp index a86b680..f7c4588 100644 --- a/Modem.cpp +++ b/Modem.cpp @@ -16,11 +16,13 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "I2CController.h" #include "DStarDefines.h" #include "DMRDefines.h" #include "YSFDefines.h" #include "P25Defines.h" #include "NXDNDefines.h" +#include "POCSAGDefines.h" #include "Thread.h" #include "Modem.h" #include "Utils.h" @@ -71,6 +73,8 @@ const unsigned char MMDVM_P25_LOST = 0x32U; const unsigned char MMDVM_NXDN_DATA = 0x40U; const unsigned char MMDVM_NXDN_LOST = 0x41U; +const unsigned char MMDVM_POCSAG_DATA = 0x50U; + const unsigned char MMDVM_ACK = 0x70U; const unsigned char MMDVM_NAK = 0x7FU; @@ -93,6 +97,7 @@ CModem::CModem(const std::string& port, bool duplex, bool rxInvert, bool txInver m_port(port), m_dmrColorCode(0U), m_ysfLoDev(false), +m_ysfTXHang(4U), m_duplex(duplex), m_rxInvert(rxInvert), m_txInvert(txInvert), @@ -106,18 +111,21 @@ m_dmrTXLevel(0U), m_ysfTXLevel(0U), m_p25TXLevel(0U), m_nxdnTXLevel(0U), +m_pocsagTXLevel(0U), m_trace(trace), m_debug(debug), m_rxFrequency(0U), m_txFrequency(0U), +m_pocsagFrequency(0U), m_dstarEnabled(false), m_dmrEnabled(false), m_ysfEnabled(false), m_p25Enabled(false), m_nxdnEnabled(false), +m_pocsagEnabled(false), m_rxDCOffset(0), m_txDCOffset(0), -m_serial(port, SERIAL_115200, true), +m_serial(NULL), m_buffer(NULL), m_length(0U), m_offset(0U), @@ -133,6 +141,7 @@ m_rxP25Data(1000U, "Modem RX P25"), m_txP25Data(1000U, "Modem TX P25"), m_rxNXDNData(1000U, "Modem RX NXDN"), m_txNXDNData(1000U, "Modem TX NXDN"), +m_txPOCSAGData(1000U, "Modem TX POCSAG"), m_rxTransparentData(1000U, "Modem RX Transparent"), m_txTransparentData(1000U, "Modem TX Transparent"), m_statusTimer(1000U, 0U, 250U), @@ -144,6 +153,7 @@ m_dmrSpace2(0U), m_ysfSpace(0U), m_p25Space(0U), m_nxdnSpace(0U), +m_pocsagSpace(0U), m_tx(false), m_cd(false), m_lockout(false), @@ -157,36 +167,49 @@ m_hwType(HWT_UNKNOWN) CModem::~CModem() { + delete m_serial; delete[] m_buffer; } -void CModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel) +void CModem::setSerialParams(const std::string& protocol, unsigned int address) { - m_rxFrequency = rxFrequency + rxOffset; - m_txFrequency = txFrequency + txOffset; - m_txDCOffset = txDCOffset; - m_rxDCOffset = rxDCOffset; - m_rfLevel = rfLevel; + // Create the serial controller instance according the protocol specified in conf. + if (protocol == "i2c") + m_serial = new CI2CController(m_port, SERIAL_115200, address, true); + else + m_serial = new CSerialController(m_port, SERIAL_115200, true); } -void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled) +void CModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency) { - m_dstarEnabled = dstarEnabled; - m_dmrEnabled = dmrEnabled; - m_ysfEnabled = ysfEnabled; - m_p25Enabled = p25Enabled; - m_nxdnEnabled = nxdnEnabled; + m_rxFrequency = rxFrequency + rxOffset; + m_txFrequency = txFrequency + txOffset; + m_txDCOffset = txDCOffset; + m_rxDCOffset = rxDCOffset; + m_rfLevel = rfLevel; + m_pocsagFrequency = pocsagFrequency + txOffset; } -void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel) +void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled) { - m_rxLevel = rxLevel; - m_cwIdTXLevel = cwIdTXLevel; - m_dstarTXLevel = dstarTXLevel; - m_dmrTXLevel = dmrTXLevel; - m_ysfTXLevel = ysfTXLevel; - m_p25TXLevel = p25TXLevel; - m_nxdnTXLevel = nxdnTXLevel; + m_dstarEnabled = dstarEnabled; + m_dmrEnabled = dmrEnabled; + m_ysfEnabled = ysfEnabled; + m_p25Enabled = p25Enabled; + m_nxdnEnabled = nxdnEnabled; + m_pocsagEnabled = pocsagEnabled; +} + +void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagTXLevel) +{ + m_rxLevel = rxLevel; + m_cwIdTXLevel = cwIdTXLevel; + m_dstarTXLevel = dstarTXLevel; + m_dmrTXLevel = dmrTXLevel; + m_ysfTXLevel = ysfTXLevel; + m_p25TXLevel = p25TXLevel; + m_nxdnTXLevel = nxdnTXLevel; + m_pocsagTXLevel = pocsagTXLevel; } void CModem::setDMRParams(unsigned int colorCode) @@ -196,22 +219,25 @@ void CModem::setDMRParams(unsigned int colorCode) m_dmrColorCode = colorCode; } -void CModem::setYSFParams(bool loDev) +void CModem::setYSFParams(bool loDev, unsigned int txHang) { - m_ysfLoDev = loDev; + m_ysfLoDev = loDev; + m_ysfTXHang = txHang; } bool CModem::open() { ::LogMessage("Opening the MMDVM"); - bool ret = m_serial.open(); + bool ret = m_serial->open(); if (!ret) return false; ret = readVersion(); if (!ret) { - m_serial.close(); + m_serial->close(); + delete m_serial; + m_serial = NULL; return false; } else { /* Stopping the inactivity timer here when a firmware version has been @@ -221,13 +247,17 @@ bool CModem::open() ret = setFrequency(); if (!ret) { - m_serial.close(); + m_serial->close(); + delete m_serial; + m_serial = NULL; return false; } ret = setConfig(); if (!ret) { - m_serial.close(); + m_serial->close(); + delete m_serial; + m_serial = NULL; return false; } @@ -241,6 +271,8 @@ bool CModem::open() void CModem::clock(unsigned int ms) { + assert(m_serial != NULL); + // Poll the modem status every 250ms m_statusTimer.clock(ms); if (m_statusTimer.hasExpired()) { @@ -474,6 +506,10 @@ void CModem::clock(unsigned int ms) // if (m_trace) // CUtils::dump(1U, "GET_STATUS", m_buffer, m_length); + m_p25Space = 0U; + m_nxdnSpace = 0U; + m_pocsagSpace = 0U; + m_tx = (m_buffer[5U] & 0x01U) == 0x01U; bool adcOverflow = (m_buffer[5U] & 0x02U) == 0x02U; @@ -493,18 +529,23 @@ void CModem::clock(unsigned int ms) bool dacOverflow = (m_buffer[5U] & 0x20U) == 0x20U; if (dacOverflow) LogError("MMDVM DAC levels have overflowed"); - + m_cd = (m_buffer[5U] & 0x40U) == 0x40U; - m_dstarSpace = m_buffer[6U]; - m_dmrSpace1 = m_buffer[7U]; - m_dmrSpace2 = m_buffer[8U]; - m_ysfSpace = m_buffer[9U]; - m_p25Space = m_buffer[10U]; - m_nxdnSpace = m_buffer[11U]; + m_dstarSpace = m_buffer[6U]; + m_dmrSpace1 = m_buffer[7U]; + m_dmrSpace2 = m_buffer[8U]; + m_ysfSpace = m_buffer[9U]; + + if (m_length > 10U) + m_p25Space = m_buffer[10U]; + if (m_length > 11U) + m_nxdnSpace = m_buffer[11U]; + if (m_length > 12U) + m_pocsagSpace = m_buffer[12U]; m_inactivityTimer.start(); - // LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u lockout=%d, cd=%d", m_buffer[5U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, int(m_lockout), int(m_cd)); + // LogMessage("status=%02X, tx=%d, space=%u,%u,%u,%u,%u,%u,%u lockout=%d, cd=%d", m_buffer[5U], int(m_tx), m_dstarSpace, m_dmrSpace1, m_dmrSpace2, m_ysfSpace, m_p25Space, m_nxdnSpace, m_pocsagSpace, int(m_lockout), int(m_cd)); } break; @@ -577,7 +618,7 @@ void CModem::clock(unsigned int ms) break; } - int ret = m_serial.write(m_buffer, len); + int ret = m_serial->write(m_buffer, len); if (ret != int(len)) LogWarning("Error when writing D-Star data to the MMDVM"); @@ -593,7 +634,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "TX DMR Data 1", m_buffer, len); - int ret = m_serial.write(m_buffer, len); + int ret = m_serial->write(m_buffer, len); if (ret != int(len)) LogWarning("Error when writing DMR data to the MMDVM"); @@ -610,7 +651,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "TX DMR Data 2", m_buffer, len); - int ret = m_serial.write(m_buffer, len); + int ret = m_serial->write(m_buffer, len); if (ret != int(len)) LogWarning("Error when writing DMR data to the MMDVM"); @@ -627,7 +668,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "TX YSF Data", m_buffer, len); - int ret = m_serial.write(m_buffer, len); + int ret = m_serial->write(m_buffer, len); if (ret != int(len)) LogWarning("Error when writing YSF data to the MMDVM"); @@ -648,7 +689,7 @@ void CModem::clock(unsigned int ms) CUtils::dump(1U, "TX P25 LDU", m_buffer, len); } - int ret = m_serial.write(m_buffer, len); + int ret = m_serial->write(m_buffer, len); if (ret != int(len)) LogWarning("Error when writing P25 data to the MMDVM"); @@ -665,7 +706,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "TX NXDN Data", m_buffer, len); - int ret = m_serial.write(m_buffer, len); + int ret = m_serial->write(m_buffer, len); if (ret != int(len)) LogWarning("Error when writing NXDN data to the MMDVM"); @@ -674,6 +715,23 @@ void CModem::clock(unsigned int ms) m_nxdnSpace--; } + if (m_pocsagSpace > 1U && !m_txPOCSAGData.isEmpty()) { + unsigned char len = 0U; + m_txPOCSAGData.getData(&len, 1U); + m_txPOCSAGData.getData(m_buffer, len); + + if (m_trace) + CUtils::dump(1U, "TX POCSAG Data", m_buffer, len); + + int ret = m_serial->write(m_buffer, len); + if (ret != int(len)) + LogWarning("Error when writing POCSAG data to the MMDVM"); + + m_playoutTimer.start(); + + m_pocsagSpace--; + } + if (!m_txTransparentData.isEmpty()) { unsigned char len = 0U; m_txTransparentData.getData(&len, 1U); @@ -682,7 +740,7 @@ void CModem::clock(unsigned int ms) if (m_trace) CUtils::dump(1U, "TX Transparent Data", m_buffer, len); - int ret = m_serial.write(m_buffer, len); + int ret = m_serial->write(m_buffer, len); if (ret != int(len)) LogWarning("Error when writing Transparent data to the MMDVM"); } @@ -690,9 +748,11 @@ void CModem::clock(unsigned int ms) void CModem::close() { + assert(m_serial != NULL); + ::LogMessage("Closing the MMDVM"); - m_serial.close(); + m_serial->close(); } unsigned int CModem::readDStarData(unsigned char* data) @@ -993,7 +1053,34 @@ bool CModem::writeNXDNData(const unsigned char* data, unsigned int length) return true; } -bool CModem::writeTransparentData(const unsigned char* data, unsigned int length) +bool CModem::hasPOCSAGSpace() const +{ + unsigned int space = m_txPOCSAGData.freeSpace() / (POCSAG_FRAME_LENGTH_BYTES + 4U); + + return space > 1U; +} + +bool CModem::writePOCSAGData(const unsigned char* data, unsigned int length) +{ + assert(data != NULL); + assert(length > 0U); + + unsigned char buffer[130U]; + + buffer[0U] = MMDVM_FRAME_START; + buffer[1U] = length + 3U; + buffer[2U] = MMDVM_POCSAG_DATA; + + ::memcpy(buffer + 3U, data, length); + + unsigned char len = length + 3U; // XXX Check lengths + m_txPOCSAGData.addData(&len, 1U); + m_txPOCSAGData.addData(buffer, len); + + return true; +} + +bool CModem::writeTransparentData(const unsigned char* data, unsigned int length, unsigned int sendFrameType) { assert(data != NULL); assert(length > 0U); @@ -1004,7 +1091,19 @@ bool CModem::writeTransparentData(const unsigned char* data, unsigned int length buffer[1U] = length + 3U; buffer[2U] = MMDVM_TRANSPARENT; - ::memcpy(buffer + 3U, data, length); + if (sendFrameType>0) { + ::memcpy(buffer + 2U, data, length); + length--; + buffer[1U]--; + //when sendFrameType==1 , only 0x80 and 0x90 (MMDVM_SERIAL and MMDVM_TRANSPARENT) are allowed + // and reverted to default (MMDVM_TRANSPARENT) for any other value + //when >1, frame type is not checked + if (sendFrameType==1) { + if ((buffer[2U] & 0xE0) != 0x80) buffer[2U] = MMDVM_TRANSPARENT; + } + } else { + ::memcpy(buffer + 3U, data, length); + } unsigned char len = length + 3U; m_txTransparentData.addData(&len, 1U); @@ -1015,6 +1114,7 @@ bool CModem::writeTransparentData(const unsigned char* data, unsigned int length bool CModem::writeSerial(const unsigned char* data, unsigned int length) { + assert(m_serial != NULL); assert(data != NULL); assert(length > 0U); @@ -1026,7 +1126,7 @@ bool CModem::writeSerial(const unsigned char* data, unsigned int length) ::memcpy(buffer + 3U, data, length); - int ret = m_serial.write(buffer, length + 3U); + int ret = m_serial->write(buffer, length + 3U); return ret != int(length + 3U); } @@ -1053,6 +1153,8 @@ bool CModem::hasError() const bool CModem::readVersion() { + assert(m_serial != NULL); + CThread::sleep(2000U); // 2s for (unsigned int i = 0U; i < 6U; i++) { @@ -1064,12 +1166,12 @@ bool CModem::readVersion() // CUtils::dump(1U, "Written", buffer, 3U); - int ret = m_serial.write(buffer, 3U); + int ret = m_serial->write(buffer, 3U); if (ret != 3) return false; #if defined(__APPLE__) - m_serial.setNonblock(true); + m_serial->setNonblock(true); #endif for (unsigned int count = 0U; count < MAX_RESPONSES; count++) { @@ -1088,6 +1190,8 @@ bool CModem::readVersion() m_hwType = HWT_MMDVM_HS_DUAL_HAT; else if (::memcmp(m_buffer + 4U, "Nano_hotSPOT", 12U) == 0) m_hwType = HWT_NANO_HOTSPOT; + else if (::memcmp(m_buffer + 4U, "Nano_DV", 7U) == 0) + m_hwType = HWT_NANO_DV; else if (::memcmp(m_buffer + 4U, "MMDVM_HS-", 9U) == 0) m_hwType = HWT_MMDVM_HS; @@ -1106,6 +1210,8 @@ bool CModem::readVersion() bool CModem::readStatus() { + assert(m_serial != NULL); + unsigned char buffer[3U]; buffer[0U] = MMDVM_FRAME_START; @@ -1114,16 +1220,18 @@ bool CModem::readStatus() // CUtils::dump(1U, "Written", buffer, 3U); - return m_serial.write(buffer, 3U) == 3; + return m_serial->write(buffer, 3U) == 3; } bool CModem::setConfig() { - unsigned char buffer[20U]; + assert(m_serial != NULL); + + unsigned char buffer[30U]; buffer[0U] = MMDVM_FRAME_START; - buffer[1U] = 19U; + buffer[1U] = 21U; buffer[2U] = MMDVM_SET_CONFIG; @@ -1152,6 +1260,8 @@ bool CModem::setConfig() buffer[4U] |= 0x08U; if (m_nxdnEnabled) buffer[4U] |= 0x10U; + if (m_pocsagEnabled) + buffer[4U] |= 0x20U; buffer[5U] = m_txDelay / 10U; // In 10ms units @@ -1177,10 +1287,14 @@ bool CModem::setConfig() buffer[18U] = (unsigned char)(m_nxdnTXLevel * 2.55F + 0.5F); - // CUtils::dump(1U, "Written", buffer, 19U); + buffer[19U] = (unsigned char)m_ysfTXHang; - int ret = m_serial.write(buffer, 19U); - if (ret != 19) + buffer[20U] = (unsigned char)(m_pocsagTXLevel * 2.55F + 0.5F); + + // CUtils::dump(1U, "Written", buffer, 21U); + + int ret = m_serial->write(buffer, 21U); + if (ret != 21) return false; unsigned int count = 0U; @@ -1212,14 +1326,22 @@ bool CModem::setConfig() bool CModem::setFrequency() { - unsigned char buffer[16U]; + assert(m_serial != NULL); + + unsigned char buffer[20U]; unsigned char len; - + if (m_hwType == HWT_DVMEGA) len = 12U; else { buffer[12U] = (unsigned char)(m_rfLevel * 2.55F + 0.5F); - len = 13U; + + buffer[13U] = (m_pocsagFrequency >> 0) & 0xFFU; + buffer[14U] = (m_pocsagFrequency >> 8) & 0xFFU; + buffer[15U] = (m_pocsagFrequency >> 16) & 0xFFU; + buffer[16U] = (m_pocsagFrequency >> 24) & 0xFFU; + + len = 17U; } buffer[0U] = MMDVM_FRAME_START; @@ -1242,7 +1364,7 @@ bool CModem::setFrequency() // CUtils::dump(1U, "Written", buffer, len); - int ret = m_serial.write(buffer, len); + int ret = m_serial->write(buffer, len); if (ret != len) return false; @@ -1273,9 +1395,11 @@ bool CModem::setFrequency() RESP_TYPE_MMDVM CModem::getResponse() { + assert(m_serial != NULL); + if (m_offset == 0U) { // Get the start of the frame or nothing at all - int ret = m_serial.read(m_buffer + 0U, 1U); + int ret = m_serial->read(m_buffer + 0U, 1U); if (ret < 0) { LogError("Error when reading from the modem"); return RTM_ERROR; @@ -1292,7 +1416,7 @@ RESP_TYPE_MMDVM CModem::getResponse() if (m_offset == 1U) { // Get the length of the frame - int ret = m_serial.read(m_buffer + 1U, 1U); + int ret = m_serial->read(m_buffer + 1U, 1U); if (ret < 0) { LogError("Error when reading from the modem"); m_offset = 0U; @@ -1314,7 +1438,7 @@ RESP_TYPE_MMDVM CModem::getResponse() if (m_offset == 2U) { // Get the frame type - int ret = m_serial.read(m_buffer + 2U, 1U); + int ret = m_serial->read(m_buffer + 2U, 1U); if (ret < 0) { LogError("Error when reading from the modem"); m_offset = 0U; @@ -1330,7 +1454,7 @@ RESP_TYPE_MMDVM CModem::getResponse() if (m_offset >= 3U) { // Use later two byte length field if (m_length == 0U) { - int ret = m_serial.read(m_buffer + 3U, 2U); + int ret = m_serial->read(m_buffer + 3U, 2U); if (ret < 0) { LogError("Error when reading from the modem"); m_offset = 0U; @@ -1345,7 +1469,7 @@ RESP_TYPE_MMDVM CModem::getResponse() } while (m_offset < m_length) { - int ret = m_serial.read(m_buffer + m_offset, m_length - m_offset); + int ret = m_serial->read(m_buffer + m_offset, m_length - m_offset); if (ret < 0) { LogError("Error when reading from the modem"); m_offset = 0U; @@ -1374,6 +1498,8 @@ HW_TYPE CModem::getHWType() const bool CModem::setMode(unsigned char mode) { + assert(m_serial != NULL); + unsigned char buffer[4U]; buffer[0U] = MMDVM_FRAME_START; @@ -1383,11 +1509,13 @@ bool CModem::setMode(unsigned char mode) // CUtils::dump(1U, "Written", buffer, 4U); - return m_serial.write(buffer, 4U) == 4; + return m_serial->write(buffer, 4U) == 4; } bool CModem::sendCWId(const std::string& callsign) { + assert(m_serial != NULL); + unsigned int length = callsign.length(); if (length > 200U) length = 200U; @@ -1403,11 +1531,13 @@ bool CModem::sendCWId(const std::string& callsign) // CUtils::dump(1U, "Written", buffer, length + 3U); - return m_serial.write(buffer, length + 3U) == int(length + 3U); + return m_serial->write(buffer, length + 3U) == int(length + 3U); } bool CModem::writeDMRStart(bool tx) { + assert(m_serial != NULL); + if (tx && m_tx) return true; if (!tx && !m_tx) @@ -1422,11 +1552,13 @@ bool CModem::writeDMRStart(bool tx) // CUtils::dump(1U, "Written", buffer, 4U); - return m_serial.write(buffer, 4U) == 4; + return m_serial->write(buffer, 4U) == 4; } bool CModem::writeDMRAbort(unsigned int slotNo) { + assert(m_serial != NULL); + if (slotNo == 1U) m_txDMRData1.clear(); else @@ -1441,11 +1573,12 @@ bool CModem::writeDMRAbort(unsigned int slotNo) // CUtils::dump(1U, "Written", buffer, 4U); - return m_serial.write(buffer, 4U) == 4; + return m_serial->write(buffer, 4U) == 4; } bool CModem::writeDMRShortLC(const unsigned char* lc) { + assert(m_serial != NULL); assert(lc != NULL); unsigned char buffer[12U]; @@ -1465,7 +1598,7 @@ bool CModem::writeDMRShortLC(const unsigned char* lc) // CUtils::dump(1U, "Written", buffer, 12U); - return m_serial.write(buffer, 12U) == 12; + return m_serial->write(buffer, 12U) == 12; } void CModem::printDebug() diff --git a/Modem.h b/Modem.h index b527373..ef9c415 100644 --- a/Modem.h +++ b/Modem.h @@ -37,11 +37,12 @@ public: CModem(const std::string& port, bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool trace, bool debug); ~CModem(); - void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel); - void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled); - void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel); + void setSerialParams(const std::string& protocol, unsigned int address); + void setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int txFrequency, int txOffset, int txDCOffset, int rxDCOffset, float rfLevel, unsigned int pocsagFrequency); + void setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled); + void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagLevel); void setDMRParams(unsigned int colorCode); - void setYSFParams(bool loDev); + void setYSFParams(bool loDev, unsigned int txHang); bool open(); @@ -61,6 +62,7 @@ public: bool hasYSFSpace() const; bool hasP25Space() const; bool hasNXDNSpace() const; + bool hasPOCSAGSpace() const; bool hasTX() const; bool hasCD() const; @@ -74,7 +76,8 @@ public: bool writeYSFData(const unsigned char* data, unsigned int length); bool writeP25Data(const unsigned char* data, unsigned int length); bool writeNXDNData(const unsigned char* data, unsigned int length); - bool writeTransparentData(const unsigned char* data, unsigned int length); + bool writePOCSAGData(const unsigned char* data, unsigned int length); + bool writeTransparentData(const unsigned char* data, unsigned int length, unsigned int sendFrameType); bool writeDMRStart(bool tx); bool writeDMRShortLC(const unsigned char* lc); @@ -96,6 +99,7 @@ private: std::string m_port; unsigned int m_dmrColorCode; bool m_ysfLoDev; + unsigned int m_ysfTXHang; bool m_duplex; bool m_rxInvert; bool m_txInvert; @@ -109,19 +113,22 @@ private: float m_ysfTXLevel; float m_p25TXLevel; float m_nxdnTXLevel; + float m_pocsagTXLevel; float m_rfLevel; bool m_trace; bool m_debug; unsigned int m_rxFrequency; unsigned int m_txFrequency; + unsigned int m_pocsagFrequency; bool m_dstarEnabled; bool m_dmrEnabled; bool m_ysfEnabled; bool m_p25Enabled; bool m_nxdnEnabled; + bool m_pocsagEnabled; int m_rxDCOffset; int m_txDCOffset; - CSerialController m_serial; + CSerialController* m_serial; unsigned char* m_buffer; unsigned int m_length; unsigned int m_offset; @@ -137,6 +144,7 @@ private: CRingBuffer m_txP25Data; CRingBuffer m_rxNXDNData; CRingBuffer m_txNXDNData; + CRingBuffer m_txPOCSAGData; CRingBuffer m_rxTransparentData; CRingBuffer m_txTransparentData; CTimer m_statusTimer; @@ -148,6 +156,7 @@ private: unsigned int m_ysfSpace; unsigned int m_p25Space; unsigned int m_nxdnSpace; + unsigned int m_pocsagSpace; bool m_tx; bool m_cd; bool m_lockout; diff --git a/NXDNControl.cpp b/NXDNControl.cpp index 55ec3a6..1d253a0 100644 --- a/NXDNControl.cpp +++ b/NXDNControl.cpp @@ -227,6 +227,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne lich.setDirection(m_remoteGateway || !m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND); lich.encode(data + 2U); + lich.setDirection(NXDN_LICH_DIRECTION_INBOUND); netData[0U] = lich.getRaw(); CNXDNSACCH sacch; @@ -245,7 +246,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne scrambler(data + 2U); - writeNetwork(netData, true); + writeNetwork(netData, data[0U] == TAG_EOT ? NNMT_VOICE_TRAILER : NNMT_VOICE_HEADER); #if defined(DUMP_NXDN) writeFile(data + 2U); @@ -402,6 +403,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne lich.setDirection(m_remoteGateway || !m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND); lich.encode(start + 2U); + lich.setDirection(NXDN_LICH_DIRECTION_INBOUND); netData[0U] = lich.getRaw(); CNXDNSACCH sacch; @@ -424,7 +426,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne scrambler(start + 2U); - writeNetwork(netData, true); + writeNetwork(netData, NNMT_VOICE_HEADER); #if defined(DUMP_NXDN) writeFile(start + 2U); @@ -446,6 +448,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne lich.setDirection(m_remoteGateway || !m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND); lich.encode(data + 2U); + lich.setDirection(NXDN_LICH_DIRECTION_INBOUND); netData[0U] = lich.getRaw(); // Regenerate SACCH if it's valid @@ -529,7 +532,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne scrambler(data + 2U); - writeNetwork(netData, false); + writeNetwork(netData, NNMT_VOICE_BODY); #if defined(DUMP_NXDN) writeFile(data + 2U); @@ -614,12 +617,15 @@ bool CNXDNControl::processData(unsigned char option, unsigned char *data) lich.setDirection(m_remoteGateway || !m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND); lich.encode(data + 2U); + lich.setDirection(NXDN_LICH_DIRECTION_INBOUND); netData[0U] = lich.getRaw(); udch.getRaw(netData + 1U); + unsigned char type = NXDN_MESSAGE_TYPE_DCALL_DATA; + if (validUDCH) { - unsigned char type = layer3.getMessageType(); + type = layer3.getMessageType(); data[0U] = type == NXDN_MESSAGE_TYPE_TX_REL ? TAG_EOT : TAG_DATA; udch.setRAN(m_ran); @@ -631,7 +637,17 @@ bool CNXDNControl::processData(unsigned char option, unsigned char *data) scrambler(data + 2U); - writeNetwork(netData, true); + switch (type) { + case NXDN_MESSAGE_TYPE_DCALL_HDR: + writeNetwork(netData, NNMT_DATA_HEADER); + break; + case NXDN_MESSAGE_TYPE_TX_REL: + writeNetwork(netData, NNMT_DATA_TRAILER); + break; + default: + writeNetwork(netData, NNMT_DATA_BODY); + break; + } if (m_duplex) writeQueueRF(data); @@ -1005,7 +1021,7 @@ void CNXDNControl::writeQueueNet(const unsigned char *data) m_queue.addData(data, len); } -void CNXDNControl::writeNetwork(const unsigned char *data, bool single) +void CNXDNControl::writeNetwork(const unsigned char *data, NXDN_NETWORK_MESSAGE_TYPE type) { assert(data != NULL); @@ -1015,7 +1031,7 @@ void CNXDNControl::writeNetwork(const unsigned char *data, bool single) if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) return; - m_network->write(data, single); + m_network->write(data, type); } void CNXDNControl::scrambler(unsigned char* data) const @@ -1043,7 +1059,7 @@ bool CNXDNControl::openFile() if (m_fp == NULL) return false; - ::fwrite("NXDN", 1U, 3U, m_fp); + ::fwrite("NXDN", 1U, 4U, m_fp); return true; } diff --git a/NXDNControl.h b/NXDNControl.h index 01651d6..0bb38d7 100644 --- a/NXDNControl.h +++ b/NXDNControl.h @@ -84,7 +84,7 @@ private: void writeQueueRF(const unsigned char* data); void writeQueueNet(const unsigned char* data); - void writeNetwork(const unsigned char* data, bool single); + void writeNetwork(const unsigned char* data, NXDN_NETWORK_MESSAGE_TYPE type); void writeNetwork(); void scrambler(unsigned char* data) const; diff --git a/NXDNConvolution.cpp b/NXDNConvolution.cpp old mode 100755 new mode 100644 diff --git a/NXDNLayer3.cpp b/NXDNLayer3.cpp index 21ffe0a..e6e731a 100644 --- a/NXDNLayer3.cpp +++ b/NXDNLayer3.cpp @@ -89,7 +89,7 @@ bool CNXDNLayer3::getIsGroup() const unsigned char CNXDNLayer3::getDataBlocks() const { - return m_data[8U] & 0x0FU; + return (m_data[8U] & 0x0FU) + 1U; } void CNXDNLayer3::getData(unsigned char* data) const diff --git a/NXDNNetwork.cpp b/NXDNNetwork.cpp index 04703b0..f724dea 100644 --- a/NXDNNetwork.cpp +++ b/NXDNNetwork.cpp @@ -56,7 +56,7 @@ bool CNXDNNetwork::open() return m_socket.open(); } -bool CNXDNNetwork::write(const unsigned char* data, bool single) +bool CNXDNNetwork::write(const unsigned char* data, NXDN_NETWORK_MESSAGE_TYPE type) { assert(data != NULL); @@ -72,9 +72,28 @@ bool CNXDNNetwork::write(const unsigned char* data, bool single) buffer[6U] = 0x08U; buffer[7U] = 0xE0U; - buffer[37U] = 0x23U; - buffer[38U] = single ? 0x1CU : 0x10U; - buffer[39U] = 0x21U; + switch (type) { + case NNMT_VOICE_HEADER: + case NNMT_VOICE_TRAILER: + buffer[37U] = 0x23U; + buffer[38U] = 0x1CU; + buffer[39U] = 0x21U; + break; + case NNMT_VOICE_BODY: + buffer[37U] = 0x23U; + buffer[38U] = 0x10U; + buffer[39U] = 0x21U; + break; + case NNMT_DATA_HEADER: + case NNMT_DATA_BODY: + case NNMT_DATA_TRAILER: + buffer[37U] = 0x23U; + buffer[38U] = 0x02U; + buffer[39U] = 0x18U; + break; + default: + return false; + } ::memcpy(buffer + 40U, data, 33U); diff --git a/NXDNNetwork.h b/NXDNNetwork.h index 4a6c053..3a5b784 100644 --- a/NXDNNetwork.h +++ b/NXDNNetwork.h @@ -27,6 +27,15 @@ #include #include +enum NXDN_NETWORK_MESSAGE_TYPE { + NNMT_VOICE_HEADER, + NNMT_VOICE_BODY, + NNMT_VOICE_TRAILER, + NNMT_DATA_HEADER, + NNMT_DATA_BODY, + NNMT_DATA_TRAILER +}; + class CNXDNNetwork { public: CNXDNNetwork(const std::string& localAddress, unsigned int localPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug); @@ -36,7 +45,7 @@ public: void enable(bool enabled); - bool write(const unsigned char* data, bool single); + bool write(const unsigned char* data, NXDN_NETWORK_MESSAGE_TYPE type); bool read(unsigned char* data); diff --git a/NXDNSACCH.cpp b/NXDNSACCH.cpp old mode 100755 new mode 100644 diff --git a/NXDNUDCH.cpp b/NXDNUDCH.cpp index 55c81bf..bdba864 100644 --- a/NXDNUDCH.cpp +++ b/NXDNUDCH.cpp @@ -187,8 +187,6 @@ void CNXDNUDCH::getRaw(unsigned char* data) const ::memset(data, 0x00U, 25U); ::memcpy(data, m_data, 23U); - - CNXDNCRC::encodeCRC15(data, 184U); } void CNXDNUDCH::setRAN(unsigned char ran) diff --git a/Nextion.cpp b/Nextion.cpp index 25a0fce..f4853ef 100644 --- a/Nextion.cpp +++ b/Nextion.cpp @@ -99,7 +99,7 @@ void CNextion::setIdleInt() sendCommand("page MMDVM"); sendCommandAction(1U); - char command[30U]; + char command[100U]; ::sprintf(command, "dim=%u", m_idleBrightness); sendCommand(command); @@ -165,6 +165,27 @@ void CNextion::setLockoutInt() m_mode = MODE_LOCKOUT; } +void CNextion::setQuitInt() +{ + sendCommand("page MMDVM"); + sendCommandAction(1U); + + char command[100]; + ::sprintf(command, "dim=%u", m_idleBrightness); + sendCommand(command); + + ::sprintf(command, "t3.txt=\"%s\"", m_ipaddress.c_str()); + sendCommand(command); + sendCommandAction(16U); + + sendCommand("t0.txt=\"MMDVM STOPPED\""); + sendCommandAction(19U); + + m_clockDisplayTimer.stop(); + + m_mode = MODE_QUIT; +} + void CNextion::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) { assert(my1 != NULL); @@ -178,7 +199,7 @@ void CNextion::writeDStarInt(const char* my1, const char* my2, const char* your, sendCommandAction(2U); } - char text[30U]; + char text[50U]; ::sprintf(text, "dim=%u", m_brightness); sendCommand(text); @@ -273,7 +294,7 @@ void CNextion::writeDMRInt(unsigned int slotNo, const std::string& src, bool gro } } - char text[30U]; + char text[50U]; ::sprintf(text, "dim=%u", m_brightness); sendCommand(text); @@ -367,7 +388,7 @@ void CNextion::writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, co } if (slotNo == 1U) { - char text[40U]; + char text[50U]; ::sprintf(text, "t0.txt=\"1 %s %s\"", type, talkerAlias); if (m_screenLayout == 2U) { @@ -384,7 +405,7 @@ void CNextion::writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, co sendCommand(text); sendCommandAction(63U); } else { - char text[40U]; + char text[50U]; ::sprintf(text, "t2.txt=\"2 %s %s\"", type, talkerAlias); if (m_screenLayout == 2U) { @@ -679,6 +700,37 @@ void CNextion::clearNXDNInt() sendCommand("t3.txt=\"\""); } +void CNextion::writePOCSAGInt(uint32_t ric, const std::string& message) +{ + if (m_mode != MODE_POCSAG) { + sendCommand("page POCSAG"); + sendCommandAction(7U); + } + + char text[200U]; + ::sprintf(text, "dim=%u", m_brightness); + sendCommand(text); + + ::sprintf(text, "t0.txt=\"RIC: %u\"", ric); + sendCommand(text); + sendCommandAction(132U); + + ::sprintf(text, "t1.txt=\"%s\"", message.c_str()); + sendCommand(text); + sendCommandAction(133U); + + m_clockDisplayTimer.stop(); + + m_mode = MODE_POCSAG; +} + +void CNextion::clearPOCSAGInt() +{ + sendCommand("t0.txt=\"Waiting\""); + sendCommandAction(134U); + sendCommand("t1.txt=\"\""); +} + void CNextion::writeCWInt() { sendCommand("t1.txt=\"Sending CW Ident\""); @@ -719,10 +771,6 @@ void CNextion::clockInt(unsigned int ms) void CNextion::close() { - sendCommand("page MMDVM"); - sendCommandAction(1U); - sendCommand("t1.txt=\"MMDVM STOPPED\""); - sendCommandAction(19U); m_serial->close(); delete m_serial; } diff --git a/Nextion.h b/Nextion.h index b08ebf8..7578acf 100644 --- a/Nextion.h +++ b/Nextion.h @@ -40,6 +40,7 @@ protected: virtual void setIdleInt(); virtual void setErrorInt(const char* text); virtual void setLockoutInt(); + virtual void setQuitInt(); virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); virtual void writeDStarRSSIInt(unsigned char rssi); @@ -68,6 +69,9 @@ protected: virtual void writeNXDNBERInt(float ber); virtual void clearNXDNInt(); + virtual void writePOCSAGInt(uint32_t ric, const std::string& message); + virtual void clearPOCSAGInt(); + virtual void writeCWInt(); virtual void clearCWInt(); diff --git a/Nextion_G4KLX/NX3224K024.HMI b/Nextion_G4KLX/NX3224K024.HMI index b73602c..61c8067 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 f644ee7..417aefd 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 d8e3c33..b4ebbc3 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 641cee6..7880b8c 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 939a774..44f1538 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 6fda9bd..3699e85 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 7b81e82..3864b34 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 72c3842..034441b 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 42c839d..7df6343 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 9844d1d..92b2843 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 8f6e955..03cd4a8 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 c4a8af7..9405c5e 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 74ad140..e1c2cf4 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 f5ddadc..71fc6d8 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 8026319..3128178 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 8927f8a..5643caa 100644 Binary files a/Nextion_G4KLX/NX4832T035.tft and b/Nextion_G4KLX/NX4832T035.tft differ diff --git a/Nextion_ON7LDS/NX3224T024-L2.HMI b/Nextion_ON7LDS/NX3224T024-L2.HMI index d3d4696..e444afa 100644 Binary files a/Nextion_ON7LDS/NX3224T024-L2.HMI and b/Nextion_ON7LDS/NX3224T024-L2.HMI differ diff --git a/Nextion_ON7LDS/NX3224T024-L2.tft b/Nextion_ON7LDS/NX3224T024-L2.tft index 081b5cf..5ef6c18 100644 Binary files a/Nextion_ON7LDS/NX3224T024-L2.tft and b/Nextion_ON7LDS/NX3224T024-L2.tft differ diff --git a/Nextion_ON7LDS/NX3224T024-L3.HMI b/Nextion_ON7LDS/NX3224T024-L3.HMI index 8a610c6..36d0843 100644 Binary files a/Nextion_ON7LDS/NX3224T024-L3.HMI and b/Nextion_ON7LDS/NX3224T024-L3.HMI differ diff --git a/Nextion_ON7LDS/NX3224T024-L3.tft b/Nextion_ON7LDS/NX3224T024-L3.tft index b3f54aa..df3405f 100644 Binary files a/Nextion_ON7LDS/NX3224T024-L3.tft and b/Nextion_ON7LDS/NX3224T024-L3.tft differ diff --git a/Nextion_ON7LDS/NX3224T028-L2.HMI b/Nextion_ON7LDS/NX3224T028-L2.HMI index 88f02b7..8fca48b 100644 Binary files a/Nextion_ON7LDS/NX3224T028-L2.HMI and b/Nextion_ON7LDS/NX3224T028-L2.HMI differ diff --git a/Nextion_ON7LDS/NX3224T028-L2.tft b/Nextion_ON7LDS/NX3224T028-L2.tft index a06d977..610f8c9 100644 Binary files a/Nextion_ON7LDS/NX3224T028-L2.tft and b/Nextion_ON7LDS/NX3224T028-L2.tft differ diff --git a/Nextion_ON7LDS/NX3224T028-L3.HMI b/Nextion_ON7LDS/NX3224T028-L3.HMI index a3ae328..f19c866 100644 Binary files a/Nextion_ON7LDS/NX3224T028-L3.HMI and b/Nextion_ON7LDS/NX3224T028-L3.HMI differ diff --git a/Nextion_ON7LDS/NX3224T028-L3.tft b/Nextion_ON7LDS/NX3224T028-L3.tft index 6f033a4..cf5c831 100644 Binary files a/Nextion_ON7LDS/NX3224T028-L3.tft and b/Nextion_ON7LDS/NX3224T028-L3.tft differ diff --git a/Nextion_ON7LDS/NX4024T032-L2.HMI b/Nextion_ON7LDS/NX4024T032-L2.HMI index 06d8ce2..4585874 100644 Binary files a/Nextion_ON7LDS/NX4024T032-L2.HMI and b/Nextion_ON7LDS/NX4024T032-L2.HMI differ diff --git a/Nextion_ON7LDS/NX4024T032-L2.tft b/Nextion_ON7LDS/NX4024T032-L2.tft index 4279d35..b310ce4 100644 Binary files a/Nextion_ON7LDS/NX4024T032-L2.tft and b/Nextion_ON7LDS/NX4024T032-L2.tft differ diff --git a/Nextion_ON7LDS/NX4832T035-L2.HMI b/Nextion_ON7LDS/NX4832T035-L2.HMI index e49e748..1925206 100644 Binary files a/Nextion_ON7LDS/NX4832T035-L2.HMI and b/Nextion_ON7LDS/NX4832T035-L2.HMI differ diff --git a/Nextion_ON7LDS/NX4832T035-L2.tft b/Nextion_ON7LDS/NX4832T035-L2.tft index 9b6642b..e2317e2 100644 Binary files a/Nextion_ON7LDS/NX4832T035-L2.tft and b/Nextion_ON7LDS/NX4832T035-L2.tft differ diff --git a/Nextion_ON7LDS/NX4832T035-L3.HMI b/Nextion_ON7LDS/NX4832T035-L3.HMI index 59f027e..4c809c8 100644 Binary files a/Nextion_ON7LDS/NX4832T035-L3.HMI and b/Nextion_ON7LDS/NX4832T035-L3.HMI differ diff --git a/Nextion_ON7LDS/NX4832T035-L3.tft b/Nextion_ON7LDS/NX4832T035-L3.tft index 35ac4e7..1b93611 100644 Binary files a/Nextion_ON7LDS/NX4832T035-L3.tft and b/Nextion_ON7LDS/NX4832T035-L3.tft differ diff --git a/Nextion_ON7LDS/README-L3 b/Nextion_ON7LDS/README-L3 index 1bf6b98..692d3c6 100644 --- a/Nextion_ON7LDS/README-L3 +++ b/Nextion_ON7LDS/README-L3 @@ -15,8 +15,8 @@ are activated. 0 = auto (future use, for now it's G4KLX layout) 1 = G4KLX layout 2 = ON7LDS layout (see README-L2) - 3 = ON7LDS DIY layout (this README file) - 4 = ON7LDS DIY layout High Speed (this README file) + 3 = ON7LDS DIY layout (this README file) + 4 = ON7LDS DIY layout High Speed (this README file) screenLayout 3 and 4 are the same, but selecting 3, MMDVMHost will communicate at 9600bps with the display and selecting 4 will set the baudrate to 115200. @@ -69,7 +69,7 @@ Check the NextionDriver program and the display layouts there to see NOTE: it might be good to *not* remove fields from the display when you do not need them, but make them small (i.e. 10 x 10 pixels) and put them aside: - Give them the same font color and background color as where you put them + Give them the same font colour and background colour as where you put them OR Put them behind some picture: put them on top of the picture, then select the picture and click the 'Bring Top' button to put it on top of all those fields @@ -94,6 +94,7 @@ changed field. 4 : page YSF 5 : page P25 6 : page NXDN + 7 : page POCSAG 11 : IDLE 12 : CW @@ -147,6 +148,9 @@ changed field. 124 : RSSI 125 : ber +132 : RIC +133 : message text +134 : waiting Fields (and their numbers) on the pages, used by MMDVMHost @@ -154,7 +158,7 @@ Fields (and their numbers) on the pages, used by MMDVMHost MMDVM t0 : owner call & ID / errortext LOCKOUT -t1 : status / ERROR +t1 : status / ERROR t2 : date & time screenLayout >1 : @@ -211,3 +215,6 @@ t1 : dst t2 : rssi t3 : ber +POCSAG +t0 : waiting / RIC +t1 : message diff --git a/Nextion_ON7LDS/README.md b/Nextion_ON7LDS/README.md index 83a8990..bae6a56 100644 --- a/Nextion_ON7LDS/README.md +++ b/Nextion_ON7LDS/README.md @@ -16,8 +16,8 @@ Layout 2 is a no-nonsense layout. It is the original (G4KLX) layout with the Tal Layout 3 (as is 4) is a layout without any predefined layout options (color, fonts). It sends the fields **and** information about what was sent to the display, so all layout processing can and should be done in the display itself. More information about the layouts can be found in - * README-L2 for the screenLayout 2 setting - * README-L2 for the screenLayout 3 and 4 settings + * README-L2 for the screenLayout 2 setting (list of field names : see README-L3) + * README-L3 for the screenLayout 3 and 4 settings When you want extra control over what has to be sent to the Nextion display, you could consider the program 'NextionDriver' at https://github.com/on7lds/NextionDriver as a companion to MMDVMHost. diff --git a/NullDisplay.cpp b/NullDisplay.cpp index a70d4f8..6219ec8 100644 --- a/NullDisplay.cpp +++ b/NullDisplay.cpp @@ -56,6 +56,10 @@ void CNullDisplay::setLockoutInt() { } +void CNullDisplay::setQuitInt() +{ +} + void CNullDisplay::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) { #if defined(RASPBERRY_PI) @@ -126,6 +130,20 @@ void CNullDisplay::clearNXDNInt() #endif } +void CNullDisplay::writePOCSAGInt(uint32_t ric, const std::string& message) +{ +#if defined(RASPBERRY_PI) + ::digitalWrite(LED_STATUS, 1); +#endif +} + +void CNullDisplay::clearPOCSAGInt() +{ +#if defined(RASPBERRY_PI) + ::digitalWrite(LED_STATUS, 0); +#endif +} + void CNullDisplay::writeCWInt() { } diff --git a/NullDisplay.h b/NullDisplay.h index cacb3b2..50f40b8 100644 --- a/NullDisplay.h +++ b/NullDisplay.h @@ -37,6 +37,7 @@ protected: virtual void setIdleInt(); virtual void setErrorInt(const char* text); virtual void setLockoutInt(); + virtual void setQuitInt(); virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); virtual void clearDStarInt(); @@ -53,6 +54,9 @@ protected: virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); virtual void clearNXDNInt(); + virtual void writePOCSAGInt(uint32_t ric, const std::string& message); + virtual void clearPOCSAGInt(); + virtual void writeCWInt(); virtual void clearCWInt(); diff --git a/OLED.cpp b/OLED.cpp index d718a2a..b4b7303 100644 --- a/OLED.cpp +++ b/OLED.cpp @@ -106,45 +106,66 @@ const unsigned char logo_fusion_bmp [] = 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - //Logo P25 128x16px - const unsigned char logo_P25_bmp [] = - { -0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -0xf8, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xfc, 0x00, 0x00, 0x3f, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x7f, -0xf8, 0x00, 0x00, 0x00, 0x03, 0xff, 0xc0, 0x00, 0x00, 0x03, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x7f, -0xf8, 0x00, 0xe0, 0x00, 0x00, 0xff, 0x00, 0x07, 0xc0, 0x00, 0xff, 0xe0, 0x00, 0x00, 0x00, 0xff, -0xf0, 0x01, 0xff, 0xfc, 0x00, 0x7e, 0x00, 0x3f, 0xf8, 0x00, 0xff, 0xe0, 0x0f, 0xff, 0xff, 0xff, -0xf0, 0x01, 0xff, 0xfe, 0x00, 0x7c, 0x00, 0x7f, 0xfc, 0x00, 0x7f, 0xe0, 0x0f, 0xff, 0xff, 0xff, -0xf0, 0x01, 0xff, 0xfe, 0x00, 0x7e, 0x00, 0x7f, 0xf8, 0x00, 0xff, 0xe0, 0x00, 0x00, 0x01, 0xff, -0xf0, 0x01, 0xff, 0xc0, 0x00, 0xff, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x1f, -0xf0, 0x00, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0x00, 0x07, 0xff, 0xff, 0x9f, 0xff, 0x80, 0x07, -0xf0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf8, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x03, -0xf0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x03, -0xf0, 0x01, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x1f, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xc0, 0x03, -0xf0, 0x01, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xfe, 0x00, 0x0f, 0xfe, 0x00, 0x0f, -0xf0, 0x01, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x80, 0x00, 0x00, 0x00, 0x3f, -0xf0, 0x01, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xf8, 0x00, 0x00, 0x03, 0xff, -0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff - }; - +//Logo P25 128x16px +const unsigned char logo_P25_bmp [] = +{ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x03, 0xff, 0xff, 0xc0, 0x01, 0xff, 0xff, 0xff, 0xf8, 0x00, +0x01, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3f, 0xff, 0xff, 0xfc, 0x01, 0xff, 0xff, 0xff, 0xf8, 0x00, +0x01, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xf8, 0x3f, 0xff, 0x01, 0xff, 0xff, 0xff, 0xf8, 0x00, +0x01, 0xff, 0xc0, 0x00, 0x7f, 0xf1, 0xff, 0xc0, 0x07, 0xff, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, +0x01, 0xff, 0xc0, 0x00, 0x3f, 0xf3, 0xff, 0x80, 0x03, 0xff, 0x81, 0xff, 0x00, 0x00, 0x00, 0x00, +0x01, 0xff, 0xc0, 0x00, 0x3f, 0xf1, 0xff, 0x80, 0x07, 0xff, 0x01, 0xff, 0xff, 0xff, 0xe0, 0x00, +0x01, 0xff, 0xc0, 0x07, 0xff, 0xe0, 0x00, 0x00, 0x1f, 0xfe, 0x01, 0xff, 0xff, 0xff, 0xfe, 0x00, +0x01, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x07, 0xff, 0x80, +0x01, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x07, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x01, 0xff, 0xc0, +0x01, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xc0, +0x01, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xff, 0xe0, 0x00, 0x03, 0xf0, 0x00, 0x03, 0xff, 0xc0, +0x01, 0xff, 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x00, 0x1f, 0xff, 0x00, +0x01, 0xff, 0xc0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xfc, 0x00, +0x01, 0xff, 0xc0, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0x80, 0x7f, 0xff, 0xff, 0xc0, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + // Logo NXDN_sm, 128x16px const unsigned char logo_NXDN_bmp [] = - { -0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -0xff, 0xf0, 0x1f, 0xf8, 0x0f, 0x00, 0xff, 0x80, 0x7c, 0x00, 0x0f, 0xff, 0x80, 0x7f, 0xe0, 0x7f, -0xff, 0xe0, 0x0f, 0xf0, 0x1f, 0x80, 0x7e, 0x01, 0xf8, 0x00, 0x00, 0x7f, 0x00, 0x3f, 0xc0, 0x7f, -0xff, 0xc0, 0x07, 0xe0, 0x3f, 0x80, 0x38, 0x07, 0xf0, 0x00, 0x00, 0x3e, 0x00, 0x3f, 0x80, 0xff, -0xff, 0x80, 0x03, 0xc0, 0x3f, 0xc0, 0x00, 0x3f, 0xe0, 0x1f, 0x80, 0x3e, 0x00, 0x1f, 0x01, 0xff, -0xff, 0x00, 0x03, 0x80, 0x7f, 0xe0, 0x00, 0xff, 0xc0, 0x3f, 0x80, 0x3c, 0x00, 0x0e, 0x03, 0xff, -0xfe, 0x00, 0x01, 0x00, 0xff, 0xe0, 0x03, 0xff, 0x80, 0x7f, 0x80, 0x78, 0x08, 0x04, 0x03, 0xff, -0xfc, 0x03, 0x00, 0x01, 0xff, 0x80, 0x01, 0xff, 0x00, 0xff, 0x00, 0xf0, 0x1c, 0x00, 0x07, 0xff, -0xfc, 0x07, 0x80, 0x03, 0xfc, 0x00, 0x01, 0xfe, 0x01, 0xfc, 0x01, 0xe0, 0x1e, 0x00, 0x0f, 0xff, -0xf8, 0x0f, 0xc0, 0x07, 0xf0, 0x0e, 0x00, 0xfc, 0x00, 0x00, 0x07, 0xc0, 0x3f, 0x00, 0x1f, 0xff, -0xf0, 0x1f, 0xe0, 0x0f, 0x80, 0x3f, 0x00, 0x7c, 0x00, 0x00, 0x3f, 0xc0, 0x7f, 0x80, 0x3f, 0xff, -0xe0, 0x3f, 0xf0, 0x0e, 0x01, 0xff, 0x80, 0x38, 0x00, 0x07, 0xff, 0x80, 0xff, 0x80, 0x7f, 0xff, -0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, -0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +{ +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xf0, 0x1f, 0xf8, 0x0f, 0x00, 0xff, 0x80, 0x7c, 0x00, 0x0f, 0xff, 0x80, 0x7f, 0xe0, 0x7f, +0xff, 0xe0, 0x0f, 0xf0, 0x1f, 0x80, 0x7e, 0x01, 0xf8, 0x00, 0x00, 0x7f, 0x00, 0x3f, 0xc0, 0x7f, +0xff, 0xc0, 0x07, 0xe0, 0x3f, 0x80, 0x38, 0x07, 0xf0, 0x00, 0x00, 0x3e, 0x00, 0x3f, 0x80, 0xff, +0xff, 0x80, 0x03, 0xc0, 0x3f, 0xc0, 0x00, 0x3f, 0xe0, 0x1f, 0x80, 0x3e, 0x00, 0x1f, 0x01, 0xff, +0xff, 0x00, 0x03, 0x80, 0x7f, 0xe0, 0x00, 0xff, 0xc0, 0x3f, 0x80, 0x3c, 0x00, 0x0e, 0x03, 0xff, +0xfe, 0x00, 0x01, 0x00, 0xff, 0xe0, 0x03, 0xff, 0x80, 0x7f, 0x80, 0x78, 0x08, 0x04, 0x03, 0xff, +0xfc, 0x03, 0x00, 0x01, 0xff, 0x80, 0x01, 0xff, 0x00, 0xff, 0x00, 0xf0, 0x1c, 0x00, 0x07, 0xff, +0xfc, 0x07, 0x80, 0x03, 0xfc, 0x00, 0x01, 0xfe, 0x01, 0xfc, 0x01, 0xe0, 0x1e, 0x00, 0x0f, 0xff, +0xf8, 0x0f, 0xc0, 0x07, 0xf0, 0x0e, 0x00, 0xfc, 0x00, 0x00, 0x07, 0xc0, 0x3f, 0x00, 0x1f, 0xff, +0xf0, 0x1f, 0xe0, 0x0f, 0x80, 0x3f, 0x00, 0x7c, 0x00, 0x00, 0x3f, 0xc0, 0x7f, 0x80, 0x3f, 0xff, +0xe0, 0x3f, 0xf0, 0x0e, 0x01, 0xff, 0x80, 0x38, 0x00, 0x07, 0xff, 0x80, 0xff, 0x80, 0x7f, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +// Logo POCASG/DAPNET, 128x16px +const unsigned char logo_POCSAG_bmp [] = +{ +0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +0xff, 0xff, 0xff, 0xf8, 0x7f, 0xfe, 0x03, 0xfe, 0xfe, 0x03, 0xdf, 0xf6, 0x00, 0x00, 0x1f, 0xff, +0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0xfc, 0xfc, 0xfe, 0xfc, 0xcf, 0xf6, 0xff, 0xfb, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0xfe, 0x7d, 0x7e, 0xfe, 0xc7, 0xf6, 0xff, 0xfb, 0xff, 0xff, +0xff, 0xff, 0xff, 0xfb, 0x7a, 0x7e, 0xff, 0x79, 0x7e, 0xfe, 0xd3, 0xf6, 0xff, 0xfb, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xf7, 0xbe, 0xff, 0x7b, 0xbe, 0xfe, 0xdb, 0xf6, 0xff, 0xfb, 0xff, 0xff, +0xff, 0xff, 0xff, 0xff, 0xff, 0xbe, 0xff, 0xbb, 0xbe, 0xfe, 0xdd, 0xf6, 0xff, 0xfb, 0xff, 0xff, +0xff, 0xff, 0xff, 0xf9, 0xff, 0xbe, 0xff, 0xb7, 0xde, 0xfe, 0xde, 0xf6, 0x01, 0xfb, 0xff, 0xff, +0xff, 0xff, 0xff, 0xee, 0x77, 0xbe, 0xff, 0xb7, 0xde, 0x81, 0xde, 0x76, 0xff, 0xfb, 0xff, 0xff, +0xff, 0xff, 0xff, 0xdf, 0xb7, 0x7e, 0xff, 0xa0, 0x1e, 0xff, 0xdf, 0x36, 0xff, 0xfb, 0xff, 0xff, +0xff, 0xff, 0xff, 0xdf, 0xbc, 0xfe, 0xff, 0x6f, 0xee, 0xff, 0xdf, 0xb6, 0xff, 0xfb, 0xff, 0xff, +0xff, 0xff, 0xff, 0xdf, 0xbf, 0xfe, 0xff, 0x6f, 0xee, 0xff, 0xdf, 0xd6, 0xff, 0xfb, 0xff, 0xff, +0xff, 0xff, 0xff, 0xdf, 0xbf, 0xfe, 0xfe, 0xdf, 0xf6, 0xff, 0xdf, 0xe6, 0xff, 0xfb, 0xff, 0xff, +0xff, 0xff, 0xff, 0xdf, 0x7f, 0xfe, 0xf9, 0xdf, 0xf6, 0xff, 0xdf, 0xe6, 0xff, 0xfb, 0xff, 0xff, +0xff, 0xff, 0xff, 0xe6, 0x7f, 0xfe, 0x07, 0xff, 0xf6, 0xff, 0xdf, 0xf6, 0x00, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -176,10 +197,10 @@ bool COLED::open() { // I2C change parameters to fit to your LCD if ( !display.init(OLED_I2C_RESET, m_displayType) ) - return false; + return false; } - + display.begin(); display.invertDisplay(m_displayInvert ? 1 : 0); @@ -200,7 +221,7 @@ bool COLED::open() void COLED::setIdleInt() { - m_mode = MODE_IDLE; + m_mode = MODE_IDLE; display.clearDisplay(); OLED_statusbar(); @@ -212,26 +233,26 @@ void COLED::setIdleInt() // display.setTextSize(1); display.startscrolldiagright(0x00,0x0f); //the MMDVM logo scrolls the whole screen display.display(); - - unsigned char info[100U]; - CNetworkInfo* m_network; - - passCounter ++; - if (passCounter > 253U) - networkInfoInitialized = false; - - if (! networkInfoInitialized) { - //LogMessage("Initialize CNetworkInfo"); - info[0]=0; - m_network = new CNetworkInfo; - m_network->getNetworkInterface(info); - m_ipaddress = (char*)info; - delete m_network; - - networkInfoInitialized = true; - passCounter = 0; - } - + + unsigned char info[100U]; + CNetworkInfo* m_network; + + passCounter ++; + if (passCounter > 253U) + networkInfoInitialized = false; + + if (! networkInfoInitialized) { + //LogMessage("Initialize CNetworkInfo"); + info[0]=0; + m_network = new CNetworkInfo; + m_network->getNetworkInterface(info); + m_ipaddress = (char*)info; + delete m_network; + + networkInfoInitialized = true; + passCounter = 0; + } + } void COLED::setErrorInt(const char* text) @@ -262,6 +283,21 @@ void COLED::setLockoutInt() display.display(); } +void COLED::setQuitInt() +{ + m_mode = MODE_QUIT; + + display.clearDisplay(); + OLED_statusbar(); + + display.setCursor(0,30); + display.setTextSize(3); + display.print("Stopped"); + + display.setTextSize(1); + display.display(); +} + void COLED::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) { m_mode = MODE_DSTAR; @@ -277,9 +313,9 @@ void COLED::writeDStarInt(const char* my1, const char* my2, const char* your, co display.setCursor(0,OLED_LINE5); display.printf("via %.8s",reflector); - - display.setCursor(0,OLED_LINE6); - display.printf("%s",m_ipaddress.c_str()); + + display.setCursor(0,OLED_LINE6); + display.printf("%s",m_ipaddress.c_str()); OLED_statusbar(); display.display(); @@ -291,9 +327,9 @@ void COLED::clearDStarInt() display.setCursor(40,OLED_LINE3); display.print("Listening"); - - display.setCursor(0,OLED_LINE5); - display.printf("%s",m_ipaddress.c_str()); + + display.setCursor(0,OLED_LINE5); + display.printf("%s",m_ipaddress.c_str()); display.display(); } @@ -302,77 +338,74 @@ void COLED::writeDMRInt(unsigned int slotNo,const std::string& src,bool group,co { if (m_mode != MODE_DMR) { - display.clearDisplay(); - m_mode = MODE_DMR; - clearDMRInt(slotNo); - } - // if both slots, use lines 2-3 for slot 1, lines 4-5 for slot 2 - // if single slot, use lines 3-4 - if ( m_slot1Enabled && m_slot2Enabled ) { + display.clearDisplay(); + m_mode = MODE_DMR; + clearDMRInt(slotNo); + } + // if both slots, use lines 2-3 for slot 1, lines 4-5 for slot 2 + // if single slot, use lines 3-4 + if ( m_slot1Enabled && m_slot2Enabled ) { - if (slotNo == 1U) { - display.fillRect(0,OLED_LINE2,display.width(),40,BLACK); - display.setCursor(0,OLED_LINE2); - display.printf("%s",src.c_str()); - display.setCursor(0,OLED_LINE3); - display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str()); - } - else - { - display.fillRect(0,OLED_LINE4,display.width(),40,BLACK); - display.setCursor(0,OLED_LINE4); - display.printf("%s",src.c_str()); - display.setCursor(0,OLED_LINE5); - display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str()); - } + if (slotNo == 1U) { + display.fillRect(0,OLED_LINE2,display.width(),40,BLACK); + display.setCursor(0,OLED_LINE2); + display.printf("%s",src.c_str()); + display.setCursor(0,OLED_LINE3); + display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str()); + } + else + { + display.fillRect(0,OLED_LINE4,display.width(),40,BLACK); + display.setCursor(0,OLED_LINE4); + display.printf("%s",src.c_str()); + display.setCursor(0,OLED_LINE5); + display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str()); + } - } - else - { - display.fillRect(0,OLED_LINE3,display.width(),20,BLACK); - display.setCursor(0,OLED_LINE3); - display.printf("%s",src.c_str()); - display.setCursor(0,OLED_LINE4); - display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str()); - } + } + else + { + display.fillRect(0,OLED_LINE3,display.width(),20,BLACK); + display.setCursor(0,OLED_LINE3); + display.printf("%s",src.c_str()); + display.setCursor(0,OLED_LINE4); + display.printf("Slot: %i %s %s%s",slotNo,type,group ? "TG: " : "",dst.c_str()); + } + + display.fillRect(0,OLED_LINE6,display.width(),20,BLACK); + display.setCursor(0,OLED_LINE6); + display.printf("%s",m_ipaddress.c_str()); - display.fillRect(0,OLED_LINE6,display.width(),20,BLACK); - display.setCursor(0,OLED_LINE6); - display.printf("%s",m_ipaddress.c_str()); - OLED_statusbar(); display.display(); - } + } void COLED::clearDMRInt(unsigned int slotNo) { - // if both slots, use lines 2-3 for slot 1, lines 4-5 for slot 2 - // if single slot, use lines 3-4 - if ( m_slot1Enabled && m_slot2Enabled ){ - if (slotNo == 1U) - { - display.fillRect(0, OLED_LINE3, display.width(), 40, BLACK); - display.setCursor(0,OLED_LINE3); - display.print("Slot: 1 Listening"); - } - else - { - display.fillRect(0, OLED_LINE5, display.width(), 40, BLACK); - display.setCursor(0, OLED_LINE5); - display.print("Slot: 2 Listening"); - } - } - else { - display.fillRect(0, OLED_LINE4, display.width(), 40, BLACK); - display.setCursor(0,OLED_LINE4); - display.printf("Slot: %i Listening",slotNo); - } + // if both slots, use lines 2-3 for slot 1, lines 4-5 for slot 2 + // if single slot, use lines 3-4 + if ( m_slot1Enabled && m_slot2Enabled ){ + if (slotNo == 1U) { + display.fillRect(0, OLED_LINE3, display.width(), 40, BLACK); + display.setCursor(0,OLED_LINE3); + display.print("Slot: 1 Listening"); + } + else { + display.fillRect(0, OLED_LINE5, display.width(), 40, BLACK); + display.setCursor(0, OLED_LINE5); + display.print("Slot: 2 Listening"); + } + } + else { + display.fillRect(0, OLED_LINE4, display.width(), 40, BLACK); + display.setCursor(0,OLED_LINE4); + display.printf("Slot: %i Listening",slotNo); + } - - display.fillRect(0, OLED_LINE6, display.width(), 20, BLACK); - display.setCursor(0,OLED_LINE6); - display.printf("%s",m_ipaddress.c_str()); + display.fillRect(0, OLED_LINE6, display.width(), 20, BLACK); + display.setCursor(0,OLED_LINE6); + display.printf("%s",m_ipaddress.c_str()); display.display(); } @@ -400,9 +433,9 @@ void COLED::clearFusionInt() display.setCursor(40,OLED_LINE4); display.print("Listening"); - - display.setCursor(0,OLED_LINE6); - display.printf("%s",m_ipaddress.c_str()); + + display.setCursor(0,OLED_LINE6); + display.printf("%s",m_ipaddress.c_str()); display.display(); } @@ -412,7 +445,7 @@ void COLED::writeP25Int(const char* source, bool group, unsigned int dest, const m_mode = MODE_P25; display.clearDisplay(); - display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK); + display.fillRect(0, OLED_LINE2, display.width(), display.height(), BLACK); display.setCursor(0,OLED_LINE3); display.printf("%s %.10s", type, source); @@ -426,13 +459,13 @@ void COLED::writeP25Int(const char* source, bool group, unsigned int dest, const void COLED::clearP25Int() { - display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK); + display.fillRect(0, OLED_LINE2, display.width(), display.height(), BLACK); display.setCursor(40,OLED_LINE4); display.print("Listening"); - - display.setCursor(0,OLED_LINE6); - display.printf("%s",m_ipaddress.c_str()); + + display.setCursor(0,OLED_LINE6); + display.printf("%s",m_ipaddress.c_str()); display.display(); } @@ -442,7 +475,7 @@ void COLED::writeNXDNInt(const char* source, bool group, unsigned int dest, cons m_mode = MODE_NXDN; display.clearDisplay(); - display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK); + display.fillRect(0, OLED_LINE2, display.width(), display.height(), BLACK); display.setCursor(0,OLED_LINE3); display.printf("%s %.10s", type, source); @@ -455,14 +488,44 @@ void COLED::writeNXDNInt(const char* source, bool group, unsigned int dest, cons } void COLED::clearNXDNInt() +{ + display.fillRect(0, OLED_LINE2, display.width(), display.height(), BLACK); + + display.setCursor(40,OLED_LINE4); + display.print("Listening"); + + display.setCursor(0,OLED_LINE6); + display.printf("%s",m_ipaddress.c_str()); + + display.display(); +} + +void COLED::writePOCSAGInt(uint32_t ric, const std::string& message) +{ + m_mode = MODE_POCSAG; + + display.clearDisplay(); + display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK); + + display.setCursor(0,OLED_LINE3); + display.printf("RIC: %u", ric); + + display.setCursor(0,OLED_LINE5); + display.printf("MSG: %s", message.c_str()); + + OLED_statusbar(); + display.display(); +} + +void COLED::clearPOCSAGInt() { display.fillRect(0, OLED_LINE1, display.width(), display.height(), BLACK); display.setCursor(40,OLED_LINE4); display.print("Listening"); - - display.setCursor(0,OLED_LINE6); - display.printf("%s",m_ipaddress.c_str()); + + display.setCursor(0,OLED_LINE6); + display.printf("%s",m_ipaddress.c_str()); display.display(); } @@ -496,13 +559,13 @@ void COLED::clearCWInt() void COLED::close() { display.clearDisplay(); - display.fillRect(0, 0, display.width(), 16, BLACK); - display.startscrollright(0x00,0x01); + display.fillRect(0, 0, display.width(), 16, BLACK); + display.startscrollright(0x00,0x01); display.setCursor(0,00); display.setTextSize(2); display.print("-CLOSE-"); display.display(); - + display.close(); } @@ -514,18 +577,20 @@ void COLED::OLED_statusbar() display.setCursor(0,0); if (m_mode == MODE_DMR) - display.drawBitmap(0, 0, logo_dmr_bmp, 128, 16, WHITE); + display.drawBitmap(0, 0, logo_dmr_bmp, 128, 16, WHITE); else if (m_mode == MODE_DSTAR) - display.drawBitmap(0, 0, logo_dstar_bmp, 128, 16, WHITE); + display.drawBitmap(0, 0, logo_dstar_bmp, 128, 16, WHITE); else if (m_mode == MODE_YSF) - display.drawBitmap(0, 0, logo_fusion_bmp, 128, 16, WHITE); + display.drawBitmap(0, 0, logo_fusion_bmp, 128, 16, WHITE); else if (m_mode == MODE_P25) - display.drawBitmap(0, 0, logo_P25_bmp, 128, 16, WHITE); + display.drawBitmap(0, 0, logo_P25_bmp, 128, 16, WHITE); else if (m_mode == MODE_NXDN) - display.drawBitmap(0, 0, logo_NXDN_bmp, 128, 16, WHITE); + display.drawBitmap(0, 0, logo_NXDN_bmp, 128, 16, WHITE); + else if (m_mode == MODE_POCSAG) + display.drawBitmap(0, 0, logo_POCSAG_bmp, 128, 16, WHITE); else - display.drawBitmap(0, 0, logo_glcd_bmp, 128, 16, WHITE); + display.drawBitmap(0, 0, logo_glcd_bmp, 128, 16, WHITE); if (m_displayScroll) - display.startscrollright(0x00,0x02); + display.startscrollright(0x00,0x02); } diff --git a/OLED.h b/OLED.h index 0379acd..cccea51 100644 --- a/OLED.h +++ b/OLED.h @@ -49,6 +49,8 @@ public: virtual void setErrorInt(const char* text); virtual void setLockoutInt(); + virtual void setQuitInt(); + virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); virtual void clearDStarInt(); @@ -65,6 +67,9 @@ public: virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); virtual void clearNXDNInt(); + virtual void writePOCSAGInt(uint32_t ric, const std::string& message); + virtual void clearPOCSAGInt(); + virtual void writeCWInt(); virtual void clearCWInt(); diff --git a/P25Control.cpp b/P25Control.cpp index 1ed8bcd..a424ee4 100644 --- a/P25Control.cpp +++ b/P25Control.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX +* Copyright (C) 2018 by Bryan Biedenkapp * * 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 @@ -176,6 +177,9 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) case P25_DUID_PDU: duid = P25_DUID_PDU; break; + case P25_DUID_TSDU: + duid = P25_DUID_TSDU; + break; default: break; } @@ -346,6 +350,85 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) return true; } + } else if (duid == P25_DUID_TSDU) { + if (m_rfState != RS_RF_DATA) { + m_rfPDUCount = 0U; + m_rfPDUBits = 0U; + m_rfState = RS_RF_DATA; + m_rfDataFrames = 0U; + } + + bool ret = m_rfData.decodeTSDU(data + 2U); + if (!ret) { + m_lastDUID = duid; + return false; + } + + unsigned int srcId = m_rfData.getSrcId(); + unsigned int dstId = m_rfData.getDstId(); + + unsigned char data[P25_TSDU_FRAME_LENGTH_BYTES + 2U]; + + switch (m_rfData.getLCF()) { + case P25_LCF_TSBK_CALL_ALERT: + LogMessage("P25, received RF TSDU transmission, CALL ALERT from %u to %u", srcId, dstId); + ::memset(data + 2U, 0x00U, P25_TSDU_FRAME_LENGTH_BYTES); + + // Regenerate Sync + CSync::addP25Sync(data + 2U); + + // Regenerate NID + m_nid.encode(data + 2U, P25_DUID_TSDU); + + // Regenerate TDULC Data + m_rfData.encodeTSDU(data + 2U); + + // Add busy bits + addBusyBits(data + 2U, P25_TSDU_FRAME_LENGTH_BITS, true, false); + + // Set first busy bits to 1,1 + setBusyBits(data + 2U, P25_SS0_START, true, true); + + if (m_duplex) { + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeQueueRF(data, P25_TSDU_FRAME_LENGTH_BYTES + 2U); + } + break; + case P25_LCF_TSBK_ACK_RSP_FNE: + LogMessage("P25, received RF TSDU transmission, ACK RESPONSE FNE from %u to %u", srcId, dstId); + ::memset(data + 2U, 0x00U, P25_TSDU_FRAME_LENGTH_BYTES); + + // Regenerate Sync + CSync::addP25Sync(data + 2U); + + // Regenerate NID + m_nid.encode(data + 2U, P25_DUID_TSDU); + + // Regenerate TDULC Data + m_rfData.encodeTSDU(data + 2U); + + // Add busy bits + addBusyBits(data + 2U, P25_TSDU_FRAME_LENGTH_BITS, true, false); + + // Set first busy bits to 1,1 + setBusyBits(data + 2U, P25_SS0_START, true, true); + + if (m_duplex) { + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeQueueRF(data, P25_TSDU_FRAME_LENGTH_BYTES + 2U); + } + break; + default: + LogMessage("P25, recieved RF TSDU transmission, unhandled LCF $%02X", m_rfData.getLCF()); + break; + } + + m_rfState = RS_RF_LISTENING; + return true; } else if (duid == P25_DUID_TERM || duid == P25_DUID_TERM_LC) { if (m_rfState == RS_RF_AUDIO) { writeNetwork(m_rfLDU, m_lastDUID, true); @@ -512,6 +595,10 @@ unsigned int CP25Control::readModem(unsigned char* data) void CP25Control::writeNetwork() { unsigned char data[100U]; + + if (m_network == NULL) + return; + unsigned int length = m_network->read(data, 100U); if (length == 0U) return; @@ -693,6 +780,14 @@ void CP25Control::writeNetwork(const unsigned char *data, unsigned char type, bo } } +void CP25Control::setBusyBits(unsigned char* data, unsigned int ssOffset, bool b1, bool b2) +{ + assert(data != NULL); + + WRITE_BIT(data, ssOffset, b1); + WRITE_BIT(data, ssOffset + 1U, b2); +} + void CP25Control::addBusyBits(unsigned char* data, unsigned int length, bool b1, bool b2) { assert(data != NULL); diff --git a/P25Control.h b/P25Control.h index 79e8d8a..fa069d3 100644 --- a/P25Control.h +++ b/P25Control.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX +* Copyright (C) 2018 by Bryan Biedenkapp * * 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 @@ -94,6 +95,7 @@ private: void writeNetwork(const unsigned char *data, unsigned char type, bool end); void writeNetwork(); + void setBusyBits(unsigned char* data, unsigned int ssOffset, bool b1, bool b2); void addBusyBits(unsigned char* data, unsigned int length, bool b1, bool b2); void checkNetLDU1(); diff --git a/P25Data.cpp b/P25Data.cpp index 3c9eb66..618fdc2 100644 --- a/P25Data.cpp +++ b/P25Data.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2016,2017 by Jonathan Naylor G4KLX +* Copyright (C) 2018 by Bryan Biedenkapp * * 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 @@ -19,6 +20,7 @@ #include "P25Data.h" #include "P25Defines.h" #include "P25Utils.h" +#include "CRC.h" #include "Hamming.h" #include "Utils.h" #include "Log.h" @@ -54,7 +56,8 @@ m_lcf(0x00U), m_emergency(false), m_srcId(0U), m_dstId(0U), -m_rs241213() +m_rs241213(), +m_trellis() { m_mi = new unsigned char[P25_MI_LENGTH_BYTES]; } @@ -206,6 +209,116 @@ void CP25Data::encodeLDU2(unsigned char* data) CP25Utils::encode(raw, data, 1356U, 1398U); } +bool CP25Data::decodeTSDU(const unsigned char* data) +{ + assert(data != NULL); + + // deinterleave + unsigned char tsbk[12U]; + unsigned char raw[25U]; + CP25Utils::decode(data, raw, 114U, 318U); + + // decode 1/2 rate Trellis & check CRC-CCITT 16 + try { + bool ret = m_trellis.decode12(raw, tsbk); + if (ret) + ret = CCRC::checkCCITT162(tsbk, 12U); + if (!ret) + return false; + } + catch (...) { + CUtils::dump(2U, "P25, CRC failed with input data", tsbk, 12U); + return false; + } + + m_lcf = tsbk[0U] & 0x3F; + m_mfId = tsbk[1U]; + + unsigned long long tsbkValue = 0U; + + // combine bytes into rs value + tsbkValue = tsbk[2U]; + tsbkValue = (tsbkValue << 8) + tsbk[3U]; + tsbkValue = (tsbkValue << 8) + tsbk[4U]; + tsbkValue = (tsbkValue << 8) + tsbk[5U]; + tsbkValue = (tsbkValue << 8) + tsbk[6U]; + tsbkValue = (tsbkValue << 8) + tsbk[7U]; + tsbkValue = (tsbkValue << 8) + tsbk[8U]; + tsbkValue = (tsbkValue << 8) + tsbk[9U]; + + switch (m_lcf) { + case P25_LCF_TSBK_CALL_ALERT: + m_dstId = (unsigned int)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address + m_srcId = (unsigned int)(tsbkValue & 0xFFFFFFU); // Source Radio Address + break; + case P25_LCF_TSBK_ACK_RSP_FNE: + m_serviceType = (unsigned char)((tsbkValue >> 56) & 0xFFU); // Service Type + m_dstId = (unsigned int)((tsbkValue >> 24) & 0xFFFFFFU); // Target Radio Address + m_srcId = (unsigned int)(tsbkValue & 0xFFFFFFU); // Source Radio Address + break; + default: + LogMessage("P25, unknown LCF value in TSDU - $%02X", m_lcf); + break; + } + + return true; +} + +void CP25Data::encodeTSDU(unsigned char* data) +{ + assert(data != NULL); + + unsigned char tsbk[12U]; + ::memset(tsbk, 0x00U, 12U); + + unsigned long long tsbkValue = 0U; + tsbk[0U] = m_lcf; + tsbk[0U] |= 0x80; + + tsbk[1U] = m_mfId; + + switch (m_lcf) { + case P25_LCF_TSBK_CALL_ALERT: + tsbkValue = 0U; + tsbkValue = (tsbkValue << 16) + 0U; + tsbkValue = (tsbkValue << 24) + m_dstId; // Target Radio Address + tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address + break; + case P25_LCF_TSBK_ACK_RSP_FNE: + tsbkValue = 0U; // Additional Info. Flag + tsbkValue = (tsbkValue << 1) + 0U; // Extended Address Flag + tsbkValue = (tsbkValue << 16) + (m_serviceType & 0xFF); // Service Type + tsbkValue = (tsbkValue << 32) + m_dstId; // Target Radio Address + tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address + break; + default: + LogMessage("P25, unknown LCF value in TSDU - $%02X", m_lcf); + break; + } + + // split rs value into bytes + tsbk[2U] = (unsigned char)((tsbkValue >> 56) & 0xFFU); + tsbk[3U] = (unsigned char)((tsbkValue >> 48) & 0xFFU); + tsbk[4U] = (unsigned char)((tsbkValue >> 40) & 0xFFU); + tsbk[5U] = (unsigned char)((tsbkValue >> 32) & 0xFFU); + tsbk[6U] = (unsigned char)((tsbkValue >> 24) & 0xFFU); + tsbk[7U] = (unsigned char)((tsbkValue >> 16) & 0xFFU); + tsbk[8U] = (unsigned char)((tsbkValue >> 8) & 0xFFU); + tsbk[9U] = (unsigned char)((tsbkValue >> 0) & 0xFFU); + + // compute CRC-CCITT 16 + CCRC::addCCITT162(tsbk, 12U); + + unsigned char raw[25U]; + ::memset(raw, 0x00U, 25U); + + // encode 1/2 rate Trellis + m_trellis.encode12(tsbk, raw); + + // interleave + CP25Utils::encode(raw, data, 114U, 318U); +} + void CP25Data::setMI(const unsigned char* mi) { assert(mi != NULL); @@ -290,6 +403,16 @@ unsigned int CP25Data::getDstId() const return m_dstId; } +void CP25Data::setServiceType(unsigned char type) +{ + m_serviceType = type; +} + +unsigned char CP25Data::getServiceType() const +{ + return m_serviceType; +} + void CP25Data::reset() { ::memset(m_mi, 0x00U, P25_MI_LENGTH_BYTES); diff --git a/P25Data.h b/P25Data.h index 6618da2..1b9eec9 100644 --- a/P25Data.h +++ b/P25Data.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2016,2017 by Jonathan Naylor G4KLX +* Copyright (C) 2018 by Bryan Biedenkapp * * 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 @@ -20,6 +21,7 @@ #define P25Data_H #include "RS241213.h" +#include "P25Trellis.h" class CP25Data { public: @@ -33,6 +35,9 @@ public: void encodeLDU2(unsigned char* data); + bool decodeTSDU(const unsigned char* data); + void encodeTSDU(unsigned char* data); + void setMI(const unsigned char* mi); void getMI(unsigned char* mi) const; @@ -57,6 +62,9 @@ public: void setDstId(unsigned int id); unsigned int getDstId() const; + void setServiceType(unsigned char type); + unsigned char getServiceType() const; + void reset(); private: @@ -68,7 +76,9 @@ private: bool m_emergency; unsigned int m_srcId; unsigned int m_dstId; + unsigned char m_serviceType; CRS241213 m_rs241213; + CP25Trellis m_trellis; void decodeLDUHamming(const unsigned char* raw, unsigned char* data); void encodeLDUHamming(unsigned char* data, const unsigned char* raw); diff --git a/P25Defines.h b/P25Defines.h index f04260f..6ceda75 100644 --- a/P25Defines.h +++ b/P25Defines.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2016 by Jonathan Naylor G4KLX + * Copyright (C) 2018 by Bryan Biedenkapp * * 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 @@ -31,6 +32,9 @@ const unsigned int P25_TERM_FRAME_LENGTH_BITS = P25_TERM_FRAME_LENGTH_BYTES * 8 const unsigned int P25_TERMLC_FRAME_LENGTH_BYTES = 54U; const unsigned int P25_TERMLC_FRAME_LENGTH_BITS = P25_TERMLC_FRAME_LENGTH_BYTES * 8U; +const unsigned int P25_TSDU_FRAME_LENGTH_BYTES = 45U; +const unsigned int P25_TSDU_FRAME_LENGTH_BITS = P25_TSDU_FRAME_LENGTH_BYTES * 8U; + const unsigned int P25_SYNC_LENGTH_BYTES = 6U; const unsigned int P25_SYNC_LENGTH_BITS = P25_SYNC_LENGTH_BYTES * 8U; @@ -54,6 +58,9 @@ const unsigned int P25_MI_LENGTH_BYTES = 9U; const unsigned char P25_LCF_GROUP = 0x00U; const unsigned char P25_LCF_PRIVATE = 0x03U; +const unsigned char P25_LCF_TSBK_CALL_ALERT = 0x1FU; +const unsigned char P25_LCF_TSBK_ACK_RSP_FNE = 0x20U; + const unsigned int P25_SS0_START = 70U; const unsigned int P25_SS1_START = 71U; const unsigned int P25_SS_INCREMENT = 72U; @@ -61,6 +68,7 @@ const unsigned int P25_SS_INCREMENT = 72U; const unsigned char P25_DUID_HEADER = 0x00U; const unsigned char P25_DUID_TERM = 0x03U; const unsigned char P25_DUID_LDU1 = 0x05U; +const unsigned char P25_DUID_TSDU = 0x07U; const unsigned char P25_DUID_LDU2 = 0x0AU; const unsigned char P25_DUID_PDU = 0x0CU; const unsigned char P25_DUID_TERM_LC = 0x0FU; diff --git a/P25NID.cpp b/P25NID.cpp index 145dce4..5e057c5 100644 --- a/P25NID.cpp +++ b/P25NID.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* Copyright (C) 2018 by Bryan Biedenkapp * * 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 +34,7 @@ m_ldu1(NULL), m_ldu2(NULL), m_termlc(NULL), m_term(NULL), +m_tsdu(NULL), m_pdu(NULL) { CBCH bch; @@ -72,6 +74,13 @@ m_pdu(NULL) bch.encode(m_term); m_term[7U] &= 0xFEU; // Clear the parity bit + m_tsdu = new unsigned char[P25_NID_LENGTH_BYTES]; + m_tsdu[0U] = (nac >> 4) & 0xFFU; + m_tsdu[1U] = (nac << 4) & 0xF0U; + m_tsdu[1U] |= P25_DUID_TSDU; + bch.encode(m_tsdu); + m_tsdu[7U] &= 0xFEU; // Clear the parity bit + m_pdu = new unsigned char[P25_NID_LENGTH_BYTES]; m_pdu[0U] = (nac >> 4) & 0xFFU; m_pdu[1U] = (nac << 4) & 0xF0U; @@ -87,6 +96,7 @@ CP25NID::~CP25NID() delete[] m_ldu2; delete[] m_termlc; delete[] m_term; + delete[] m_tsdu; delete[] m_pdu; } @@ -127,6 +137,12 @@ bool CP25NID::decode(const unsigned char* data) return true; } + errs = CP25Utils::compare(nid, m_tsdu, P25_NID_LENGTH_BYTES); + if (errs < MAX_NID_ERRS) { + m_duid = P25_DUID_TSDU; + return true; + } + errs = CP25Utils::compare(nid, m_pdu, P25_NID_LENGTH_BYTES); if (errs < MAX_NID_ERRS) { m_duid = P25_DUID_PDU; @@ -156,6 +172,9 @@ void CP25NID::encode(unsigned char* data, unsigned char duid) const case P25_DUID_TERM_LC: CP25Utils::encode(m_termlc, data, 48U, 114U); break; + case P25_DUID_TSDU: + CP25Utils::encode(m_tsdu, data, 48U, 114U); + break; case P25_DUID_PDU: CP25Utils::encode(m_pdu, data, 48U, 114U); break; diff --git a/P25NID.h b/P25NID.h index bb0031f..fe7ee74 100644 --- a/P25NID.h +++ b/P25NID.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* Copyright (C) 2018 by Bryan Biedenkapp * * 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 @@ -37,6 +38,7 @@ private: unsigned char* m_ldu2; unsigned char* m_termlc; unsigned char* m_term; + unsigned char* m_tsdu; unsigned char* m_pdu; }; diff --git a/POCSAGControl.cpp b/POCSAGControl.cpp new file mode 100644 index 0000000..498a0a7 --- /dev/null +++ b/POCSAGControl.cpp @@ -0,0 +1,416 @@ +/* +* Copyright (C) 2018 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 "POCSAGControl.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include +#include + +// #define DUMP_POCSAG + +const struct BCD { + char m_c; + uint32_t m_bcd[5U]; +} BCD_VALUES[] = { + { '0', {0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}}, + { '1', {0x40000000U, 0x04000000U, 0x00400000U, 0x00040000U, 0x00004000U}}, + { '2', {0x20000000U, 0x02000000U, 0x00200000U, 0x00020000U, 0x00002000U}}, + { '3', {0x60000000U, 0x06000000U, 0x00600000U, 0x00060000U, 0x00006000U}}, + { '4', {0x10000000U, 0x01000000U, 0x00100000U, 0x00010000U, 0x00001000U}}, + { '5', {0x50000000U, 0x05000000U, 0x00500000U, 0x00050000U, 0x00005000U}}, + { '6', {0x30000000U, 0x03000000U, 0x00300000U, 0x00030000U, 0x00003000U}}, + { '7', {0x70000000U, 0x07000000U, 0x00700000U, 0x00070000U, 0x00007000U}}, + { '8', {0x08000000U, 0x00800000U, 0x00080000U, 0x00008000U, 0x00000800U}}, + { '9', {0x48000000U, 0x04800000U, 0x00480000U, 0x00048000U, 0x00004800U}}, + { 'U', {0x68000000U, 0x06800000U, 0x00680000U, 0x00068000U, 0x00006800U}}, + { ' ', {0x18000000U, 0x01800000U, 0x00180000U, 0x00018000U, 0x00001800U}}, + { '-', {0x58000000U, 0x05800000U, 0x00580000U, 0x00058000U, 0x00005800U}}, + { ')', {0x38000000U, 0x03800000U, 0x00380000U, 0x00038000U, 0x00003800U}}, + { '(', {0x78000000U, 0x07800000U, 0x00780000U, 0x00078000U, 0x00007800U}}, + { 0, {0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}} +}; + +const uint32_t BCD_SPACES[] = {0x19999800U, 0x01999800U, 0x00199800U, 0x00019800U, 0x00001800U}; + +const uint32_t DATA_MASK[] = { 0x40000000U, 0x20000000U, 0x10000000U, + 0x08000000U, 0x04000000U, 0x02000000U, 0x01000000U, + 0x00800000U, 0x00400000U, 0x00200000U, 0x00100000U, + 0x00080000U, 0x00040000U, 0x00020000U, 0x00010000U, + 0x00008000U, 0x00004000U, 0x00002000U, 0x00001000U, + 0x00000800U}; + +const unsigned char FUNCTIONAL_NUMERIC = 0U; +const unsigned char FUNCTIONAL_ALERT1 = 1U; +const unsigned char FUNCTIONAL_ALERT2 = 2U; +const unsigned char FUNCTIONAL_ALPHANUMERIC = 3U; + +CPOCSAGControl::CPOCSAGControl(CPOCSAGNetwork* network, CDisplay* display) : +m_network(network), +m_display(display), +m_queue(5000U, "POCSAG Control"), +m_frames(0U), +m_count(0U), +m_output(), +m_buffer(), +m_ric(0U), +m_text(), +m_state(PS_NONE), +m_fp(NULL) +{ + assert(display != NULL); +} + +CPOCSAGControl::~CPOCSAGControl() +{ + m_output.clear(); + m_buffer.clear(); +} + +unsigned int CPOCSAGControl::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; +} + +bool CPOCSAGControl::processData() +{ + if (m_network == NULL) + return false; + + unsigned char data[300U]; + unsigned int length = m_network->read(data); + if (length == 0U) + return false; + + m_ric = 0U; + m_ric |= (data[0U] << 16) & 0x00FF0000U; + m_ric |= (data[1U] << 8) & 0x0000FF00U; + m_ric |= (data[2U] << 0) & 0x000000FFU; + + unsigned char functional = data[3U]; + + m_buffer.clear(); + addAddress(functional); + + switch (functional) { + case FUNCTIONAL_ALPHANUMERIC: + m_text = std::string((char*)(data + 4U), length - 4U); + LogDebug("Message to %07u, func Alphanumeric: \"%s\"", m_ric, m_text.c_str()); + packASCII(); + break; + case FUNCTIONAL_NUMERIC: + m_text = std::string((char*)(data + 4U), length - 4U); + LogDebug("Message to %07u, func Numeric: \"%s\"", m_ric, m_text.c_str()); + packNumeric(); + break; + case FUNCTIONAL_ALERT1: + m_text.clear(); + LogDebug("Message to %07u, func Alert 1", m_ric); + break; + case FUNCTIONAL_ALERT2: + m_text = std::string((char*)(data + 4U), length - 4U); + LogDebug("Message to %07u, func Alert 2: \"%s\"", m_ric, m_text.c_str()); + packASCII(); + break; + default: + break; + } + + // Ensure data is an even number of words + if ((m_buffer.size() % 2U) == 1U) + m_buffer.push_back(POCSAG_IDLE_WORD); + + return true; +} + +void CPOCSAGControl::clock(unsigned int ms) +{ + if (m_state == PS_NONE) { + bool ret = processData(); + if (!ret) + return; + + m_display->writePOCSAG(m_ric, m_text); + m_state = PS_WAITING; + m_frames = 0U; + m_count = 1U; + +#if defined(DUMP_POCSAG) + openFile(); +#endif + } + + m_output.clear(); + m_output.push_back(POCSAG_SYNC_WORD); + + for (unsigned int i = 0U; i < POCSAG_FRAME_ADDRESSES; i++) { + if (m_state == PS_WAITING) { + if (i == (m_ric % POCSAG_FRAME_ADDRESSES)) { + uint32_t w1 = m_buffer.front(); + m_buffer.pop_front(); + uint32_t w2 = m_buffer.front(); + m_buffer.pop_front(); + + m_output.push_back(w1); + m_output.push_back(w2); + + m_state = PS_SENDING; + } else { + m_output.push_back(POCSAG_IDLE_WORD); + m_output.push_back(POCSAG_IDLE_WORD); + } + } else if (m_state == PS_SENDING) { + if (m_buffer.empty()) { + m_output.push_back(POCSAG_IDLE_WORD); + m_output.push_back(POCSAG_IDLE_WORD); + + bool ret = processData(); + if (ret) { + m_display->writePOCSAG(m_ric, m_text); + m_state = PS_WAITING; + m_count++; + } else { + m_state = PS_ENDING; + } + } else { + uint32_t w1 = m_buffer.front(); + m_buffer.pop_front(); + uint32_t w2 = m_buffer.front(); + m_buffer.pop_front(); + + m_output.push_back(w1); + m_output.push_back(w2); + } + } else { // PS_ENDING + m_output.push_back(POCSAG_IDLE_WORD); + m_output.push_back(POCSAG_IDLE_WORD); + } + } + + writeQueue(); + m_frames++; + + if (m_state == PS_ENDING) { + LogMessage("POCSAG, transmitted %u frame(s) of data from %u message(s)", m_frames, m_count); + m_display->clearPOCSAG(); + m_state = PS_NONE; + +#if defined(DUMP_POCSAG) + closeFile(); +#endif + } +} + +void CPOCSAGControl::addAddress(unsigned char functional) +{ + uint32_t word = 0x00000000U; + + switch (functional) { + case FUNCTIONAL_ALPHANUMERIC: + word = 0x00001800U; + break; + case FUNCTIONAL_ALERT1: + word = 0x00000800U; + break; + case FUNCTIONAL_ALERT2: + word = 0x00001000U; + break; + case FUNCTIONAL_NUMERIC: + default: + break; + } + + word |= (m_ric / POCSAG_FRAME_ADDRESSES) << 13; + + addBCHAndParity(word); + + m_buffer.push_back(word); +} + +void CPOCSAGControl::packASCII() +{ + const unsigned char MASK = 0x01U; + + uint32_t word = 0x80000000U; + unsigned int n = 0U; + + for (std::string::const_iterator it = m_text.cbegin(); it != m_text.cend(); ++it) { + unsigned char c = *it; + for (unsigned int j = 0U; j < 7U; j++, c >>= 1) { + bool b = (c & MASK) == MASK; + if (b) + word |= DATA_MASK[n]; + n++; + + if (n == 20U) { + addBCHAndParity(word); + m_buffer.push_back(word); + word = 0x80000000U; + n = 0U; + } + } + } + + if (n > 0U) { + addBCHAndParity(word); + m_buffer.push_back(word); + } +} + +void CPOCSAGControl::packNumeric() +{ + uint32_t word = 0x80000000U; + unsigned int n = 0U; + + for (std::string::const_iterator it = m_text.cbegin(); it != m_text.cend(); ++it) { + char c = *it; + + const BCD* bcd = NULL; + for (unsigned int i = 0U; BCD_VALUES[i].m_c != 0; i++) { + if (BCD_VALUES[i].m_c == c) { + bcd = BCD_VALUES + i; + break; + } + } + + if (bcd != NULL) { + word |= bcd->m_bcd[n]; + n++; + + if (n == 5U) { + addBCHAndParity(word); + m_buffer.push_back(word); + word = 0x80000000U; + n = 0U; + } + } + } + + // Pack the remainder of the word with BCD spaces. + if (n != 0U) { + word |= BCD_SPACES[n]; + + addBCHAndParity(word); + m_buffer.push_back(word); + } +} + +void CPOCSAGControl::addBCHAndParity(uint32_t& word) const +{ + uint32_t temp = word; + + for (unsigned int i = 0U; i < 21U; i++, temp <<= 1) { + if (temp & 0x80000000U) + temp ^= 0xED200000U; + } + + word |= (temp >> 21); + + temp = word; + + unsigned int parity = 0U; + for (unsigned int i = 0U; i < 32U; i++, temp <<= 1) { + if (temp & 0x80000000U) + parity++; + } + + if ((parity % 2U) == 1U) + word |= 0x00000001U; +} + +void CPOCSAGControl::writeQueue() +{ + // Convert 32-bit words to bytes + unsigned char data[POCSAG_FRAME_LENGTH_BYTES]; + unsigned char len = 0U; + for (std::deque::const_iterator it = m_output.cbegin(); it != m_output.cend(); ++it) { + uint32_t word = *it; + + data[len++] = word >> 24; + data[len++] = word >> 16; + data[len++] = word >> 8; + data[len++] = word >> 0; + } + + m_output.clear(); + +#if defined(DUMP_POCSAG) + writeFile(data); +#endif + + CUtils::dump(1U, "Data to MMDVM", data, len); + + assert(len == POCSAG_FRAME_LENGTH_BYTES); + + unsigned int space = m_queue.freeSpace(); + if (space < (len + 1U)) { + LogError("POCSAG, overflow in the POCSAG RF queue"); + return; + } + + m_queue.addData(&len, 1U); + m_queue.addData(data, len); +} + +bool CPOCSAGControl::openFile() +{ + if (m_fp != NULL) + return true; + + time_t t; + ::time(&t); + + struct tm* tm = ::localtime(&t); + + char name[100U]; + ::sprintf(name, "POCSAG_%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("POCSAG", 1U, 6U, m_fp); + + return true; +} + +bool CPOCSAGControl::writeFile(const unsigned char* data) +{ + if (m_fp == NULL) + return false; + + ::fwrite(data, 1U, POCSAG_FRAME_LENGTH_BYTES, m_fp); + + return true; +} + +void CPOCSAGControl::closeFile() +{ + if (m_fp != NULL) { + ::fclose(m_fp); + m_fp = NULL; + } +} diff --git a/POCSAGControl.h b/POCSAGControl.h new file mode 100644 index 0000000..828e4cb --- /dev/null +++ b/POCSAGControl.h @@ -0,0 +1,74 @@ +/* +* Copyright (C) 2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if !defined(POCSAGControl_H) +#define POCSAGControl_H + +#include "POCSAGNetwork.h" +#include "POCSAGDefines.h" +#include "RingBuffer.h" +#include "Display.h" +#include "Defines.h" + +#include + +#include +#include + +class CPOCSAGControl { +public: + CPOCSAGControl(CPOCSAGNetwork* network, CDisplay* display); + ~CPOCSAGControl(); + + unsigned int readModem(unsigned char* data); + + void clock(unsigned int ms); + +private: + CPOCSAGNetwork* m_network; + CDisplay* m_display; + CRingBuffer m_queue; + unsigned int m_frames; + unsigned int m_count; + + enum POCSAG_STATE { + PS_NONE, + PS_WAITING, + PS_SENDING, + PS_ENDING + }; + + std::deque m_output; + std::deque m_buffer; + uint32_t m_ric; + std::string m_text; + POCSAG_STATE m_state; + FILE* m_fp; + + bool processData(); + void writeQueue(); + void addAddress(unsigned char functional); + void packASCII(); + void packNumeric(); + void addBCHAndParity(uint32_t& word) const; + bool openFile(); + bool writeFile(const unsigned char* data); + void closeFile(); +}; + +#endif diff --git a/POCSAGDefines.h b/POCSAGDefines.h new file mode 100644 index 0000000..4b822b3 --- /dev/null +++ b/POCSAGDefines.h @@ -0,0 +1,35 @@ +/* +* Copyright (C) 2018 by Jonathan Naylor G4KLX +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#if !defined(POCSAGDEFINES_H) +#define POCSAGDEFINES_H + +#include + +const unsigned int POCSAG_RADIO_SYMBOL_LENGTH = 20U; // At 24 kHz sample rate + +const unsigned int POCSAG_FRAME_LENGTH_WORDS = 17U; +const unsigned int POCSAG_FRAME_LENGTH_BYTES = POCSAG_FRAME_LENGTH_WORDS * sizeof(uint32_t); + +const unsigned int POCSAG_FRAME_ADDRESSES = 8U; + +const uint32_t POCSAG_SYNC_WORD = 0x7CD215D8U; + +const uint32_t POCSAG_IDLE_WORD = 0x7A89C197U; + +#endif diff --git a/POCSAGNetwork.cpp b/POCSAGNetwork.cpp new file mode 100644 index 0000000..d617458 --- /dev/null +++ b/POCSAGNetwork.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "POCSAGDefines.h" +#include "POCSAGNetwork.h" +#include "Defines.h" +#include "Utils.h" +#include "Log.h" + +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 200U; + +CPOCSAGNetwork::CPOCSAGNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug) : +m_socket(myAddress, myPort), +m_address(), +m_port(gatewayPort), +m_debug(debug), +m_enabled(false), +m_buffer(1000U, "POCSAG Network") +{ + m_address = CUDPSocket::lookup(gatewayAddress); +} + +CPOCSAGNetwork::~CPOCSAGNetwork() +{ +} + +bool CPOCSAGNetwork::open() +{ + LogMessage("Opening POCSAG network connection"); + + if (m_address.s_addr == INADDR_NONE) + return false; + + return m_socket.open(); +} + +void CPOCSAGNetwork::clock(unsigned int ms) +{ + unsigned char buffer[BUFFER_LENGTH]; + + in_addr address; + unsigned int port; + int length = m_socket.read(buffer, BUFFER_LENGTH, address, port); + if (length <= 0) + return; + + // Check if the data is for us + if (m_address.s_addr != address.s_addr || m_port != port) { + LogMessage("POCSAG packet received from an invalid source, %08X != %08X and/or %u != %u", m_address.s_addr, address.s_addr, m_port, port); + return; + } + + // Invalid packet type? + if (::memcmp(buffer, "POCSAG", 6U) != 0) + return; + + if (!m_enabled) + return; + + if (m_debug) + CUtils::dump(1U, "POCSAG Network Data Received", buffer, length); + + unsigned char len = length - 6U; + m_buffer.addData(&len, 1U); + m_buffer.addData(buffer + 6U, length - 6U); +} + +unsigned int CPOCSAGNetwork::read(unsigned char* data) +{ + assert(data != NULL); + + if (m_buffer.isEmpty()) + return 0U; + + unsigned char len = 0U; + m_buffer.getData(&len, 1U); + m_buffer.getData(data, len); + + return len; +} + +void CPOCSAGNetwork::reset() +{ +} + +void CPOCSAGNetwork::close() +{ + m_socket.close(); + + LogMessage("Closing POCSAG network connection"); +} + +void CPOCSAGNetwork::enable(bool enabled) +{ + if (enabled && !m_enabled) + reset(); + + unsigned char c = enabled ? 0x00U : 0xFFU; + + m_socket.write(&c, 1U, m_address, m_port); + + m_enabled = enabled; +} diff --git a/POCSAGNetwork.h b/POCSAGNetwork.h new file mode 100644 index 0000000..fe95203 --- /dev/null +++ b/POCSAGNetwork.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef POCSAGNetwork_H +#define POCSAGNetwork_H + +#include "POCSAGDefines.h" +#include "RingBuffer.h" +#include "UDPSocket.h" +#include "Timer.h" + +#include +#include + +class CPOCSAGNetwork { +public: + CPOCSAGNetwork(const std::string& myAddress, unsigned int myPort, const std::string& gatewayAddress, unsigned int gatewayPort, bool debug); + ~CPOCSAGNetwork(); + + bool open(); + + void enable(bool enabled); + + unsigned int read(unsigned char* data); + + void reset(); + + void close(); + + void clock(unsigned int ms); + +private: + CUDPSocket m_socket; + in_addr m_address; + unsigned int m_port; + bool m_debug; + bool m_enabled; + CRingBuffer m_buffer; +}; + +#endif diff --git a/README.md b/README.md index 2758361..953e4ce 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -These are the source files for building the MMDVMHost, the program that interfaces to the MMDVM or DVMega on the one side, and a suitable network on the other. It supports D-Star, DMR, P25 Phase 1, NXDN, and System Fusion on the MMDVM, and D-Star, DMR, and System Fusion on the DVMega. +These are the source files for building the MMDVMHost, the program that interfaces to the MMDVM or DVMega on the one side, and a suitable network on the other. It supports D-Star, DMR, P25 Phase 1, NXDN, System Fusion, and POCSAG paging on the MMDVM, and D-Star, DMR, and System Fusion on the DVMega. -On the D-Star side the MMDVMHost interfaces with the ircDDB Gateway, on DMR it can connect to BrandMeister, DMR+, HB Link, XLX or [DMRGateway](https://github.com/g4klx/DMRGateway) (to connect to multiple DMR networks at once) on System Fusion it connects to the YSF Gateway. On P25 it connects to the P25 Gateway. +On the D-Star side the MMDVMHost interfaces with the ircDDB Gateway, on DMR it can connect to BrandMeister, DMR+, HB Link, XLX or [DMRGateway](https://github.com/g4klx/DMRGateway) (to connect to multiple DMR networks at once) on System Fusion it connects to the YSF Gateway to allow access to the FCS and YSF networks. On P25 it connects to the P25 Gateway. On NXDN it connects to the NXDN Gateway which provides access to the NXDN and NXCore talk groups. Finally it uses the DAPNET Gateway to access DAPNET to receive paging messages. It builds on 32-bit and 64-bit Linux as well as on Windows using Visual Studio 2017 on x86 and x64. It can optionally control various Displays. Currently these are: @@ -8,7 +8,7 @@ It builds on 32-bit and 64-bit Linux as well as on Windows using Visual Studio 2 - Support for HD44780 via 4 bit GPIO connection (user selectable pins) - Adafruit 16x2 LCD+Keypad Kits (I2C) - Connection via PCF8574 GPIO Extender (I2C) -- Nextion TFTs (sizes 2.4", 2.8", 3.2" and 3.5") +- Nextion TFTs (all sizes, both Basic and Enhanced versions) - TFT display sold by Hobbytronics in UK - OLED 128x64 (SSD1306) - LCDproc diff --git a/SerialController.cpp b/SerialController.cpp index 7856c24..83cc104 100644 --- a/SerialController.cpp +++ b/SerialController.cpp @@ -35,6 +35,9 @@ #include #include #include +#if !defined(__APPLE__) +#include +#endif #endif @@ -237,119 +240,120 @@ CSerialController::~CSerialController() bool CSerialController::open() { assert(m_fd == -1); + #if defined(__APPLE__) - m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); /*open in block mode under OSX*/ + m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); /*open in block mode under OSX*/ #else - m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0); + m_fd = ::open(m_device.c_str(), O_RDWR | O_NOCTTY | O_NDELAY, 0); #endif - if (m_fd < 0) { - LogError("Cannot open device - %s", m_device.c_str()); - return false; - } - - if (::isatty(m_fd) == 0) { - LogError("%s is not a TTY device", m_device.c_str()); - ::close(m_fd); - return false; - } - - termios termios; - if (::tcgetattr(m_fd, &termios) < 0) { - LogError("Cannot get the attributes for %s", m_device.c_str()); - ::close(m_fd); - return false; - } - - #if defined(__APPLE__) - termios.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */ - termios.c_cflag &= ~CSIZE; - termios.c_cflag |= CS8; /* 8-bit characters */ - termios.c_cflag &= ~PARENB; /* no parity bit */ - termios.c_cflag &= ~CSTOPB; /* only need 1 stop bit */ - termios.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */ - - /* setup for non-canonical mode */ - termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - termios.c_oflag &= ~OPOST; - - /* fetch bytes as they become available */ - termios.c_cc[VMIN] = 1; - termios.c_cc[VTIME] = 1; -#else - termios.c_lflag &= ~(ECHO | ECHOE | ICANON | IEXTEN | ISIG); - termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | IXOFF | IXANY); - termios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS); - termios.c_cflag |= CS8; - termios.c_oflag &= ~(OPOST); - termios.c_cc[VMIN] = 0; - termios.c_cc[VTIME] = 10; -#endif - - switch (m_speed) { - case SERIAL_1200: - ::cfsetospeed(&termios, B1200); - ::cfsetispeed(&termios, B1200); - break; - case SERIAL_2400: - ::cfsetospeed(&termios, B2400); - ::cfsetispeed(&termios, B2400); - break; - case SERIAL_4800: - ::cfsetospeed(&termios, B4800); - ::cfsetispeed(&termios, B4800); - break; - case SERIAL_9600: - ::cfsetospeed(&termios, B9600); - ::cfsetispeed(&termios, B9600); - break; - case SERIAL_19200: - ::cfsetospeed(&termios, B19200); - ::cfsetispeed(&termios, B19200); - break; - case SERIAL_38400: - ::cfsetospeed(&termios, B38400); - ::cfsetispeed(&termios, B38400); - break; - case SERIAL_115200: - ::cfsetospeed(&termios, B115200); - ::cfsetispeed(&termios, B115200); - break; - case SERIAL_230400: - ::cfsetospeed(&termios, B230400); - ::cfsetispeed(&termios, B230400); - break; - default: - LogError("Unsupported serial port speed - %d", int(m_speed)); - ::close(m_fd); + if (m_fd < 0) { + LogError("Cannot open device - %s", m_device.c_str()); return false; - } + } - if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) { - LogError("Cannot set the attributes for %s", m_device.c_str()); - ::close(m_fd); - return false; - } - - if (m_assertRTS) { - unsigned int y; - if (::ioctl(m_fd, TIOCMGET, &y) < 0) { - LogError("Cannot get the control attributes for %s", m_device.c_str()); + if (::isatty(m_fd) == 0) { + LogError("%s is not a TTY device", m_device.c_str()); ::close(m_fd); return false; } - y |= TIOCM_RTS; - - if (::ioctl(m_fd, TIOCMSET, &y) < 0) { - LogError("Cannot set the control attributes for %s", m_device.c_str()); + termios termios; + if (::tcgetattr(m_fd, &termios) < 0) { + LogError("Cannot get the attributes for %s", m_device.c_str()); ::close(m_fd); return false; } - } #if defined(__APPLE__) - setNonblock(false); + termios.c_cflag |= (CLOCAL | CREAD); /* ignore modem controls */ + termios.c_cflag &= ~CSIZE; + termios.c_cflag |= CS8; /* 8-bit characters */ + termios.c_cflag &= ~PARENB; /* no parity bit */ + termios.c_cflag &= ~CSTOPB; /* only need 1 stop bit */ + termios.c_cflag &= ~CRTSCTS; /* no hardware flowcontrol */ + + /* setup for non-canonical mode */ + termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + termios.c_oflag &= ~OPOST; + + /* fetch bytes as they become available */ + termios.c_cc[VMIN] = 1; + termios.c_cc[VTIME] = 1; +#else + termios.c_lflag &= ~(ECHO | ECHOE | ICANON | IEXTEN | ISIG); + termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | IXOFF | IXANY); + termios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS); + termios.c_cflag |= CS8; + termios.c_oflag &= ~(OPOST); + termios.c_cc[VMIN] = 0; + termios.c_cc[VTIME] = 10; +#endif + + switch (m_speed) { + case SERIAL_1200: + ::cfsetospeed(&termios, B1200); + ::cfsetispeed(&termios, B1200); + break; + case SERIAL_2400: + ::cfsetospeed(&termios, B2400); + ::cfsetispeed(&termios, B2400); + break; + case SERIAL_4800: + ::cfsetospeed(&termios, B4800); + ::cfsetispeed(&termios, B4800); + break; + case SERIAL_9600: + ::cfsetospeed(&termios, B9600); + ::cfsetispeed(&termios, B9600); + break; + case SERIAL_19200: + ::cfsetospeed(&termios, B19200); + ::cfsetispeed(&termios, B19200); + break; + case SERIAL_38400: + ::cfsetospeed(&termios, B38400); + ::cfsetispeed(&termios, B38400); + break; + case SERIAL_115200: + ::cfsetospeed(&termios, B115200); + ::cfsetispeed(&termios, B115200); + break; + case SERIAL_230400: + ::cfsetospeed(&termios, B230400); + ::cfsetispeed(&termios, B230400); + break; + default: + LogError("Unsupported serial port speed - %d", int(m_speed)); + ::close(m_fd); + return false; + } + + if (::tcsetattr(m_fd, TCSANOW, &termios) < 0) { + LogError("Cannot set the attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + + if (m_assertRTS) { + unsigned int y; + if (::ioctl(m_fd, TIOCMGET, &y) < 0) { + LogError("Cannot get the control attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + + y |= TIOCM_RTS; + + if (::ioctl(m_fd, TIOCMSET, &y) < 0) { + LogError("Cannot set the control attributes for %s", m_device.c_str()); + ::close(m_fd); + return false; + } + } + +#if defined(__APPLE__) + setNonblock(false); #endif return true; @@ -380,6 +384,7 @@ int CSerialController::read(unsigned char* buffer, unsigned int length) unsigned int offset = 0U; while (offset < length) { + fd_set fds; FD_ZERO(&fds); FD_SET(m_fd, &fds); diff --git a/SerialController.h b/SerialController.h index 63bd8be..6dfc461 100644 --- a/SerialController.h +++ b/SerialController.h @@ -57,7 +57,7 @@ public: virtual int setNonblock(bool nonblock); #endif -private: +protected: std::string m_device; SERIAL_SPEED m_speed; bool m_assertRTS; diff --git a/StopWatch.cpp b/StopWatch.cpp index 77d539d..481241b 100644 --- a/StopWatch.cpp +++ b/StopWatch.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,21 +21,32 @@ #if defined(_WIN32) || defined(_WIN64) CStopWatch::CStopWatch() : -m_frequency(), +m_frequencyS(), +m_frequencyMS(), m_start() { - ::QueryPerformanceFrequency(&m_frequency); + ::QueryPerformanceFrequency(&m_frequencyS); + + m_frequencyMS.QuadPart = m_frequencyS.QuadPart / 1000ULL; } CStopWatch::~CStopWatch() { } -unsigned long CStopWatch::start() +unsigned long long CStopWatch::time() const +{ + LARGE_INTEGER now; + ::QueryPerformanceCounter(&now); + + return (unsigned long long)(now.QuadPart / m_frequencyMS.QuadPart); +} + +unsigned long long CStopWatch::start() { ::QueryPerformanceCounter(&m_start); - return (unsigned long)(m_start.QuadPart / m_frequency.QuadPart); + return (unsigned long long)(m_start.QuadPart / m_frequencyS.QuadPart); } unsigned int CStopWatch::elapsed() @@ -46,15 +57,16 @@ unsigned int CStopWatch::elapsed() LARGE_INTEGER temp; temp.QuadPart = (now.QuadPart - m_start.QuadPart) * 1000; - return (unsigned int)(temp.QuadPart / m_frequency.QuadPart); + return (unsigned int)(temp.QuadPart / m_frequencyS.QuadPart); } #else #include +#include CStopWatch::CStopWatch() : -m_start() +m_startMS(0ULL) { } @@ -62,23 +74,32 @@ CStopWatch::~CStopWatch() { } -unsigned long CStopWatch::start() -{ - ::gettimeofday(&m_start, NULL); - - return m_start.tv_usec; -} - -unsigned int CStopWatch::elapsed() +unsigned long long CStopWatch::time() const { struct timeval now; ::gettimeofday(&now, NULL); - unsigned int elapsed = (now.tv_sec - m_start.tv_sec) * 1000U; - elapsed += now.tv_usec / 1000U; - elapsed -= m_start.tv_usec / 1000U; + return now.tv_sec * 1000ULL + now.tv_usec / 1000ULL; +} - return elapsed; +unsigned long long CStopWatch::start() +{ + struct timespec now; + ::clock_gettime(CLOCK_MONOTONIC, &now); + + m_startMS = now.tv_sec * 1000ULL + now.tv_nsec / 1000000ULL; + + return m_startMS; +} + +unsigned int CStopWatch::elapsed() +{ + struct timespec now; + ::clock_gettime(CLOCK_MONOTONIC, &now); + + unsigned long long nowMS = now.tv_sec * 1000ULL + now.tv_nsec / 1000000ULL; + + return nowMS - m_startMS; } #endif diff --git a/StopWatch.h b/StopWatch.h index 811047e..3f8fa19 100644 --- a/StopWatch.h +++ b/StopWatch.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,15 +31,18 @@ public: CStopWatch(); ~CStopWatch(); - unsigned long start(); - unsigned int elapsed(); + unsigned long long time() const; + + unsigned long long start(); + unsigned int elapsed(); private: #if defined(_WIN32) || defined(_WIN64) - LARGE_INTEGER m_frequency; + LARGE_INTEGER m_frequencyS; + LARGE_INTEGER m_frequencyMS; LARGE_INTEGER m_start; #else - struct timeval m_start; + unsigned long long m_startMS; #endif }; diff --git a/TFTSerial.cpp b/TFTSerial.cpp index 08762b1..c2338f3 100644 --- a/TFTSerial.cpp +++ b/TFTSerial.cpp @@ -145,6 +145,22 @@ void CTFTSerial::setLockoutInt() m_mode = MODE_LOCKOUT; } +void CTFTSerial::setQuitInt() +{ + // Clear the screen + clearScreen(); + + setFontSize(FONT_LARGE); + + // Draw MMDVM logo + displayBitmap(0U, 0U, "MMDVM_sm.bmp"); + + gotoPosPixel(20U, 60U); + displayText("STOPPED"); + + m_mode = MODE_QUIT; +} + void CTFTSerial::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) { assert(my1 != NULL); @@ -395,6 +411,20 @@ void CTFTSerial::clearNXDNInt() displayText(" "); } +void CTFTSerial::writePOCSAGInt(uint32_t ric, const std::string& message) +{ + gotoPosPixel(15U, 90U); + displayText("POCSAG TX"); + + m_mode = MODE_CW; +} + +void CTFTSerial::clearPOCSAGInt() +{ + gotoPosPixel(45U, 90U); + displayText("IDLE"); +} + void CTFTSerial::writeCWInt() { gotoPosPixel(45U, 90U); diff --git a/TFTSerial.h b/TFTSerial.h index 7ad1e88..296a4b1 100644 --- a/TFTSerial.h +++ b/TFTSerial.h @@ -39,6 +39,7 @@ protected: virtual void setIdleInt(); virtual void setErrorInt(const char* text); virtual void setLockoutInt(); + virtual void setQuitInt(); virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); virtual void clearDStarInt(); @@ -55,6 +56,9 @@ protected: virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); virtual void clearNXDNInt(); + virtual void writePOCSAGInt(uint32_t ric, const std::string& message); + virtual void clearPOCSAGInt(); + virtual void writeCWInt(); virtual void clearCWInt(); diff --git a/UMP/UMP.ino b/UMP/UMP.ino index d35d18f..4cf98e3 100644 --- a/UMP/UMP.ino +++ b/UMP/UMP.ino @@ -29,6 +29,7 @@ #define PIN_YSF 5 #define PIN_P25 6 #define PIN_NXDN 7 +#define PIN_POCSAG 8 #define PIN_TX 10 #define PIN_CD 11 @@ -64,17 +65,19 @@ void setup() pinMode(PIN_YSF, OUTPUT); pinMode(PIN_P25, OUTPUT); pinMode(PIN_NXDN, OUTPUT); + pinMode(PIN_POCSAG, OUTPUT); pinMode(PIN_TX, OUTPUT); pinMode(PIN_CD, OUTPUT); pinMode(PIN_LOCKOUT, INPUT); - digitalWrite(PIN_DSTAR, LOW); - digitalWrite(PIN_DMR, LOW); - digitalWrite(PIN_YSF, LOW); - digitalWrite(PIN_P25, LOW); - digitalWrite(PIN_NXDN, LOW); - digitalWrite(PIN_TX, LOW); - digitalWrite(PIN_CD, LOW); + digitalWrite(PIN_DSTAR, LOW); + digitalWrite(PIN_DMR, LOW); + digitalWrite(PIN_YSF, LOW); + digitalWrite(PIN_P25, LOW); + digitalWrite(PIN_NXDN, LOW); + digitalWrite(PIN_POCSAG, LOW); + digitalWrite(PIN_TX, LOW); + digitalWrite(PIN_CD, LOW); } #define UMP_FRAME_START 0xF0U @@ -95,6 +98,7 @@ void setup() #define MODE_YSF 3U #define MODE_P25 4U #define MODE_NXDN 5U +#define MODE_POCSAG 6U bool m_started = false; uint32_t m_count = 0U; @@ -129,11 +133,12 @@ void loop() m_started = true; break; case UMP_SET_MODE: - digitalWrite(PIN_DSTAR, m_buffer[3U] == MODE_DSTAR ? HIGH : LOW); - digitalWrite(PIN_DMR, m_buffer[3U] == MODE_DMR ? HIGH : LOW); - digitalWrite(PIN_YSF, m_buffer[3U] == MODE_YSF ? HIGH : LOW); - digitalWrite(PIN_P25, m_buffer[3U] == MODE_P25 ? HIGH : LOW); - digitalWrite(PIN_NXDN, m_buffer[3U] == MODE_NXDN ? HIGH : LOW); + digitalWrite(PIN_DSTAR, m_buffer[3U] == MODE_DSTAR ? HIGH : LOW); + digitalWrite(PIN_DMR, m_buffer[3U] == MODE_DMR ? HIGH : LOW); + digitalWrite(PIN_YSF, m_buffer[3U] == MODE_YSF ? HIGH : LOW); + digitalWrite(PIN_P25, m_buffer[3U] == MODE_P25 ? HIGH : LOW); + digitalWrite(PIN_NXDN, m_buffer[3U] == MODE_NXDN ? HIGH : LOW); + digitalWrite(PIN_POCSAG, m_buffer[3U] == MODE_POCSAG ? HIGH : LOW); break; case UMP_SET_TX: digitalWrite(PIN_TX, m_buffer[3U] == 0x01U ? HIGH : LOW); diff --git a/Version.h b/Version.h index 5b15b50..e14b7aa 100644 --- a/Version.h +++ b/Version.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,6 @@ #if !defined(VERSION_H) #define VERSION_H -const char* VERSION = "20180327"; +const char* VERSION = "20180723"; #endif diff --git a/linux/DMRIDUpdate.sh b/linux/DMRIDUpdate.sh index 659ce30..a5e5060 100755 --- a/linux/DMRIDUpdate.sh +++ b/linux/DMRIDUpdate.sh @@ -52,8 +52,8 @@ DMRIDPATH=/path/to/DMR/ID/file DMRIDFILE=${DMRIDPATH}/DMRIds.dat -# DMR-MARC.net has discontinued real time access. Now they offer a nightly dump instead. -DATABASEURL='https://www.dmr-marc.net/static/users.csv' +# DMR IDs now served by RadioID.net +DATABASEURL='https://ham-digital.org/status/users.csv' # # How many DMR ID files do you want backed up (0 = do not keep backups)