diff --git a/.gitignore b/.gitignore index 4009b5a..ae5746a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ Release x64 MMDVMHost RemoteCommand +Images *.o *.opendb *.bak diff --git a/AMBEFEC.cpp b/AMBEFEC.cpp index 1f2563d..62e81aa 100644 --- a/AMBEFEC.cpp +++ b/AMBEFEC.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010,2014,2016,2018,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2010,2014,2016,2018,2021,2023,2025 by Jonathan Naylor G4KLX * Copyright (C) 2016 Mathias Weyland, HB9FRV * * This program is free software; you can redistribute it and/or modify @@ -442,20 +442,24 @@ const unsigned int PRNG_TABLE[] = { 0xECDB0FU, 0xB542DAU, 0x9E5131U, 0xC7ABA5U, 0x8C38FEU, 0x97010BU, 0xDED290U, 0xA4CC7DU, 0xAD3D2EU, 0xF6B6B3U, 0xF9A540U, 0x205ED9U, 0x634EB6U, 0x5A9567U, 0x11A6D8U, 0x0B3F09U}; +#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_NXDN) const unsigned int DMR_A_TABLE[] = { 0U, 4U, 8U, 12U, 16U, 20U, 24U, 28U, 32U, 36U, 40U, 44U, 48U, 52U, 56U, 60U, 64U, 68U, 1U, 5U, 9U, 13U, 17U, 21U}; const unsigned int DMR_B_TABLE[] = {25U, 29U, 33U, 37U, 41U, 45U, 49U, 53U, 57U, 61U, 65U, 69U, 2U, 6U, 10U, 14U, 18U, 22U, 26U, 30U, 34U, 38U, 42U}; const unsigned int DMR_C_TABLE[] = {46U, 50U, 54U, 58U, 62U, 66U, 70U, 3U, 7U, 11U, 15U, 19U, 23U, 27U, 31U, 35U, 39U, 43U, 47U, 51U, 55U, 59U, 63U, 67U, 71U}; - +#endif +#if defined(USE_DSTAR) const unsigned int DSTAR_A_TABLE[] = {0U, 6U, 12U, 18U, 24U, 30U, 36U, 42U, 48U, 54U, 60U, 66U, 1U, 7U, 13U, 19U, 25U, 31U, 37U, 43U, 49U, 55U, 61U, 67U}; const unsigned int DSTAR_B_TABLE[] = {2U, 8U, 14U, 20U, 26U, 32U, 38U, 44U, 50U, 56U, 62U, 68U, 3U, 9U, 15U, 21U, 27U, 33U, 39U, 45U, 51U, 57U, 63U, 69U}; const unsigned int DSTAR_C_TABLE[] = {4U, 10U, 16U, 22U, 28U, 34U, 40U, 46U, 52U, 58U, 64U, 70U, 5U, 11U, 17U, 23U, 29U, 35U, 41U, 47U, 53U, 59U, 65U, 71U}; +#endif +#if defined(USE_YSF) || defined(USE_P25) const unsigned int IMBE_INTERLEAVE[] = { 0, 7, 12, 19, 24, 31, 36, 43, 48, 55, 60, 67, 72, 79, 84, 91, 96, 103, 108, 115, 120, 127, 132, 139, 1, 6, 13, 18, 25, 30, 37, 42, 49, 54, 61, 66, 73, 78, 85, 90, 97, 102, 109, 114, 121, 126, 133, 138, @@ -464,6 +468,7 @@ const unsigned int IMBE_INTERLEAVE[] = { 4, 11, 16, 23, 28, 35, 40, 47, 52, 59, 64, 71, 76, 83, 88, 95, 100, 107, 112, 119, 124, 131, 136, 143, 5, 10, 17, 22, 29, 34, 41, 46, 53, 58, 65, 70, 77, 82, 89, 94, 101, 106, 113, 118, 125, 130, 137, 142 }; +#endif CAMBEFEC::CAMBEFEC() { @@ -473,6 +478,7 @@ CAMBEFEC::~CAMBEFEC() { } +#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_NXDN) unsigned int CAMBEFEC::regenerateDMR(unsigned char* bytes) const { assert(bytes != nullptr); @@ -573,7 +579,9 @@ unsigned int CAMBEFEC::regenerateDMR(unsigned char* bytes) const return errors; } +#endif +#if defined(USE_DSTAR) unsigned int CAMBEFEC::regenerateDStar(unsigned char* bytes) const { assert(bytes != nullptr); @@ -605,7 +613,9 @@ unsigned int CAMBEFEC::regenerateDStar(unsigned char* bytes) const return errors; } +#endif +#if defined(USE_YSF) || defined(USE_NXDN) unsigned int CAMBEFEC::regenerateYSFDN(unsigned char* bytes) const { assert(bytes != nullptr); @@ -656,7 +666,9 @@ unsigned int CAMBEFEC::regenerateYSFDN(unsigned char* bytes) const return errors; } +#endif +#if defined(USE_YSF) || defined(USE_P25) unsigned int CAMBEFEC::regenerateIMBE(unsigned char* bytes) const { assert(bytes != nullptr); @@ -790,7 +802,9 @@ unsigned int CAMBEFEC::regenerateIMBE(unsigned char* bytes) const return errors; } +#endif +#if defined(USE_DSTAR) unsigned int CAMBEFEC::regenerateDStar(unsigned int& a, unsigned int& b) const { unsigned int orig_a = a; @@ -823,7 +837,9 @@ unsigned int CAMBEFEC::regenerateDStar(unsigned int& a, unsigned int& b) const return errsA + errsB; } +#endif +#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_NXDN) unsigned int CAMBEFEC::regenerateDMR(unsigned int& a, unsigned int& b, unsigned int& c) const { unsigned int orig_a = a; @@ -866,3 +882,4 @@ unsigned int CAMBEFEC::regenerateDMR(unsigned int& a, unsigned int& b, unsigned return errsA + errsB; } +#endif diff --git a/AMBEFEC.h b/AMBEFEC.h index 7e242d4..960e5ac 100644 --- a/AMBEFEC.h +++ b/AMBEFEC.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010,2014,2016,2018 by Jonathan Naylor G4KLX + * Copyright (C) 2010,2014,2016,2018,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,22 +19,36 @@ #if !defined(AMBEFEC_H) #define AMBEFEC_H +#include "Defines.h" + class CAMBEFEC { public: CAMBEFEC(); ~CAMBEFEC(); +#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_NXDN) unsigned int regenerateDMR(unsigned char* bytes) const; +#endif +#if defined(USE_DSTAR) unsigned int regenerateDStar(unsigned char* bytes) const; +#endif +#if defined(USE_YSF) || defined(USE_NXDN) unsigned int regenerateYSFDN(unsigned char* bytes) const; +#endif +#if defined(USE_YSF) || defined(USE_P25) unsigned int regenerateIMBE(unsigned char* bytes) const; +#endif private: +#if defined(USE_DSTAR) unsigned int regenerateDStar(unsigned int& a, unsigned int& b) const; +#endif +#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_NXDN) unsigned int regenerateDMR(unsigned int& a, unsigned int& b,unsigned int& c) const; +#endif }; #endif diff --git a/BCH.cpp b/BCH.cpp index 938f322..6bb8beb 100644 --- a/BCH.cpp +++ b/BCH.cpp @@ -71,6 +71,8 @@ #include "BCH.h" +#if defined(USE_P25) + #include #include #include @@ -137,3 +139,6 @@ void CBCH::encode(unsigned char* nid) WRITE_BIT(nid, i + 16U, b); } } + +#endif + diff --git a/BCH.h b/BCH.h index fa1265d..1791f1f 100644 --- a/BCH.h +++ b/BCH.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(BCH_H) #define BCH_H +#include "Defines.h" + +#if defined(USE_P25) + class CBCH { public: CBCH(); @@ -31,3 +35,6 @@ private: }; #endif + +#endif + diff --git a/BPTC19696.cpp b/BPTC19696.cpp index 334fec2..667fc0a 100644 --- a/BPTC19696.cpp +++ b/BPTC19696.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 by Ian Wraith - * Copyright (C) 2015,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,8 @@ #include "BPTC19696.h" +#if defined(USE_DMR) + #include "Hamming.h" #include "Utils.h" @@ -345,3 +347,6 @@ void CBPTC19696::encodeExtractBinary(unsigned char* data) CUtils::bitsToByteBE(m_rawData + 180U, data[31U]); CUtils::bitsToByteBE(m_rawData + 188U, data[32U]); } + +#endif + diff --git a/BPTC19696.h b/BPTC19696.h index 0a40a78..cc86b5b 100644 --- a/BPTC19696.h +++ b/BPTC19696.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(BPTC19696_H) #define BPTC19696_H +#include "Defines.h" + +#if defined(USE_DMR) + class CBPTC19696 { public: @@ -45,3 +49,6 @@ private: }; #endif + +#endif + diff --git a/CASTInfo.cpp b/CASTInfo.cpp deleted file mode 100644 index fcc98f8..0000000 --- a/CASTInfo.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2016,2018,2020,2021,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "CASTInfo.h" - -static bool networkInfoInitialized = false; -static unsigned char passCounter = 0; - -CCASTInfo::CCASTInfo(CModem* modem) : -CDisplay(), -m_modem(modem), -m_ipaddress() -{ -} - -CCASTInfo::~CCASTInfo() -{ -} - -bool CCASTInfo::open() -{ - return true; -} - -void CCASTInfo::setIdleInt() -{ - 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; - - if (m_modem != nullptr) - m_modem->writeIPInfo(m_ipaddress); - - networkInfoInitialized = true; - passCounter = 0; - } -} - -void CCASTInfo::setErrorInt(const char* text) -{ -} - -void CCASTInfo::setLockoutInt() -{ -} - -void CCASTInfo::setQuitInt() -{ -} - -void CCASTInfo::setFMInt() -{ -} - -void CCASTInfo::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) -{ - if (m_modem != nullptr) - m_modem->writeDStarInfo(my1, my2, your, type, reflector); -} - -void CCASTInfo::clearDStarInt() -{ -} - -void CCASTInfo::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) -{ - if (m_modem != nullptr) - m_modem->writeDMRInfo(slotNo, src, group, dst, type); -} - -void CCASTInfo::clearDMRInt(unsigned int slotNo) -{ -} - -void CCASTInfo::writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) -{ - if (m_modem != nullptr) - m_modem->writeYSFInfo(source, dest, dgid, type, origin); -} - -void CCASTInfo::clearFusionInt() -{ -} - -void CCASTInfo::writeP25Int(const char* source, bool group, unsigned int dest, const char* type) -{ - if (m_modem != nullptr) - m_modem->writeP25Info(source, group, dest, type); -} - -void CCASTInfo::clearP25Int() -{ -} - -void CCASTInfo::writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type) -{ - if (m_modem != nullptr) - m_modem->writeNXDNInfo(source, group, dest, type); -} - -void CCASTInfo::clearNXDNInt() -{ -} - -void CCASTInfo::writePOCSAGInt(uint32_t ric, const std::string& message) -{ - if (m_modem != nullptr) - m_modem->writePOCSAGInfo(ric, message); -} - -void CCASTInfo::clearPOCSAGInt() -{ -} - -void CCASTInfo::writeCWInt() -{ -} - -void CCASTInfo::clearCWInt() -{ -} - -void CCASTInfo::close() -{ -} diff --git a/CASTInfo.h b/CASTInfo.h deleted file mode 100644 index 5609852..0000000 --- a/CASTInfo.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2016,2018,2020,2021,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#if !defined(CASTINFO_H) -#define CASTINFO_H - -#include "Display.h" - -#include - -#include "NetworkInfo.h" -#include "Modem.h" - -class CCASTInfo : public CDisplay -{ -public: - CCASTInfo(CModem* modem); - virtual ~CCASTInfo(); - - virtual bool open(); - - virtual void close(); - -protected: - virtual void setIdleInt(); - virtual void setErrorInt(const char* text); - virtual void setLockoutInt(); - virtual void setQuitInt(); - virtual void setFMInt(); - - virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); - virtual void clearDStarInt(); - - virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); - virtual void clearDMRInt(unsigned int slotNo); - - virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin); - virtual void clearFusionInt(); - - virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type); - virtual void clearP25Int(); - - virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); - virtual void clearNXDNInt(); - - virtual void writePOCSAGInt(uint32_t ric, const std::string& message); - virtual void clearPOCSAGInt(); - - virtual void writeCWInt(); - virtual void clearCWInt(); - -private: - CModem* m_modem; - std::string m_ipaddress; -}; - -#endif diff --git a/CRC.cpp b/CRC.cpp index d7a8390..fce82ac 100644 --- a/CRC.cpp +++ b/CRC.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ #include #include +#if defined(USE_DMR) const uint8_t CRC8_TABLE[] = { 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, @@ -48,8 +49,10 @@ const uint8_t CRC8_TABLE[] = { 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, - 0xFA, 0xFD, 0xF4, 0xF3, 0x01 }; + 0xFA, 0xFD, 0xF4, 0xF3, 0x01}; +#endif +#if defined(USE_DSTAR) const uint16_t CCITT16_TABLE1[] = { 0x0000U, 0x1189U, 0x2312U, 0x329bU, 0x4624U, 0x57adU, 0x6536U, 0x74bfU, 0x8c48U, 0x9dc1U, 0xaf5aU, 0xbed3U, 0xca6cU, 0xdbe5U, 0xe97eU, 0xf8f7U, @@ -82,8 +85,10 @@ const uint16_t CCITT16_TABLE1[] = { 0xe70eU, 0xf687U, 0xc41cU, 0xd595U, 0xa12aU, 0xb0a3U, 0x8238U, 0x93b1U, 0x6b46U, 0x7acfU, 0x4854U, 0x59ddU, 0x2d62U, 0x3cebU, 0x0e70U, 0x1ff9U, 0xf78fU, 0xe606U, 0xd49dU, 0xc514U, 0xb1abU, 0xa022U, 0x92b9U, 0x8330U, - 0x7bc7U, 0x6a4eU, 0x58d5U, 0x495cU, 0x3de3U, 0x2c6aU, 0x1ef1U, 0x0f78U }; + 0x7bc7U, 0x6a4eU, 0x58d5U, 0x495cU, 0x3de3U, 0x2c6aU, 0x1ef1U, 0x0f78U}; +#endif +#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_P25) const uint16_t CCITT16_TABLE2[] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, @@ -116,9 +121,10 @@ const uint16_t CCITT16_TABLE2[] = { 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, - 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 }; - + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0}; +#endif +#if defined(USE_DMR) bool CCRC::checkFiveBit(bool* in, unsigned int tcrc) { assert(in != nullptr); @@ -144,7 +150,9 @@ void CCRC::encodeFiveBit(const bool* in, unsigned int& tcrc) tcrc = total; } +#endif +#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_P25) void CCRC::addCCITT162(unsigned char *in, unsigned int length) { assert(in != nullptr); @@ -185,7 +193,9 @@ bool CCRC::checkCCITT162(const unsigned char *in, unsigned int length) return crc8[0U] == in[length - 1U] && crc8[1U] == in[length - 2U]; } +#endif +#if defined(USE_DSTAR) void CCRC::addCCITT161(unsigned char *in, unsigned int length) { assert(in != nullptr); @@ -226,7 +236,9 @@ bool CCRC::checkCCITT161(const unsigned char *in, unsigned int length) return crc8[0U] == in[length - 2U] && crc8[1U] == in[length - 1U]; } +#endif +#if defined(USE_DMR) unsigned char CCRC::crc8(const unsigned char *in, unsigned int length) { assert(in != nullptr); @@ -238,3 +250,6 @@ unsigned char CCRC::crc8(const unsigned char *in, unsigned int length) return crc; } + +#endif + diff --git a/CRC.h b/CRC.h index e80c3f6..ac6b419 100644 --- a/CRC.h +++ b/CRC.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,19 +19,35 @@ #if !defined(CRC_H) #define CRC_H +#include "Defines.h" + class CCRC { public: +#if defined(USE_DMR) static bool checkFiveBit(bool* in, unsigned int tcrc); static void encodeFiveBit(const bool* in, unsigned int& tcrc); +#endif +#if defined(USE_DSTAR) static void addCCITT161(unsigned char* in, unsigned int length); +#endif + +#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_P25) static void addCCITT162(unsigned char* in, unsigned int length); +#endif +#if defined(USE_DSTAR) static bool checkCCITT161(const unsigned char* in, unsigned int length); - static bool checkCCITT162(const unsigned char* in, unsigned int length); +#endif +#if defined(USE_DMR) || defined(USE_YSF) || defined(USE_P25) + static bool checkCCITT162(const unsigned char* in, unsigned int length); +#endif + +#if defined(USE_DMR) static unsigned char crc8(const unsigned char* in, unsigned int length); +#endif }; #endif diff --git a/Conf.cpp b/Conf.cpp index 52a93a2..da1b3d2 100644 --- a/Conf.cpp +++ b/Conf.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2023,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,30 +32,58 @@ enum class SECTION { GENERAL, INFO, LOG, + MQTT, CWID, +#if defined(USE_DMR) || defined(USE_P25) DMRID_LOOKUP, +#endif +#if defined(USE_NXDN) NXDNID_LOOKUP, +#endif MODEM, TRANSPARENT, +#if defined(USE_DSTAR) DSTAR, +#endif +#if defined(USE_DMR) DMR, +#endif +#if defined(USE_YSF) FUSION, +#endif +#if defined(USE_P25) P25, +#endif +#if defined(USE_NXDN) NXDN, +#endif +#if defined(USE_POCSAG) POCSAG, +#endif +#if defined(USE_FM) FM, +#endif +#if defined(USE_DSTAR) DSTAR_NETWORK, +#endif +#if defined(USE_DMR) DMR_NETWORK, +#endif +#if defined(USE_YSF) FUSION_NETWORK, +#endif +#if defined(USE_P25) P25_NETWORK, +#endif +#if defined(USE_NXDN) NXDN_NETWORK, +#endif +#if defined(USE_POCSAG) POCSAG_NETWORK, +#endif +#if defined(USE_FM) FM_NETWORK, - TFTSERIAL_DISPLAY, - HD44780_DISPLAY, - NEXTION_DISPLAY, - OLED_DISPLAY, - LCDPROC_DISPLAY, +#endif LOCK_FILE, REMOTE_CONTROL }; @@ -66,7 +94,6 @@ m_callsign(), m_id(0U), m_timeout(120U), m_duplex(true), -m_display(), m_daemon(false), m_rxFrequency(0U), m_txFrequency(0U), @@ -77,18 +104,26 @@ m_height(0), m_location(), m_description(), m_url(), +m_logMQTTLevel(0U), m_logDisplayLevel(0U), -m_logFileLevel(0U), -m_logFilePath(), -m_logFileRoot(), -m_logFileRotate(true), +m_mqttHost("127.0.0.1"), +m_mqttPort(1883), +m_mqttKeepalive(60U), +m_mqttName("mmdvm"), +m_mqttAuthEnabled(false), +m_mqttUsername(), +m_mqttPassword(), m_cwIdEnabled(false), m_cwIdTime(10U), m_cwIdCallsign(), +#if defined(USE_DMR) || defined(USE_P25) m_dmrIdLookupFile(), m_dmrIdLookupTime(0U), +#endif +#if defined(USE_NXDN) m_nxdnIdLookupFile(), m_nxdnIdLookupTime(0U), +#endif m_modemProtocol("uart"), m_modemUARTPort(), m_modemUARTSpeed(115200U), @@ -102,7 +137,9 @@ m_modemRXInvert(false), m_modemTXInvert(false), m_modemPTTInvert(false), m_modemTXDelay(100U), +#if defined(USE_DMR) m_modemDMRDelay(0U), +#endif m_modemTXOffset(0), m_modemRXOffset(0), m_modemRXDCOffset(0), @@ -126,6 +163,7 @@ m_transparentRemoteAddress(), m_transparentRemotePort(0U), m_transparentLocalPort(0U), m_transparentSendFrameType(0U), +#if defined(USE_DSTAR) m_dstarEnabled(false), m_dstarModule("C"), m_dstarSelfOnly(false), @@ -136,12 +174,16 @@ m_dstarAckTime(750U), m_dstarAckMessage(DSTAR_ACK::BER), m_dstarErrorReply(true), m_dstarRemoteGateway(false), +#endif m_dstarModeHang(10U), +#if defined(USE_DMR) m_dmrEnabled(false), m_dmrBeacons(DMR_BEACONS::OFF), m_dmrBeaconInterval(60U), m_dmrBeaconDuration(3U), +#endif m_dmrId(0U), +#if defined(USE_DMR) m_dmrColorCode(2U), m_dmrSelfOnly(false), m_dmrEmbeddedLCOnly(false), @@ -153,34 +195,50 @@ m_dmrSlot1TGWhiteList(), m_dmrSlot2TGWhiteList(), m_dmrCallHang(10U), m_dmrTXHang(4U), +#endif m_dmrModeHang(10U), +#if defined(USE_DMR) m_dmrOVCM(DMR_OVCM::OFF), m_dmrProtect(false), +#endif +#if defined(USE_YSF) m_fusionEnabled(false), m_fusionLowDeviation(false), m_fusionRemoteGateway(false), m_fusionSelfOnly(false), m_fusionTXHang(4U), +#endif m_fusionModeHang(10U), +#if defined(USE_P25) m_p25Enabled(false), +#endif m_p25Id(0U), +#if defined(USE_P25) m_p25NAC(0x293U), m_p25SelfOnly(false), m_p25OverrideUID(false), m_p25RemoteGateway(false), m_p25TXHang(5U), +#endif m_p25ModeHang(10U), +#if defined(USE_NXDN) m_nxdnEnabled(false), m_nxdnId(0U), m_nxdnRAN(1U), m_nxdnSelfOnly(false), m_nxdnRemoteGateway(false), m_nxdnTXHang(5U), +#endif m_nxdnModeHang(10U), +#if defined(USE_POCSAG) m_pocsagEnabled(false), +#endif m_pocsagFrequency(0U), +#if defined(USE_FM) m_fmEnabled(false), +#endif m_fmCallsign(), +#if defined(USE_FM) m_fmCallsignSpeed(20U), m_fmCallsignFrequency(1000U), m_fmCallsignTime(10U), @@ -197,7 +255,9 @@ m_fmAckFrequency(1750U), m_fmAckMinTime(5U), m_fmAckDelay(1000U), m_fmAckLevel(80.0F), +#endif m_fmTimeout(180U), +#if defined(USE_FM) m_fmTimeoutLevel(80.0F), m_fmCTCSSFrequency(88.6F), m_fmCTCSSHighThreshold(30U), @@ -214,49 +274,66 @@ m_fmSquelchLowThreshold(20U), m_fmRFAudioBoost(1U), m_fmMaxDevLevel(90.0F), m_fmExtAudioBoost(1U), +#endif m_fmModeHang(10U), +#if defined(USE_DSTAR) m_dstarNetworkEnabled(false), m_dstarGatewayAddress(), m_dstarGatewayPort(0U), m_dstarLocalAddress(), m_dstarLocalPort(0U), +#endif m_dstarNetworkModeHang(3U), +#if defined(USE_DSTAR) m_dstarNetworkDebug(false), +#endif +#if defined(USE_DMR) m_dmrNetworkEnabled(false), -m_dmrNetworkType("Gateway"), -m_dmrNetworkRemoteAddress(), -m_dmrNetworkRemotePort(0U), +m_dmrNetworkGatewayAddress(), +m_dmrNetworkGatewayPort(0U), m_dmrNetworkLocalAddress(), m_dmrNetworkLocalPort(0U), -m_dmrNetworkPassword(), -m_dmrNetworkOptions(), m_dmrNetworkDebug(false), m_dmrNetworkJitter(360U), m_dmrNetworkSlot1(true), m_dmrNetworkSlot2(true), +#endif m_dmrNetworkModeHang(3U), +#if defined(USE_YSF) m_fusionNetworkEnabled(false), m_fusionNetworkLocalAddress(), m_fusionNetworkLocalPort(0U), m_fusionNetworkGatewayAddress(), m_fusionNetworkGatewayPort(0U), +#endif m_fusionNetworkModeHang(3U), +#if defined(USE_YSF) m_fusionNetworkDebug(false), +#endif +#if defined(USE_P25) m_p25NetworkEnabled(false), m_p25GatewayAddress(), m_p25GatewayPort(0U), m_p25LocalAddress(), m_p25LocalPort(0U), +#endif m_p25NetworkModeHang(3U), +#if defined(USE_P25) m_p25NetworkDebug(false), +#endif +#if defined(USE_NXDN) m_nxdnNetworkEnabled(false), m_nxdnNetworkProtocol("Icom"), m_nxdnGatewayAddress(), m_nxdnGatewayPort(0U), m_nxdnLocalAddress(), m_nxdnLocalPort(0U), +#endif m_nxdnNetworkModeHang(3U), +#if defined(USE_NXDN) m_nxdnNetworkDebug(false), +#endif +#if defined(USE_POCSAG) m_pocsagNetworkEnabled(false), m_pocsagGatewayAddress(), m_pocsagGatewayPort(0U), @@ -264,10 +341,9 @@ m_pocsagLocalAddress(), m_pocsagLocalPort(0U), m_pocsagNetworkModeHang(3U), m_pocsagNetworkDebug(false), +#endif +#if defined(USE_FM) m_fmNetworkEnabled(false), -m_fmNetworkProtocol("USRP"), -m_fmNetworkSampleRate(48000U), -m_fmNetworkSquelchFile(), m_fmGatewayAddress(), m_fmGatewayPort(0U), m_fmLocalAddress(), @@ -276,47 +352,14 @@ m_fmPreEmphasis(true), m_fmDeEmphasis(true), m_fmTXAudioGain(1.0F), m_fmRXAudioGain(1.0F), +#endif m_fmNetworkModeHang(3U), +#if defined(USE_FM) m_fmNetworkDebug(false), -m_tftSerialPort("/dev/ttyAMA0"), -m_tftSerialBrightness(50U), -m_tftSerialScreenLayout(0U), -m_hd44780Rows(2U), -m_hd44780Columns(16U), -m_hd44780Pins(), -m_hd44780i2cAddress(), -m_hd44780PWM(false), -m_hd44780PWMPin(), -m_hd44780PWMBright(), -m_hd44780PWMDim(), -m_hd44780DisplayClock(false), -m_hd44780UTC(false), -m_nextionPort("/dev/ttyAMA0"), -m_nextionBrightness(50U), -m_nextionDisplayClock(false), -m_nextionUTC(false), -m_nextionIdleBrightness(20U), -m_nextionScreenLayout(0U), -m_nextionTempInFahrenheit(false), -m_nextionOutput(false), -m_nextionUDPPort(6759), -m_oledType(3U), -m_oledBrightness(0U), -m_oledInvert(false), -m_oledScroll(false), -m_oledRotate(false), -m_oledLogoScreensaver(true), -m_lcdprocAddress(), -m_lcdprocPort(0U), -m_lcdprocLocalPort(0U), -m_lcdprocDisplayClock(false), -m_lcdprocUTC(false), -m_lcdprocDimOnIdle(false), +#endif m_lockFileEnabled(false), m_lockFileName(), -m_remoteControlEnabled(false), -m_remoteControlAddress("127.0.0.1"), -m_remoteControlPort(0U) +m_remoteControlEnabled(false) { } @@ -346,54 +389,78 @@ bool CConf::read() section = SECTION::INFO; else if (::strncmp(buffer, "[Log]", 5U) == 0) section = SECTION::LOG; + else if (::strncmp(buffer, "[MQTT]", 6U) == 0) + section = SECTION::MQTT; else if (::strncmp(buffer, "[CW Id]", 7U) == 0) section = SECTION::CWID; +#if defined(USE_DMR) || defined(USE_P25) else if (::strncmp(buffer, "[DMR Id Lookup]", 15U) == 0) section = SECTION::DMRID_LOOKUP; +#endif +#if defined(USE_NXDN) else if (::strncmp(buffer, "[NXDN Id Lookup]", 16U) == 0) section = SECTION::NXDNID_LOOKUP; +#endif else if (::strncmp(buffer, "[Modem]", 7U) == 0) section = SECTION::MODEM; else if (::strncmp(buffer, "[Transparent Data]", 18U) == 0) section = SECTION::TRANSPARENT; +#if defined(USE_DSTAR) else if (::strncmp(buffer, "[D-Star]", 8U) == 0) section = SECTION::DSTAR; +#endif +#if defined(USE_DMR) else if (::strncmp(buffer, "[DMR]", 5U) == 0) section = SECTION::DMR; +#endif +#if defined(USE_YSF) else if (::strncmp(buffer, "[System Fusion]", 15U) == 0) section = SECTION::FUSION; +#endif +#if defined(USE_P25) else if (::strncmp(buffer, "[P25]", 5U) == 0) section = SECTION::P25; +#endif +#if defined(USE_NXDN) else if (::strncmp(buffer, "[NXDN]", 6U) == 0) section = SECTION::NXDN; +#endif +#if defined(USE_POCSAG) else if (::strncmp(buffer, "[POCSAG]", 8U) == 0) section = SECTION::POCSAG; +#endif +#if defined(USE_FM) else if (::strncmp(buffer, "[FM]", 4U) == 0) section = SECTION::FM; +#endif +#if defined(USE_DSTAR) else if (::strncmp(buffer, "[D-Star Network]", 16U) == 0) section = SECTION::DSTAR_NETWORK; +#endif +#if defined(USE_DMR) else if (::strncmp(buffer, "[DMR Network]", 13U) == 0) section = SECTION::DMR_NETWORK; +#endif +#if defined(USE_YSF) else if (::strncmp(buffer, "[System Fusion Network]", 23U) == 0) section = SECTION::FUSION_NETWORK; +#endif +#if defined(USE_P25) else if (::strncmp(buffer, "[P25 Network]", 13U) == 0) section = SECTION::P25_NETWORK; +#endif +#if defined(USE_NXDN) else if (::strncmp(buffer, "[NXDN Network]", 14U) == 0) section = SECTION::NXDN_NETWORK; +#endif +#if defined(USE_POCSAG) else if (::strncmp(buffer, "[POCSAG Network]", 16U) == 0) section = SECTION::POCSAG_NETWORK; +#endif +#if defined(USE_FM) else if (::strncmp(buffer, "[FM Network]", 12U) == 0) section = SECTION::FM_NETWORK; - else if (::strncmp(buffer, "[TFT Serial]", 12U) == 0) - section = SECTION::TFTSERIAL_DISPLAY; - else if (::strncmp(buffer, "[HD44780]", 9U) == 0) - section = SECTION::HD44780_DISPLAY; - else if (::strncmp(buffer, "[Nextion]", 9U) == 0) - section = SECTION::NEXTION_DISPLAY; - else if (::strncmp(buffer, "[OLED]", 6U) == 0) - section = SECTION::OLED_DISPLAY; - else if (::strncmp(buffer, "[LCDproc]", 9U) == 0) - section = SECTION::LCDPROC_DISPLAY; +#endif else if (::strncmp(buffer, "[Lock File]", 11U) == 0) section = SECTION::LOCK_FILE; else if (::strncmp(buffer, "[Remote Control]", 16U) == 0) @@ -448,8 +515,6 @@ bool CConf::read() m_dstarModeHang = m_dmrModeHang = m_fusionModeHang = m_p25ModeHang = m_nxdnModeHang = m_fmModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "NetModeHang") == 0) m_dstarNetworkModeHang = m_dmrNetworkModeHang = m_fusionNetworkModeHang = m_p25NetworkModeHang = m_nxdnNetworkModeHang = m_fmNetworkModeHang = (unsigned int)::atoi(value); - else if (::strcmp(key, "Display") == 0) - m_display = value; else if (::strcmp(key, "Daemon") == 0) m_daemon = ::atoi(value) == 1; } else if (section == SECTION::INFO) { @@ -472,16 +537,25 @@ bool CConf::read() else if (::strcmp(key, "URL") == 0) m_url = value; } else if (section == SECTION::LOG) { - if (::strcmp(key, "FilePath") == 0) - m_logFilePath = value; - else if (::strcmp(key, "FileRoot") == 0) - m_logFileRoot = value; - else if (::strcmp(key, "FileLevel") == 0) - m_logFileLevel = (unsigned int)::atoi(value); + if (::strcmp(key, "MQTTLevel") == 0) + m_logMQTTLevel = (unsigned int)::atoi(value); else if (::strcmp(key, "DisplayLevel") == 0) m_logDisplayLevel = (unsigned int)::atoi(value); - else if (::strcmp(key, "FileRotate") == 0) - m_logFileRotate = ::atoi(value) == 1; + } else if (section == SECTION::MQTT) { + if (::strcmp(key, "Host") == 0) + m_mqttHost = value; + else if (::strcmp(key, "Port") == 0) + m_mqttPort = (unsigned short)::atoi(value); + else if (::strcmp(key, "Keepalive") == 0) + m_mqttKeepalive = (unsigned int)::atoi(value); + else if (::strcmp(key, "Name") == 0) + m_mqttName = value; + else if (::strcmp(key, "Auth") == 0) + m_mqttAuthEnabled = ::atoi(value) == 1; + else if (::strcmp(key, "Username") == 0) + m_mqttUsername = value; + else if (::strcmp(key, "Password") == 0) + m_mqttPassword = value; } else if (section == SECTION::CWID) { if (::strcmp(key, "Enable") == 0) m_cwIdEnabled = ::atoi(value) == 1; @@ -493,16 +567,20 @@ bool CConf::read() value[i] = ::toupper(value[i]); m_cwIdCallsign = value; } +#if defined(USE_DMR) || defined(USE_P25) } else if (section == SECTION::DMRID_LOOKUP) { if (::strcmp(key, "File") == 0) m_dmrIdLookupFile = value; else if (::strcmp(key, "Time") == 0) m_dmrIdLookupTime = (unsigned int)::atoi(value); +#endif +#if defined(USE_NXDN) } else if (section == SECTION::NXDNID_LOOKUP) { if (::strcmp(key, "File") == 0) m_nxdnIdLookupFile = value; else if (::strcmp(key, "Time") == 0) m_nxdnIdLookupTime = (unsigned int)::atoi(value); +#endif } else if (section == SECTION::MODEM) { if (::strcmp(key, "Protocol") == 0) m_modemProtocol = value; @@ -530,8 +608,10 @@ bool CConf::read() m_modemPTTInvert = ::atoi(value) == 1; else if (::strcmp(key, "TXDelay") == 0) m_modemTXDelay = (unsigned int)::atoi(value); +#if defined(USE_DMR) else if (::strcmp(key, "DMRDelay") == 0) m_modemDMRDelay = (unsigned int)::atoi(value); +#endif else if (::strcmp(key, "RXOffset") == 0) m_modemRXOffset = ::atoi(value); else if (::strcmp(key, "TXOffset") == 0) @@ -548,20 +628,34 @@ bool CConf::read() m_modemFMTXLevel = m_modemCWIdTXLevel = m_modemDStarTXLevel = m_modemDMRTXLevel = m_modemYSFTXLevel = m_modemP25TXLevel = m_modemNXDNTXLevel = m_modemPOCSAGTXLevel = float(::atof(value)); else if (::strcmp(key, "CWIdTXLevel") == 0) m_modemCWIdTXLevel = float(::atof(value)); +#if defined(USE_DSTAR) else if (::strcmp(key, "D-StarTXLevel") == 0) m_modemDStarTXLevel = float(::atof(value)); +#endif +#if defined(USE_DMR) else if (::strcmp(key, "DMRTXLevel") == 0) m_modemDMRTXLevel = float(::atof(value)); +#endif +#if defined(USE_YSF) else if (::strcmp(key, "YSFTXLevel") == 0) m_modemYSFTXLevel = float(::atof(value)); +#endif +#if defined(USE_P25) else if (::strcmp(key, "P25TXLevel") == 0) m_modemP25TXLevel = float(::atof(value)); +#endif +#if defined(USE_NXDN) else if (::strcmp(key, "NXDNTXLevel") == 0) m_modemNXDNTXLevel = float(::atof(value)); +#endif +#if defined(USE_POCSAG) else if (::strcmp(key, "POCSAGTXLevel") == 0) m_modemPOCSAGTXLevel = float(::atof(value)); +#endif +#if defined(USE_FM) else if (::strcmp(key, "FMTXLevel") == 0) m_modemFMTXLevel = float(::atof(value)); +#endif else if (::strcmp(key, "RSSIMappingFile") == 0) m_modemRSSIMappingFile = value; else if (::strcmp(key, "UseCOSAsLockout") == 0) @@ -581,6 +675,7 @@ bool CConf::read() m_transparentLocalPort = (unsigned short)::atoi(value); else if (::strcmp(key, "SendFrameType") == 0) m_transparentSendFrameType = (unsigned int)::atoi(value); +#if defined(USE_DSTAR) } else if (section == SECTION::DSTAR) { if (::strcmp(key, "Enable") == 0) m_dstarEnabled = ::atoi(value) == 1; @@ -630,6 +725,9 @@ bool CConf::read() else if (::strcmp(key, "ModeHang") == 0) m_dstarModeHang = (unsigned int)::atoi(value); } else if (section == SECTION::DMR) { +#endif +#if defined(USE_DMR) + } else if (section == SECTION::DMR) { if (::strcmp(key, "Enable") == 0) m_dmrEnabled = ::atoi(value) == 1; else if (::strcmp(key, "Beacons") == 0) @@ -715,6 +813,8 @@ bool CConf::read() } } else if (::strcmp(key, "Protect") == 0) m_dmrProtect = ::atoi(value) == 1; +#endif +#if defined(USE_YSF) } else if (section == SECTION::FUSION) { if (::strcmp(key, "Enable") == 0) m_fusionEnabled = ::atoi(value) == 1; @@ -728,6 +828,8 @@ bool CConf::read() m_fusionTXHang = (unsigned int)::atoi(value); else if (::strcmp(key, "ModeHang") == 0) m_fusionModeHang = (unsigned int)::atoi(value); +#endif +#if defined(USE_P25) } else if (section == SECTION::P25) { if (::strcmp(key, "Enable") == 0) m_p25Enabled = ::atoi(value) == 1; @@ -745,6 +847,8 @@ bool CConf::read() m_p25TXHang = (unsigned int)::atoi(value); else if (::strcmp(key, "ModeHang") == 0) m_p25ModeHang = (unsigned int)::atoi(value); +#endif +#if defined(USE_NXDN) } else if (section == SECTION::NXDN) { if (::strcmp(key, "Enable") == 0) m_nxdnEnabled = ::atoi(value) == 1; @@ -760,11 +864,15 @@ bool CConf::read() m_nxdnTXHang = (unsigned int)::atoi(value); else if (::strcmp(key, "ModeHang") == 0) m_nxdnModeHang = (unsigned int)::atoi(value); +#endif +#if defined(USE_POCSAG) } 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); +#endif +#if defined(USE_FM) } else if (section == SECTION::FM) { if (::strcmp(key, "Enable") == 0) m_fmEnabled = ::atoi(value) == 1; @@ -851,6 +959,8 @@ bool CConf::read() m_fmExtAudioBoost = (unsigned int)::atoi(value); else if (::strcmp(key, "ModeHang") == 0) m_fmModeHang = (unsigned int)::atoi(value); +#endif +#if defined(USE_DSTAR) } else if (section == SECTION::DSTAR_NETWORK) { if (::strcmp(key, "Enable") == 0) m_dstarNetworkEnabled = ::atoi(value) == 1; @@ -866,23 +976,19 @@ bool CConf::read() m_dstarNetworkModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) m_dstarNetworkDebug = ::atoi(value) == 1; +#endif +#if defined(USE_DMR) } else if (section == SECTION::DMR_NETWORK) { if (::strcmp(key, "Enable") == 0) m_dmrNetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Type") == 0) - m_dmrNetworkType = value; - else if (::strcmp(key, "RemoteAddress") == 0) - m_dmrNetworkRemoteAddress = value; - else if (::strcmp(key, "RemotePort") == 0) - m_dmrNetworkRemotePort = (unsigned short)::atoi(value); + else if (::strcmp(key, "GatewayAddress") == 0) + m_dmrNetworkGatewayAddress = value; + else if (::strcmp(key, "GatewayPort") == 0) + m_dmrNetworkGatewayPort = (unsigned short)::atoi(value); else if (::strcmp(key, "LocalAddress") == 0) m_dmrNetworkLocalAddress = value; else if (::strcmp(key, "LocalPort") == 0) m_dmrNetworkLocalPort = (unsigned short)::atoi(value); - else if (::strcmp(key, "Password") == 0) - m_dmrNetworkPassword = value; - else if (::strcmp(key, "Options") == 0) - m_dmrNetworkOptions = value; else if (::strcmp(key, "Debug") == 0) m_dmrNetworkDebug = ::atoi(value) == 1; else if (::strcmp(key, "Jitter") == 0) @@ -893,6 +999,8 @@ bool CConf::read() m_dmrNetworkSlot2 = ::atoi(value) == 1; else if (::strcmp(key, "ModeHang") == 0) m_dmrNetworkModeHang = (unsigned int)::atoi(value); +#endif +#if defined(USE_YSF) } else if (section == SECTION::FUSION_NETWORK) { if (::strcmp(key, "Enable") == 0) m_fusionNetworkEnabled = ::atoi(value) == 1; @@ -908,6 +1016,8 @@ bool CConf::read() m_fusionNetworkModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) m_fusionNetworkDebug = ::atoi(value) == 1; +#endif +#if defined(USE_P25) } else if (section == SECTION::P25_NETWORK) { if (::strcmp(key, "Enable") == 0) m_p25NetworkEnabled = ::atoi(value) == 1; @@ -923,6 +1033,8 @@ bool CConf::read() m_p25NetworkModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) m_p25NetworkDebug = ::atoi(value) == 1; +#endif +#if defined(USE_NXDN) } else if (section == SECTION::NXDN_NETWORK) { if (::strcmp(key, "Enable") == 0) m_nxdnNetworkEnabled = ::atoi(value) == 1; @@ -940,6 +1052,8 @@ bool CConf::read() m_nxdnNetworkModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) m_nxdnNetworkDebug = ::atoi(value) == 1; +#endif +#if defined(USE_POCSAG) } else if (section == SECTION::POCSAG_NETWORK) { if (::strcmp(key, "Enable") == 0) m_pocsagNetworkEnabled = ::atoi(value) == 1; @@ -955,15 +1069,11 @@ bool CConf::read() m_pocsagNetworkModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) m_pocsagNetworkDebug = ::atoi(value) == 1; +#endif +#if defined(USE_FM) } else if (section == SECTION::FM_NETWORK) { if (::strcmp(key, "Enable") == 0) m_fmNetworkEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Protocol") == 0) - m_fmNetworkProtocol = value; - else if (::strcmp(key, "SampleRate") == 0) - m_fmNetworkSampleRate = (unsigned int)::atoi(value); - else if (::strcmp(key, "SquelchFile") == 0) - m_fmNetworkSquelchFile = value; else if (::strcmp(key, "LocalAddress") == 0) m_fmLocalAddress = value; else if (::strcmp(key, "LocalPort") == 0) @@ -984,85 +1094,7 @@ bool CConf::read() m_fmNetworkModeHang = (unsigned int)::atoi(value); else if (::strcmp(key, "Debug") == 0) m_fmNetworkDebug = ::atoi(value) == 1; - } else if (section == SECTION::TFTSERIAL_DISPLAY) { - if (::strcmp(key, "Port") == 0) - m_tftSerialPort = value; - else if (::strcmp(key, "Brightness") == 0) - m_tftSerialBrightness = (unsigned int)::atoi(value); - else if (::strcmp(key, "ScreenLayout") == 0) - m_tftSerialScreenLayout = (unsigned int)::atoi(value); - } else if (section == SECTION::HD44780_DISPLAY) { - if (::strcmp(key, "Rows") == 0) - m_hd44780Rows = (unsigned int)::atoi(value); - else if (::strcmp(key, "Columns") == 0) - m_hd44780Columns = (unsigned int)::atoi(value); - else if (::strcmp(key, "I2CAddress") == 0) - m_hd44780i2cAddress = (unsigned int)::strtoul(value, nullptr, 16); - else if (::strcmp(key, "PWM") == 0) - m_hd44780PWM = ::atoi(value) == 1; - else if (::strcmp(key, "PWMPin") == 0) - m_hd44780PWMPin = (unsigned int)::atoi(value); - else if (::strcmp(key, "PWMBright") == 0) - m_hd44780PWMBright = (unsigned int)::atoi(value); - else if (::strcmp(key, "PWMDim") == 0) - m_hd44780PWMDim = (unsigned int)::atoi(value); - else if (::strcmp(key, "DisplayClock") == 0) - m_hd44780DisplayClock = ::atoi(value) == 1; - else if (::strcmp(key, "UTC") == 0) - m_hd44780UTC = ::atoi(value) == 1; - else if (::strcmp(key, "Pins") == 0) { - char* p = ::strtok(value, ",\r\n"); - while (p != nullptr) { - unsigned int pin = (unsigned int)::atoi(p); - m_hd44780Pins.push_back(pin); - p = ::strtok(nullptr, ",\r\n"); - } - } - } else if (section == SECTION::NEXTION_DISPLAY) { - if (::strcmp(key, "Port") == 0) - m_nextionPort = value; - else if (::strcmp(key, "Brightness") == 0) - m_nextionIdleBrightness = m_nextionBrightness = (unsigned int)::atoi(value); - else if (::strcmp(key, "DisplayClock") == 0) - m_nextionDisplayClock = ::atoi(value) == 1; - else if (::strcmp(key, "UTC") == 0) - m_nextionUTC = ::atoi(value) == 1; - else if (::strcmp(key, "IdleBrightness") == 0) - m_nextionIdleBrightness = (unsigned int)::atoi(value); - else if (::strcmp(key, "ScreenLayout") == 0) - m_nextionScreenLayout = (unsigned int)::strtoul(value, nullptr, 0); - else if (::strcmp(key, "DisplayTempInFahrenheit") == 0) - m_nextionTempInFahrenheit = ::atoi(value) == 1; - else if (::strcmp(key, "NextionOutput") == 0) - m_nextionOutput = ::atoi(value) == 1; - else if (::strcmp(key, "NextionUDPPort") == 0) - m_nextionUDPPort = (unsigned short)::atoi(value); - } else if (section == SECTION::OLED_DISPLAY) { - if (::strcmp(key, "Type") == 0) - m_oledType = (unsigned char)::atoi(value); - else if (::strcmp(key, "Brightness") == 0) - m_oledBrightness = (unsigned char)::atoi(value); - else if (::strcmp(key, "Invert") == 0) - m_oledInvert = ::atoi(value) == 1; - else if (::strcmp(key, "Scroll") == 0) - m_oledScroll = ::atoi(value) == 1; - else if (::strcmp(key, "Rotate") == 0) - m_oledRotate = ::atoi(value) == 1; - else if (::strcmp(key, "LogoScreensaver") == 0) - m_oledLogoScreensaver = ::atoi(value) == 1; - } else if (section == SECTION::LCDPROC_DISPLAY) { - if (::strcmp(key, "Address") == 0) - m_lcdprocAddress = value; - else if (::strcmp(key, "Port") == 0) - m_lcdprocPort = (unsigned short)::atoi(value); - else if (::strcmp(key, "LocalPort") == 0) - m_lcdprocLocalPort = (unsigned short)::atoi(value); - else if (::strcmp(key, "DisplayClock") == 0) - m_lcdprocDisplayClock = ::atoi(value) == 1; - else if (::strcmp(key, "UTC") == 0) - m_lcdprocUTC = ::atoi(value) == 1; - else if (::strcmp(key, "DimOnIdle") == 0) - m_lcdprocDimOnIdle = ::atoi(value) == 1; +#endif } else if (section == SECTION::LOCK_FILE) { if (::strcmp(key, "Enable") == 0) m_lockFileEnabled = ::atoi(value) == 1; @@ -1071,8 +1103,6 @@ bool CConf::read() } else if (section == SECTION::REMOTE_CONTROL) { if (::strcmp(key, "Enable") == 0) m_remoteControlEnabled = ::atoi(value) == 1; - else if (::strcmp(key, "Port") == 0) - m_remoteControlPort = (unsigned short)::atoi(value); } } @@ -1101,11 +1131,6 @@ bool CConf::getDuplex() const return m_duplex; } -std::string CConf::getDisplay() const -{ - return m_display; -} - bool CConf::getDaemon() const { return m_daemon; @@ -1156,29 +1181,49 @@ std::string CConf::getURL() const return m_url; } +unsigned int CConf::getLogMQTTLevel() const +{ + return m_logMQTTLevel; +} + unsigned int CConf::getLogDisplayLevel() const { return m_logDisplayLevel; } -unsigned int CConf::getLogFileLevel() const +std::string CConf::getMQTTHost() const { - return m_logFileLevel; + return m_mqttHost; } -std::string CConf::getLogFilePath() const +unsigned short CConf::getMQTTPort() const { - return m_logFilePath; + return m_mqttPort; } -std::string CConf::getLogFileRoot() const +unsigned int CConf::getMQTTKeepalive() const { - return m_logFileRoot; + return m_mqttKeepalive; } -bool CConf::getLogFileRotate() const +std::string CConf::getMQTTName() const { - return m_logFileRotate; + return m_mqttName; +} + +bool CConf::getMQTTAuthEnabled() const +{ + return m_mqttAuthEnabled; +} + +std::string CConf::getMQTTUsername() const +{ + return m_mqttUsername; +} + +std::string CConf::getMQTTPassword() const +{ + return m_mqttPassword; } bool CConf::getCWIdEnabled() const @@ -1196,6 +1241,7 @@ std::string CConf::getCWIdCallsign() const return m_cwIdCallsign; } +#if defined(USE_DMR) || defined(USE_P25) std::string CConf::getDMRIdLookupFile() const { return m_dmrIdLookupFile; @@ -1205,7 +1251,9 @@ unsigned int CConf::getDMRIdLookupTime() const { return m_dmrIdLookupTime; } +#endif +#if defined(USE_NXDN) std::string CConf::getNXDNIdLookupFile() const { return m_nxdnIdLookupFile; @@ -1215,6 +1263,7 @@ unsigned int CConf::getNXDNIdLookupTime() const { return m_nxdnIdLookupTime; } +#endif std::string CConf::getModemProtocol() const { @@ -1281,10 +1330,12 @@ unsigned int CConf::getModemTXDelay() const return m_modemTXDelay; } +#if defined(USE_DMR) unsigned int CConf::getModemDMRDelay() const { return m_modemDMRDelay; } +#endif int CConf::getModemRXOffset() const { @@ -1321,40 +1372,54 @@ float CConf::getModemCWIdTXLevel() const return m_modemCWIdTXLevel; } +#if defined(USE_DSTAR) float CConf::getModemDStarTXLevel() const { return m_modemDStarTXLevel; } +#endif +#if defined(USE_DMR) float CConf::getModemDMRTXLevel() const { return m_modemDMRTXLevel; } +#endif +#if defined(USE_YSF) float CConf::getModemYSFTXLevel() const { return m_modemYSFTXLevel; } +#endif +#if defined(USE_P25) float CConf::getModemP25TXLevel() const { return m_modemP25TXLevel; } +#endif +#if defined(USE_NXDN) float CConf::getModemNXDNTXLevel() const { return m_modemNXDNTXLevel; } +#endif +#if defined(USE_POCSAG) float CConf::getModemPOCSAGTXLevel() const { return m_modemPOCSAGTXLevel; } +#endif +#if defined(USE_FM) float CConf::getModemFMTXLevel() const { return m_modemFMTXLevel; } +#endif std::string CConf::getModemRSSIMappingFile () const { @@ -1401,6 +1466,7 @@ unsigned int CConf::getTransparentSendFrameType() const return m_transparentSendFrameType; } +#if defined(USE_DSTAR) bool CConf::getDStarEnabled() const { return m_dstarEnabled; @@ -1455,7 +1521,9 @@ unsigned int CConf::getDStarModeHang() const { return m_dstarModeHang; } +#endif +#if defined(USE_DMR) bool CConf::getDMREnabled() const { return m_dmrEnabled; @@ -1550,7 +1618,9 @@ bool CConf::getDMRProtect() const { return m_dmrProtect; } +#endif +#if defined(USE_YSF) bool CConf::getFusionEnabled() const { return m_fusionEnabled; @@ -1580,7 +1650,9 @@ unsigned int CConf::getFusionModeHang() const { return m_fusionModeHang; } +#endif +#if defined(USE_P25) bool CConf::getP25Enabled() const { return m_p25Enabled; @@ -1620,7 +1692,9 @@ unsigned int CConf::getP25ModeHang() const { return m_p25ModeHang; } +#endif +#if defined(USE_NXDN) bool CConf::getNXDNEnabled() const { return m_nxdnEnabled; @@ -1655,7 +1729,9 @@ unsigned int CConf::getNXDNModeHang() const { return m_nxdnModeHang; } +#endif +#if defined(USE_POCSAG) bool CConf::getPOCSAGEnabled() const { return m_pocsagEnabled; @@ -1665,7 +1741,9 @@ unsigned int CConf::getPOCSAGFrequency() const { return m_pocsagFrequency; } +#endif +#if defined(USE_FM) bool CConf::getFMEnabled() const { return m_fmEnabled; @@ -1845,7 +1923,9 @@ unsigned int CConf::getFMModeHang() const { return m_fmModeHang; } +#endif +#if defined(USE_DSTAR) bool CConf::getDStarNetworkEnabled() const { return m_dstarNetworkEnabled; @@ -1880,25 +1960,22 @@ bool CConf::getDStarNetworkDebug() const { return m_dstarNetworkDebug; } +#endif +#if defined(USE_DMR) bool CConf::getDMRNetworkEnabled() const { return m_dmrNetworkEnabled; } -std::string CConf::getDMRNetworkType() const +std::string CConf::getDMRNetworkGatewayAddress() const { - return m_dmrNetworkType; + return m_dmrNetworkGatewayAddress; } -std::string CConf::getDMRNetworkRemoteAddress() const +unsigned short CConf::getDMRNetworkGatewayPort() const { - return m_dmrNetworkRemoteAddress; -} - -unsigned short CConf::getDMRNetworkRemotePort() const -{ - return m_dmrNetworkRemotePort; + return m_dmrNetworkGatewayPort; } std::string CConf::getDMRNetworkLocalAddress() const @@ -1911,16 +1988,6 @@ unsigned short CConf::getDMRNetworkLocalPort() const return m_dmrNetworkLocalPort; } -std::string CConf::getDMRNetworkPassword() const -{ - return m_dmrNetworkPassword; -} - -std::string CConf::getDMRNetworkOptions() const -{ - return m_dmrNetworkOptions; -} - unsigned int CConf::getDMRNetworkModeHang() const { return m_dmrNetworkModeHang; @@ -1945,7 +2012,9 @@ bool CConf::getDMRNetworkSlot2() const { return m_dmrNetworkSlot2; } +#endif +#if defined(USE_YSF) bool CConf::getFusionNetworkEnabled() const { return m_fusionNetworkEnabled; @@ -1980,7 +2049,9 @@ bool CConf::getFusionNetworkDebug() const { return m_fusionNetworkDebug; } +#endif +#if defined(USE_P25) bool CConf::getP25NetworkEnabled() const { return m_p25NetworkEnabled; @@ -2015,7 +2086,9 @@ bool CConf::getP25NetworkDebug() const { return m_p25NetworkDebug; } +#endif +#if defined(USE_NXDN) bool CConf::getNXDNNetworkEnabled() const { return m_nxdnNetworkEnabled; @@ -2055,7 +2128,9 @@ bool CConf::getNXDNNetworkDebug() const { return m_nxdnNetworkDebug; } +#endif +#if defined(USE_POCSAG) bool CConf::getPOCSAGNetworkEnabled() const { return m_pocsagNetworkEnabled; @@ -2090,27 +2165,14 @@ bool CConf::getPOCSAGNetworkDebug() const { return m_pocsagNetworkDebug; } +#endif +#if defined(USE_FM) bool CConf::getFMNetworkEnabled() const { return m_fmNetworkEnabled; } -std::string CConf::getFMNetworkProtocol() const -{ - return m_fmNetworkProtocol; -} - -unsigned int CConf::getFMNetworkSampleRate() const -{ - return m_fmNetworkSampleRate; -} - -std::string CConf::getFMNetworkSquelchFile() const -{ - return m_fmNetworkSquelchFile; -} - std::string CConf::getFMGatewayAddress() const { return m_fmGatewayAddress; @@ -2160,176 +2222,7 @@ bool CConf::getFMNetworkDebug() const { return m_fmNetworkDebug; } - -std::string CConf::getTFTSerialPort() const -{ - return m_tftSerialPort; -} - -unsigned int CConf::getTFTSerialBrightness() const -{ - return m_tftSerialBrightness; -} - -unsigned int CConf::getTFTSerialScreenLayout() const -{ - return m_tftSerialScreenLayout; -} - -unsigned int CConf::getHD44780Rows() const -{ - return m_hd44780Rows; -} - -unsigned int CConf::getHD44780Columns() const -{ - return m_hd44780Columns; -} - -std::vector CConf::getHD44780Pins() const -{ - return m_hd44780Pins; -} - -unsigned int CConf::getHD44780i2cAddress() const -{ - return m_hd44780i2cAddress; -} - -bool CConf::getHD44780PWM() const -{ - return m_hd44780PWM; -} - -unsigned int CConf::getHD44780PWMPin() const -{ - return m_hd44780PWMPin; -} - -unsigned int CConf::getHD44780PWMBright() const -{ - return m_hd44780PWMBright; -} - -unsigned int CConf::getHD44780PWMDim() const -{ - return m_hd44780PWMDim; -} - -bool CConf::getHD44780DisplayClock() const -{ - return m_hd44780DisplayClock; -} - -bool CConf::getHD44780UTC() const -{ - return m_hd44780UTC; -} - -std::string CConf::getNextionPort() const -{ - return m_nextionPort; -} - -unsigned int CConf::getNextionBrightness() const -{ - return m_nextionBrightness; -} - -bool CConf::getNextionDisplayClock() const -{ - return m_nextionDisplayClock; -} - -bool CConf::getNextionUTC() const -{ - return m_nextionUTC; -} - -unsigned int CConf::getNextionIdleBrightness() const -{ - return m_nextionIdleBrightness; -} - -unsigned int CConf::getNextionScreenLayout() const -{ - return m_nextionScreenLayout; -} - -bool CConf::getNextionTempInFahrenheit() const -{ - return m_nextionTempInFahrenheit; -} - -bool CConf::getNextionOutput() const -{ - return m_nextionOutput; -} - -unsigned short CConf::getNextionUDPPort() const -{ - return m_nextionUDPPort; -} - -unsigned char CConf::getOLEDType() const -{ - return m_oledType; -} - -unsigned char CConf::getOLEDBrightness() const -{ - return m_oledBrightness; -} - -bool CConf::getOLEDInvert() const -{ - return m_oledInvert; -} - -bool CConf::getOLEDScroll() const -{ - return m_oledScroll; -} - -bool CConf::getOLEDRotate() const -{ - return m_oledRotate; -} - -bool CConf::getOLEDLogoScreensaver() const -{ - return m_oledLogoScreensaver; -} - -std::string CConf::getLCDprocAddress() const -{ - return m_lcdprocAddress; -} - -unsigned short CConf::getLCDprocPort() const -{ - return m_lcdprocPort; -} - -unsigned short CConf::getLCDprocLocalPort() const -{ - return m_lcdprocLocalPort; -} - -bool CConf::getLCDprocDisplayClock() const -{ - return m_lcdprocDisplayClock; -} - -bool CConf::getLCDprocUTC() const -{ - return m_lcdprocUTC; -} - -bool CConf::getLCDprocDimOnIdle() const -{ - return m_lcdprocDimOnIdle; -} +#endif bool CConf::getLockFileEnabled() const { @@ -2346,12 +2239,3 @@ bool CConf::getRemoteControlEnabled() const return m_remoteControlEnabled; } -std::string CConf::getRemoteControlAddress() const -{ - return m_remoteControlAddress; -} - -unsigned short CConf::getRemoteControlPort() const -{ - return m_remoteControlPort; -} diff --git a/Conf.h b/Conf.h index e45c934..cccac5d 100644 --- a/Conf.h +++ b/Conf.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2023,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,609 +25,622 @@ class CConf { public: - CConf(const std::string& file); - ~CConf(); + CConf(const std::string& file); + ~CConf(); - bool read(); + bool read(); - // The General section - std::string getCallsign() const; - unsigned int getId() const; - unsigned int getTimeout() const; - bool getDuplex() const; - std::string getDisplay() const; - bool getDaemon() const; + // The General section + std::string getCallsign() const; + unsigned int getId() const; + unsigned int getTimeout() const; + bool getDuplex() const; + bool getDaemon() const; - // The Info section - unsigned int getRXFrequency() const; - unsigned int getTXFrequency() const; - unsigned int getPower() const; - float getLatitude() const; - float getLongitude() const; - int getHeight() const; - std::string getLocation() const; - std::string getDescription() const; - std::string getURL() const; + // The Info section + unsigned int getRXFrequency() const; + unsigned int getTXFrequency() const; + unsigned int getPower() const; + float getLatitude() const; + float getLongitude() const; + int getHeight() const; + std::string getLocation() const; + std::string getDescription() const; + std::string getURL() const; - // The Log section - unsigned int getLogDisplayLevel() const; - unsigned int getLogFileLevel() const; - std::string getLogFilePath() const; - std::string getLogFileRoot() const; - bool getLogFileRotate() const; + // The Log section + unsigned int getLogMQTTLevel() const; + unsigned int getLogDisplayLevel() const; - // The CW ID section - bool getCWIdEnabled() const; - unsigned int getCWIdTime() const; - std::string getCWIdCallsign() const; + // The MQTT section + std::string getMQTTHost() const; + unsigned short getMQTTPort() const; + unsigned int getMQTTKeepalive() const; + std::string getMQTTName() const; + bool getMQTTAuthEnabled() const; + std::string getMQTTUsername() const; + std::string getMQTTPassword() const; - // The DMR Id section - std::string getDMRIdLookupFile() const; - unsigned int getDMRIdLookupTime() const; + // The CW ID section + bool getCWIdEnabled() const; + unsigned int getCWIdTime() const; + std::string getCWIdCallsign() const; - // The NXDN Id section - std::string getNXDNIdLookupFile() const; - unsigned int getNXDNIdLookupTime() const; +#if defined(USE_DMR) || defined(USE_P25) + // The DMR Id section + std::string getDMRIdLookupFile() const; + unsigned int getDMRIdLookupTime() const; +#endif - // The Modem section - std::string getModemProtocol() const; - std::string getModemUARTPort() const; - unsigned int getModemUARTSpeed() const; - std::string getModemI2CPort() const; - unsigned int getModemI2CAddress() const; - std::string getModemModemAddress() const; - unsigned short getModemModemPort() const; - std::string getModemLocalAddress() const; - unsigned short getModemLocalPort() const; - bool getModemRXInvert() const; - bool getModemTXInvert() const; - bool getModemPTTInvert() const; - unsigned int getModemTXDelay() const; - unsigned int getModemDMRDelay() const; - int getModemTXOffset() const; - int getModemRXOffset() const; - int getModemRXDCOffset() const; - int getModemTXDCOffset() const; - float getModemRFLevel() const; - float getModemRXLevel() const; - float getModemCWIdTXLevel() const; - float getModemDStarTXLevel() const; - float getModemDMRTXLevel() const; - float getModemYSFTXLevel() const; - float getModemP25TXLevel() const; - float getModemNXDNTXLevel() const; - float getModemPOCSAGTXLevel() const; - float getModemFMTXLevel() const; - std::string getModemRSSIMappingFile() const; - bool getModemUseCOSAsLockout() const; - bool getModemTrace() const; - bool getModemDebug() const; +#if defined(USE_NXDN) + // The NXDN Id section + std::string getNXDNIdLookupFile() const; + unsigned int getNXDNIdLookupTime() const; +#endif - // The Transparent Data section - bool getTransparentEnabled() const; - std::string getTransparentRemoteAddress() const; - unsigned short getTransparentRemotePort() const; - unsigned short getTransparentLocalPort() const; - unsigned int getTransparentSendFrameType() const; + // The Modem section + std::string getModemProtocol() const; + std::string getModemUARTPort() const; + unsigned int getModemUARTSpeed() const; + std::string getModemI2CPort() const; + unsigned int getModemI2CAddress() const; + std::string getModemModemAddress() const; + unsigned short getModemModemPort() const; + std::string getModemLocalAddress() const; + unsigned short getModemLocalPort() const; + bool getModemRXInvert() const; + bool getModemTXInvert() const; + bool getModemPTTInvert() const; + unsigned int getModemTXDelay() const; +#if defined(USE_DMR) + unsigned int getModemDMRDelay() const; +#endif + int getModemTXOffset() const; + int getModemRXOffset() const; + int getModemRXDCOffset() const; + int getModemTXDCOffset() const; + float getModemRFLevel() const; + float getModemRXLevel() const; + float getModemCWIdTXLevel() const; +#if defined(USE_DSTAR) + float getModemDStarTXLevel() const; +#endif +#if defined(USE_DMR) + float getModemDMRTXLevel() const; +#endif +#if defined(USE_YSF) + float getModemYSFTXLevel() const; +#endif +#if defined(USE_P25) + float getModemP25TXLevel() const; +#endif +#if defined(USE_NXDN) + float getModemNXDNTXLevel() const; +#endif +#if defined(USE_POCSAG) + float getModemPOCSAGTXLevel() const; +#endif +#if defined(USE_FM) + float getModemFMTXLevel() const; +#endif + std::string getModemRSSIMappingFile() const; + bool getModemUseCOSAsLockout() const; + bool getModemTrace() const; + bool getModemDebug() const; - // The D-Star section - bool getDStarEnabled() const; - std::string getDStarModule() const; - bool getDStarSelfOnly() const; - std::vector getDStarBlackList() const; - std::vector getDStarWhiteList() const; - bool getDStarAckReply() const; - unsigned int getDStarAckTime() const; - DSTAR_ACK getDStarAckMessage() const; - bool getDStarErrorReply() const; - bool getDStarRemoteGateway() const; - unsigned int getDStarModeHang() const; + // The Transparent Data section + bool getTransparentEnabled() const; + std::string getTransparentRemoteAddress() const; + unsigned short getTransparentRemotePort() const; + unsigned short getTransparentLocalPort() const; + unsigned int getTransparentSendFrameType() const; - // The DMR section - bool getDMREnabled() const; - DMR_BEACONS getDMRBeacons() const; - unsigned int getDMRBeaconInterval() const; - unsigned int getDMRBeaconDuration() const; - unsigned int getDMRId() const; - unsigned int getDMRColorCode() const; - bool getDMREmbeddedLCOnly() const; - bool getDMRDumpTAData() const; - bool getDMRSelfOnly() const; - std::vector getDMRPrefixes() const; - std::vector getDMRBlackList() const; - std::vector getDMRWhiteList() const; - std::vector getDMRSlot1TGWhiteList() const; - std::vector getDMRSlot2TGWhiteList() const; - unsigned int getDMRCallHang() const; - unsigned int getDMRTXHang() const; - unsigned int getDMRModeHang() const; - DMR_OVCM getDMROVCM() const; - bool getDMRProtect() const; +#if defined(USE_DSTAR) + // The D-Star section + bool getDStarEnabled() const; + std::string getDStarModule() const; + bool getDStarSelfOnly() const; + std::vector getDStarBlackList() const; + std::vector getDStarWhiteList() const; + bool getDStarAckReply() const; + unsigned int getDStarAckTime() const; + DSTAR_ACK getDStarAckMessage() const; + bool getDStarErrorReply() const; + bool getDStarRemoteGateway() const; + unsigned int getDStarModeHang() const; +#endif - // The System Fusion section - bool getFusionEnabled() const; - bool getFusionLowDeviation() const; - bool getFusionRemoteGateway() const; - bool getFusionSelfOnly() const; - unsigned int getFusionTXHang() const; - unsigned int getFusionModeHang() const; +#if defined(USE_DMR) + // The DMR section + bool getDMREnabled() const; + DMR_BEACONS getDMRBeacons() const; + unsigned int getDMRBeaconInterval() const; + unsigned int getDMRBeaconDuration() const; + unsigned int getDMRId() const; + unsigned int getDMRColorCode() const; + bool getDMREmbeddedLCOnly() const; + bool getDMRDumpTAData() const; + bool getDMRSelfOnly() const; + std::vector getDMRPrefixes() const; + std::vector getDMRBlackList() const; + std::vector getDMRWhiteList() const; + std::vector getDMRSlot1TGWhiteList() const; + std::vector getDMRSlot2TGWhiteList() const; + unsigned int getDMRCallHang() const; + unsigned int getDMRTXHang() const; + unsigned int getDMRModeHang() const; + DMR_OVCM getDMROVCM() const; + bool getDMRProtect() const; +#endif - // The P25 section - bool getP25Enabled() const; - unsigned int getP25Id() const; - unsigned int getP25NAC() const; - bool getP25SelfOnly() const; - bool getP25OverrideUID() const; - bool getP25RemoteGateway() const; - unsigned int getP25TXHang() const; - unsigned int getP25ModeHang() const; +#if defined(USE_YSF) + // The System Fusion section + bool getFusionEnabled() const; + bool getFusionLowDeviation() const; + bool getFusionRemoteGateway() const; + bool getFusionSelfOnly() const; + unsigned int getFusionTXHang() const; + unsigned int getFusionModeHang() const; +#endif - // The NXDN section - bool getNXDNEnabled() const; - unsigned int getNXDNId() const; - unsigned int getNXDNRAN() const; - bool getNXDNSelfOnly() const; - bool getNXDNRemoteGateway() const; - unsigned int getNXDNTXHang() const; - unsigned int getNXDNModeHang() const; +#if defined(USE_P25) + // The P25 section + bool getP25Enabled() const; + unsigned int getP25Id() const; + unsigned int getP25NAC() const; + bool getP25SelfOnly() const; + bool getP25OverrideUID() const; + bool getP25RemoteGateway() const; + unsigned int getP25TXHang() const; + unsigned int getP25ModeHang() const; +#endif - // The POCSAG section - bool getPOCSAGEnabled() const; - unsigned int getPOCSAGFrequency() const; +#if defined(USE_NXDN) + // The NXDN section + bool getNXDNEnabled() const; + unsigned int getNXDNId() const; + unsigned int getNXDNRAN() const; + bool getNXDNSelfOnly() const; + bool getNXDNRemoteGateway() const; + unsigned int getNXDNTXHang() const; + unsigned int getNXDNModeHang() const; +#endif - // The FM Section - bool getFMEnabled() const; - std::string getFMCallsign() const; - unsigned int getFMCallsignSpeed() const; - unsigned int getFMCallsignFrequency() const; - unsigned int getFMCallsignTime() const; - unsigned int getFMCallsignHoldoff() const; - float getFMCallsignHighLevel() const; - float getFMCallsignLowLevel() const; - bool getFMCallsignAtStart() const; - bool getFMCallsignAtEnd() const; - bool getFMCallsignAtLatch() const; - std::string getFMRFAck() const; - std::string getFMExtAck() const; - unsigned int getFMAckSpeed() const; - unsigned int getFMAckFrequency() const; - unsigned int getFMAckMinTime() const; - unsigned int getFMAckDelay() const; - float getFMAckLevel() const; - unsigned int getFMTimeout() const; - float getFMTimeoutLevel() const; - float getFMCTCSSFrequency() const; - unsigned int getFMCTCSSHighThreshold() const; - unsigned int getFMCTCSSLowThreshold() const; - float getFMCTCSSLevel() const; - unsigned int getFMKerchunkTime() const; - unsigned int getFMHangTime() const; - unsigned int getFMAccessMode() const; - bool getFMLinkMode() const; - bool getFMCOSInvert() const; - bool getFMNoiseSquelch() const; - unsigned int getFMSquelchHighThreshold() const; - unsigned int getFMSquelchLowThreshold() const; - unsigned int getFMRFAudioBoost() const; - float getFMMaxDevLevel() const; - unsigned int getFMExtAudioBoost() const; - unsigned int getFMModeHang() const; +#if defined(USE_POCSAG) + // The POCSAG section + bool getPOCSAGEnabled() const; + unsigned int getPOCSAGFrequency() const; +#endif - // The D-Star Network section - bool getDStarNetworkEnabled() const; - std::string getDStarGatewayAddress() const; - unsigned short getDStarGatewayPort() const; - std::string getDStarLocalAddress() const; - unsigned short getDStarLocalPort() const; - unsigned int getDStarNetworkModeHang() const; - bool getDStarNetworkDebug() const; +#if defined(USE_FM) + // The FM Section + bool getFMEnabled() const; + std::string getFMCallsign() const; + unsigned int getFMCallsignSpeed() const; + unsigned int getFMCallsignFrequency() const; + unsigned int getFMCallsignTime() const; + unsigned int getFMCallsignHoldoff() const; + float getFMCallsignHighLevel() const; + float getFMCallsignLowLevel() const; + bool getFMCallsignAtStart() const; + bool getFMCallsignAtEnd() const; + bool getFMCallsignAtLatch() const; + std::string getFMRFAck() const; + std::string getFMExtAck() const; + unsigned int getFMAckSpeed() const; + unsigned int getFMAckFrequency() const; + unsigned int getFMAckMinTime() const; + unsigned int getFMAckDelay() const; + float getFMAckLevel() const; + unsigned int getFMTimeout() const; + float getFMTimeoutLevel() const; + float getFMCTCSSFrequency() const; + unsigned int getFMCTCSSHighThreshold() const; + unsigned int getFMCTCSSLowThreshold() const; + float getFMCTCSSLevel() const; + unsigned int getFMKerchunkTime() const; + unsigned int getFMHangTime() const; + unsigned int getFMAccessMode() const; + bool getFMLinkMode() const; + bool getFMCOSInvert() const; + bool getFMNoiseSquelch() const; + unsigned int getFMSquelchHighThreshold() const; + unsigned int getFMSquelchLowThreshold() const; + unsigned int getFMRFAudioBoost() const; + float getFMMaxDevLevel() const; + unsigned int getFMExtAudioBoost() const; + unsigned int getFMModeHang() const; +#endif - // The DMR Network section - bool getDMRNetworkEnabled() const; - std::string getDMRNetworkType() const; - std::string getDMRNetworkRemoteAddress() const; - unsigned short getDMRNetworkRemotePort() const; - std::string getDMRNetworkLocalAddress() const; - unsigned short getDMRNetworkLocalPort() const; - std::string getDMRNetworkPassword() const; - std::string getDMRNetworkOptions() const; - bool getDMRNetworkDebug() const; - unsigned int getDMRNetworkJitter() const; - bool getDMRNetworkSlot1() const; - bool getDMRNetworkSlot2() const; - unsigned int getDMRNetworkModeHang() const; +#if defined(USE_DSTAR) + // The D-Star Network section + bool getDStarNetworkEnabled() const; + std::string getDStarGatewayAddress() const; + unsigned short getDStarGatewayPort() const; + std::string getDStarLocalAddress() const; + unsigned short getDStarLocalPort() const; + unsigned int getDStarNetworkModeHang() const; + bool getDStarNetworkDebug() const; +#endif - // The System Fusion Network section - bool getFusionNetworkEnabled() const; - std::string getFusionNetworkLocalAddress() const; - unsigned short getFusionNetworkLocalPort() const; - std::string getFusionNetworkGatewayAddress() const; - unsigned short getFusionNetworkGatewayPort() const; - unsigned int getFusionNetworkModeHang() const; - bool getFusionNetworkDebug() const; +#if defined(USE_DMR) + // The DMR Network section + bool getDMRNetworkEnabled() const; + std::string getDMRNetworkGatewayAddress() const; + unsigned short getDMRNetworkGatewayPort() const; + std::string getDMRNetworkLocalAddress() const; + unsigned short getDMRNetworkLocalPort() const; + bool getDMRNetworkDebug() const; + unsigned int getDMRNetworkJitter() const; + bool getDMRNetworkSlot1() const; + bool getDMRNetworkSlot2() const; + unsigned int getDMRNetworkModeHang() const; +#endif - // The P25 Network section - bool getP25NetworkEnabled() const; - std::string getP25GatewayAddress() const; - unsigned short getP25GatewayPort() const; - std::string getP25LocalAddress() const; - unsigned short getP25LocalPort() const; - unsigned int getP25NetworkModeHang() const; - bool getP25NetworkDebug() const; +#if defined(USE_YSF) + // The System Fusion Network section + bool getFusionNetworkEnabled() const; + std::string getFusionNetworkLocalAddress() const; + unsigned short getFusionNetworkLocalPort() const; + std::string getFusionNetworkGatewayAddress() const; + unsigned short getFusionNetworkGatewayPort() const; + unsigned int getFusionNetworkModeHang() const; + bool getFusionNetworkDebug() const; +#endif - // The NXDN Network section - bool getNXDNNetworkEnabled() const; - std::string getNXDNNetworkProtocol() const; - std::string getNXDNGatewayAddress() const; - unsigned short getNXDNGatewayPort() const; - std::string getNXDNLocalAddress() const; - unsigned short getNXDNLocalPort() const; - unsigned int getNXDNNetworkModeHang() const; - bool getNXDNNetworkDebug() const; +#if defined(USE_P25) + // The P25 Network section + bool getP25NetworkEnabled() const; + std::string getP25GatewayAddress() const; + unsigned short getP25GatewayPort() const; + std::string getP25LocalAddress() const; + unsigned short getP25LocalPort() const; + unsigned int getP25NetworkModeHang() const; + bool getP25NetworkDebug() const; +#endif - // The POCSAG Network section - bool getPOCSAGNetworkEnabled() const; - std::string getPOCSAGGatewayAddress() const; - unsigned short getPOCSAGGatewayPort() const; - std::string getPOCSAGLocalAddress() const; - unsigned short getPOCSAGLocalPort() const; - unsigned int getPOCSAGNetworkModeHang() const; - bool getPOCSAGNetworkDebug() const; +#if defined(USE_NXDN) + // The NXDN Network section + bool getNXDNNetworkEnabled() const; + std::string getNXDNNetworkProtocol() const; + std::string getNXDNGatewayAddress() const; + unsigned short getNXDNGatewayPort() const; + std::string getNXDNLocalAddress() const; + unsigned short getNXDNLocalPort() const; + unsigned int getNXDNNetworkModeHang() const; + bool getNXDNNetworkDebug() const; +#endif - // The FM Network section - bool getFMNetworkEnabled() const; - std::string getFMNetworkProtocol() const; - unsigned int getFMNetworkSampleRate() const; - std::string getFMNetworkSquelchFile() const; - std::string getFMGatewayAddress() const; - unsigned short getFMGatewayPort() const; - std::string getFMLocalAddress() const; - unsigned short getFMLocalPort() const; - bool getFMPreEmphasis() const; - bool getFMDeEmphasis() const; - float getFMTXAudioGain() const; - float getFMRXAudioGain() const; - unsigned int getFMNetworkModeHang() const; - bool getFMNetworkDebug() const; +#if defined(USE_POCSAG) + // The POCSAG Network section + bool getPOCSAGNetworkEnabled() const; + std::string getPOCSAGGatewayAddress() const; + unsigned short getPOCSAGGatewayPort() const; + std::string getPOCSAGLocalAddress() const; + unsigned short getPOCSAGLocalPort() const; + unsigned int getPOCSAGNetworkModeHang() const; + bool getPOCSAGNetworkDebug() const; +#endif - // The TFTSERIAL section - std::string getTFTSerialPort() const; - unsigned int getTFTSerialBrightness() const; - unsigned int getTFTSerialScreenLayout() const; +#if defined(USE_FM) + // The FM Network section + bool getFMNetworkEnabled() const; + std::string getFMGatewayAddress() const; + unsigned short getFMGatewayPort() const; + std::string getFMLocalAddress() const; + unsigned short getFMLocalPort() const; + bool getFMPreEmphasis() const; + bool getFMDeEmphasis() const; + float getFMTXAudioGain() const; + float getFMRXAudioGain() const; + unsigned int getFMNetworkModeHang() const; + bool getFMNetworkDebug() const; +#endif - // The HD44780 section - unsigned int getHD44780Rows() const; - unsigned int getHD44780Columns() const; - std::vector getHD44780Pins() const; - unsigned int getHD44780i2cAddress() const; - bool getHD44780PWM() const; - unsigned int getHD44780PWMPin() const; - unsigned int getHD44780PWMBright() const; - unsigned int getHD44780PWMDim() const; - bool getHD44780DisplayClock() const; - bool getHD44780UTC() const; + // The Lock File section + bool getLockFileEnabled() const; + std::string getLockFileName() const; - // The Nextion section - std::string getNextionPort() const; - unsigned int getNextionBrightness() const; - bool getNextionDisplayClock() const; - bool getNextionUTC() const; - unsigned int getNextionIdleBrightness() const; - unsigned int getNextionScreenLayout() const; - bool getNextionTempInFahrenheit() const; - bool getNextionOutput() const; - unsigned short getNextionUDPPort() const; - - // The OLED section - unsigned char getOLEDType() const; - unsigned char getOLEDBrightness() const; - bool getOLEDInvert() const; - bool getOLEDScroll() const; - bool getOLEDRotate() const; - bool getOLEDLogoScreensaver() const; - - // The LCDproc section - std::string getLCDprocAddress() const; - unsigned short getLCDprocPort() const; - unsigned short getLCDprocLocalPort() const; - bool getLCDprocDisplayClock() const; - bool getLCDprocUTC() const; - bool getLCDprocDimOnIdle() const; - - // The Lock File section - bool getLockFileEnabled() const; - std::string getLockFileName() const; - - // The Remote Control section - bool getRemoteControlEnabled() const; - std::string getRemoteControlAddress() const; - unsigned short getRemoteControlPort() const; + // The Remote Control section + bool getRemoteControlEnabled() const; private: - std::string m_file; - std::string m_callsign; - unsigned int m_id; - unsigned int m_timeout; - bool m_duplex; - std::string m_display; - bool m_daemon; + std::string m_file; + std::string m_callsign; + unsigned int m_id; + unsigned int m_timeout; + bool m_duplex; + bool m_daemon; - unsigned int m_rxFrequency; - unsigned int m_txFrequency; - unsigned int m_power; - float m_latitude; - float m_longitude; - int m_height; - std::string m_location; - std::string m_description; - std::string m_url; + unsigned int m_rxFrequency; + unsigned int m_txFrequency; + unsigned int m_power; + float m_latitude; + float m_longitude; + int m_height; + std::string m_location; + std::string m_description; + std::string m_url; - unsigned int m_logDisplayLevel; - unsigned int m_logFileLevel; - std::string m_logFilePath; - std::string m_logFileRoot; - bool m_logFileRotate; + unsigned int m_logMQTTLevel; + unsigned int m_logDisplayLevel; - bool m_cwIdEnabled; - unsigned int m_cwIdTime; - std::string m_cwIdCallsign; + std::string m_mqttHost; + unsigned short m_mqttPort; + unsigned int m_mqttKeepalive; + std::string m_mqttName; + bool m_mqttAuthEnabled; + std::string m_mqttUsername; + std::string m_mqttPassword; - std::string m_dmrIdLookupFile; - unsigned int m_dmrIdLookupTime; + bool m_cwIdEnabled; + unsigned int m_cwIdTime; + std::string m_cwIdCallsign; - std::string m_nxdnIdLookupFile; - unsigned int m_nxdnIdLookupTime; +#if defined(USE_DMR) || defined(USE_P25) + std::string m_dmrIdLookupFile; + unsigned int m_dmrIdLookupTime; +#endif - std::string m_modemProtocol; - std::string m_modemUARTPort; - unsigned int m_modemUARTSpeed; - std::string m_modemI2CPort; - unsigned int m_modemI2CAddress; - std::string m_modemModemAddress; - unsigned short m_modemModemPort; - std::string m_modemLocalAddress; - unsigned short m_modemLocalPort; - bool m_modemRXInvert; - bool m_modemTXInvert; - bool m_modemPTTInvert; - unsigned int m_modemTXDelay; - unsigned int m_modemDMRDelay; - int m_modemTXOffset; - int m_modemRXOffset; - int m_modemRXDCOffset; - int m_modemTXDCOffset; - float m_modemRFLevel; - float m_modemRXLevel; - float m_modemCWIdTXLevel; - float m_modemDStarTXLevel; - float m_modemDMRTXLevel; - float m_modemYSFTXLevel; - float m_modemP25TXLevel; - float m_modemNXDNTXLevel; - float m_modemPOCSAGTXLevel; - float m_modemFMTXLevel; - std::string m_modemRSSIMappingFile; - bool m_modemUseCOSAsLockout; - bool m_modemTrace; - bool m_modemDebug; +#if defined(USE_NXDN) + std::string m_nxdnIdLookupFile; + unsigned int m_nxdnIdLookupTime; +#endif - bool m_transparentEnabled; - std::string m_transparentRemoteAddress; - unsigned short m_transparentRemotePort; - unsigned short m_transparentLocalPort; - unsigned int m_transparentSendFrameType; + std::string m_modemProtocol; + std::string m_modemUARTPort; + unsigned int m_modemUARTSpeed; + std::string m_modemI2CPort; + unsigned int m_modemI2CAddress; + std::string m_modemModemAddress; + unsigned short m_modemModemPort; + std::string m_modemLocalAddress; + unsigned short m_modemLocalPort; + bool m_modemRXInvert; + bool m_modemTXInvert; + bool m_modemPTTInvert; + unsigned int m_modemTXDelay; +#if defined(USE_DMR) + unsigned int m_modemDMRDelay; +#endif + int m_modemTXOffset; + int m_modemRXOffset; + int m_modemRXDCOffset; + int m_modemTXDCOffset; + float m_modemRFLevel; + float m_modemRXLevel; + float m_modemCWIdTXLevel; + float m_modemDStarTXLevel; + float m_modemDMRTXLevel; + float m_modemYSFTXLevel; + float m_modemP25TXLevel; + float m_modemNXDNTXLevel; + float m_modemPOCSAGTXLevel; + float m_modemFMTXLevel; + std::string m_modemRSSIMappingFile; + bool m_modemUseCOSAsLockout; + bool m_modemTrace; + bool m_modemDebug; - bool m_dstarEnabled; - std::string m_dstarModule; - bool m_dstarSelfOnly; - std::vector m_dstarBlackList; - std::vector m_dstarWhiteList; - bool m_dstarAckReply; - unsigned int m_dstarAckTime; - DSTAR_ACK m_dstarAckMessage; - bool m_dstarErrorReply; - bool m_dstarRemoteGateway; - unsigned int m_dstarModeHang; + bool m_transparentEnabled; + std::string m_transparentRemoteAddress; + unsigned short m_transparentRemotePort; + unsigned short m_transparentLocalPort; + unsigned int m_transparentSendFrameType; - bool m_dmrEnabled; - DMR_BEACONS m_dmrBeacons; - unsigned int m_dmrBeaconInterval; - unsigned int m_dmrBeaconDuration; - unsigned int m_dmrId; - unsigned int m_dmrColorCode; - bool m_dmrSelfOnly; - bool m_dmrEmbeddedLCOnly; - bool m_dmrDumpTAData; - std::vector m_dmrPrefixes; - std::vector m_dmrBlackList; - std::vector m_dmrWhiteList; - std::vector m_dmrSlot1TGWhiteList; - std::vector m_dmrSlot2TGWhiteList; - unsigned int m_dmrCallHang; - unsigned int m_dmrTXHang; - unsigned int m_dmrModeHang; - DMR_OVCM m_dmrOVCM; - bool m_dmrProtect; +#if defined(USE_DSTAR) + bool m_dstarEnabled; + std::string m_dstarModule; + bool m_dstarSelfOnly; + std::vector m_dstarBlackList; + std::vector m_dstarWhiteList; + bool m_dstarAckReply; + unsigned int m_dstarAckTime; + DSTAR_ACK m_dstarAckMessage; + bool m_dstarErrorReply; + bool m_dstarRemoteGateway; +#endif + unsigned int m_dstarModeHang; - bool m_fusionEnabled; - bool m_fusionLowDeviation; - bool m_fusionRemoteGateway; - bool m_fusionSelfOnly; - unsigned int m_fusionTXHang; - unsigned int m_fusionModeHang; +#if defined(USE_DMR) + bool m_dmrEnabled; + DMR_BEACONS m_dmrBeacons; + unsigned int m_dmrBeaconInterval; + unsigned int m_dmrBeaconDuration; +#endif + unsigned int m_dmrId; +#if defined(USE_DMR) + unsigned int m_dmrColorCode; + bool m_dmrSelfOnly; + bool m_dmrEmbeddedLCOnly; + bool m_dmrDumpTAData; + std::vector m_dmrPrefixes; + std::vector m_dmrBlackList; + std::vector m_dmrWhiteList; + std::vector m_dmrSlot1TGWhiteList; + std::vector m_dmrSlot2TGWhiteList; + unsigned int m_dmrCallHang; + unsigned int m_dmrTXHang; +#endif + unsigned int m_dmrModeHang; +#if defined(USE_DMR) + DMR_OVCM m_dmrOVCM; + bool m_dmrProtect; +#endif - bool m_p25Enabled; - unsigned int m_p25Id; - unsigned int m_p25NAC; - bool m_p25SelfOnly; - bool m_p25OverrideUID; - bool m_p25RemoteGateway; - unsigned int m_p25TXHang; - unsigned int m_p25ModeHang; +#if defined(USE_YSF) + bool m_fusionEnabled; + bool m_fusionLowDeviation; + bool m_fusionRemoteGateway; + bool m_fusionSelfOnly; + unsigned int m_fusionTXHang; +#endif + unsigned int m_fusionModeHang; - bool m_nxdnEnabled; - unsigned int m_nxdnId; - unsigned int m_nxdnRAN; - bool m_nxdnSelfOnly; - bool m_nxdnRemoteGateway; - unsigned int m_nxdnTXHang; - unsigned int m_nxdnModeHang; +#if defined(USE_P25) + bool m_p25Enabled; +#endif + unsigned int m_p25Id; +#if defined(USE_P25) + unsigned int m_p25NAC; + bool m_p25SelfOnly; + bool m_p25OverrideUID; + bool m_p25RemoteGateway; + unsigned int m_p25TXHang; +#endif + unsigned int m_p25ModeHang; - bool m_pocsagEnabled; - unsigned int m_pocsagFrequency; +#if defined(USE_NXDN) + bool m_nxdnEnabled; + unsigned int m_nxdnId; + unsigned int m_nxdnRAN; + bool m_nxdnSelfOnly; + bool m_nxdnRemoteGateway; + unsigned int m_nxdnTXHang; +#endif + unsigned int m_nxdnModeHang; - bool m_fmEnabled; - std::string m_fmCallsign; - unsigned int m_fmCallsignSpeed; - unsigned int m_fmCallsignFrequency; - unsigned int m_fmCallsignTime; - unsigned int m_fmCallsignHoldoff; - float m_fmCallsignHighLevel; - float m_fmCallsignLowLevel; - bool m_fmCallsignAtStart; - bool m_fmCallsignAtEnd; - bool m_fmCallsignAtLatch; - std::string m_fmRFAck; - std::string m_fmExtAck; - unsigned int m_fmAckSpeed; - unsigned int m_fmAckFrequency; - unsigned int m_fmAckMinTime; - unsigned int m_fmAckDelay; - float m_fmAckLevel; - unsigned int m_fmTimeout; - float m_fmTimeoutLevel; - float m_fmCTCSSFrequency; - unsigned int m_fmCTCSSHighThreshold; - unsigned int m_fmCTCSSLowThreshold; - float m_fmCTCSSLevel; - unsigned int m_fmKerchunkTime; - unsigned int m_fmHangTime; - unsigned int m_fmAccessMode; - bool m_fmLinkMode; - bool m_fmCOSInvert; - bool m_fmNoiseSquelch; - unsigned int m_fmSquelchHighThreshold; - unsigned int m_fmSquelchLowThreshold; - unsigned int m_fmRFAudioBoost; - float m_fmMaxDevLevel; - unsigned int m_fmExtAudioBoost; - unsigned int m_fmModeHang; +#if defined(USE_POCSAG) + bool m_pocsagEnabled; +#endif + unsigned int m_pocsagFrequency; - bool m_dstarNetworkEnabled; - std::string m_dstarGatewayAddress; - unsigned short m_dstarGatewayPort; - std::string m_dstarLocalAddress; - unsigned short m_dstarLocalPort; - unsigned int m_dstarNetworkModeHang; - bool m_dstarNetworkDebug; +#if defined(USE_FM) + bool m_fmEnabled; +#endif + std::string m_fmCallsign; +#if defined(USE_FM) + unsigned int m_fmCallsignSpeed; + unsigned int m_fmCallsignFrequency; + unsigned int m_fmCallsignTime; + unsigned int m_fmCallsignHoldoff; + float m_fmCallsignHighLevel; + float m_fmCallsignLowLevel; + bool m_fmCallsignAtStart; + bool m_fmCallsignAtEnd; + bool m_fmCallsignAtLatch; + std::string m_fmRFAck; + std::string m_fmExtAck; + unsigned int m_fmAckSpeed; + unsigned int m_fmAckFrequency; + unsigned int m_fmAckMinTime; + unsigned int m_fmAckDelay; + float m_fmAckLevel; +#endif + unsigned int m_fmTimeout; +#if defined(USE_FM) + float m_fmTimeoutLevel; + float m_fmCTCSSFrequency; + unsigned int m_fmCTCSSHighThreshold; + unsigned int m_fmCTCSSLowThreshold; + float m_fmCTCSSLevel; + unsigned int m_fmKerchunkTime; + unsigned int m_fmHangTime; + unsigned int m_fmAccessMode; + bool m_fmLinkMode; + bool m_fmCOSInvert; + bool m_fmNoiseSquelch; + unsigned int m_fmSquelchHighThreshold; + unsigned int m_fmSquelchLowThreshold; + unsigned int m_fmRFAudioBoost; + float m_fmMaxDevLevel; + unsigned int m_fmExtAudioBoost; +#endif + unsigned int m_fmModeHang; - bool m_dmrNetworkEnabled; - std::string m_dmrNetworkType; - std::string m_dmrNetworkRemoteAddress; - unsigned short m_dmrNetworkRemotePort; - std::string m_dmrNetworkLocalAddress; - unsigned short m_dmrNetworkLocalPort; - std::string m_dmrNetworkPassword; - std::string m_dmrNetworkOptions; - bool m_dmrNetworkDebug; - unsigned int m_dmrNetworkJitter; - bool m_dmrNetworkSlot1; - bool m_dmrNetworkSlot2; - unsigned int m_dmrNetworkModeHang; +#if defined(USE_DSTAR) + bool m_dstarNetworkEnabled; + std::string m_dstarGatewayAddress; + unsigned short m_dstarGatewayPort; + std::string m_dstarLocalAddress; + unsigned short m_dstarLocalPort; +#endif + unsigned int m_dstarNetworkModeHang; +#if defined(USE_DSTAR) + bool m_dstarNetworkDebug; +#endif - bool m_fusionNetworkEnabled; - std::string m_fusionNetworkLocalAddress; - unsigned short m_fusionNetworkLocalPort; - std::string m_fusionNetworkGatewayAddress; - unsigned short m_fusionNetworkGatewayPort; - unsigned int m_fusionNetworkModeHang; - bool m_fusionNetworkDebug; +#if defined(USE_DMR) + bool m_dmrNetworkEnabled; + std::string m_dmrNetworkGatewayAddress; + unsigned short m_dmrNetworkGatewayPort; + std::string m_dmrNetworkLocalAddress; + unsigned short m_dmrNetworkLocalPort; + bool m_dmrNetworkDebug; + unsigned int m_dmrNetworkJitter; + bool m_dmrNetworkSlot1; + bool m_dmrNetworkSlot2; +#endif + unsigned int m_dmrNetworkModeHang; - bool m_p25NetworkEnabled; - std::string m_p25GatewayAddress; - unsigned short m_p25GatewayPort; - std::string m_p25LocalAddress; - unsigned short m_p25LocalPort; - unsigned int m_p25NetworkModeHang; - bool m_p25NetworkDebug; +#if defined(USE_YSF) + bool m_fusionNetworkEnabled; + std::string m_fusionNetworkLocalAddress; + unsigned short m_fusionNetworkLocalPort; + std::string m_fusionNetworkGatewayAddress; + unsigned short m_fusionNetworkGatewayPort; +#endif + unsigned int m_fusionNetworkModeHang; +#if defined(USE_YSF) + bool m_fusionNetworkDebug; +#endif - bool m_nxdnNetworkEnabled; - std::string m_nxdnNetworkProtocol; - std::string m_nxdnGatewayAddress; - unsigned short m_nxdnGatewayPort; - std::string m_nxdnLocalAddress; - unsigned short m_nxdnLocalPort; - unsigned int m_nxdnNetworkModeHang; - bool m_nxdnNetworkDebug; +#if defined(USE_P25) + bool m_p25NetworkEnabled; + std::string m_p25GatewayAddress; + unsigned short m_p25GatewayPort; + std::string m_p25LocalAddress; + unsigned short m_p25LocalPort; +#endif + unsigned int m_p25NetworkModeHang; +#if defined(USE_P25) + bool m_p25NetworkDebug; +#endif - bool m_pocsagNetworkEnabled; - std::string m_pocsagGatewayAddress; - unsigned short m_pocsagGatewayPort; - std::string m_pocsagLocalAddress; - unsigned short m_pocsagLocalPort; - unsigned int m_pocsagNetworkModeHang; - bool m_pocsagNetworkDebug; +#if defined(USE_NXDN) + bool m_nxdnNetworkEnabled; + std::string m_nxdnNetworkProtocol; + std::string m_nxdnGatewayAddress; + unsigned short m_nxdnGatewayPort; + std::string m_nxdnLocalAddress; + unsigned short m_nxdnLocalPort; +#endif + unsigned int m_nxdnNetworkModeHang; +#if defined(USE_NXDN) + bool m_nxdnNetworkDebug; +#endif - bool m_fmNetworkEnabled; - std::string m_fmNetworkProtocol; - unsigned int m_fmNetworkSampleRate; - std::string m_fmNetworkSquelchFile; - std::string m_fmGatewayAddress; - unsigned short m_fmGatewayPort; - std::string m_fmLocalAddress; - unsigned short m_fmLocalPort; - bool m_fmPreEmphasis; - bool m_fmDeEmphasis; - float m_fmTXAudioGain; - float m_fmRXAudioGain; - unsigned int m_fmNetworkModeHang; - bool m_fmNetworkDebug; +#if defined(USE_POCSAG) + bool m_pocsagNetworkEnabled; + std::string m_pocsagGatewayAddress; + unsigned short m_pocsagGatewayPort; + std::string m_pocsagLocalAddress; + unsigned short m_pocsagLocalPort; + unsigned int m_pocsagNetworkModeHang; + bool m_pocsagNetworkDebug; +#endif - std::string m_tftSerialPort; - unsigned int m_tftSerialBrightness; - unsigned int m_tftSerialScreenLayout; +#if defined(USE_FM) + bool m_fmNetworkEnabled; + std::string m_fmGatewayAddress; + unsigned short m_fmGatewayPort; + std::string m_fmLocalAddress; + unsigned short m_fmLocalPort; + bool m_fmPreEmphasis; + bool m_fmDeEmphasis; + float m_fmTXAudioGain; + float m_fmRXAudioGain; +#endif + unsigned int m_fmNetworkModeHang; +#if defined(USE_FM) + bool m_fmNetworkDebug; +#endif - unsigned int m_hd44780Rows; - unsigned int m_hd44780Columns; - std::vector m_hd44780Pins; - unsigned int m_hd44780i2cAddress; - bool m_hd44780PWM; - unsigned int m_hd44780PWMPin; - unsigned int m_hd44780PWMBright; - unsigned int m_hd44780PWMDim; - bool m_hd44780DisplayClock; - bool m_hd44780UTC; + bool m_lockFileEnabled; + std::string m_lockFileName; - std::string m_nextionPort; - unsigned int m_nextionBrightness; - bool m_nextionDisplayClock; - bool m_nextionUTC; - unsigned int m_nextionIdleBrightness; - unsigned int m_nextionScreenLayout; - bool m_nextionTempInFahrenheit; - bool m_nextionOutput; - unsigned short m_nextionUDPPort; - - unsigned char m_oledType; - unsigned char m_oledBrightness; - bool m_oledInvert; - bool m_oledScroll; - bool m_oledRotate; - bool m_oledLogoScreensaver; - - std::string m_lcdprocAddress; - unsigned short m_lcdprocPort; - unsigned short m_lcdprocLocalPort; - bool m_lcdprocDisplayClock; - bool m_lcdprocUTC; - bool m_lcdprocDimOnIdle; - - bool m_lockFileEnabled; - std::string m_lockFileName; - - bool m_remoteControlEnabled; - std::string m_remoteControlAddress; - unsigned short m_remoteControlPort; + bool m_remoteControlEnabled; }; #endif + diff --git a/DMRAccessControl.cpp b/DMRAccessControl.cpp index 75ec36c..29a2935 100644 --- a/DMRAccessControl.cpp +++ b/DMRAccessControl.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2016 by Simon Rune G7RZU - * Copyright (C) 2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2016,2017,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,8 @@ #include "DMRAccessControl.h" #include "Log.h" +#if defined(USE_DMR) + #include #include #include @@ -97,3 +99,6 @@ bool CDMRAccessControl::validateTGId(unsigned int slotNo, bool group, unsigned i return std::find(m_slot2TGWhiteList.begin(), m_slot2TGWhiteList.end(), id) != m_slot2TGWhiteList.end(); } } + +#endif + diff --git a/DMRAccessControl.h b/DMRAccessControl.h index 7d4aec1..480560b 100644 --- a/DMRAccessControl.h +++ b/DMRAccessControl.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2016 by Simon Rune G7RZU - * Copyright (C) 2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2016,2017,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,10 @@ #if !defined(DMRAccessControl_H) #define DMRAccessControl_H +#include "Defines.h" + +#if defined(USE_DMR) + #include class CDMRAccessControl { @@ -42,3 +46,6 @@ private: }; #endif + +#endif + diff --git a/DMRCSBK.cpp b/DMRCSBK.cpp index e909ae6..451c01e 100644 --- a/DMRCSBK.cpp +++ b/DMRCSBK.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2020,2021,2022,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2020,2021,2022,2023,2025 by Jonathan Naylor G4KLX * Copyright (C) 2019 by Patrick Maier DK5MP * * This program is free software; you can redistribute it and/or modify @@ -22,6 +22,8 @@ #include "Utils.h" #include "CRC.h" +#if defined(USE_DMR) + #include #include @@ -246,3 +248,6 @@ void CDMRCSBK::setCBF(unsigned char cbf) { m_CBF = m_data[3U] = cbf; } + +#endif + diff --git a/DMRCSBK.h b/DMRCSBK.h index af6bffa..169023b 100644 --- a/DMRCSBK.h +++ b/DMRCSBK.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2020,2021,2022,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2020,2021,2022,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,9 @@ #define DMRCSBK_H #include "DMRDefines.h" +#include "Defines.h" + +#if defined(USE_DMR) enum class CSBKO : unsigned char { NONE = 0x00, @@ -81,3 +84,6 @@ private: }; #endif + +#endif + diff --git a/DMRControl.cpp b/DMRControl.cpp index 3943e9a..88bab80 100644 --- a/DMRControl.cpp +++ b/DMRControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2021,2025 Jonathan Naylor, G4KLX + * Copyright (C) 2015-2021,2023,2025 Jonathan Naylor, G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -13,15 +13,16 @@ #include "DMRControl.h" #include "DMRAccessControl.h" -#include "Defines.h" #include "DMRCSBK.h" #include "Log.h" +#if defined(USE_DMR) + #include #include #include -CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector& prefixes, const std::vector& blacklist, const std::vector& whitelist, const std::vector& slot1TGWhitelist, const std::vector& slot2TGWhitelist, unsigned int timeout, CModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter, DMR_OVCM ovcm, bool protect) : +CDMRControl::CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector& prefixes, const std::vector& blacklist, const std::vector& whitelist, const std::vector& slot1TGWhitelist, const std::vector& slot2TGWhitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter, DMR_OVCM ovcm, bool protect) : m_colorCode(colorCode), m_modem(modem), m_network(network), @@ -31,14 +32,13 @@ m_lookup(lookup) { assert(id != 0U); assert(modem != nullptr); - assert(display != nullptr); assert(lookup != nullptr); assert(rssi != nullptr); // Load black and white lists to DMRAccessControl CDMRAccessControl::init(blacklist, whitelist, slot1TGWhitelist, slot2TGWhitelist, selfOnly, prefixes, id); - CDMRSlot::init(colorCode, embeddedLCOnly, dumpTAData, callHang, modem, network, display, duplex, m_lookup, rssi, jitter, ovcm, protect); + CDMRSlot::init(colorCode, embeddedLCOnly, dumpTAData, callHang, modem, network, duplex, m_lookup, rssi, jitter, ovcm, protect); } CDMRControl::~CDMRControl() @@ -136,3 +136,6 @@ void CDMRControl::enable(bool enabled) m_slot1.enable(enabled); m_slot2.enable(enabled); } + +#endif + diff --git a/DMRControl.h b/DMRControl.h index 7ed14de..fa63039 100644 --- a/DMRControl.h +++ b/DMRControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,16 +22,19 @@ #include "RSSIInterpolator.h" #include "DMRNetwork.h" #include "DMRLookup.h" -#include "Display.h" #include "DMRSlot.h" #include "DMRData.h" #include "Modem.h" +#include "Defines.h" + +#if defined(USE_DMR) + #include class CDMRControl { public: - CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector& prefixes, const std::vector& blacklist, const std::vector& whitelist, const std::vector& slot1TGWhitelist, const std::vector& slot2TGWhitelist, unsigned int timeout, CModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter, DMR_OVCM ovcm, bool protect); + CDMRControl(unsigned int id, unsigned int colorCode, unsigned int callHang, bool selfOnly, bool embeddedLCOnly, bool dumpTAData, const std::vector& prefixes, const std::vector& blacklist, const std::vector& whitelist, const std::vector& slot1TGWhitelist, const std::vector& slot2TGWhitelist, unsigned int timeout, CModem* modem, CDMRNetwork* network, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssi, unsigned int jitter, DMR_OVCM ovcm, bool protect); ~CDMRControl(); bool processWakeup(const unsigned char* data); @@ -51,10 +54,13 @@ public: private: unsigned int m_colorCode; CModem* m_modem; - IDMRNetwork* m_network; + CDMRNetwork* m_network; CDMRSlot m_slot1; CDMRSlot m_slot2; CDMRLookup* m_lookup; }; #endif + +#endif + diff --git a/DMRData.cpp b/DMRData.cpp index 3ac72a6..850663e 100644 --- a/DMRData.cpp +++ b/DMRData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2025 Jonathan Naylor, G4KLX + * Copyright (C) 2015,2016,2017,2023,2025 Jonathan Naylor, G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,11 +16,12 @@ #include "Utils.h" #include "Log.h" +#if defined(USE_DMR) + #include #include #include - CDMRData::CDMRData(const CDMRData& data) : m_slotNo(data.m_slotNo), m_data(nullptr), @@ -183,3 +184,6 @@ void CDMRData::setData(const unsigned char* buffer) ::memcpy(m_data, buffer, DMR_FRAME_LENGTH_BYTES); } + +#endif + diff --git a/DMRData.h b/DMRData.h index c5dc4fc..15f7c25 100644 --- a/DMRData.h +++ b/DMRData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor, G4KLX + * Copyright (C) 2015,2016,2017,2023 by Jonathan Naylor, G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,6 +15,9 @@ #define DMRData_H #include "DMRDefines.h" +#include "Defines.h" + +#if defined(USE_DMR) class CDMRData { public: @@ -68,3 +71,6 @@ private: }; #endif + +#endif + diff --git a/DMRDataHeader.cpp b/DMRDataHeader.cpp index 0b44be4..164cafb 100644 --- a/DMRDataHeader.cpp +++ b/DMRDataHeader.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2012 by Ian Wraith - * Copyright (C) 2015,2016,2017,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,8 @@ #include "CRC.h" #include "Log.h" +#if defined(USE_DMR) + #include #include #include @@ -173,3 +175,6 @@ CDMRDataHeader& CDMRDataHeader::operator=(const CDMRDataHeader& header) return *this; } + +#endif + diff --git a/DMRDataHeader.h b/DMRDataHeader.h index 97c6036..ae273b5 100644 --- a/DMRDataHeader.h +++ b/DMRDataHeader.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #ifndef DMRDataHeader_H #define DMRDataHeader_H +#include "Defines.h" + +#if defined(USE_DMR) + class CDMRDataHeader { public: @@ -52,3 +56,5 @@ private: #endif +#endif + diff --git a/DMRDirectNetwork.cpp b/DMRDirectNetwork.cpp deleted file mode 100644 index 1d0902b..0000000 --- a/DMRDirectNetwork.cpp +++ /dev/null @@ -1,644 +0,0 @@ -/* - * Copyright (C) 2015,2016,2017,2018,2020,2021,2024,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "DMRDirectNetwork.h" - -#include "SHA256.h" -#include "Utils.h" -#include "Log.h" - -#include -#include - -const unsigned int BUFFER_LENGTH = 500U; - -const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U; - - -CDMRDirectNetwork::CDMRDirectNetwork(const std::string& address, unsigned short port, const std::string& localAddress, unsigned short localPort, unsigned int id, const std::string& password, bool duplex, const char* version, bool slot1, bool slot2, HW_TYPE hwType, bool debug) : -m_address(address), -m_port(port), -m_addr(), -m_addrLen(0U), -m_id(nullptr), -m_password(password), -m_duplex(duplex), -m_version(version), -m_debug(debug), -m_socket(localAddress, localPort), -m_enabled(false), -m_slot1(slot1), -m_slot2(slot2), -m_hwType(hwType), -m_status(STATUS::WAITING_CONNECT), -m_retryTimer(1000U, 10U), -m_timeoutTimer(1000U, 60U), -m_buffer(nullptr), -m_streamId(nullptr), -m_salt(nullptr), -m_rxData(1000U, "DMR Network"), -m_options(), -m_random(), -m_callsign(), -m_rxFrequency(0U), -m_txFrequency(0U), -m_power(0U), -m_colorCode(0U), -m_latitude(0.0F), -m_longitude(0.0F), -m_height(0), -m_location(), -m_description(), -m_url(), -m_beacon(false) -{ - assert(!address.empty()); - assert(port > 0U); - assert(id > 1000U); - assert(!password.empty()); - - if (CUDPSocket::lookup(m_address, m_port, m_addr, m_addrLen) != 0) - m_addrLen = 0U; - - m_buffer = new unsigned char[BUFFER_LENGTH]; - m_salt = new unsigned char[sizeof(uint32_t)]; - m_id = new uint8_t[4U]; - m_streamId = new uint32_t[2U]; - - m_id[0U] = id >> 24; - m_id[1U] = id >> 16; - m_id[2U] = id >> 8; - m_id[3U] = id >> 0; - - std::random_device rd; - std::mt19937 mt(rd()); - m_random = mt; - - std::uniform_int_distribution dist(0x00000001, 0xfffffffe); - m_streamId[0U] = dist(m_random); - m_streamId[1U] = dist(m_random); -} - -CDMRDirectNetwork::~CDMRDirectNetwork() -{ - delete[] m_streamId; - delete[] m_buffer; - delete[] m_salt; - delete[] m_id; -} - -void CDMRDirectNetwork::setOptions(const std::string& options) -{ - m_options = options; -} - -void CDMRDirectNetwork::setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url) -{ - m_callsign = callsign; - m_rxFrequency = rxFrequency; - m_txFrequency = txFrequency; - m_power = power; - m_colorCode = colorCode; - m_latitude = latitude; - m_longitude = longitude; - m_height = height; - m_location = location; - m_description = description; - m_url = url; -} - -bool CDMRDirectNetwork::open() -{ - if (m_addrLen == 0U) { - LogError("DMR, Could not lookup the address of the DMR Network"); - return false; - } - - LogMessage("DMR, Opening DMR Network"); - - m_status = STATUS::WAITING_CONNECT; - m_timeoutTimer.stop(); - m_retryTimer.start(); - - return m_socket.open(m_addr); -} - -void CDMRDirectNetwork::enable(bool enabled) -{ - if (!enabled && m_enabled) - m_rxData.clear(); - - m_enabled = enabled; -} - -bool CDMRDirectNetwork::read(CDMRData& data) -{ - if (m_status != STATUS::RUNNING) - return false; - - if (m_rxData.isEmpty()) - return false; - - unsigned char length = 0U; - m_rxData.getData(&length, 1U); - m_rxData.getData(m_buffer, length); - - // Is this a data packet? - if (::memcmp(m_buffer, "DMRD", 4U) != 0) - return false; - - unsigned char seqNo = m_buffer[4U]; - - unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0); - - unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0); - - unsigned int slotNo = (m_buffer[15U] & 0x80U) == 0x80U ? 2U : 1U; - - // DMO mode slot disabling - if (slotNo == 1U && !m_duplex) - return false; - - // Individual slot disabling - if (slotNo == 1U && !m_slot1) - return false; - if (slotNo == 2U && !m_slot2) - return false; - - FLCO flco = (m_buffer[15U] & 0x40U) == 0x40U ? FLCO::USER_USER : FLCO::GROUP; - - data.setSeqNo(seqNo); - data.setSlotNo(slotNo); - data.setSrcId(srcId); - data.setDstId(dstId); - data.setFLCO(flco); - - bool dataSync = (m_buffer[15U] & 0x20U) == 0x20U; - bool voiceSync = (m_buffer[15U] & 0x10U) == 0x10U; - - if (dataSync) { - unsigned char dataType = m_buffer[15U] & 0x0FU; - data.setData(m_buffer + 20U); - data.setDataType(dataType); - data.setN(0U); - } else if (voiceSync) { - data.setData(m_buffer + 20U); - data.setDataType(DT_VOICE_SYNC); - data.setN(0U); - } else { - unsigned char n = m_buffer[15U] & 0x0FU; - data.setData(m_buffer + 20U); - data.setDataType(DT_VOICE); - data.setN(n); - } - - return true; -} - -bool CDMRDirectNetwork::write(const CDMRData& data) -{ - if (m_status != STATUS::RUNNING) - return false; - - unsigned char buffer[HOMEBREW_DATA_PACKET_LENGTH]; - ::memset(buffer, 0x00U, HOMEBREW_DATA_PACKET_LENGTH); - - buffer[0U] = 'D'; - buffer[1U] = 'M'; - buffer[2U] = 'R'; - buffer[3U] = 'D'; - - unsigned int srcId = data.getSrcId(); - buffer[5U] = srcId >> 16; - buffer[6U] = srcId >> 8; - buffer[7U] = srcId >> 0; - - unsigned int dstId = data.getDstId(); - buffer[8U] = dstId >> 16; - buffer[9U] = dstId >> 8; - buffer[10U] = dstId >> 0; - - ::memcpy(buffer + 11U, m_id, 4U); - - unsigned int slotNo = data.getSlotNo(); - - // Individual slot disabling - if (slotNo == 1U && !m_slot1) - return false; - if (slotNo == 2U && !m_slot2) - return false; - - buffer[15U] = slotNo == 1U ? 0x00U : 0x80U; - - FLCO flco = data.getFLCO(); - buffer[15U] |= flco == FLCO::GROUP ? 0x00U : 0x40U; - - unsigned int slotIndex = slotNo - 1U; - - std::uniform_int_distribution dist(0x00000001, 0xfffffffe); - unsigned char dataType = data.getDataType(); - if (dataType == DT_VOICE_SYNC) { - buffer[15U] |= 0x10U; - } else if (dataType == DT_VOICE) { - buffer[15U] |= data.getN(); - } else { - if (dataType == DT_VOICE_LC_HEADER) - m_streamId[slotIndex] = dist(m_random); - - if (dataType == DT_CSBK || dataType == DT_DATA_HEADER) - m_streamId[slotIndex] = dist(m_random); - - buffer[15U] |= (0x20U | dataType); - } - - buffer[4U] = data.getSeqNo(); - - ::memcpy(buffer + 16U, m_streamId + slotIndex, 4U); - - data.getData(buffer + 20U); - - buffer[53U] = data.getBER(); - - buffer[54U] = data.getRSSI(); - - write(buffer, HOMEBREW_DATA_PACKET_LENGTH); - - return true; -} - -bool CDMRDirectNetwork::writeRadioPosition(unsigned int id, const unsigned char* data) -{ - if (m_status != STATUS::RUNNING) - return false; - - unsigned char buffer[20U]; - - ::memcpy(buffer + 0U, "DMRG", 4U); - - buffer[4U] = id >> 16; - buffer[5U] = id >> 8; - buffer[6U] = id >> 0; - - ::memcpy(buffer + 7U, data + 2U, 7U); - - return write(buffer, 14U); -} - -bool CDMRDirectNetwork::writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data) -{ - if (m_status != STATUS::RUNNING) - return false; - - unsigned char buffer[20U]; - - ::memcpy(buffer + 0U, "DMRA", 4U); - - buffer[4U] = id >> 16; - buffer[5U] = id >> 8; - buffer[6U] = id >> 0; - - buffer[7U] = type; - - ::memcpy(buffer + 8U, data + 2U, 7U); - - return write(buffer, 15U); -} - -bool CDMRDirectNetwork::isConnected() const -{ - return (m_status == STATUS::RUNNING); -} - -void CDMRDirectNetwork::close(bool sayGoodbye) -{ - LogMessage("DMR, Closing DMR Network"); - - if (sayGoodbye && (m_status == STATUS::RUNNING)) { - unsigned char buffer[9U]; - ::memcpy(buffer + 0U, "RPTCL", 5U); - ::memcpy(buffer + 5U, m_id, 4U); - write(buffer, 9U); - } - - m_socket.close(); - - m_retryTimer.stop(); - m_timeoutTimer.stop(); -} - -void CDMRDirectNetwork::clock(unsigned int ms) -{ - m_retryTimer.clock(ms); - if (m_retryTimer.isRunning() && m_retryTimer.hasExpired()) { - switch (m_status) { - case STATUS::WAITING_CONNECT: - writeLogin(); - m_status = STATUS::WAITING_LOGIN; - break; - case STATUS::WAITING_LOGIN: - writeLogin(); - break; - case STATUS::WAITING_AUTHORISATION: - writeAuthorisation(); - break; - case STATUS::WAITING_OPTIONS: - writeOptions(); - break; - case STATUS::WAITING_CONFIG: - writeConfig(); - break; - case STATUS::RUNNING: - writePing(); - break; - default: - break; - } - - m_retryTimer.start(); - } - - sockaddr_storage address; - unsigned int addrlen; - int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, addrlen); - if (length < 0) { - LogError("DMR, Socket has failed, retrying connection to the master"); - close(false); - open(); - return; - } - - if (length > 0) { - if (!CUDPSocket::match(m_addr, address)) { - LogMessage("DMR, packet received from an invalid source"); - return; - } - - if (m_debug) - CUtils::dump(1U, "DMR, Network Received", m_buffer, length); - - if (::memcmp(m_buffer, "DMRD", 4U) == 0) { - if (m_enabled) { - unsigned char len = length; - m_rxData.addData(&len, 1U); - m_rxData.addData(m_buffer, len); - } - } else if (::memcmp(m_buffer, "MSTNAK", 6U) == 0) { - if (m_status == STATUS::RUNNING) { - LogWarning("DMR, Login to the master has failed, retrying login ..."); - m_status = STATUS::WAITING_LOGIN; - m_timeoutTimer.start(); - m_retryTimer.start(); - } else { - /* Once the modem death spiral has been prevented in Modem.cpp - the Network sometimes times out and reaches here. - We want it to reconnect so... */ - LogError("DMR, Login to the master has failed, retrying network ..."); - close(false); - open(); - return; - } - } else if (::memcmp(m_buffer, "RPTACK", 6U) == 0) { - switch (m_status) { - case STATUS::WAITING_LOGIN: - LogDebug("DMR, Sending authorisation"); - ::memcpy(m_salt, m_buffer + 6U, sizeof(uint32_t)); - writeAuthorisation(); - m_status = STATUS::WAITING_AUTHORISATION; - m_timeoutTimer.start(); - m_retryTimer.start(); - break; - case STATUS::WAITING_AUTHORISATION: - LogDebug("DMR, Sending configuration"); - writeConfig(); - m_status = STATUS::WAITING_CONFIG; - m_timeoutTimer.start(); - m_retryTimer.start(); - break; - case STATUS::WAITING_CONFIG: - if (m_options.empty()) { - LogMessage("DMR, Logged into the master successfully"); - m_status = STATUS::RUNNING; - } else { - LogDebug("DMR, Sending options"); - writeOptions(); - m_status = STATUS::WAITING_OPTIONS; - } - m_timeoutTimer.start(); - m_retryTimer.start(); - break; - case STATUS::WAITING_OPTIONS: - LogMessage("DMR, Logged into the master successfully"); - m_status = STATUS::RUNNING; - m_timeoutTimer.start(); - m_retryTimer.start(); - break; - default: - break; - } - } else if (::memcmp(m_buffer, "MSTCL", 5U) == 0) { - LogError("DMR, Master is closing down"); - close(false); - open(); - } else if (::memcmp(m_buffer, "MSTPONG", 7U) == 0) { - m_timeoutTimer.start(); - } else if (::memcmp(m_buffer, "RPTSBKN", 7U) == 0) { - m_beacon = true; - } else { - CUtils::dump("DMR, Unknown packet from the master", m_buffer, length); - } - } - - m_timeoutTimer.clock(ms); - if (m_timeoutTimer.isRunning() && m_timeoutTimer.hasExpired()) { - LogError("DMR, Connection to the master has timed out, retrying connection"); - close(false); - open(); - } -} - -bool CDMRDirectNetwork::writeLogin() -{ - unsigned char buffer[8U]; - - ::memcpy(buffer + 0U, "RPTL", 4U); - ::memcpy(buffer + 4U, m_id, 4U); - - return write(buffer, 8U); -} - -bool CDMRDirectNetwork::writeAuthorisation() -{ - size_t size = m_password.size(); - - unsigned char* in = new unsigned char[size + sizeof(uint32_t)]; - ::memcpy(in, m_salt, sizeof(uint32_t)); - for (size_t i = 0U; i < size; i++) - in[i + sizeof(uint32_t)] = m_password.at(i); - - unsigned char out[40U]; - ::memcpy(out + 0U, "RPTK", 4U); - ::memcpy(out + 4U, m_id, 4U); - - CSHA256 sha256; - sha256.buffer(in, (unsigned int)(size + sizeof(uint32_t)), out + 8U); - - delete[] in; - - return write(out, 40U); -} - -bool CDMRDirectNetwork::writeOptions() -{ - char buffer[300U]; - - ::memcpy(buffer + 0U, "RPTO", 4U); - ::memcpy(buffer + 4U, m_id, 4U); - ::strcpy(buffer + 8U, m_options.c_str()); - - return write((unsigned char*)buffer, (unsigned int)m_options.length() + 8U); -} - -bool CDMRDirectNetwork::writeConfig() -{ - const char* software; - char slots = '0'; - if (m_duplex) { - if (m_slot1 && m_slot2) - slots = '3'; - else if (m_slot1 && !m_slot2) - slots = '1'; - else if (!m_slot1 && m_slot2) - slots = '2'; - - switch (m_hwType) { - case HW_TYPE::MMDVM: - software = "MMDVM"; - break; - case HW_TYPE::MMDVM_HS: - software = "MMDVM_MMDVM_HS"; - break; - case HW_TYPE::MMDVM_HS_DUAL_HAT: - software = "MMDVM_MMDVM_HS_Dual_Hat"; - break; - case HW_TYPE::NANO_HOTSPOT: - software = "MMDVM_Nano_hotSPOT"; - break; - default: - software = "MMDVM_Unknown"; - break; - } - } else { - slots = '4'; - - switch (m_hwType) { - case HW_TYPE::MMDVM: - software = "MMDVM_DMO"; - break; - case HW_TYPE::DVMEGA: - software = "MMDVM_DVMega"; - break; - case HW_TYPE::MMDVM_ZUMSPOT: - software = "MMDVM_ZUMspot"; - break; - case HW_TYPE::MMDVM_HS_HAT: - software = "MMDVM_MMDVM_HS_Hat"; - break; - case HW_TYPE::MMDVM_HS_DUAL_HAT: - software = "MMDVM_MMDVM_HS_Dual_Hat"; - break; - case HW_TYPE::NANO_HOTSPOT: - software = "MMDVM_Nano_hotSPOT"; - break; - case HW_TYPE::NANO_DV: - software = "MMDVM_Nano_DV"; - break; - case HW_TYPE::D2RG_MMDVM_HS: - software = "MMDVM_D2RG_MMDVM_HS"; - break; - case HW_TYPE::MMDVM_HS: - software = "MMDVM_MMDVM_HS"; - break; - default: - software = "MMDVM_Unknown"; - break; - } - } - - char buffer[400U]; - - ::memcpy(buffer + 0U, "RPTC", 4U); - ::memcpy(buffer + 4U, m_id, 4U); - - char latitude[20U]; - ::sprintf(latitude, "%08f", m_latitude); - - char longitude[20U]; - ::sprintf(longitude, "%09f", m_longitude); - - unsigned int power = m_power; - if (power > 99U) - power = 99U; - - int height = m_height; - if (height > 999) - height = 999; - - ::sprintf(buffer + 8U, "%-8.8s%09u%09u%02u%02u%8.8s%9.9s%03d%-20.20s%-19.19s%c%-124.124s%-40.40s%-40.40s", m_callsign.c_str(), - m_rxFrequency, m_txFrequency, power, m_colorCode, latitude, longitude, height, m_location.c_str(), - m_description.c_str(), slots, m_url.c_str(), m_version, software); - - return write((unsigned char*)buffer, 302U); -} - -bool CDMRDirectNetwork::writePing() -{ - unsigned char buffer[11U]; - - ::memcpy(buffer + 0U, "RPTPING", 7U); - ::memcpy(buffer + 7U, m_id, 4U); - - return write(buffer, 11U); -} - -bool CDMRDirectNetwork::wantsBeacon() -{ - bool beacon = m_beacon; - - m_beacon = false; - - return beacon; -} - -bool CDMRDirectNetwork::write(const unsigned char* data, unsigned int length) -{ - assert(data != nullptr); - assert(length > 0U); - - if (m_debug) - CUtils::dump(1U, "DMR Network Transmitted", data, length); - - bool ret = m_socket.write(data, length, m_addr, m_addrLen); - if (!ret) { - LogError("DMR, Socket has failed when writing data to the master, retrying connection"); - close(false); - open(); - return false; - } - - return true; -} diff --git a/DMRDirectNetwork.h b/DMRDirectNetwork.h deleted file mode 100644 index ed8c4c6..0000000 --- a/DMRDirectNetwork.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2015,2016,2017,2018,2020,2021,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#if !defined(DMRDirectNetwork_H) -#define DMRDirectNetwork_H - -#include "DMRNetwork.h" -#include "UDPSocket.h" -#include "Timer.h" -#include "RingBuffer.h" -#include "DMRData.h" - -#include -#include -#include - -class CDMRDirectNetwork : public IDMRNetwork -{ -public: - CDMRDirectNetwork(const std::string& remoteAddress, unsigned short remotePort, const std::string& localAddress, unsigned short localPort, unsigned int id, const std::string& password, bool duplex, const char* version, bool slot1, bool slot2, HW_TYPE hwType, bool debug); - virtual ~CDMRDirectNetwork(); - - virtual void setOptions(const std::string& options); - - virtual void setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url); - - virtual bool open(); - - virtual void enable(bool enabled); - - virtual bool read(CDMRData& data); - - virtual bool write(const CDMRData& data); - - virtual bool writeRadioPosition(unsigned int id, const unsigned char* data); - - virtual bool writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data); - - virtual bool wantsBeacon(); - - virtual void clock(unsigned int ms); - - virtual bool isConnected() const; - - virtual void close(bool sayGoodbye); - -private: - std::string m_address; - unsigned short m_port; - sockaddr_storage m_addr; - unsigned int m_addrLen; - uint8_t* m_id; - std::string m_password; - bool m_duplex; - const char* m_version; - bool m_debug; - CUDPSocket m_socket; - bool m_enabled; - bool m_slot1; - bool m_slot2; - HW_TYPE m_hwType; - - enum class STATUS { - WAITING_CONNECT, - WAITING_LOGIN, - WAITING_AUTHORISATION, - WAITING_CONFIG, - WAITING_OPTIONS, - RUNNING - }; - - STATUS m_status; - CTimer m_retryTimer; - CTimer m_timeoutTimer; - unsigned char* m_buffer; - uint32_t* m_streamId; - unsigned char* m_salt; - - CRingBuffer m_rxData; - - std::string m_options; - - std::mt19937 m_random; - std::string m_callsign; - unsigned int m_rxFrequency; - unsigned int m_txFrequency; - unsigned int m_power; - unsigned int m_colorCode; - float m_latitude; - float m_longitude; - int m_height; - std::string m_location; - std::string m_description; - std::string m_url; - bool m_beacon; - - bool writeLogin(); - bool writeAuthorisation(); - bool writeOptions(); - bool writeConfig(); - bool writePing(); - - bool write(const unsigned char* data, unsigned int length); -}; - -#endif diff --git a/DMREMB.cpp b/DMREMB.cpp index 9bc361b..89c4635 100644 --- a/DMREMB.cpp +++ b/DMREMB.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,9 +17,10 @@ */ #include "DMREMB.h" - #include "QR1676.h" +#if defined(USE_DMR) + #include #include @@ -98,3 +99,6 @@ void CDMREMB::setLCSS(unsigned char lcss) { m_LCSS = lcss; } + +#endif + diff --git a/DMREMB.h b/DMREMB.h index 517be1e..5ba2f88 100644 --- a/DMREMB.h +++ b/DMREMB.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(DMREMB_H) #define DMREMB_H +#include "Defines.h" + +#if defined(USE_DMR) + class CDMREMB { public: @@ -44,3 +48,6 @@ private: }; #endif + +#endif + diff --git a/DMREmbeddedData.cpp b/DMREmbeddedData.cpp index affb6f3..97e8491 100644 --- a/DMREmbeddedData.cpp +++ b/DMREmbeddedData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,11 +17,12 @@ */ #include "DMREmbeddedData.h" - #include "Hamming.h" #include "Utils.h" #include "CRC.h" +#if defined(USE_DMR) + #include #include #include @@ -320,3 +321,6 @@ bool CDMREmbeddedData::getRawData(unsigned char* data) const return true; } + +#endif + diff --git a/DMREmbeddedData.h b/DMREmbeddedData.h index dbadec8..8ce65d8 100644 --- a/DMREmbeddedData.h +++ b/DMREmbeddedData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,8 +20,11 @@ #define DMREmbeddedData_H #include "DMRDefines.h" +#include "Defines.h" #include "DMRLC.h" +#if defined(USE_DMR) + enum class LC_STATE { NONE, FIRST, @@ -61,3 +64,5 @@ private: }; #endif + +#endif diff --git a/DMRFullLC.cpp b/DMRFullLC.cpp index f9c9c6c..31c7e53 100644 --- a/DMRFullLC.cpp +++ b/DMRFullLC.cpp @@ -1,6 +1,6 @@ /* - * Copyright (C) 2012 by Ian Wraith - * Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2012 by Ian Wraith + * Copyright (C) 2015,2016,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,12 +18,13 @@ */ #include "DMRFullLC.h" - #include "DMRDefines.h" #include "RS129.h" #include "Utils.h" #include "Log.h" +#if defined(USE_DMR) + #include #include @@ -97,3 +98,6 @@ void CDMRFullLC::encode(const CDMRLC& lc, unsigned char* data, unsigned char typ m_bptc.encode(lcData, data); } + +#endif + diff --git a/DMRFullLC.h b/DMRFullLC.h index d3294ef..006b56c 100644 --- a/DMRFullLC.h +++ b/DMRFullLC.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,8 +21,10 @@ #include "DMRLC.h" #include "DMRSlotType.h" - #include "BPTC19696.h" +#include "Defines.h" + +#if defined(USE_DMR) class CDMRFullLC { @@ -40,3 +42,5 @@ private: #endif +#endif + diff --git a/DMRGatewayNetwork.cpp b/DMRGatewayNetwork.cpp deleted file mode 100644 index 94a659f..0000000 --- a/DMRGatewayNetwork.cpp +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Copyright (C) 2015-2021,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * 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 "DMRGatewayNetwork.h" - -#include "Utils.h" -#include "Log.h" - -#include -#include -#include -#include - -const unsigned int BUFFER_LENGTH = 500U; - -const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U; - - -CDMRGatewayNetwork::CDMRGatewayNetwork(const std::string& address, unsigned short port, const std::string& localAddress, unsigned short localPort, unsigned int id, bool duplex, const char* version, bool slot1, bool slot2, HW_TYPE hwType, bool debug) : -m_addressStr(address), -m_addr(), -m_addrLen(0U), -m_port(port), -m_id(nullptr), -m_duplex(duplex), -m_version(version), -m_debug(debug), -m_socket(localAddress, localPort), -m_enabled(false), -m_slot1(slot1), -m_slot2(slot2), -m_hwType(hwType), -m_buffer(nullptr), -m_streamId(nullptr), -m_rxData(1000U, "DMR Network"), -m_beacon(false), -m_random(), -m_callsign(), -m_rxFrequency(0U), -m_txFrequency(0U), -m_power(0U), -m_colorCode(0U), -m_pingTimer(1000U, 10U) -{ - assert(!address.empty()); - assert(port > 0U); - assert(id > 1000U); - - if (CUDPSocket::lookup(m_addressStr, m_port, m_addr, m_addrLen) != 0) - m_addrLen = 0U; - - m_buffer = new unsigned char[BUFFER_LENGTH]; - m_id = new uint8_t[4U]; - m_streamId = new uint32_t[2U]; - - m_id[0U] = id >> 24; - m_id[1U] = id >> 16; - m_id[2U] = id >> 8; - m_id[3U] = id >> 0; - - std::random_device rd; - std::mt19937 mt(rd()); - m_random = mt; - - std::uniform_int_distribution dist(0x00000001, 0xfffffffe); - m_streamId[0U] = dist(m_random); - m_streamId[1U] = dist(m_random); -} - -CDMRGatewayNetwork::~CDMRGatewayNetwork() -{ - delete[] m_buffer; - delete[] m_streamId; - delete[] m_id; -} - -void CDMRGatewayNetwork::setConfig(const std::string & callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url) -{ - m_callsign = callsign; - m_rxFrequency = rxFrequency; - m_txFrequency = txFrequency; - m_power = power; - m_colorCode = colorCode; -} - -void CDMRGatewayNetwork::setOptions(const std::string& options) -{ -} - -bool CDMRGatewayNetwork::open() -{ - if (m_addrLen == 0U) { - LogError("DMR, Unable to resolve the address of the DMR Network"); - return false; - } - - LogMessage("DMR, Opening DMR Network"); - - bool ret = m_socket.open(m_addr); - if (ret) - m_pingTimer.start(); - - return ret; -} - -void CDMRGatewayNetwork::enable(bool enabled) -{ - if (!enabled && m_enabled) - m_rxData.clear(); - - m_enabled = enabled; -} - -bool CDMRGatewayNetwork::read(CDMRData& data) -{ - if (m_rxData.isEmpty()) - return false; - - unsigned char length = 0U; - m_rxData.getData(&length, 1U); - m_rxData.getData(m_buffer, length); - - // Is this a data packet? - if (::memcmp(m_buffer, "DMRD", 4U) != 0) - return false; - - unsigned char seqNo = m_buffer[4U]; - - unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0); - - unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0); - - unsigned int slotNo = (m_buffer[15U] & 0x80U) == 0x80U ? 2U : 1U; - - // DMO mode slot disabling - if (slotNo == 1U && !m_duplex) - return false; - - // Individual slot disabling - if (slotNo == 1U && !m_slot1) - return false; - if (slotNo == 2U && !m_slot2) - return false; - - FLCO flco = (m_buffer[15U] & 0x40U) == 0x40U ? FLCO::USER_USER : FLCO::GROUP; - - data.setSeqNo(seqNo); - data.setSlotNo(slotNo); - data.setSrcId(srcId); - data.setDstId(dstId); - data.setFLCO(flco); - - bool dataSync = (m_buffer[15U] & 0x20U) == 0x20U; - bool voiceSync = (m_buffer[15U] & 0x10U) == 0x10U; - - if (dataSync) { - unsigned char dataType = m_buffer[15U] & 0x0FU; - data.setData(m_buffer + 20U); - data.setDataType(dataType); - data.setN(0U); - } else if (voiceSync) { - data.setData(m_buffer + 20U); - data.setDataType(DT_VOICE_SYNC); - data.setN(0U); - } else { - unsigned char n = m_buffer[15U] & 0x0FU; - data.setData(m_buffer + 20U); - data.setDataType(DT_VOICE); - data.setN(n); - } - - return true; -} - -bool CDMRGatewayNetwork::write(const CDMRData& data) -{ - unsigned char buffer[HOMEBREW_DATA_PACKET_LENGTH]; - ::memset(buffer, 0x00U, HOMEBREW_DATA_PACKET_LENGTH); - - buffer[0U] = 'D'; - buffer[1U] = 'M'; - buffer[2U] = 'R'; - buffer[3U] = 'D'; - - unsigned int srcId = data.getSrcId(); - buffer[5U] = srcId >> 16; - buffer[6U] = srcId >> 8; - buffer[7U] = srcId >> 0; - - unsigned int dstId = data.getDstId(); - buffer[8U] = dstId >> 16; - buffer[9U] = dstId >> 8; - buffer[10U] = dstId >> 0; - - ::memcpy(buffer + 11U, m_id, 4U); - - unsigned int slotNo = data.getSlotNo(); - - // Individual slot disabling - if (slotNo == 1U && !m_slot1) - return false; - if (slotNo == 2U && !m_slot2) - return false; - - buffer[15U] = slotNo == 1U ? 0x00U : 0x80U; - - FLCO flco = data.getFLCO(); - buffer[15U] |= flco == FLCO::GROUP ? 0x00U : 0x40U; - - unsigned int slotIndex = slotNo - 1U; - - std::uniform_int_distribution dist(0x00000001, 0xfffffffe); - unsigned char dataType = data.getDataType(); - if (dataType == DT_VOICE_SYNC) { - buffer[15U] |= 0x10U; - } else if (dataType == DT_VOICE) { - buffer[15U] |= data.getN(); - } else { - if (dataType == DT_VOICE_LC_HEADER) - m_streamId[slotIndex] = dist(m_random); - - if (dataType == DT_CSBK || dataType == DT_DATA_HEADER) - m_streamId[slotIndex] = dist(m_random); - - buffer[15U] |= (0x20U | dataType); - } - - buffer[4U] = data.getSeqNo(); - - ::memcpy(buffer + 16U, m_streamId + slotIndex, 4U); - - data.getData(buffer + 20U); - - buffer[53U] = data.getBER(); - - buffer[54U] = data.getRSSI(); - - write(buffer, HOMEBREW_DATA_PACKET_LENGTH); - - return true; -} - -bool CDMRGatewayNetwork::writeRadioPosition(unsigned int id, const unsigned char* data) -{ - unsigned char buffer[20U]; - - ::memcpy(buffer + 0U, "DMRG", 4U); - - buffer[4U] = id >> 16; - buffer[5U] = id >> 8; - buffer[6U] = id >> 0; - - ::memcpy(buffer + 7U, data + 2U, 7U); - - return write(buffer, 14U); -} - -bool CDMRGatewayNetwork::writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data) -{ - unsigned char buffer[20U]; - - ::memcpy(buffer + 0U, "DMRA", 4U); - - buffer[4U] = id >> 16; - buffer[5U] = id >> 8; - buffer[6U] = id >> 0; - - buffer[7U] = type; - - ::memcpy(buffer + 8U, data + 2U, 7U); - - return write(buffer, 15U); -} - -bool CDMRGatewayNetwork::isConnected() const -{ - return (m_addrLen != 0); -} - -void CDMRGatewayNetwork::close(bool sayGoodbye) -{ - LogMessage("DMR, Closing DMR Network"); - - m_socket.close(); -} - -void CDMRGatewayNetwork::clock(unsigned int ms) -{ - m_pingTimer.clock(ms); - if (m_pingTimer.isRunning() && m_pingTimer.hasExpired()) { - writeConfig(); - m_pingTimer.start(); - } - - sockaddr_storage address; - unsigned int addrLen; - int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, addrLen); - if (length <= 0) - return; - - if (!CUDPSocket::match(m_addr, address)) { - LogMessage("DMR, packet received from an invalid source"); - return; - } - - if (m_debug) - CUtils::dump(1U, "DMR Network Received", m_buffer, length); - - if (::memcmp(m_buffer, "DMRD", 4U) == 0) { - if (m_enabled) { - unsigned char len = length; - m_rxData.addData(&len, 1U); - m_rxData.addData(m_buffer, len); - } - } else if (::memcmp(m_buffer, "DMRP", 4U) == 0) { - ; - } else if (::memcmp(m_buffer, "DMRB", 4U) == 0) { - m_beacon = true; - } else { - CUtils::dump("DMR, unknown packet from the DMR Network", m_buffer, length); - } -} - -bool CDMRGatewayNetwork::writeConfig() -{ - const char* software; - char slots = '0'; - if (m_duplex) { - if (m_slot1 && m_slot2) - slots = '3'; - else if (m_slot1 && !m_slot2) - slots = '1'; - else if (!m_slot1 && m_slot2) - slots = '2'; - - switch (m_hwType) { - case HW_TYPE::MMDVM: - software = "MMDVM"; - break; - case HW_TYPE::MMDVM_HS: - software = "MMDVM_MMDVM_HS"; - break; - case HW_TYPE::MMDVM_HS_DUAL_HAT: - software = "MMDVM_MMDVM_HS_Dual_Hat"; - break; - case HW_TYPE::NANO_HOTSPOT: - software = "MMDVM_Nano_hotSPOT"; - break; - default: - software = "MMDVM_Unknown"; - break; - } - } else { - slots = '4'; - - switch (m_hwType) { - case HW_TYPE::MMDVM: - software = "MMDVM_DMO"; - break; - case HW_TYPE::DVMEGA: - software = "MMDVM_DVMega"; - break; - case HW_TYPE::MMDVM_ZUMSPOT: - software = "MMDVM_ZUMspot"; - break; - case HW_TYPE::MMDVM_HS_HAT: - software = "MMDVM_MMDVM_HS_Hat"; - break; - case HW_TYPE::MMDVM_HS_DUAL_HAT: - software = "MMDVM_MMDVM_HS_Dual_Hat"; - break; - case HW_TYPE::NANO_HOTSPOT: - software = "MMDVM_Nano_hotSPOT"; - break; - case HW_TYPE::NANO_DV: - software = "MMDVM_Nano_DV"; - break; - case HW_TYPE::D2RG_MMDVM_HS: - software = "MMDVM_D2RG_MMDVM_HS"; - break; - case HW_TYPE::MMDVM_HS: - software = "MMDVM_MMDVM_HS"; - break; - case HW_TYPE::OPENGD77_HS: - software = "MMDVM_OpenGD77_HS"; - break; - case HW_TYPE::SKYBRIDGE: - software = "MMDVM_SkyBridge"; - break; - default: - software = "MMDVM_Unknown"; - break; - } - } - - unsigned int power = m_power; - if (power > 99U) - power = 99U; - - char buffer[150U]; - - ::memcpy(buffer + 0U, "DMRC", 4U); - ::memcpy(buffer + 4U, m_id, 4U); - ::sprintf(buffer + 8U, "%-8.8s%09u%09u%02u%02u%c%-40.40s%-40.40s", - m_callsign.c_str(), m_rxFrequency, m_txFrequency, power, m_colorCode, slots, m_version, - software); - - return write((unsigned char*)buffer, 119U); -} - -bool CDMRGatewayNetwork::wantsBeacon() -{ - bool beacon = m_beacon; - - m_beacon = false; - - return beacon; -} - -bool CDMRGatewayNetwork::write(const unsigned char* data, unsigned int length) -{ - assert(data != nullptr); - assert(length > 0U); - - if (m_debug) - CUtils::dump(1U, "DMR Network Transmitted", data, length); - - bool ret = m_socket.write(data, length, m_addr, m_addrLen); - if (!ret) { - LogError("DMR, socket error when writing to the DMR Network"); - return false; - } - - return true; -} diff --git a/DMRGatewayNetwork.h b/DMRGatewayNetwork.h deleted file mode 100644 index 160dc51..0000000 --- a/DMRGatewayNetwork.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2015,2016,2017,2018,2020,2021 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(DMRGatewayNetwork_H) -#define DMRGatewayNetwork_H - -#include "DMRNetwork.h" -#include "UDPSocket.h" -#include "Timer.h" -#include "RingBuffer.h" -#include "DMRData.h" -#include "Defines.h" - -#include -#include -#include - -class CDMRGatewayNetwork : public IDMRNetwork -{ -public: - CDMRGatewayNetwork(const std::string& remoteAddress, unsigned short remotePort, const std::string& localAddress, unsigned short localPort, unsigned int id, bool duplex, const char* version, bool slot1, bool slot2, HW_TYPE hwType, bool debug); - virtual ~CDMRGatewayNetwork(); - - virtual void setOptions(const std::string& options); - - virtual void setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url); - - virtual bool open(); - - virtual void enable(bool enabled); - - virtual bool read(CDMRData& data); - - virtual bool write(const CDMRData& data); - - virtual bool writeRadioPosition(unsigned int id, const unsigned char* data); - - virtual bool writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data); - - virtual bool wantsBeacon(); - - virtual void clock(unsigned int ms); - - virtual bool isConnected() const; - - virtual void close(bool sayGoodbye); - -private: - std::string m_addressStr; - sockaddr_storage m_addr; - unsigned int m_addrLen; - unsigned short m_port; - uint8_t* m_id; - bool m_duplex; - const char* m_version; - bool m_debug; - CUDPSocket m_socket; - bool m_enabled; - bool m_slot1; - bool m_slot2; - HW_TYPE m_hwType; - unsigned char* m_buffer; - uint32_t* m_streamId; - CRingBuffer m_rxData; - bool m_beacon; - std::mt19937 m_random; - std::string m_callsign; - unsigned int m_rxFrequency; - unsigned int m_txFrequency; - unsigned int m_power; - unsigned int m_colorCode; - CTimer m_pingTimer; - - bool writeConfig(); - - bool write(const unsigned char* data, unsigned int length); -}; - -#endif diff --git a/DMRLC.cpp b/DMRLC.cpp index 5bd6fde..ecfba19 100644 --- a/DMRLC.cpp +++ b/DMRLC.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2019,2021,2022,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2019,2021,2022,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,9 +17,10 @@ */ #include "DMRLC.h" - #include "Utils.h" +#if defined(USE_DMR) + #include #include @@ -216,3 +217,6 @@ void CDMRLC::setDstId(unsigned int id) { m_dstId = id; } + +#endif + diff --git a/DMRLC.h b/DMRLC.h index 22e133e..760b179 100644 --- a/DMRLC.h +++ b/DMRLC.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2019,2021,2022 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2019,2021,2022,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,9 @@ #define DMRLC_H #include "DMRDefines.h" +#include "Defines.h" + +#if defined(USE_DMR) class CDMRLC { @@ -63,3 +66,5 @@ private: #endif +#endif + diff --git a/DMRLookup.cpp b/DMRLookup.cpp index afe0920..82cda77 100644 --- a/DMRLookup.cpp +++ b/DMRLookup.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2017,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2017,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,8 @@ #include "Timer.h" #include "Log.h" +#if defined(USE_DMR) || defined(USE_P25) + #include #include #include @@ -134,3 +136,6 @@ bool CDMRLookup::exists(unsigned int id) { return m_table.lookup(id, nullptr); } + +#endif + diff --git a/DMRLookup.h b/DMRLookup.h index 2a9de72..41301bc 100644 --- a/DMRLookup.h +++ b/DMRLookup.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2017,2021 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2017,2021,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,9 @@ #include "Thread.h" #include "UserDB.h" +#include "Defines.h" + +#if defined(USE_DMR) || defined(USE_P25) #include @@ -51,3 +54,6 @@ private: }; #endif + +#endif + diff --git a/DMRNetwork.cpp b/DMRNetwork.cpp index 4b1b77c..19808fd 100644 --- a/DMRNetwork.cpp +++ b/DMRNetwork.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2021,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,7 +17,434 @@ */ #include "DMRNetwork.h" +#include "Utils.h" +#include "Log.h" -IDMRNetwork::~IDMRNetwork() +#if defined(USE_DMR) + +#include +#include +#include +#include + +const unsigned int BUFFER_LENGTH = 500U; + +const unsigned int HOMEBREW_DATA_PACKET_LENGTH = 55U; + + +CDMRNetwork::CDMRNetwork(const std::string& address, unsigned short port, const std::string& localAddress, unsigned short localPort, unsigned int id, bool duplex, const char* version, bool slot1, bool slot2, HW_TYPE hwType, bool debug) : +m_addressStr(address), +m_addr(), +m_addrLen(0U), +m_port(port), +m_id(nullptr), +m_duplex(duplex), +m_version(version), +m_debug(debug), +m_socket(localAddress, localPort), +m_enabled(false), +m_slot1(slot1), +m_slot2(slot2), +m_hwType(hwType), +m_buffer(nullptr), +m_streamId(nullptr), +m_rxData(1000U, "DMR Network"), +m_beacon(false), +m_random(), +m_callsign(), +m_rxFrequency(0U), +m_txFrequency(0U), +m_power(0U), +m_colorCode(0U), +m_pingTimer(1000U, 10U) { + assert(!address.empty()); + assert(port > 0U); + assert(id > 1000U); + + if (CUDPSocket::lookup(m_addressStr, m_port, m_addr, m_addrLen) != 0) + m_addrLen = 0U; + + m_buffer = new unsigned char[BUFFER_LENGTH]; + m_id = new uint8_t[4U]; + m_streamId = new uint32_t[2U]; + + m_id[0U] = id >> 24; + m_id[1U] = id >> 16; + m_id[2U] = id >> 8; + m_id[3U] = id >> 0; + + std::random_device rd; + std::mt19937 mt(rd()); + m_random = mt; + + std::uniform_int_distribution dist(0x00000001, 0xfffffffe); + m_streamId[0U] = dist(m_random); + m_streamId[1U] = dist(m_random); } + +CDMRNetwork::~CDMRNetwork() +{ + delete[] m_buffer; + delete[] m_streamId; + delete[] m_id; +} + +void CDMRNetwork::setConfig(const std::string & callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode) +{ + m_callsign = callsign; + m_rxFrequency = rxFrequency; + m_txFrequency = txFrequency; + m_power = power; + m_colorCode = colorCode; +} + +bool CDMRNetwork::open() +{ + if (m_addrLen == 0U) { + LogError("Unable to resolve the address of the DMR Network"); + return false; + } + + LogMessage("DMR, Opening DMR Network"); + + bool ret = m_socket.open(m_addr); + if (ret) + m_pingTimer.start(); + + return ret; +} + +void CDMRNetwork::enable(bool enabled) +{ + if (!enabled && m_enabled) + m_rxData.clear(); + + m_enabled = enabled; +} + +bool CDMRNetwork::read(CDMRData& data) +{ + if (m_rxData.isEmpty()) + return false; + + unsigned char length = 0U; + m_rxData.getData(&length, 1U); + m_rxData.getData(m_buffer, length); + + // Is this a data packet? + if (::memcmp(m_buffer, "DMRD", 4U) != 0) + return false; + + unsigned char seqNo = m_buffer[4U]; + + unsigned int srcId = (m_buffer[5U] << 16) | (m_buffer[6U] << 8) | (m_buffer[7U] << 0); + + unsigned int dstId = (m_buffer[8U] << 16) | (m_buffer[9U] << 8) | (m_buffer[10U] << 0); + + unsigned int slotNo = (m_buffer[15U] & 0x80U) == 0x80U ? 2U : 1U; + + // DMO mode slot disabling + if (slotNo == 1U && !m_duplex) + return false; + + // Individual slot disabling + if (slotNo == 1U && !m_slot1) + return false; + if (slotNo == 2U && !m_slot2) + return false; + + FLCO flco = (m_buffer[15U] & 0x40U) == 0x40U ? FLCO::USER_USER : FLCO::GROUP; + + data.setSeqNo(seqNo); + data.setSlotNo(slotNo); + data.setSrcId(srcId); + data.setDstId(dstId); + data.setFLCO(flco); + + bool dataSync = (m_buffer[15U] & 0x20U) == 0x20U; + bool voiceSync = (m_buffer[15U] & 0x10U) == 0x10U; + + if (dataSync) { + unsigned char dataType = m_buffer[15U] & 0x0FU; + data.setData(m_buffer + 20U); + data.setDataType(dataType); + data.setN(0U); + } else if (voiceSync) { + data.setData(m_buffer + 20U); + data.setDataType(DT_VOICE_SYNC); + data.setN(0U); + } else { + unsigned char n = m_buffer[15U] & 0x0FU; + data.setData(m_buffer + 20U); + data.setDataType(DT_VOICE); + data.setN(n); + } + + return true; +} + +bool CDMRNetwork::write(const CDMRData& data) +{ + unsigned char buffer[HOMEBREW_DATA_PACKET_LENGTH]; + ::memset(buffer, 0x00U, HOMEBREW_DATA_PACKET_LENGTH); + + buffer[0U] = 'D'; + buffer[1U] = 'M'; + buffer[2U] = 'R'; + buffer[3U] = 'D'; + + unsigned int srcId = data.getSrcId(); + buffer[5U] = srcId >> 16; + buffer[6U] = srcId >> 8; + buffer[7U] = srcId >> 0; + + unsigned int dstId = data.getDstId(); + buffer[8U] = dstId >> 16; + buffer[9U] = dstId >> 8; + buffer[10U] = dstId >> 0; + + ::memcpy(buffer + 11U, m_id, 4U); + + unsigned int slotNo = data.getSlotNo(); + + // Individual slot disabling + if (slotNo == 1U && !m_slot1) + return false; + if (slotNo == 2U && !m_slot2) + return false; + + buffer[15U] = slotNo == 1U ? 0x00U : 0x80U; + + FLCO flco = data.getFLCO(); + buffer[15U] |= flco == FLCO::GROUP ? 0x00U : 0x40U; + + unsigned int slotIndex = slotNo - 1U; + + std::uniform_int_distribution dist(0x00000001, 0xfffffffe); + unsigned char dataType = data.getDataType(); + if (dataType == DT_VOICE_SYNC) { + buffer[15U] |= 0x10U; + } else if (dataType == DT_VOICE) { + buffer[15U] |= data.getN(); + } else { + if (dataType == DT_VOICE_LC_HEADER) + m_streamId[slotIndex] = dist(m_random); + + if (dataType == DT_CSBK || dataType == DT_DATA_HEADER) + m_streamId[slotIndex] = dist(m_random); + + buffer[15U] |= (0x20U | dataType); + } + + buffer[4U] = data.getSeqNo(); + + ::memcpy(buffer + 16U, m_streamId + slotIndex, 4U); + + data.getData(buffer + 20U); + + buffer[53U] = data.getBER(); + + buffer[54U] = data.getRSSI(); + + write(buffer, HOMEBREW_DATA_PACKET_LENGTH); + + return true; +} + +bool CDMRNetwork::writeRadioPosition(unsigned int id, const unsigned char* data) +{ + unsigned char buffer[20U]; + + ::memcpy(buffer + 0U, "DMRG", 4U); + + buffer[4U] = id >> 16; + buffer[5U] = id >> 8; + buffer[6U] = id >> 0; + + ::memcpy(buffer + 7U, data + 2U, 7U); + + return write(buffer, 14U); +} + +bool CDMRNetwork::writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data) +{ + unsigned char buffer[20U]; + + ::memcpy(buffer + 0U, "DMRA", 4U); + + buffer[4U] = id >> 16; + buffer[5U] = id >> 8; + buffer[6U] = id >> 0; + + buffer[7U] = type; + + ::memcpy(buffer + 8U, data + 2U, 7U); + + return write(buffer, 15U); +} + +bool CDMRNetwork::isConnected() const +{ + return (m_addrLen != 0); +} + +void CDMRNetwork::close(bool sayGoodbye) +{ + LogMessage("DMR, Closing DMR Network"); + + m_socket.close(); +} + +void CDMRNetwork::clock(unsigned int ms) +{ + m_pingTimer.clock(ms); + if (m_pingTimer.isRunning() && m_pingTimer.hasExpired()) { + writeConfig(); + m_pingTimer.start(); + } + + sockaddr_storage address; + unsigned int addrLen; + int length = m_socket.read(m_buffer, BUFFER_LENGTH, address, addrLen); + if (length <= 0) + return; + + if (!CUDPSocket::match(m_addr, address)) { + LogMessage("DMR, packet received from an invalid source"); + return; + } + + if (m_debug) + CUtils::dump(1U, "DMR Network Received", m_buffer, length); + + if (::memcmp(m_buffer, "DMRD", 4U) == 0) { + if (m_enabled) { + unsigned char len = length; + m_rxData.addData(&len, 1U); + m_rxData.addData(m_buffer, len); + } + } else if (::memcmp(m_buffer, "DMRP", 4U) == 0) { + ; + } else if (::memcmp(m_buffer, "DMRB", 4U) == 0) { + m_beacon = true; + } else { + CUtils::dump("DMR, unknown packet from the DMR Network", m_buffer, length); + } +} + +bool CDMRNetwork::writeConfig() +{ + const char* software; + char slots = '0'; + if (m_duplex) { + if (m_slot1 && m_slot2) + slots = '3'; + else if (m_slot1 && !m_slot2) + slots = '1'; + else if (!m_slot1 && m_slot2) + slots = '2'; + + switch (m_hwType) { + case HW_TYPE::MMDVM: + software = "MMDVM"; + break; + case HW_TYPE::MMDVM_HS: + software = "MMDVM_MMDVM_HS"; + break; + case HW_TYPE::MMDVM_HS_DUAL_HAT: + software = "MMDVM_MMDVM_HS_Dual_Hat"; + break; + case HW_TYPE::NANO_HOTSPOT: + software = "MMDVM_Nano_hotSPOT"; + break; + default: + software = "MMDVM_Unknown"; + break; + } + } else { + slots = '4'; + + switch (m_hwType) { + case HW_TYPE::MMDVM: + software = "MMDVM_DMO"; + break; + case HW_TYPE::DVMEGA: + software = "MMDVM_DVMega"; + break; + case HW_TYPE::MMDVM_ZUMSPOT: + software = "MMDVM_ZUMspot"; + break; + case HW_TYPE::MMDVM_HS_HAT: + software = "MMDVM_MMDVM_HS_Hat"; + break; + case HW_TYPE::MMDVM_HS_DUAL_HAT: + software = "MMDVM_MMDVM_HS_Dual_Hat"; + break; + case HW_TYPE::NANO_HOTSPOT: + software = "MMDVM_Nano_hotSPOT"; + break; + case HW_TYPE::NANO_DV: + software = "MMDVM_Nano_DV"; + break; + case HW_TYPE::D2RG_MMDVM_HS: + software = "MMDVM_D2RG_MMDVM_HS"; + break; + case HW_TYPE::MMDVM_HS: + software = "MMDVM_MMDVM_HS"; + break; + case HW_TYPE::OPENGD77_HS: + software = "MMDVM_OpenGD77_HS"; + break; + case HW_TYPE::SKYBRIDGE: + software = "MMDVM_SkyBridge"; + break; + default: + software = "MMDVM_Unknown"; + break; + } + } + + unsigned int power = m_power; + if (power > 99U) + power = 99U; + + char buffer[150U]; + + ::memcpy(buffer + 0U, "DMRC", 4U); + ::memcpy(buffer + 4U, m_id, 4U); + ::sprintf(buffer + 8U, "%-8.8s%09u%09u%02u%02u%c%-40.40s%-40.40s", + m_callsign.c_str(), m_rxFrequency, m_txFrequency, power, m_colorCode, slots, m_version, + software); + + return write((unsigned char*)buffer, 119U); +} + +bool CDMRNetwork::wantsBeacon() +{ + bool beacon = m_beacon; + + m_beacon = false; + + return beacon; +} + +bool CDMRNetwork::write(const unsigned char* data, unsigned int length) +{ + assert(data != nullptr); + assert(length > 0U); + + if (m_debug) + CUtils::dump(1U, "DMR Network Transmitted", data, length); + + bool ret = m_socket.write(data, length, m_addr, m_addrLen); + if (!ret) { + LogError("DMR, socket error when writing to the DMR Network"); + return false; + } + + return true; +} + +#endif + diff --git a/DMRNetwork.h b/DMRNetwork.h index 006e097..2d90953 100644 --- a/DMRNetwork.h +++ b/DMRNetwork.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2018,2020,2021 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018,2020,2021,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,40 +19,78 @@ #if !defined(DMRNetwork_H) #define DMRNetwork_H +#include "UDPSocket.h" +#include "Timer.h" +#include "RingBuffer.h" #include "DMRData.h" +#include "Defines.h" + +#if defined(USE_DMR) #include +#include +#include -class IDMRNetwork +class CDMRNetwork { public: - virtual ~IDMRNetwork() = 0; + CDMRNetwork(const std::string& gatewayAddress, unsigned short gatewayPort, const std::string& localAddress, unsigned short localPort, unsigned int id, bool duplex, const char* version, bool slot1, bool slot2, HW_TYPE hwType, bool debug); + ~CDMRNetwork(); - virtual void setOptions(const std::string& options) = 0; + void setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode); - virtual void setConfig(const std::string& callsign, unsigned int rxFrequency, unsigned int txFrequency, unsigned int power, unsigned int colorCode, float latitude, float longitude, int height, const std::string& location, const std::string& description, const std::string& url) = 0; + bool open(); - virtual bool open() = 0; + void enable(bool enabled); - virtual void enable(bool enabled) = 0; + bool read(CDMRData& data); - virtual bool read(CDMRData& data) = 0; + bool write(const CDMRData& data); - virtual bool write(const CDMRData& data) = 0; + bool writeRadioPosition(unsigned int id, const unsigned char* data); - virtual bool writeRadioPosition(unsigned int id, const unsigned char* data) = 0; + bool writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data); - virtual bool writeTalkerAlias(unsigned int id, unsigned char type, const unsigned char* data) = 0; + bool wantsBeacon(); - virtual bool wantsBeacon() = 0; + void clock(unsigned int ms); - virtual void clock(unsigned int ms) = 0; + bool isConnected() const; - virtual bool isConnected() const = 0; - - virtual void close(bool sayGoodbye) = 0; + void close(bool sayGoodbye); private: + std::string m_addressStr; + sockaddr_storage m_addr; + unsigned int m_addrLen; + unsigned short m_port; + uint8_t* m_id; + bool m_duplex; + const char* m_version; + bool m_debug; + CUDPSocket m_socket; + bool m_enabled; + bool m_slot1; + bool m_slot2; + HW_TYPE m_hwType; + unsigned char* m_buffer; + uint32_t* m_streamId; + CRingBuffer m_rxData; + bool m_beacon; + std::mt19937 m_random; + std::string m_callsign; + unsigned int m_rxFrequency; + unsigned int m_txFrequency; + unsigned int m_power; + unsigned int m_colorCode; + CTimer m_pingTimer; + + bool writeConfig(); + + bool write(const unsigned char* data, unsigned int length); }; #endif + +#endif + diff --git a/DMRShortLC.cpp b/DMRShortLC.cpp index a511aca..aca67fa 100644 --- a/DMRShortLC.cpp +++ b/DMRShortLC.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,10 +17,11 @@ */ #include "DMRShortLC.h" - #include "Hamming.h" #include "Utils.h" +#if defined(USE_DMR) + #include #include #include @@ -226,3 +227,6 @@ void CDMRShortLC::encodeExtractBinary(unsigned char* data) CUtils::bitsToByteBE(m_rawData + 56U, data[7U]); CUtils::bitsToByteBE(m_rawData + 64U, data[8U]); } + +#endif + diff --git a/DMRShortLC.h b/DMRShortLC.h index af21589..5dc38da 100644 --- a/DMRShortLC.h +++ b/DMRShortLC.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(DMRSHORTLC_H) #define DMRSHORTLC_H +#include "Defines.h" + +#if defined(USE_DMR) + class CDMRShortLC { public: @@ -45,3 +49,6 @@ private: }; #endif + +#endif + diff --git a/DMRSlot.cpp b/DMRSlot.cpp index ac20000..7e4dbf2 100644 --- a/DMRSlot.cpp +++ b/DMRSlot.cpp @@ -26,6 +26,8 @@ #include "CRC.h" #include "Log.h" +#if defined(USE_DMR) + #include #include #include @@ -38,8 +40,7 @@ bool CDMRSlot::m_embeddedLCOnly = false; bool CDMRSlot::m_dumpTAData = true; CModem* CDMRSlot::m_modem = nullptr; -IDMRNetwork* CDMRSlot::m_network = nullptr; -CDisplay* CDMRSlot::m_display = nullptr; +CDMRNetwork* CDMRSlot::m_network = nullptr; bool CDMRSlot::m_duplex = true; CDMRLookup* CDMRSlot::m_lookup = nullptr; unsigned int CDMRSlot::m_hangCount = 3U * 17U; @@ -70,7 +71,8 @@ const unsigned int NO_HEADERS_SIMPLEX = 8U; const unsigned int NO_HEADERS_DUPLEX = 3U; const unsigned int NO_PREAMBLE_CSBK = 15U; -// #define DUMP_DMR +const unsigned int RSSI_COUNT = 3U; // 3 * 360ms = 1080ms +const unsigned int BER_COUNT = 18U * 141U; // 18 * 60ms = 1080ms CDMRSlot::CDMRSlot(unsigned int slotNo, unsigned int timeout) : m_slotNo(slotNo), @@ -113,13 +115,16 @@ m_rfTimeout(false), m_netTimeout(false), m_lastFrame(nullptr), m_lastFrameValid(false), -m_rssi(0U), -m_maxRSSI(0U), -m_minRSSI(0U), -m_aveRSSI(0U), +m_rssi(0), +m_maxRSSI(0), +m_minRSSI(0), +m_aveRSSI(0), +m_rssiCountTotal(0U), +m_rssiAccum(0), m_rssiCount(0U), -m_enabled(true), -m_fp(nullptr) +m_bitErrsAccum(0U), +m_bitsCount(0U), +m_enabled(true) { m_lastFrame = new unsigned char[DMR_FRAME_LENGTH_BYTES + 2U]; @@ -144,14 +149,20 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) return false; if ((data[0U] == TAG_LOST) && (m_rfState == RPT_RF_STATE::AUDIO)) { - std::string src = m_lookup->find(m_rfLC->getSrcId()); - std::string dst = m_lookup->find(m_rfLC->getDstId()); + unsigned int srcId = m_rfLC->getSrcId(); + unsigned int dstId = m_rfLC->getDstId(); + std::string src = m_lookup->find(srcId); + std::string dst = m_lookup->find(dstId); FLCO flco = m_rfLC->getFLCO(); - if (m_rssi != 0U) - LogMessage("DMR Slot %u, RF voice transmission lost from %s to %s%s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_slotNo, src.c_str(), flco == FLCO::GROUP ? "TG " : "", dst.c_str(), float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else + if (m_rssi != 0) { + LogMessage("DMR Slot %u, RF voice transmission lost from %s to %s%s, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", m_slotNo, src.c_str(), flco == FLCO::GROUP ? "TG " : "", dst.c_str(), float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCountTotal); + writeJSONRF("lost", float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + } else { LogMessage("DMR Slot %u, RF voice transmission lost from %s to %s%s, %.1f seconds, BER: %.1f%%", m_slotNo, src.c_str(), flco == FLCO::GROUP ? "TG " : "", dst.c_str(), float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeJSONRF("lost", float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits)); + } + if (m_rfTimeout) { writeEndRF(); return false; @@ -162,11 +173,14 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) } if ((data[0U] == TAG_LOST) && (m_rfState == RPT_RF_STATE::DATA)) { - std::string src = m_lookup->find(m_rfLC->getSrcId()); - std::string dst = m_lookup->find(m_rfLC->getDstId()); + unsigned int srcId = m_rfLC->getSrcId(); + unsigned int dstId = m_rfLC->getDstId(); + std::string src = m_lookup->find(srcId); + std::string dst = m_lookup->find(dstId); FLCO flco = m_rfLC->getFLCO(); LogMessage("DMR Slot %u, RF data transmission lost from %s to %s%s", m_slotNo, src.c_str(), flco == FLCO::GROUP ? "TG " : "", dst.c_str()); + writeJSONRF("lost"); writeEndRF(); return false; } @@ -183,19 +197,19 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) raw |= (data[36U] << 0) & 0x00FFU; // Convert the raw RSSI to dBm - int rssi = m_rssiMapper->interpolate(raw); - if (rssi != 0) - LogDebug("DMR Slot %u, raw RSSI: %u, reported RSSI: %d dBm", m_slotNo, raw, rssi); + m_rssi = m_rssiMapper->interpolate(raw); + if (m_rssi != 0) + LogDebug("DMR Slot %u, raw RSSI: %u, reported RSSI: %d dBm", m_slotNo, raw, m_rssi); - // RSSI is always reported as positive - m_rssi = (rssi >= 0) ? rssi : -rssi; - - if (m_rssi > m_minRSSI) + if (m_rssi < m_minRSSI) m_minRSSI = m_rssi; - if (m_rssi < m_maxRSSI) + if (m_rssi > m_maxRSSI) m_maxRSSI = m_rssi; m_aveRSSI += m_rssi; + m_rssiCountTotal++; + + m_rssiAccum += m_rssi; m_rssiCount++; } @@ -221,7 +235,9 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) unsigned int srcId = lc->getSrcId(); unsigned int dstId = lc->getDstId(); - FLCO flco = lc->getFLCO(); + std::string src = m_lookup->find(srcId); + std::string dst = m_lookup->find(dstId); + FLCO flco = lc->getFLCO(); if (!m_protect) { if (lc->getPF()) { @@ -234,6 +250,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (!CDMRAccessControl::validateSrcId(srcId)) { LogMessage("DMR Slot %u, RF user %u rejected", m_slotNo, srcId); + writeJSONRF("rejected", srcId, src, flco == FLCO::GROUP, dstId); delete lc; m_rfState = RPT_RF_STATE::LISTENING; return false; @@ -241,6 +258,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (!CDMRAccessControl::validateTGId(m_slotNo, flco == FLCO::GROUP, dstId)) { LogMessage("DMR Slot %u, RF user %u rejected for using TG %u", m_slotNo, srcId, dstId); + writeJSONRF("rejected", srcId, src, flco == FLCO::GROUP, dstId); delete lc; m_rfState = RPT_RF_STATE::LISTENING; return false; @@ -282,11 +300,17 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) m_rfEmbeddedWriteN = 1U; m_rfTalkerId = TALKER_ID_NONE; - m_minRSSI = m_rssi; - m_maxRSSI = m_rssi; - m_aveRSSI = m_rssi; + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; + m_rssiCountTotal = 1U; + + m_rssiAccum = m_rssi; m_rssiCount = 1U; + m_bitsCount = 0U; + m_bitErrsAccum = 0U; + if (m_duplex) { m_queue.clear(); m_modem->writeDMRAbort(m_slotNo); @@ -299,16 +323,14 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) m_rfState = RPT_RF_STATE::AUDIO; - std::string src = m_lookup->find(srcId); - std::string dst = m_lookup->find(dstId); - if (m_netState == RPT_NET_STATE::IDLE) { setShortLC(m_slotNo, dstId, flco, ACTIVITY_TYPE::VOICE); - m_display->writeDMR(m_slotNo, src, flco == FLCO::GROUP, dst, "R"); - m_display->writeDMRRSSI(m_slotNo, m_rssi); + writeJSONRSSI(); + writeJSONBER(); } LogMessage("DMR Slot %u, received RF voice header from %s to %s%s", m_slotNo, src.c_str(), flco == FLCO::GROUP ? "TG " : "", dst.c_str()); + writeJSONRF("start", srcId, src, flco == FLCO::GROUP, dstId); return true; } else if (dataType == DT_VOICE_PI_HEADER) { @@ -364,16 +386,19 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) } } - std::string src = m_lookup->find(m_rfLC->getSrcId()); - std::string dst = m_lookup->find(m_rfLC->getDstId()); + unsigned int srcId = m_rfLC->getSrcId(); + unsigned int dstId = m_rfLC->getDstId(); + std::string src = m_lookup->find(srcId); + std::string dst = m_lookup->find(dstId); FLCO flco = m_rfLC->getFLCO(); - if (m_rssi != 0U) - LogMessage("DMR Slot %u, received RF end of voice transmission from %s to %s%s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_slotNo, src.c_str(), flco == FLCO::GROUP ? "TG " : "", dst.c_str(), float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else + if (m_rssi != 0) { + LogMessage("DMR Slot %u, received RF end of voice transmission from %s to %s%s, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", m_slotNo, src.c_str(), flco == FLCO::GROUP ? "TG " : "", dst.c_str(), float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + writeJSONRF("end", float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + } else { LogMessage("DMR Slot %u, received RF end of voice transmission from %s to %s%s, %.1f seconds, BER: %.1f%%", m_slotNo, src.c_str(), flco == FLCO::GROUP ? "TG " : "", dst.c_str(), float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits)); - - m_display->writeDMRTA(m_slotNo, nullptr, " "); + writeJSONRF("end", float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits)); + } if (m_rfTimeout) { writeEndRF(); @@ -394,15 +419,19 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) bool gi = dataHeader.getGI(); unsigned int srcId = dataHeader.getSrcId(); unsigned int dstId = dataHeader.getDstId(); + std::string src = m_lookup->find(srcId); + std::string dst = m_lookup->find(dstId); if (!CDMRAccessControl::validateSrcId(srcId)) { LogMessage("DMR Slot %u, RF user %u rejected", m_slotNo, srcId); + writeJSONRF("rejected", srcId, src, gi, dstId); m_rfState = RPT_RF_STATE::LISTENING; return false; } if (!CDMRAccessControl::validateTGId(m_slotNo, gi, dstId)) { LogMessage("DMR Slot %u, RF user %u rejected for using TG %u", m_slotNo, srcId, dstId); + writeJSONRF("rejected", srcId, src, gi, dstId); m_rfState = RPT_RF_STATE::LISTENING; return false; } @@ -430,19 +459,17 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) m_rfState = RPT_RF_STATE::DATA; - std::string src = m_lookup->find(srcId); - std::string dst = m_lookup->find(dstId); - if (m_netState == RPT_NET_STATE::IDLE) { setShortLC(m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::USER_USER, ACTIVITY_TYPE::DATA); - m_display->writeDMR(m_slotNo, src, gi, dst, "R"); - m_display->writeDMRRSSI(m_slotNo, m_rssi); + writeJSONRSSI(); } LogMessage("DMR Slot %u, received RF data header from %s to %s%s, %u blocks", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str(), m_rfFrames); + writeJSONRF("start", srcId, src, gi, dstId, m_rfFrames); if (m_rfFrames == 0U) { LogMessage("DMR Slot %u, ended RF data transmission from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG " : "", dst.c_str()); + writeJSONRF("end"); writeEndRF(); } @@ -466,16 +493,20 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) bool gi = csbk.getGI(); unsigned int srcId = csbk.getSrcId(); unsigned int dstId = csbk.getDstId(); + std::string src = m_lookup->find(srcId); + std::string dst = m_lookup->find(dstId); if (srcId != 0U || dstId != 0U) { if (!CDMRAccessControl::validateSrcId(srcId)) { LogMessage("DMR Slot %u, RF user %u rejected", m_slotNo, srcId); + writeJSONRF("rejected", srcId, src, gi, dstId); m_rfState = RPT_RF_STATE::LISTENING; return false; } if (!CDMRAccessControl::validateTGId(m_slotNo, gi, dstId)) { LogMessage("DMR Slot %u, RF user %u rejected for using TG %u", m_slotNo, srcId, dstId); + writeJSONRF("rejected", srcId, src, gi, dstId); m_rfState = RPT_RF_STATE::LISTENING; return false; } @@ -498,33 +529,38 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) writeNetworkRF(data, DT_CSBK, gi ? FLCO::GROUP : FLCO::USER_USER, srcId, dstId); - std::string src = m_lookup->find(srcId); - std::string dst = m_lookup->find(dstId); - switch (csbko) { case CSBKO::UUVREQ: LogMessage("DMR Slot %u, received RF Unit to Unit Voice Service Request CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str()); + writeJSONRF("csbk", "Unit to Unit Voice Service Request", srcId, src, gi, dstId); break; case CSBKO::UUANSRSP: LogMessage("DMR Slot %u, received RF Unit to Unit Voice Service Answer Response CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str()); + writeJSONRF("csbk", "Unit to Unit Voice Answer Response", srcId, src, gi, dstId); break; case CSBKO::NACKRSP: LogMessage("DMR Slot %u, received RF Negative Acknowledgment Response CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str()); + writeJSONRF("csbk", "Negative Acknowledgment Response", srcId, src, gi, dstId); break; case CSBKO::PRECCSBK: LogMessage("DMR Slot %u, received RF %s Preamble CSBK (%u to follow) from %s to %s%s", m_slotNo, csbk.getDataContent() ? "Data" : "CSBK", csbk.getCBF(), src.c_str(), gi ? "TG ": "", dst.c_str()); + writeJSONRF("csbk", "Preamble", srcId, src, gi, dstId); break; case CSBKO::CALL_ALERT: LogMessage("DMR Slot %u, received RF Call Alert CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG " : "", dst.c_str()); + writeJSONRF("csbk", "Call Alert", srcId, src, gi, dstId); break; case CSBKO::CALL_ALERT_ACK: LogMessage("DMR Slot %u, received RF Call Alert Ack CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG " : "", dst.c_str()); + writeJSONRF("csbk", "Call Alert Ack", srcId, src, gi, dstId); break; case CSBKO::RADIO_CHECK: LogMessage("DMR Slot %u, received RF Radio Check %s CSBK from %s to %s%s", m_slotNo, /* TBD */ 1 ? "Req" : "Ack", src.c_str(), gi ? "TG " : "", dst.c_str()); + writeJSONRF("csbk", "Radio Check", srcId, src, gi, dstId); break; case CSBKO::CALL_EMERGENCY: LogMessage("DMR Slot %u, received RF Call Emergency CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG " : "", dst.c_str()); + writeJSONRF("csbk", "Call Emergency", srcId, src, gi, dstId); break; default: LogWarning("DMR Slot %u, unhandled RF CSBK type - 0x%02X", m_slotNo, csbko); @@ -534,8 +570,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) // If data preamble, signal its existence if ((m_netState == RPT_NET_STATE::IDLE) && (csbko == CSBKO::PRECCSBK) && csbk.getDataContent()) { setShortLC(m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::USER_USER, ACTIVITY_TYPE::DATA); - m_display->writeDMR(m_slotNo, src, gi, dst, "R"); - m_display->writeDMRRSSI(m_slotNo, m_rssi); + writeJSONRSSI(); } return true; @@ -587,6 +622,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (m_rfFrames == 0U) { LogMessage("DMR Slot %u, ended RF data transmission", m_slotNo); + writeJSONRF("end"); writeEndRF(); } @@ -603,11 +639,14 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (fid == FID_ETSI || fid == FID_DMRA) { errors = m_fec.regenerateDMR(data + 2U); LogDebug("DMR Slot %u, audio sequence no. 0, errs: %u/141 (%.1f%%)", m_slotNo, errors, float(errors) / 1.41F); - m_display->writeDMRBER(m_slotNo, float(errors) / 1.41F); - m_rfErrs += errors; + m_rfErrs += errors; + m_bitErrsAccum += errors; + + writeJSONBER(); } - m_rfBits += 141U; + m_bitsCount += 141U; + m_rfBits += 141U; m_rfFrames++; m_rfEmbeddedReadN = (m_rfEmbeddedReadN + 1U) % 2U; @@ -615,7 +654,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) m_rfEmbeddedData[m_rfEmbeddedWriteN].reset(); - m_display->writeDMRRSSI(m_slotNo, m_rssi); + writeJSONRSSI(); if (!m_rfTimeout) { data[0U] = TAG_DATA; @@ -651,11 +690,14 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (fid == FID_ETSI || fid == FID_DMRA) { errors = m_fec.regenerateDMR(data + 2U); LogDebug("DMR Slot %u, audio sequence no. %u, errs: %u/141 (%.1f%%)", m_slotNo, m_rfN, errors, float(errors) / 1.41F); - m_display->writeDMRBER(m_slotNo, float(errors) / 1.41F); - m_rfErrs += errors; + m_rfErrs += errors; + m_bitErrsAccum += errors; + + writeJSONBER(); } - m_rfBits += 141U; + m_bitsCount += 141U; + m_rfBits += 141U; m_rfFrames++; // Get the LCSS from the EMB @@ -696,8 +738,10 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (!(m_rfTalkerId & TALKER_ID_HEADER)) { if (m_rfTalkerId == TALKER_ID_NONE) m_rfTalkerAlias.reset(); - m_rfTalkerAlias.add(0U, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, m_rfTalkerAlias.get(), "R"); + + bool complete = m_rfTalkerAlias.add(0U, data + 2U, 7U); + if (complete) + writeJSONText(m_rfTalkerAlias.get()); if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Header", m_slotNo); @@ -715,8 +759,10 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (!(m_rfTalkerId & TALKER_ID_BLOCK1)) { if (m_rfTalkerId == TALKER_ID_NONE) m_rfTalkerAlias.reset(); - m_rfTalkerAlias.add(1U, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, m_rfTalkerAlias.get(), "R"); + + bool complete = m_rfTalkerAlias.add(1U, data + 2U, 7U); + if (complete) + writeJSONText(m_rfTalkerAlias.get()); if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 1", m_slotNo); @@ -734,8 +780,10 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (!(m_rfTalkerId & TALKER_ID_BLOCK2)) { if (m_rfTalkerId == TALKER_ID_NONE) m_rfTalkerAlias.reset(); - m_rfTalkerAlias.add(2U, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, m_rfTalkerAlias.get(), "R"); + + bool complete = m_rfTalkerAlias.add(2U, data + 2U, 7U); + if (complete) + writeJSONText(m_rfTalkerAlias.get()); if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 2", m_slotNo); @@ -753,8 +801,10 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (!(m_rfTalkerId & TALKER_ID_BLOCK3)) { if (m_rfTalkerId == TALKER_ID_NONE) m_rfTalkerAlias.reset(); - m_rfTalkerAlias.add(3U, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, m_rfTalkerAlias.get(), "R"); + + bool complete = m_rfTalkerAlias.add(3U, data + 2U, 7U); + if (complete) + writeJSONText(m_rfTalkerAlias.get()); if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 3", m_slotNo); @@ -820,7 +870,9 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (lc != nullptr) { unsigned int srcId = lc->getSrcId(); unsigned int dstId = lc->getDstId(); - FLCO flco = lc->getFLCO(); + std::string src = m_lookup->find(srcId); + std::string dst = m_lookup->find(dstId); + FLCO flco = lc->getFLCO(); if (!m_protect) { if (lc->getPF()) { @@ -833,6 +885,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (!CDMRAccessControl::validateSrcId(srcId)) { LogMessage("DMR Slot %u, RF user %u rejected", m_slotNo, srcId); + writeJSONRF("rejected", srcId, src, flco == FLCO::GROUP, dstId); delete lc; m_rfState = RPT_RF_STATE::LISTENING; return false; @@ -840,6 +893,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (!CDMRAccessControl::validateTGId(m_slotNo, flco == FLCO::GROUP, dstId)) { LogMessage("DMR Slot %u, RF user %u rejected for using TG %u", m_slotNo, srcId, dstId); + writeJSONRF("rejected", srcId, src, flco == FLCO::GROUP, dstId); delete lc; m_rfState = RPT_RF_STATE::LISTENING; return false; @@ -885,11 +939,17 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) m_rfEmbeddedWriteN = 1U; m_rfTalkerId = TALKER_ID_NONE; - m_minRSSI = m_rssi; - m_maxRSSI = m_rssi; - m_aveRSSI = m_rssi; + m_minRSSI = m_rssi; + m_maxRSSI = m_rssi; + m_aveRSSI = m_rssi; + m_rssiCountTotal = 1U; + + m_rssiAccum = m_rssi; m_rssiCount = 1U; + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + if (m_duplex) { m_queue.clear(); m_modem->writeDMRAbort(m_slotNo); @@ -916,10 +976,12 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (fid == FID_ETSI || fid == FID_DMRA) { errors = m_fec.regenerateDMR(data + 2U); LogDebug("DMR Slot %u, audio sequence no. %u, errs: %u/141 (%.1f%%)", m_slotNo, m_rfN, errors, float(errors) / 1.41F); - m_rfErrs += errors; + m_bitErrsAccum += errors; + m_rfErrs += errors; } - m_rfBits += 141U; + m_bitsCount += 141U; + m_rfBits += 141U; m_rfFrames++; data[0U] = TAG_DATA; @@ -932,17 +994,14 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) m_rfState = RPT_RF_STATE::AUDIO; - std::string src = m_lookup->find(srcId); - std::string dst = m_lookup->find(dstId); - if (m_netState == RPT_NET_STATE::IDLE) { setShortLC(m_slotNo, dstId, flco, ACTIVITY_TYPE::VOICE); - m_display->writeDMR(m_slotNo, src, flco == FLCO::GROUP, dst, "R"); - m_display->writeDMRRSSI(m_slotNo, m_rssi); - m_display->writeDMRBER(m_slotNo, float(errors) / 1.41F); + writeJSONRSSI(); + writeJSONBER(); } LogMessage("DMR Slot %u, received RF late entry from %s to %s%s", m_slotNo, src.c_str(), flco == FLCO::GROUP ? "TG " : "", dst.c_str()); + writeJSONRF("late_entry", srcId, src, flco == FLCO::GROUP, dstId); return true; } @@ -971,10 +1030,8 @@ void CDMRSlot::writeEndRF(bool writeEnd) { m_rfState = RPT_RF_STATE::LISTENING; - if (m_netState == RPT_NET_STATE::IDLE) { + if (m_netState == RPT_NET_STATE::IDLE) setShortLC(m_slotNo, 0U); - m_display->clearDMR(m_slotNo); - } if (writeEnd) { if ((m_netState == RPT_NET_STATE::IDLE) && m_duplex && !m_rfTimeout) { @@ -1006,6 +1063,9 @@ void CDMRSlot::writeEndRF(bool writeEnd) m_rfErrs = 0U; m_rfBits = 1U; + m_bitErrsAccum = 0U; + m_bitsCount = 1U; + m_rfSeqNo = 0U; m_rfN = 0U; @@ -1019,8 +1079,6 @@ void CDMRSlot::writeEndNet(bool writeEnd) setShortLC(m_slotNo, 0U); - m_display->clearDMR(m_slotNo); - m_lastFrameValid = false; if (writeEnd && !m_netTimeout) { @@ -1064,10 +1122,6 @@ void CDMRSlot::writeEndNet(bool writeEnd) delete m_netLC; m_netLC = nullptr; - -#if defined(DUMP_DMR) - closeFile(); -#endif } void CDMRSlot::writeNetwork(const CDMRData& dmrData) @@ -1169,14 +1223,9 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) std::string dst = m_lookup->find(dstId); class CUserDBentry cn; m_lookup->findWithName(srcId, &cn); - m_display->writeDMR(m_slotNo, cn, flco == FLCO::GROUP, dst, "N"); - -#if defined(DUMP_DMR) - openFile(); - writeFile(data); -#endif LogMessage("DMR Slot %u, received network voice header from %s to %s%s", m_slotNo, src.c_str(), flco == FLCO::GROUP ? "TG " : "", dst.c_str()); + writeJSONNet("start", srcId, src, flco == FLCO::GROUP, dstId); } else if (dataType == DT_VOICE_PI_HEADER) { if (m_netState != RPT_NET_STATE::AUDIO) { CDMRLC* lc = new CDMRLC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId()); @@ -1228,9 +1277,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) writeQueueRF(start); } -#if defined(DUMP_DMR) - openFile(); -#endif m_netFrames = 0U; m_netLost = 0U; m_netBits = 1U; @@ -1244,9 +1290,8 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) class CUserDBentry cn; m_lookup->findWithName(srcId, &cn); - m_display->writeDMR(m_slotNo, cn, m_netLC->getFLCO() == FLCO::GROUP, dst, "N"); - LogMessage("DMR Slot %u, received network late entry from %s to %s%s", m_slotNo, src.c_str(), m_netLC->getFLCO() == FLCO::GROUP ? "TG " : "", dst.c_str()); + writeJSONNet("late_entry", srcId, src, m_netLC->getFLCO() == FLCO::GROUP, dstId); } // Regenerate the Slot Type @@ -1267,12 +1312,7 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) data[0U] = TAG_DATA; data[1U] = 0x00U; - if (m_protect) - writeQueueNet(data); - -#if defined(DUMP_DMR) - writeFile(data); -#endif + writeQueueNet(data); } else if (dataType == DT_TERMINATOR_WITH_LC) { if (m_netState != RPT_NET_STATE::AUDIO) return; @@ -1303,18 +1343,16 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) } } -#if defined(DUMP_DMR) - writeFile(data); - closeFile(); -#endif - std::string src = m_lookup->find(m_netLC->getSrcId()); - std::string dst = m_lookup->find(m_netLC->getDstId()); + unsigned int srcId = m_netLC->getSrcId(); + unsigned int dstId = m_netLC->getDstId(); + std::string src = m_lookup->find(srcId); + std::string dst = m_lookup->find(dstId); FLCO flco = m_netLC->getFLCO(); // We've received the voice header and terminator haven't we? m_netFrames += 2U; LogMessage("DMR Slot %u, received network end of voice transmission from %s to %s%s, %.1f seconds, %u%% packet loss, BER: %.1f%%", m_slotNo, src.c_str(), flco == FLCO::GROUP ? "TG " : "", dst.c_str(), float(m_netFrames) / 16.667F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); - m_display->writeDMRTA(m_slotNo, nullptr, " "); + writeJSONNet("end", float(m_netFrames) / 16.667F, float(m_netLost * 100U) / float(m_netFrames), float(m_netErrs * 100U) / float(m_netBits)); writeEndNet(); } else if (dataType == DT_DATA_HEADER) { if (m_netState == RPT_NET_STATE::DATA) @@ -1361,12 +1399,12 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) std::string src = m_lookup->find(srcId); std::string dst = m_lookup->find(dstId); - m_display->writeDMR(m_slotNo, src, gi, dst, "N"); - LogMessage("DMR Slot %u, received network data header from %s to %s%s, %u blocks", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str(), m_netFrames); + writeJSONNet("start", srcId, src, gi, dstId, m_netFrames); if (m_netFrames == 0U) { LogMessage("DMR Slot %u, ended network data transmission from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG " : "", dst.c_str()); + writeJSONNet("end"); writeEndNet(); } } else if (dataType == DT_VOICE_SYNC) { @@ -1425,9 +1463,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) writeQueueRF(start); } -#if defined(DUMP_DMR) - openFile(); -#endif m_netFrames = 0U; m_netLost = 0U; m_netBits = 1U; @@ -1446,9 +1481,8 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) class CUserDBentry cn; m_lookup->findWithName(srcId, &cn); - m_display->writeDMR(m_slotNo, cn, m_netLC->getFLCO() == FLCO::GROUP, dst, "N"); - LogMessage("DMR Slot %u, received network late entry from %s to %s%s", m_slotNo, src.c_str(), m_netLC->getFLCO() == FLCO::GROUP ? "TG " : "", dst.c_str()); + writeJSONNet("late_entry", srcId, src, m_netLC->getFLCO() == FLCO::GROUP, dstId); } if (m_netState == RPT_NET_STATE::AUDIO) { @@ -1488,10 +1522,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) // Save details in case we need to infill data m_netN = dmrData.getN(); - -#if defined(DUMP_DMR) - writeFile(data); -#endif } } else if (dataType == DT_VOICE) { if (m_netState != RPT_NET_STATE::AUDIO) @@ -1533,8 +1563,10 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) if (!(m_netTalkerId & TALKER_ID_HEADER)) { if (!m_netTalkerId) m_netTalkerAlias.reset(); - m_netTalkerAlias.add(0U, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, m_netTalkerAlias.get(), "N"); + + bool complete = m_netTalkerAlias.add(0U, data + 2U, 7U); + if (complete) + writeJSONText(m_netTalkerAlias.get()); if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Header", m_slotNo); @@ -1548,8 +1580,10 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) if (!(m_netTalkerId & TALKER_ID_BLOCK1)) { if (!m_netTalkerId) m_netTalkerAlias.reset(); - m_netTalkerAlias.add(1U, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, m_netTalkerAlias.get(), "N"); + + bool complete = m_netTalkerAlias.add(1U, data + 2U, 7U); + if (complete) + writeJSONText(m_netTalkerAlias.get()); if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 1", m_slotNo); @@ -1563,8 +1597,10 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) if (!(m_netTalkerId & TALKER_ID_BLOCK2)) { if (!m_netTalkerId) m_netTalkerAlias.reset(); - m_netTalkerAlias.add(2U, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, m_netTalkerAlias.get(), "N"); + + bool complete = m_netTalkerAlias.add(2U, data + 2U, 7U); + if (complete) + writeJSONText(m_netTalkerAlias.get()); if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 2", m_slotNo); @@ -1578,8 +1614,10 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) if (!(m_netTalkerId & TALKER_ID_BLOCK3)) { if (!m_netTalkerId) m_netTalkerAlias.reset(); - m_netTalkerAlias.add(3U, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, m_netTalkerAlias.get(), "N"); + + bool complete = m_netTalkerAlias.add(3U, data + 2U, 7U); + if (complete) + writeJSONText(m_netTalkerAlias.get()); if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 3", m_slotNo); @@ -1635,10 +1673,6 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) // Save details in case we need to infill data m_netN = dmrData.getN(); - -#if defined(DUMP_DMR) - writeFile(data); -#endif } else if (dataType == DT_CSBK) { CDMRCSBK csbk; bool valid = csbk.put(data + 2U); @@ -1699,36 +1733,37 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) } else writeQueueNet(data); -#if defined(DUMP_DMR) - openFile(); - writeFile(data); - closeFile(); -#endif - std::string src = m_lookup->find(srcId); std::string dst = m_lookup->find(dstId); switch (csbko) { case CSBKO::UUVREQ: LogMessage("DMR Slot %u, received network Unit to Unit Voice Service Request CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str()); + writeJSONNet("csbk", "Unit to Unit Voice Service Request", srcId, src, gi, dstId); break; case CSBKO::UUANSRSP: LogMessage("DMR Slot %u, received network Unit to Unit Voice Service Answer Response CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str()); + writeJSONNet("csbk", "Unit to Unit Voice Service Answer Response", srcId, src, gi, dstId); break; case CSBKO::NACKRSP: LogMessage("DMR Slot %u, received network Negative Acknowledgment Response CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str()); + writeJSONNet("csbk", "UNegative Acknowledgment Response", srcId, src, gi, dstId); break; case CSBKO::PRECCSBK: LogMessage("DMR Slot %u, received network %s Preamble CSBK (%u to follow) from %s to %s%s", m_slotNo, csbk.getDataContent() ? "Data" : "CSBK", csbk.getCBF(), src.c_str(), gi ? "TG " : "", dst.c_str()); + writeJSONNet("csbk", "Preamble", srcId, src, gi, dstId); break; case CSBKO::CALL_ALERT: LogMessage("DMR Slot %u, received network Call Alert CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG " : "", dst.c_str()); + writeJSONNet("csbk", "Call Alert", srcId, src, gi, dstId); break; case CSBKO::CALL_ALERT_ACK: LogMessage("DMR Slot %u, received network Call Alert Ack CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG " : "", dst.c_str()); + writeJSONNet("csbk", "Call Alert Ack", srcId, src, gi, dstId); break; case CSBKO::RADIO_CHECK: LogMessage("DMR Slot %u, received network Radio Check %s CSBK from %s to %s%s", m_slotNo, /* TBD */ 1 ? "Req" : "Ack", src.c_str(), gi ? "TG " : "", dst.c_str()); + writeJSONNet("csbk", "Radio Check", srcId, src, gi, dstId); break; default: LogWarning("DMR Slot %u, unhandled network CSBK type - 0x%02X", m_slotNo, csbko); @@ -1736,11 +1771,9 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) } // If data preamble, signal its existence - if ((csbko == CSBKO::PRECCSBK) && csbk.getDataContent()) { + if ((csbko == CSBKO::PRECCSBK) && csbk.getDataContent()) setShortLC(m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::USER_USER, ACTIVITY_TYPE::DATA); - m_display->writeDMR(m_slotNo, src, gi, dst, "N"); - } - } else if ((dataType == DT_RATE_12_DATA) || (dataType == DT_RATE_34_DATA) || (dataType == DT_RATE_1_DATA)) { + } else if (dataType == DT_RATE_12_DATA || dataType == DT_RATE_34_DATA || dataType == DT_RATE_1_DATA) { if ((m_netState != RPT_NET_STATE::DATA) || (m_netFrames == 0U)) { writeEndNet(); return; @@ -1787,13 +1820,11 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) data[0U] = m_netFrames == 0U ? TAG_EOT : TAG_DATA; data[1U] = 0x00U; -#if defined(DUMP_DMR) - writeFile(data); -#endif writeQueueNet(data); if (m_netFrames == 0U) { LogMessage("DMR Slot %u, ended network data transmission", m_slotNo); + writeJSONNet("end"); writeEndNet(); } } else { @@ -1858,6 +1889,7 @@ void CDMRSlot::clock() if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) { if (!m_rfTimeout) { LogMessage("DMR Slot %u, RF user has timed out", m_slotNo); + writeJSONRF("timeout"); m_rfTimeout = true; } } @@ -1866,6 +1898,7 @@ void CDMRSlot::clock() if (m_netTimeoutTimer.isRunning() && m_netTimeoutTimer.hasExpired()) { if (!m_netTimeout) { LogMessage("DMR Slot %u, network user has timed out", m_slotNo); + writeJSONNet("timeout"); m_netTimeout = true; } } @@ -1881,16 +1914,12 @@ void CDMRSlot::clock() // We've received the voice header haven't we? m_netFrames += 1U; LogMessage("DMR Slot %u, network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%", m_slotNo, float(m_netFrames) / 16.667F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); + writeJSONNet("lost", float(m_netFrames) / 16.667F, float(m_netLost * 100U) / float(m_netFrames), float(m_netErrs * 100U) / float(m_netBits)); writeEndNet(true); -#if defined(DUMP_DMR) - closeFile(); -#endif } else { LogMessage("DMR Slot %u, network watchdog has expired", m_slotNo); + writeJSONNet("lost"); writeEndNet(); -#if defined(DUMP_DMR) - closeFile(); -#endif } } } @@ -1949,7 +1978,7 @@ void CDMRSlot::writeNetworkRF(const unsigned char* data, unsigned char dataType, dmrData.setN(m_rfN); dmrData.setSeqNo(m_rfSeqNo); dmrData.setBER(errors); - dmrData.setRSSI(m_rssi); + dmrData.setRSSI(-m_rssi); // Always report as positive m_rfSeqNo++; @@ -1982,10 +2011,9 @@ void CDMRSlot::writeQueueNet(const unsigned char *data) m_queue.addData(data, len); } -void CDMRSlot::init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter, DMR_OVCM ovcm, bool protect) +void CDMRSlot::init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, CDMRNetwork* network, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter, DMR_OVCM ovcm, bool protect) { assert(modem != nullptr); - assert(display != nullptr); assert(lookup != nullptr); assert(rssiMapper != nullptr); @@ -1994,7 +2022,6 @@ void CDMRSlot::init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData m_dumpTAData = dumpTAData; m_modem = modem; m_network = network; - m_display = display; m_duplex = duplex; m_lookup = lookup; m_hangCount = callHang * 17U; @@ -2112,46 +2139,6 @@ void CDMRSlot::setShortLC(unsigned int slotNo, unsigned int id, FLCO flco, ACTIV m_modem->writeDMRShortLC(sLC); } -bool CDMRSlot::openFile() -{ - if (m_fp != nullptr) - return true; - - time_t t; - ::time(&t); - - struct tm* tm = ::localtime(&t); - - char name[100U]; - ::sprintf(name, "DMR_%u_%04d%02d%02d_%02d%02d%02d.ambe", m_slotNo, 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 == nullptr) - return false; - - ::fwrite("DMR", 1U, 3U, m_fp); - - return true; -} - -bool CDMRSlot::writeFile(const unsigned char* data) -{ - if (m_fp == nullptr) - return false; - - ::fwrite(data, 1U, DMR_FRAME_LENGTH_BYTES + 2U, m_fp); - - return true; -} - -void CDMRSlot::closeFile() -{ - if (m_fp != nullptr) { - ::fclose(m_fp); - m_fp = nullptr; - } -} - bool CDMRSlot::insertSilence(const unsigned char* data, unsigned char seqNo) { assert(data != nullptr); @@ -2263,6 +2250,9 @@ void CDMRSlot::enable(bool enabled) m_rfErrs = 0U; m_rfBits = 1U; + m_bitErrsAccum = 0U; + m_bitsCount = 1U; + m_rfSeqNo = 0U; m_rfN = 0U; @@ -2304,3 +2294,234 @@ void CDMRSlot::enable(bool enabled) m_enabled = enabled; } + +void CDMRSlot::writeJSONRSSI() +{ + if (m_rssi == 0U) + return; + + if (m_rssiCount >= RSSI_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "DMR"; + json["slot"] = int(m_slotNo); + + json["value"] = m_rssiAccum / int(m_rssiCount); + + WriteJSON("RSSI", json); + + m_rssiAccum = 0; + m_rssiCount = 0U; + } +} + +void CDMRSlot::writeJSONBER() +{ + if (m_bitsCount >= BER_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "DMR"; + json["slot"] = int(m_slotNo); + + json["value"] = float(m_bitErrsAccum * 100U) / float(m_bitsCount); + + WriteJSON("BER", json); + + m_bitErrsAccum = 0U; + m_bitsCount = 1U; + } +} + +void CDMRSlot::writeJSONText(const unsigned char* text) +{ + assert(text != nullptr); + + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "DMR"; + json["slot"] = int(m_slotNo); + + json["value"] = std::string((char*)text); + + WriteJSON("Text", json); +} + +void CDMRSlot::writeJSONRF(const char* action) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, action); + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, "rf", action, srcId, srcInfo, grp, dstId); + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONRF(const char* action, const char* desc, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(action != nullptr); + assert(desc != nullptr); + + nlohmann::json json; + + writeJSON(json, "rf", action, srcId, srcInfo, grp, dstId); + + json["csbk_desc"] = desc; + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, unsigned int frames) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, "rf", action, srcId, srcInfo, grp, dstId); + + json["frames"] = int(frames); + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONRF(const char* action, float duration, float ber) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, action); + + json["duration"] = duration; + json["ber"] = ber; + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, action); + + json["duration"] = duration; + json["ber"] = ber; + + nlohmann::json rssi; + rssi["min"] = minRSSI; + rssi["max"] = maxRSSI; + rssi["ave"] = aveRSSI; + + json["rssi"] = rssi; + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONNet(const char* action) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, action); + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONNet(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, "network", action, srcId, srcInfo, grp, dstId); + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONNet(const char* action, const char* desc, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(action != nullptr); + assert(desc != nullptr); + + nlohmann::json json; + + writeJSON(json, "network", action, srcId, srcInfo, grp, dstId); + + json["csbk_desc"] = desc; + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONNet(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, unsigned int frames) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, "network", action, srcId, srcInfo, grp, dstId); + + json["frames"] = int(frames); + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONNet(const char* action, float duration, float loss, float ber) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, action); + + json["duration"] = duration; + json["loss"] = loss; + json["ber"] = ber; + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSON(nlohmann::json& json, const char* action) +{ + assert(action != nullptr); + + json["timestamp"] = CUtils::createTimestamp(); + json["action"] = action; + json["slot"] = int(m_slotNo); +} + +void CDMRSlot::writeJSON(nlohmann::json& json, const char* source, const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(source != nullptr); + assert(action != nullptr); + + json["timestamp"] = CUtils::createTimestamp(); + json["source"] = source; + json["action"] = action; + json["slot"] = int(m_slotNo); + json["source_id"] = int(srcId); + json["destination_id"] = int(dstId); + json["destination_type"] = grp ? "group" : "individual"; + + if (!srcInfo.empty()) + json["source_info"] = srcInfo; +} + +#endif + diff --git a/DMRSlot.h b/DMRSlot.h index a998a44..efeafd7 100644 --- a/DMRSlot.h +++ b/DMRSlot.h @@ -29,14 +29,17 @@ #include "AMBEFEC.h" #include "DMRSlot.h" #include "DMRData.h" -#include "Display.h" #include "Defines.h" #include "Timer.h" #include "Modem.h" #include "DMRLC.h" +#if defined(USE_DMR) + #include +#include + enum class ACTIVITY_TYPE { NONE, VOICE, @@ -62,7 +65,7 @@ public: void enable(bool enabled); - static void init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, IDMRNetwork* network, CDisplay* display, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter, DMR_OVCM ovcm, bool protect); + static void init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, CDMRNetwork* network, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter, DMR_OVCM ovcm, bool protect); private: unsigned int m_slotNo; @@ -105,13 +108,16 @@ private: bool m_netTimeout; unsigned char* m_lastFrame; bool m_lastFrameValid; - unsigned char m_rssi; - unsigned char m_maxRSSI; - unsigned char m_minRSSI; - unsigned int m_aveRSSI; + int m_rssi; + int m_maxRSSI; + int m_minRSSI; + int m_aveRSSI; + unsigned int m_rssiCountTotal; + int m_rssiAccum; unsigned int m_rssiCount; + unsigned int m_bitErrsAccum; + unsigned int m_bitsCount; bool m_enabled; - FILE* m_fp; static unsigned int m_colorCode; @@ -119,8 +125,7 @@ private: static bool m_dumpTAData; static CModem* m_modem; - static IDMRNetwork* m_network; - static CDisplay* m_display; + static CDMRNetwork* m_network; static bool m_duplex; static CDMRLookup* m_lookup; static unsigned int m_hangCount; @@ -151,14 +156,33 @@ private: void writeEndRF(bool writeEnd = false); void writeEndNet(bool writeEnd = false); - bool openFile(); - bool writeFile(const unsigned char* data); - void closeFile(); - bool insertSilence(const unsigned char* data, unsigned char seqNo); void insertSilence(unsigned int count); static void setShortLC(unsigned int slotNo, unsigned int id, FLCO flco = FLCO::GROUP, ACTIVITY_TYPE type = ACTIVITY_TYPE::NONE); + + void writeJSONRSSI(); + void writeJSONBER(); + void writeJSONText(const unsigned char* text); + + void writeJSONRF(const char* action); + void writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); + void writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, unsigned int frames); + void writeJSONRF(const char* action, const char* desc, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); + void writeJSONRF(const char* action, float duration, float ber); + void writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI); + + void writeJSONNet(const char* action); + void writeJSONNet(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); + void writeJSONNet(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, unsigned int frames); + void writeJSONNet(const char* action, float duration, float loss, float ber); + void writeJSONNet(const char* action, const char* desc, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); + + void writeJSON(nlohmann::json& json, const char* action); + void writeJSON(nlohmann::json& json, const char* source, const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); }; #endif + +#endif + diff --git a/DMRSlotType.cpp b/DMRSlotType.cpp index aa043c2..96c0ea1 100644 --- a/DMRSlotType.cpp +++ b/DMRSlotType.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,9 +17,10 @@ */ #include "DMRSlotType.h" - #include "Golay2087.h" +#if defined(USE_DMR) + #include #include @@ -90,3 +91,6 @@ void CDMRSlotType::setDataType(unsigned char type) { m_dataType = type; } + +#endif + diff --git a/DMRSlotType.h b/DMRSlotType.h index e76821f..bce1173 100644 --- a/DMRSlotType.h +++ b/DMRSlotType.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(DMRSLOTTYPE_H) #define DMRSLOTTYPE_H +#include "Defines.h" + +#if defined(USE_DMR) + class CDMRSlotType { public: @@ -40,3 +44,6 @@ private: }; #endif + +#endif + diff --git a/DMRTA.cpp b/DMRTA.cpp index b10da90..72a1188 100644 --- a/DMRTA.cpp +++ b/DMRTA.cpp @@ -15,6 +15,8 @@ #include "DMRTA.h" #include "Log.h" +#if defined(USE_DMR) + #include #include @@ -134,3 +136,5 @@ bool CDMRTA::decodeTA() return taLen >= taSize; } +#endif + diff --git a/DMRTA.h b/DMRTA.h index ccf48d9..175388e 100644 --- a/DMRTA.h +++ b/DMRTA.h @@ -15,6 +15,10 @@ #ifndef DMRTA_H #define DMRTA_H +#include "Defines.h" + +#if defined(USE_DMR) + class CDMRTA { public: CDMRTA(unsigned int slotNo); @@ -36,3 +40,6 @@ private: }; #endif + +#endif + diff --git a/DStarControl.cpp b/DStarControl.cpp index e7a7957..72e20d4 100644 --- a/DStarControl.cpp +++ b/DStarControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019,2021,2023,2025 Jonathan Naylor, G4KLX + * Copyright (C) 2015-2019,2021,2023,2025,2026 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 @@ -15,7 +15,8 @@ #include "Utils.h" #include "Sync.h" #include "Log.h" -#include "SMeter.h" + +#if defined(USE_DSTAR) #include #include @@ -25,6 +26,9 @@ const unsigned int MAX_SYNC_BIT_ERRORS = 2U; +const unsigned int RSSI_COUNT = 3U * 21U; // 3 * 420ms = 1260ms +const unsigned int BER_COUNT = 50U * 48U; // 50 * 20ms = 1000ms + bool CallsignCompare(const std::string& arg, const unsigned char* my) { for (unsigned int i = 0U; i < (DSTAR_LONG_CALLSIGN_LENGTH - 1U); i++) { @@ -35,9 +39,7 @@ bool CallsignCompare(const std::string& arg, const unsigned char* my) return true; } -// #define DUMP_DSTAR - -CDStarControl::CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool ackReply, unsigned int ackTime, DSTAR_ACK ackMessage, bool errorReply, const std::vector& blackList, const std::vector& whiteList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper) : +CDStarControl::CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool ackReply, unsigned int ackTime, DSTAR_ACK ackMessage, bool errorReply, const std::vector& blackList, const std::vector& whiteList, CDStarNetwork* network, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper) : m_callsign(nullptr), m_gateway(nullptr), m_selfOnly(selfOnly), @@ -48,7 +50,6 @@ m_remoteGateway(remoteGateway), m_blackList(blackList), m_whiteList(whiteList), m_network(network), -m_display(display), m_duplex(duplex), m_queue(5000U, "D-Star Control"), m_rfHeader(), @@ -75,14 +76,15 @@ m_fec(), m_rfBits(1U), m_netBits(1U), m_rfErrs(0U), -m_netErrs(0U), m_lastFrame(nullptr), m_lastFrameValid(false), m_rssiMapper(rssiMapper), -m_rssi(0U), -m_maxRSSI(0U), -m_minRSSI(0U), -m_aveRSSI(0U), +m_rssi(0), +m_maxRSSI(0), +m_minRSSI(0), +m_aveRSSI(0), +m_rssiCountTotal(0U), +m_rssiAccum(0), m_rssiCount(0U), m_enabled(true), m_fp(nullptr), @@ -91,9 +93,11 @@ m_RFdataLookBackLen(0U), m_RFdataLookBackIndex(0U), m_NetdataLookBack(nullptr), m_NetdataLookBackLen(0U), -m_NetdataLookBackIndex(0U) +m_NetdataLookBackIndex(0U), +m_bitErrsAccum(0U), +m_bitsCount(0U), +m_enabled(true) { - assert(display != nullptr); assert(rssiMapper != nullptr); m_callsign = new unsigned char[DSTAR_LONG_CALLSIGN_LENGTH]; @@ -143,10 +147,13 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) m_rfHeader.getMyCall2(my2); m_rfHeader.getYourCall(your); - if (m_rssi != 0U) - LogMessage("D-Star, transmission lost from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else + if (m_rssi != 0) { + LogMessage("D-Star, transmission lost from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + writeJSONRF("lost", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + } else { LogMessage("D-Star, transmission lost from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeJSONRF("lost", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + } writeEndRF(); return false; } @@ -177,20 +184,20 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) raw |= (data[43U] << 0) & 0x00FFU; // Convert the raw RSSI to dBm - int rssi = m_rssiMapper->interpolate(raw); - if (rssi != 0) - LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi); + int m_rssi = m_rssiMapper->interpolate(raw); + if (m_rssi != 0) + LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, m_rssi); - // RSSI is always reported as positive - m_rssi = (rssi >= 0) ? rssi : -rssi; - - if (m_rssi > m_minRSSI) + if (m_rssi < m_minRSSI) m_minRSSI = m_rssi; - if (m_rssi < m_maxRSSI) + if (m_rssi > m_maxRSSI) m_maxRSSI = m_rssi; m_aveRSSI += m_rssi; - m_rssiCount++; + m_rssiCountTotal++; + + m_rssiAccum += m_rssi; + m_rssiCountTotal++; } // Have we got RSSI bytes on the end of D-Star data? @@ -200,20 +207,20 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) raw |= (data[14U] << 0) & 0x00FFU; // Convert the raw RSSI to dBm - int rssi = m_rssiMapper->interpolate(raw); - if (rssi != 0) - LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi); + int m_rssi = m_rssiMapper->interpolate(raw); + if (m_rssi != 0) + LogDebug("D-Star, raw RSSI: %u, reported RSSI: %d dBm", raw, m_rssi); - // RSSI is always reported as positive - m_rssi = (rssi >= 0) ? rssi : -rssi; - - if (m_rssi > m_minRSSI) + if (m_rssi < m_minRSSI) m_minRSSI = m_rssi; - if (m_rssi < m_maxRSSI) + if (m_rssi > m_maxRSSI) m_maxRSSI = m_rssi; m_aveRSSI += m_rssi; - m_rssiCount++; + m_rssiCountTotal++; + + m_rssiAccum += m_rssi; + m_rssiCountTotal++; } if (type == TAG_HEADER) { @@ -223,35 +230,6 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; header.getMyCall1(my1); - // Is this a transmission destined for a repeater? - if (!header.isRepeater()) { - LogMessage("D-Star, non repeater RF header received from %8.8s", my1); - m_rfState = RPT_RF_STATE::INVALID; - return true; - } - - unsigned char callsign[DSTAR_LONG_CALLSIGN_LENGTH]; - header.getRPTCall1(callsign); - - // Is it for us? - if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) { - LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", callsign, my1); - m_rfState = RPT_RF_STATE::INVALID; - return true; - } - - if (m_selfOnly && ::memcmp(my1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH - 1U) != 0 && !(std::find_if(m_whiteList.begin(), m_whiteList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_whiteList.end())) { - LogMessage("D-Star, invalid access attempt from %8.8s", my1); - m_rfState = RPT_RF_STATE::REJECTED; - return true; - } - - if (!m_selfOnly && std::find_if(m_blackList.begin(), m_blackList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_blackList.end()) { - LogMessage("D-Star, invalid access attempt from %8.8s", my1); - m_rfState = RPT_RF_STATE::REJECTED; - return true; - } - unsigned char gateway[DSTAR_LONG_CALLSIGN_LENGTH]; header.getRPTCall2(gateway); @@ -261,6 +239,39 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH]; header.getYourCall(your); + unsigned char rpt1[DSTAR_LONG_CALLSIGN_LENGTH]; + header.getRPTCall1(rpt1); + + // Is this a transmission destined for a repeater? + if (!header.isRepeater()) { + LogMessage("D-Star, non repeater RF header received from %8.8s", my1); + m_rfState = RPT_RF_STATE::INVALID; + writeJSONRF("invalid", my1, my2, your); + return true; + } + + // Is it for us? + if (::memcmp(rpt1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) { + LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", rpt1, my1); + m_rfState = RPT_RF_STATE::INVALID; + writeJSONRF("invalid", my1, my2, your); + return true; + } + + if (m_selfOnly && ::memcmp(my1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH - 1U) != 0 && !(std::find_if(m_whiteList.begin(), m_whiteList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_whiteList.end())) { + LogMessage("D-Star, invalid access attempt from %8.8s", my1); + m_rfState = RPT_RF_STATE::REJECTED; + writeJSONRF("rejected", my1, my2, your); + return true; + } + + if (!m_selfOnly && std::find_if(m_blackList.begin(), m_blackList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_blackList.end()) { + LogMessage("D-Star, invalid access attempt from %8.8s", my1); + m_rfState = RPT_RF_STATE::REJECTED; + writeJSONRF("rejected", my1, my2, your); + return true; + } + m_net = ::memcmp(gateway, m_gateway, DSTAR_LONG_CALLSIGN_LENGTH) == 0; // Only start the timeout if not already running @@ -280,6 +291,9 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) m_minRSSI = m_rssi; m_maxRSSI = m_rssi; m_aveRSSI = m_rssi; + m_rssiCountTotal = 1U; + + m_rssiAccum = m_rssi; m_rssiCount = 1U; m_rfSlowData.start(); @@ -306,12 +320,11 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) m_rfState = RPT_RF_STATE::AUDIO; - if (m_netState == RPT_NET_STATE::IDLE) { - m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "R", " "); - m_display->writeDStarRSSI(m_rssi); - } + if (m_netState == RPT_NET_STATE::IDLE) + writeJSONRSSI(); LogMessage("D-Star, received RF header from %8.8s/%4.4s to %8.8s", my1, my2, your); + writeJSONRF("start", my1, my2, your); } else if (type == TAG_EOT) { if (m_rfState == RPT_RF_STATE::REJECTED) { m_rfState = RPT_RF_STATE::LISTENING; @@ -341,10 +354,13 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) m_rfHeader.getMyCall2(my2); m_rfHeader.getYourCall(your); - if (m_rssi != 0U) - LogMessage("D-Star, received RF end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else + if (m_rssi != 0) { + LogMessage("D-Star, received RF end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + writeJSONRF("end", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + } else { LogMessage("D-Star, received RF end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeJSONRF("end", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + } writeEndRF(); } @@ -429,13 +445,13 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) } if (m_rfState == RPT_RF_STATE::DATA) { - // Send the RSSI data to the display if (m_rfN == 0U) - m_display->writeDStarRSSI(m_rssi); + writeJSONRSSI(); LogDebug("D-Star, RF fast data sequence no. %u", m_rfN); - m_rfBits += 48U; + m_bitsCount += 48U; + m_rfBits += 48U; m_rfFrames++; if (m_net) @@ -455,9 +471,8 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) m_rfN = (m_rfN + 1U) % 21U; } else if (m_rfState == RPT_RF_STATE::AUDIO) { - // Send the RSSI data to the display if (m_rfN == 0U) - m_display->writeDStarRSSI(m_rssi); + writeJSONRSSI(); unsigned int errors = 0U; if (::memcmp(data + 1U, DSTAR_NULL_AMBE_DATA_BYTES_SCRAMBLED, DSTAR_VOICE_FRAME_LENGTH_BYTES) == 0) { @@ -465,11 +480,14 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) } else { errors = m_fec.regenerateDStar(data + 1U); LogDebug("D-Star, RF audio sequence no. %u, errs: %u/48 (%.1f%%)", m_rfN, errors, float(errors) / 0.48F); - m_display->writeDStarBER(float(errors) / 0.48F); + + m_bitErrsAccum += errors; + m_rfErrs += errors; + writeJSONBER(); } - m_rfErrs += errors; - m_rfBits += 48U; + m_bitsCount += 48U; + m_rfBits += 48U; m_rfFrames++; if (m_rfN != 0U) { @@ -506,44 +524,48 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; m_rfHeader.getMyCall1(my1); + unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH]; + m_rfHeader.getMyCall2(my2); + + unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH]; + m_rfHeader.getYourCall(your); + + unsigned char rpt1[DSTAR_LONG_CALLSIGN_LENGTH]; + m_rfHeader.getRPTCall1(rpt1); + // Is this a transmission destined for a repeater? if (!m_rfHeader.isRepeater()) { LogMessage("D-Star, non repeater RF header received from %8.8s", my1); m_rfState = RPT_RF_STATE::INVALID; + writeJSONRF("invalid", my1, my2, your); return true; } - unsigned char callsign[DSTAR_LONG_CALLSIGN_LENGTH]; - m_rfHeader.getRPTCall1(callsign); - // Is it for us? - if (::memcmp(callsign, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) { - LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", callsign, my1); + if (::memcmp(rpt1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH) != 0) { + LogMessage("D-Star, received RF header for wrong repeater (%8.8s) from %8.8s", rpt1, my1); m_rfState = RPT_RF_STATE::INVALID; + writeJSONRF("invalid", my1, my2, your); return true; } if (m_selfOnly && ::memcmp(my1, m_callsign, DSTAR_LONG_CALLSIGN_LENGTH - 1U) != 0 && !(std::find_if(m_whiteList.begin(), m_whiteList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_whiteList.end())) { LogMessage("D-Star, invalid access attempt from %8.8s", my1); m_rfState = RPT_RF_STATE::REJECTED; + writeJSONRF("rejected", my1, my2, your); return true; } if (!m_selfOnly && std::find_if(m_blackList.begin(), m_blackList.end(), std::bind(CallsignCompare, std::placeholders::_1, my1)) != m_blackList.end()) { LogMessage("D-Star, invalid access attempt from %8.8s", my1); m_rfState = RPT_RF_STATE::REJECTED; + writeJSONRF("rejected", my1, my2, your); return true; } unsigned char gateway[DSTAR_LONG_CALLSIGN_LENGTH]; m_rfHeader.getRPTCall2(gateway); - unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH]; - m_rfHeader.getMyCall2(my2); - - unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH]; - m_rfHeader.getYourCall(your); - m_net = ::memcmp(gateway, m_gateway, DSTAR_LONG_CALLSIGN_LENGTH) == 0; // Only reset the timeout if the timeout is not running @@ -564,7 +586,7 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) m_minRSSI = m_rssi; m_maxRSSI = m_rssi; m_aveRSSI = m_rssi; - m_rssiCount = 1U; + m_rssiCountTotal = 1U; if (m_duplex) { unsigned char start[DSTAR_HEADER_LENGTH_BYTES + 1U]; @@ -600,10 +622,12 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) } else { errors = m_fec.regenerateDStar(data + 1U); LogDebug("D-Star, audio sequence no. %u, errs: %u/48 (%.1f%%)", m_rfN, errors, float(errors) / 0.48F); + m_bitErrsAccum += errors; + m_rfErrs += errors; } - m_rfErrs += errors; - m_rfBits += 48U; + m_bitsCount += 48U; + m_rfBits += 48U; if (m_net) writeNetworkDataRF(data, errors, false); @@ -615,15 +639,15 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) m_rfState = RPT_RF_STATE::AUDIO; + m_rfN = (m_rfN + 1U) % 21U; + if (m_netState == RPT_NET_STATE::IDLE) { - m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "R", " "); - m_display->writeDStarRSSI(m_rssi); - m_display->writeDStarBER(float(errors) / 0.48F); + writeJSONRSSI(); + writeJSONBER(); } LogMessage("D-Star, received RF late entry from %8.8s/%4.4s to %8.8s", my1, my2, your); - - m_rfN = (m_rfN + 1U) % 21U; + writeJSONRF("late_entry", my1, my2, your); } } else { CUtils::dump("D-Star, unknown data from modem", data, DSTAR_FRAME_LENGTH_BYTES + 1U); @@ -654,8 +678,6 @@ void CDStarControl::writeEndRF() m_rfTimeoutTimer.stop(); if (m_netState == RPT_NET_STATE::IDLE) { - m_display->clearDStar(); - m_ackTimer.start(); if (m_network != nullptr) @@ -669,18 +691,12 @@ void CDStarControl::writeEndNet() m_lastFrameValid = false; - m_display->clearDStar(); - m_netTimeoutTimer.stop(); m_networkWatchdog.stop(); m_packetTimer.stop(); if (m_network != nullptr) m_network->reset(); - -#if defined(DUMP_DSTAR) - closeFile(); -#endif } void CDStarControl::writeNetwork() @@ -733,7 +749,6 @@ void CDStarControl::writeNetwork() m_netN = 20U; m_netBits = 1U; - m_netErrs = 0U; if (m_remoteGateway) { header.setRepeater(true); @@ -744,22 +759,18 @@ void CDStarControl::writeNetwork() writeQueueHeaderNet(data); -#if defined(DUMP_DSTAR) - openFile(); - writeFile(data + 1U, length - 1U); -#endif m_netState = RPT_NET_STATE::AUDIO; LINK_STATUS status = LINK_STATUS::NONE; unsigned char reflector[DSTAR_LONG_CALLSIGN_LENGTH]; m_network->getStatus(status, reflector); if ((status == LINK_STATUS::LINKED_DEXTRA) || (status == LINK_STATUS::LINKED_DPLUS) || (status == LINK_STATUS::LINKED_DCS) || (status == LINK_STATUS::LINKED_CCS) || (status == LINK_STATUS::LINKED_LOOPBACK)) { - m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "N", (char*) reflector); LogMessage("D-Star, received network header from %8.8s/%4.4s to %8.8s via %8.8s", my1, my2, your, reflector); + writeJSONNet("start", my1, my2, your, reflector); } else { - m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "N", (char*) " "); LogMessage("D-Star, received network header from %8.8s/%4.4s to %8.8s", my1, my2, your); - } + writeJSONNet("start", my1, my2, your); + } m_elapsed.start(); } else if (type == TAG_EOT) { @@ -770,10 +781,6 @@ void CDStarControl::writeNetwork() data[1U] = TAG_EOT; -#if defined(DUMP_DSTAR) - writeFile(data + 1U, length - 1U); - closeFile(); -#endif unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH]; unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH]; @@ -783,7 +790,8 @@ void CDStarControl::writeNetwork() // We've received the header and EOT haven't we? m_netFrames += 2U; - LogMessage("D-Star, received network end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, %u%% packet loss, BER: %.1f%%", my1, my2, your, float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); + LogMessage("D-Star, received network end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, %u%% packet loss", my1, my2, your, float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames); + writeJSONNet("end", float(m_netFrames) / 50.0F, float(m_netLost * 100U) / float(m_netFrames)); writeEndNet(); } else if (type == TAG_DATA) { @@ -853,7 +861,7 @@ void CDStarControl::writeNetworkData(unsigned char* data, unsigned int length) if (m_netState == RPT_NET_STATE::DATA) { unsigned char type = m_netSlowData.getType(true); // as per specs chapter 6.1.3, this could be any value bewtween 0x81 and 0x9C as one fast data block can only contain 28 bytes 0x80 + 28d = 0x9C - if(((type & DSTAR_SLOW_DATA_TYPE_FASTDATA_MASK) == DSTAR_SLOW_DATA_TYPE_FASTDATA) && type > 0x9CU) { + if (((type & DSTAR_SLOW_DATA_TYPE_FASTDATA_MASK) == DSTAR_SLOW_DATA_TYPE_FASTDATA) && type > 0x9CU) { LogMessage("Invalid fast data type 0x%02X received, returning to audio mode (Net)", type); m_netState = RPT_NET_STATE::AUDIO; } @@ -890,9 +898,6 @@ void CDStarControl::writeNetworkData(unsigned char* data, unsigned int length) m_packetTimer.start(); m_netFrames++; -#if defined(DUMP_DSTAR) - writeFile(data + 1U, length - 1U); -#endif writeQueueDataNet(data + 1U); } @@ -918,10 +923,9 @@ void CDStarControl::writeNetworkData(unsigned char* data, unsigned int length) m_packetTimer.start(); m_netFrames++; -#if defined(DUMP_DSTAR) - writeFile(data + 1U, length - 1U); -#endif writeQueueDataNet(data + 1U); + } else { + CUtils::dump("D-Star, unknown data from network", data, DSTAR_FRAME_LENGTH_BYTES + 1U); } } @@ -956,12 +960,17 @@ void CDStarControl::clock() if (m_networkWatchdog.hasExpired()) { // We're received the header haven't we? + unsigned char my1[DSTAR_LONG_CALLSIGN_LENGTH]; + unsigned char my2[DSTAR_SHORT_CALLSIGN_LENGTH]; + unsigned char your[DSTAR_LONG_CALLSIGN_LENGTH]; + m_netHeader.getMyCall1(my1); + m_netHeader.getMyCall2(my2); + m_netHeader.getYourCall(your); m_netFrames += 1U; - LogMessage("D-Star, network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); + + LogMessage("D-Star, network watchdog has expired, %.1f seconds, %u%% packet loss", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames); + writeJSONNet("lost", float(m_netFrames) / 50.0F, float(m_netLost * 100U) / float(m_netFrames)); writeEndNet(); -#if defined(DUMP_DSTAR) - closeFile(); -#endif } } @@ -1141,46 +1150,6 @@ void CDStarControl::writeNetworkDataRF(const unsigned char* data, unsigned int e m_network->writeData(data + 1U, DSTAR_FRAME_LENGTH_BYTES, errors, end, m_netState != RPT_NET_STATE::IDLE); } -bool CDStarControl::openFile() -{ - if (m_fp != nullptr) - return true; - - time_t t; - ::time(&t); - - struct tm* tm = ::localtime(&t); - - char name[100U]; - ::sprintf(name, "DStar_%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 == nullptr) - return false; - - ::fwrite("DSTAR", 1U, 4U, m_fp); - - return true; -} - -bool CDStarControl::writeFile(const unsigned char* data, unsigned int length) -{ - if (m_fp == nullptr) - return false; - - ::fwrite(data, 1U, length, m_fp); - - return true; -} - -void CDStarControl::closeFile() -{ - if (m_fp != nullptr) { - ::fclose(m_fp); - m_fp = nullptr; - } -} - bool CDStarControl::insertSilence(const unsigned char* data, unsigned char seqNo) { assert(data != nullptr); @@ -1289,18 +1258,32 @@ void CDStarControl::sendAck() if ((m_ackMessage == DSTAR_ACK::RSSI) && (m_rssi != 0U)) { if ((status == LINK_STATUS::LINKED_DEXTRA) || (status == LINK_STATUS::LINKED_DPLUS) || (status == LINK_STATUS::LINKED_DCS) || (status == LINK_STATUS::LINKED_CCS) || (status == LINK_STATUS::LINKED_LOOPBACK)) { CUtils::removeChar(reflector, ' ');//remove space from reflector so all nicely fits onto 20 chars in case rssi < 99dBm - ::sprintf(text, "%-8.8s %.1f%% -%udBm ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCount); + ::sprintf(text, "%-8.8s %.1f%% %ddBm ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / int(m_rssiCountTotal)); } else { - ::sprintf(text, "BER:%.1f%% -%udBm ", float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCount); + ::sprintf(text, "BER:%.1f%% %ddBm ", float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / int(m_rssiCountTotal)); } } else if ((m_ackMessage == DSTAR_ACK::SMETER) && (m_rssi != 0U)) { - unsigned int signal, plus; + const int RSSI_S1 = -141; + const int RSSI_S9 = -93; + + int signal = 0; + int plus = 0; + int rssi = m_aveRSSI / int(m_rssiCountTotal); + + if (rssi < RSSI_S1) { + plus = RSSI_S1 - rssi; + } else if (rssi < RSSI_S9 && rssi >= RSSI_S1) { + signal = ((rssi - RSSI_S1) / 6) + 1; + plus = (rssi - RSSI_S1) % 6; + } else { + plus = rssi - RSSI_S9; + } + char signalText[15U]; - CSMeter::getSignal(m_aveRSSI / m_rssiCount, signal, plus); - if (plus != 0U) - ::sprintf(signalText, "S%u+%02u", signal, plus); + if (plus != 0) + ::sprintf(signalText, "S%d+%02d", signal, plus); else - ::sprintf(signalText, "S%u", signal); + ::sprintf(signalText, "S%d", signal); if ((status == LINK_STATUS::LINKED_DEXTRA) || (status == LINK_STATUS::LINKED_DPLUS) || (status == LINK_STATUS::LINKED_DCS) || (status == LINK_STATUS::LINKED_CCS) || (status == LINK_STATUS::LINKED_LOOPBACK)) ::sprintf(text, "%-8.8s %.1f%% %s ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), signalText); @@ -1354,18 +1337,32 @@ void CDStarControl::sendError() if ((m_ackMessage == DSTAR_ACK::RSSI) && (m_rssi != 0U)) { if ((status == LINK_STATUS::LINKED_DEXTRA) || (status == LINK_STATUS::LINKED_DPLUS) || (status == LINK_STATUS::LINKED_DCS) || (status == LINK_STATUS::LINKED_CCS) || (status == LINK_STATUS::LINKED_LOOPBACK)) { CUtils::removeChar(reflector, ' ');//remove space from reflector so all nicely fits onto 20 chars in case rssi < 99dBm - ::sprintf(text, "%-8.8s %.1f%% -%udBm ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCount); + ::sprintf(text, "%-8.8s %.1f%% %ddBm ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / int(m_rssiCountTotal)); } else { - ::sprintf(text, "BER:%.1f%% -%udBm ", float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCount); + ::sprintf(text, "BER:%.1f%% %ddBm ", float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / int(m_rssiCountTotal)); } } else if ((m_ackMessage == DSTAR_ACK::SMETER) && (m_rssi != 0U)) { - unsigned int signal, plus; + const int RSSI_S1 = -141; + const int RSSI_S9 = -93; + + int signal = 0; + int plus = 0; + int rssi = m_aveRSSI / int(m_rssiCountTotal); + + if (rssi < RSSI_S1) { + plus = RSSI_S1 - rssi; + } else if (rssi < RSSI_S9 && rssi >= RSSI_S1) { + signal = ((rssi - RSSI_S1) / 6) + 1; + plus = (rssi - RSSI_S1) % 6; + } else { + plus = rssi - RSSI_S9; + } + char signalText[15U]; - CSMeter::getSignal(m_aveRSSI / m_rssiCount, signal, plus); if (plus != 0U) - ::sprintf(signalText, "S%u+%02u", signal, plus); + ::sprintf(signalText, "S%d+%02d", signal, plus); else - ::sprintf(signalText, "S%u", signal); + ::sprintf(signalText, "S%d", signal); if ((status == LINK_STATUS::LINKED_DEXTRA) || (status == LINK_STATUS::LINKED_DPLUS) || (status == LINK_STATUS::LINKED_DCS) || (status == LINK_STATUS::LINKED_CCS) || (status == LINK_STATUS::LINKED_LOOPBACK)) ::sprintf(text, "%-8.8s %.1f%% %s ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), signalText); @@ -1439,3 +1436,171 @@ void CDStarControl::enable(bool enabled) m_enabled = enabled; } + +void CDStarControl::writeJSONRSSI() +{ + if (m_rssi == 0) + return; + + if (m_rssiCountTotal >= RSSI_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "D-Star"; + + json["value"] = m_rssiAccum / int(m_rssiCountTotal); + + WriteJSON("RSSI", json); + + m_rssiAccum = 0; + m_rssiCountTotal = 0U; + } +} + +void CDStarControl::writeJSONBER() +{ + if (m_bitsCount >= BER_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "D-Star"; + + json["value"] = float(m_bitErrsAccum * 100U) / float(m_bitsCount); + + WriteJSON("BER", json); + + m_bitErrsAccum = 0U; + m_bitsCount = 1U; + } +} + +void CDStarControl::writeJSONText(const unsigned char* text) +{ + assert(text != nullptr); + + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "D-Star"; + + json["value"] = std::string((char*)text); + + WriteJSON("Text", json); +} + +void CDStarControl::writeJSONRF(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your) +{ + assert(action != nullptr); + assert(my1 != nullptr); + assert(my2 != nullptr); + assert(your != nullptr); + + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + + json["source_cs"] = convertBuffer(my1, DSTAR_LONG_CALLSIGN_LENGTH); + json["source_ext"] = convertBuffer(my2, DSTAR_SHORT_CALLSIGN_LENGTH); + json["destination_cs"] = convertBuffer(your, DSTAR_LONG_CALLSIGN_LENGTH); + + json["source"] = "rf"; + json["action"] = action; + + WriteJSON("D-Star", json); +} + +void CDStarControl::writeJSONRF(const char* action, float duration, float ber) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSONRF(json, action, duration, ber); + + WriteJSON("D-Star", json); +} + +void CDStarControl::writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSONRF(json, action, duration, ber); + + nlohmann::json rssi; + rssi["min"] = minRSSI; + rssi["max"] = maxRSSI; + rssi["ave"] = aveRSSI; + + json["rssi"] = rssi; + + WriteJSON("D-Star", json); +} + +void CDStarControl::writeJSONNet(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, const unsigned char* reflector) +{ + assert(action != nullptr); + assert(my1 != nullptr); + assert(my2 != nullptr); + assert(your != nullptr); + + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + + json["source_cs"] = convertBuffer(my1, DSTAR_LONG_CALLSIGN_LENGTH); + json["source_ext"] = convertBuffer(my2, DSTAR_SHORT_CALLSIGN_LENGTH); + json["destination_cs"] = convertBuffer(your, DSTAR_LONG_CALLSIGN_LENGTH); + + json["source"] = "network"; + json["action"] = action; + + if (reflector != nullptr) + json["reflector"] = convertBuffer(reflector, DSTAR_LONG_CALLSIGN_LENGTH); + + WriteJSON("D-Star", json); +} + +void CDStarControl::writeJSONNet(const char* action, float duration, float loss) +{ + assert(action != nullptr); + + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + + json["duration"] = duration; + json["loss"] = loss; + + json["action"] = action; + + WriteJSON("D-Star", json); +} + +void CDStarControl::writeJSONRF(nlohmann::json& json, const char* action, float duration, float ber) +{ + assert(action != nullptr); + + json["timestamp"] = CUtils::createTimestamp(); + + json["duration"] = duration; + json["ber"] = ber; + + json["action"] = action; +} + +std::string CDStarControl::convertBuffer(const unsigned char* buffer, unsigned int length) const +{ + assert(buffer != nullptr); + + std::string callsign((char*)buffer, length); + + size_t pos = callsign.find_first_of(' '); + if (pos != std::string::npos) + callsign = callsign.substr(0U, pos); + + return callsign; +} + +#endif diff --git a/DStarControl.h b/DStarControl.h index 17ab58a..a21c7e1 100644 --- a/DStarControl.h +++ b/DStarControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2019,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2019,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,17 +27,20 @@ #include "RingBuffer.h" #include "StopWatch.h" #include "AMBEFEC.h" -#include "Display.h" #include "Defines.h" #include "Timer.h" #include "Modem.h" +#if defined(USE_DSTAR) + #include #include +#include + class CDStarControl { public: - CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool ackReply, unsigned int ackTime, DSTAR_ACK ackMessage, bool errorReply, const std::vector& blackList, const std::vector& whiteList, CDStarNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper); + CDStarControl(const std::string& callsign, const std::string& module, bool selfOnly, bool ackReply, unsigned int ackTime, DSTAR_ACK ackMessage, bool errorReply, const std::vector& blackList, const std::vector& whiteList, CDStarNetwork* network, unsigned int timeout, bool duplex, bool remoteGateway, CRSSIInterpolator* rssiMapper); ~CDStarControl(); bool writeModem(unsigned char* data, unsigned int len); @@ -61,7 +64,6 @@ private: std::vector m_blackList; std::vector m_whiteList; CDStarNetwork* m_network; - CDisplay* m_display; bool m_duplex; CRingBuffer m_queue; CDStarHeader m_rfHeader; @@ -88,15 +90,18 @@ private: unsigned int m_rfBits; unsigned int m_netBits; unsigned int m_rfErrs; - unsigned int m_netErrs; unsigned char* m_lastFrame; bool m_lastFrameValid; CRSSIInterpolator* m_rssiMapper; - unsigned char m_rssi; - unsigned char m_maxRSSI; - unsigned char m_minRSSI; - unsigned int m_aveRSSI; + int m_rssi; + int m_maxRSSI; + int m_minRSSI; + int m_aveRSSI; + unsigned int m_rssiCountTotal; + int m_rssiAccum; unsigned int m_rssiCount; + unsigned int m_bitErrsAccum; + unsigned int m_bitsCount; bool m_enabled; FILE* m_fp; unsigned char * m_RFdataLookBack; @@ -121,9 +126,19 @@ private: void writeEndRF(); void writeEndNet(); - bool openFile(); - bool writeFile(const unsigned char* data, unsigned int length); - void closeFile(); + void writeJSONRSSI(); + void writeJSONBER(); + void writeJSONText(const unsigned char* text); + + void writeJSONRF(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your); + void writeJSONRF(const char* action, float duration, float ber); + void writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI); + void writeJSONNet(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, const unsigned char* reflector = nullptr); + void writeJSONNet(const char* action, float duration, float loss); + + void writeJSONRF(nlohmann::json& json, const char* action, float duration, float ber); + + std::string convertBuffer(const unsigned char* buffer, unsigned int length) const; bool insertSilence(const unsigned char* data, unsigned char seqNo); void insertSilence(unsigned int count); @@ -135,3 +150,5 @@ private: }; #endif + +#endif diff --git a/DStarHeader.cpp b/DStarHeader.cpp index 3316d35..a74c36c 100644 --- a/DStarHeader.cpp +++ b/DStarHeader.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,8 @@ #include "DStarHeader.h" #include "CRC.h" +#if defined(USE_DSTAR) + #include #include #include @@ -157,3 +159,6 @@ void CDStarHeader::get(unsigned char* header) const CCRC::addCCITT161(header, DSTAR_HEADER_LENGTH_BYTES); } + +#endif + diff --git a/DStarHeader.h b/DStarHeader.h index 838cc7d..ce63ba9 100644 --- a/DStarHeader.h +++ b/DStarHeader.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2023,2026 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,10 @@ #ifndef DStarHeader_H #define DStarHeader_H +#include "Defines.h" + +#if defined(USE_DSTAR) + #include "DStarDefines.h" class CDStarHeader { @@ -58,3 +62,6 @@ private: }; #endif + +#endif + diff --git a/DStarNetwork.cpp b/DStarNetwork.cpp index ead40a7..7a0acff 100644 --- a/DStarNetwork.cpp +++ b/DStarNetwork.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014,2016,2019,2020,2021,2024,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2014,2016,2019,2020,2021,2023,2024,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,8 @@ #include "Utils.h" #include "Log.h" +#if defined(USE_DSTAR) + #include #include #include @@ -346,3 +348,6 @@ void CDStarNetwork::getStatus(LINK_STATUS& status, unsigned char* reflector) ::memcpy(reflector, m_linkReflector, DSTAR_LONG_CALLSIGN_LENGTH); } + +#endif + diff --git a/DStarNetwork.h b/DStarNetwork.h index d7a1cc4..72d4cc0 100644 --- a/DStarNetwork.h +++ b/DStarNetwork.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014,2016,2020,2021 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2014,2016,2020,2021,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,8 +22,11 @@ #include "DStarDefines.h" #include "RingBuffer.h" #include "UDPSocket.h" +#include "Defines.h" #include "Timer.h" +#if defined(USE_DSTAR) + #include #include #include @@ -73,3 +76,6 @@ private: }; #endif + +#endif + diff --git a/DStarSlowData.cpp b/DStarSlowData.cpp index b14d4b2..c2c2003 100644 --- a/DStarSlowData.cpp +++ b/DStarSlowData.cpp @@ -22,6 +22,8 @@ #include "CRC.h" #include "Log.h" +#if defined(USE_DSTAR) + #include #include #include @@ -246,3 +248,6 @@ bool CDStarSlowData::isComplete() const { return m_complete; } + +#endif + diff --git a/DStarSlowData.h b/DStarSlowData.h index 4353bf9..6dbb1ab 100644 --- a/DStarSlowData.h +++ b/DStarSlowData.h @@ -20,6 +20,9 @@ #define DStarSlowData_H #include "DStarHeader.h" +#include "Defines.h" + +#if defined(USE_DSTAR) class CDStarSlowData { public: @@ -61,3 +64,6 @@ private: }; #endif + +#endif + diff --git a/Defines.h b/Defines.h index 8f436b9..4be4a10 100644 --- a/Defines.h +++ b/Defines.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2017,2018,2020,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2017,2018,2020,2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,15 @@ #if !defined(Defines_H) #define Defines_H +// Define the wanted modes to compile into the host here +#define USE_DSTAR +#define USE_DMR +#define USE_YSF +#define USE_P25 +#define USE_NXDN +#define USE_POCSAG +#define USE_FM + const unsigned char MODE_IDLE = 0U; const unsigned char MODE_DSTAR = 1U; const unsigned char MODE_DMR = 2U; @@ -38,6 +47,7 @@ const unsigned char TAG_HEADER = 0x00U; const unsigned char TAG_DATA = 0x01U; const unsigned char TAG_LOST = 0x02U; const unsigned char TAG_EOT = 0x03U; +const unsigned char TAG_RSSI = 0x04U; const unsigned int DSTAR_MODEM_DATA_LEN = 220U; diff --git a/Display.cpp b/Display.cpp deleted file mode 100644 index 978968f..0000000 --- a/Display.cpp +++ /dev/null @@ -1,684 +0,0 @@ -/* - * Copyright (C) 2016,2017,2018,2020,2021,2023,2024,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "Display.h" -#include "Defines.h" -#include "UARTController.h" -#include "ModemSerialPort.h" -#include "NullDisplay.h" -#include "TFTSurenoo.h" -#include "UDPSocket.h" -#include "LCDproc.h" -#include "Nextion.h" -#include "CASTInfo.h" -#include "Conf.h" -#include "Modem.h" -#include "Log.h" - -#if defined(HD44780) -#include "HD44780.h" -#endif - -#if defined(OLED) -#include "OLED.h" -#endif - -#include -#include -#include - -CDisplay::CDisplay() : -m_timer1(3000U, 3U), -m_timer2(3000U, 3U), -m_mode1(MODE_IDLE), -m_mode2(MODE_IDLE) -{ -} - -CDisplay::~CDisplay() -{ -} - -void CDisplay::setIdle() -{ - m_timer1.stop(); - m_timer2.stop(); - - m_mode1 = MODE_IDLE; - m_mode2 = MODE_IDLE; - - setIdleInt(); -} - -void CDisplay::setLockout() -{ - m_timer1.stop(); - m_timer2.stop(); - - m_mode1 = MODE_IDLE; - m_mode2 = MODE_IDLE; - - setLockoutInt(); -} - -void CDisplay::setError(const char* text) -{ - assert(text != nullptr); - - m_timer1.stop(); - m_timer2.stop(); - - m_mode1 = MODE_IDLE; - m_mode2 = MODE_IDLE; - - setErrorInt(text); -} - -void CDisplay::setQuit() -{ - m_timer1.stop(); - m_timer2.stop(); - - m_mode1 = MODE_QUIT; - m_mode2 = MODE_QUIT; - - setQuitInt(); -} - -void CDisplay::setFM() -{ - m_timer1.stop(); - m_timer2.stop(); - - m_mode1 = MODE_FM; - m_mode2 = MODE_FM; - - setFMInt(); -} - -void CDisplay::writeDStar(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) -{ - assert(my1 != nullptr); - assert(my2 != nullptr); - assert(your != nullptr); - assert(type != nullptr); - assert(reflector != nullptr); - - m_timer1.start(); - m_mode1 = MODE_IDLE; - - writeDStarInt(my1, my2, your, type, reflector); -} - -void CDisplay::writeDStarRSSI(unsigned char rssi) -{ - if (rssi != 0U) - writeDStarRSSIInt(rssi); -} - -void CDisplay::writeDStarBER(float ber) -{ - writeDStarBERInt(ber); -} - -void CDisplay::clearDStar() -{ - if (m_timer1.hasExpired()) { - clearDStarInt(); - m_timer1.stop(); - m_mode1 = MODE_IDLE; - } else { - m_mode1 = MODE_DSTAR; - } -} - -void CDisplay::writeDMR(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) -{ - assert(type != nullptr); - - if (slotNo == 1U) { - m_timer1.start(); - m_mode1 = MODE_IDLE; - } else { - m_timer2.start(); - m_mode2 = MODE_IDLE; - } - writeDMRInt(slotNo, src, group, dst, type); -} - -void CDisplay::writeDMR(unsigned int slotNo, const class CUserDBentry& src, bool group, const std::string& dst, const char* type) -{ - assert(type != nullptr); - - if (slotNo == 1U) { - m_timer1.start(); - m_mode1 = MODE_IDLE; - } else { - m_timer2.start(); - m_mode2 = MODE_IDLE; - } - - if (int err = writeDMRIntEx(slotNo, src, group, dst, type)) { - std::string src_str = src.get(keyCALLSIGN); - if (err < 0 && !src.get(keyFIRST_NAME).empty()) { - // emulate the result of old CDMRLookup::findWithName() - // (it returned callsign and firstname) - src_str += " " + src.get(keyFIRST_NAME); - } - writeDMRInt(slotNo, src_str, group, dst, type); - } -} - -void CDisplay::writeDMRRSSI(unsigned int slotNo, unsigned char rssi) -{ - if (rssi != 0U) - writeDMRRSSIInt(slotNo, rssi); -} - -void CDisplay::writeDMRTA(unsigned int slotNo, const unsigned char* talkerAlias, const char* type) -{ - if (::strcmp(type, " ") == 0) { - writeDMRTAInt(slotNo, (unsigned char*)"", type); - return; - } - - if (::strlen((char*)talkerAlias) >= 4U) - writeDMRTAInt(slotNo, (unsigned char*)talkerAlias, type); -} - -void CDisplay::writeDMRBER(unsigned int slotNo, float ber) -{ - writeDMRBERInt(slotNo, ber); -} - -void CDisplay::clearDMR(unsigned int slotNo) -{ - if (slotNo == 1U) { - if (m_timer1.hasExpired()) { - clearDMRInt(slotNo); - m_timer1.stop(); - m_mode1 = MODE_IDLE; - } else { - m_mode1 = MODE_DMR; - } - } else { - if (m_timer2.hasExpired()) { - clearDMRInt(slotNo); - m_timer2.stop(); - m_mode2 = MODE_IDLE; - } else { - m_mode2 = MODE_DMR; - } - } -} - -void CDisplay::writeFusion(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) -{ - assert(source != nullptr); - assert(dest != nullptr); - assert(type != nullptr); - assert(origin != nullptr); - - m_timer1.start(); - m_mode1 = MODE_IDLE; - - writeFusionInt(source, dest, dgid, type, origin); -} - -void CDisplay::writeFusionRSSI(unsigned char rssi) -{ - if (rssi != 0U) - writeFusionRSSIInt(rssi); -} - -void CDisplay::writeFusionBER(float ber) -{ - writeFusionBERInt(ber); -} - -void CDisplay::clearFusion() -{ - if (m_timer1.hasExpired()) { - clearFusionInt(); - m_timer1.stop(); - m_mode1 = MODE_IDLE; - } else { - m_mode1 = MODE_YSF; - } -} - -void CDisplay::writeP25(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(source != nullptr); - assert(type != nullptr); - - m_timer1.start(); - m_mode1 = MODE_IDLE; - - writeP25Int(source, group, dest, type); -} - -void CDisplay::writeP25RSSI(unsigned char rssi) -{ - if (rssi != 0U) - writeP25RSSIInt(rssi); -} - -void CDisplay::writeP25BER(float ber) -{ - writeP25BERInt(ber); -} - -void CDisplay::clearP25() -{ - if (m_timer1.hasExpired()) { - clearP25Int(); - m_timer1.stop(); - m_mode1 = MODE_IDLE; - } else { - m_mode1 = MODE_P25; - } -} - -void CDisplay::writeNXDN(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(source != nullptr); - assert(type != nullptr); - - m_timer1.start(); - m_mode1 = MODE_IDLE; - - writeNXDNInt(source, group, dest, type); -} - -void CDisplay::writeNXDN(const class CUserDBentry& source, bool group, unsigned int dest, const char* type) -{ - assert(type != nullptr); - - m_timer1.start(); - m_mode1 = MODE_IDLE; - - if (writeNXDNIntEx(source, group, dest, type)) - writeNXDNInt(source.get(keyCALLSIGN).c_str(), group, dest, type); -} - -void CDisplay::writeNXDNRSSI(unsigned char rssi) -{ - if (rssi != 0U) - writeNXDNRSSIInt(rssi); -} - -void CDisplay::writeNXDNBER(float ber) -{ - writeNXDNBERInt(ber); -} - -void CDisplay::clearNXDN() -{ - if (m_timer1.hasExpired()) { - clearNXDNInt(); - m_timer1.stop(); - m_mode1 = MODE_IDLE; - } else { - m_mode1 = MODE_NXDN; - } -} - -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(); - m_mode1 = MODE_CW; - - writeCWInt(); -} - -void CDisplay::clock(unsigned int ms) -{ - m_timer1.clock(ms); - if (m_timer1.isRunning() && m_timer1.hasExpired()) { - switch (m_mode1) { - case MODE_DSTAR: - clearDStarInt(); - m_mode1 = MODE_IDLE; - m_timer1.stop(); - break; - case MODE_DMR: - clearDMRInt(1U); - m_mode1 = MODE_IDLE; - m_timer1.stop(); - break; - case MODE_YSF: - clearFusionInt(); - m_mode1 = MODE_IDLE; - m_timer1.stop(); - break; - case MODE_P25: - clearP25Int(); - m_mode1 = MODE_IDLE; - m_timer1.stop(); - break; - case MODE_NXDN: - clearNXDNInt(); - 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; - m_timer1.stop(); - break; - default: - break; - } - } - - // Timer/mode 2 are only used for DMR - m_timer2.clock(ms); - if (m_timer2.isRunning() && m_timer2.hasExpired()) { - if (m_mode2 == MODE_DMR) { - clearDMRInt(2U); - m_mode2 = MODE_IDLE; - m_timer2.stop(); - } - } - - clockInt(ms); -} - -void CDisplay::clockInt(unsigned int ms) -{ -} - -void CDisplay::writeDStarRSSIInt(unsigned char rssi) -{ -} - -void CDisplay::writeDStarBERInt(float ber) -{ -} - -int CDisplay::writeDMRIntEx(unsigned int slotNo, const class CUserDBentry& src, bool group, const std::string& dst, const char* type) -{ - /* - * return value: - * < 0 error condition (i.e. not supported) - * -> call writeXXXXInt() to display - * = 0 no error, writeXXXXIntEx() displayed whole status - * = 1 no error, writeXXXXIntEx() displayed partial status - * -> call writeXXXXInt() to display remain part - * > 1 reserved for future use - */ - return -1; // not supported -} - -void CDisplay::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi) -{ -} - -void CDisplay::writeDMRTAInt(unsigned int slotNo, const unsigned char* talkerAlias, const char* type) -{ -} - -void CDisplay::writeDMRBERInt(unsigned int slotNo, float ber) -{ -} - -void CDisplay::writeFusionRSSIInt(unsigned char rssi) -{ -} - -void CDisplay::writeFusionBERInt(float ber) -{ -} - -void CDisplay::writeP25RSSIInt(unsigned char rssi) -{ -} - -void CDisplay::writeP25BERInt(float ber) -{ -} - -void CDisplay::writeNXDNRSSIInt(unsigned char rssi) -{ -} - -void CDisplay::writeNXDNBERInt(float ber) -{ -} - -int CDisplay::writeNXDNIntEx(const class CUserDBentry& source, bool group, unsigned int dest, const char* type) -{ - /* return value definition is same as writeDMRIntEx() */ - return -1; // not supported -} - - -/* Factory method extracted from MMDVMHost.cpp - BG5HHP */ -CDisplay* CDisplay::createDisplay(const CConf& conf, CModem* modem) -{ - CDisplay *display = nullptr; - - std::string type = conf.getDisplay(); - unsigned int dmrid = conf.getDMRId(); - - LogInfo("Display Parameters"); - LogInfo(" Type: %s", type.c_str()); - - if (type == "TFT Surenoo") { - std::string port = conf.getTFTSerialPort(); - unsigned int brightness = conf.getTFTSerialBrightness(); - unsigned int screenLayout = conf.getTFTSerialScreenLayout(); - - LogInfo(" Port: %s", port.c_str()); - LogInfo(" Brightness: %u", brightness); - LogInfo(" Screen Layout: %u", screenLayout); - - ISerialPort* serial = nullptr; - if (port == "modem") - serial = new IModemSerialPort(modem); - else - serial = new CUARTController(port, 115200U); - - display = new CTFTSurenoo(conf.getCallsign(), dmrid, serial, brightness, conf.getDuplex(), screenLayout); - } 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(); - unsigned int txFrequency = conf.getTXFrequency(); - unsigned int rxFrequency = conf.getRXFrequency(); - bool displayTempInF = conf.getNextionTempInFahrenheit(); - - 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); - LogInfo(" Temperature in Fahrenheit: %s ", displayTempInF ? "yes" : "no"); - - 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") { - CUDPSocket* socket = nullptr; - struct sockaddr_storage addr; - unsigned int addrLength = 0U; - - bool nextionOutput = conf.getNextionOutput(); - if (nextionOutput) { - unsigned short nextionUDPPort = conf.getNextionUDPPort(); - - LogInfo(" Output Port: %u", nextionUDPPort); - - CUDPSocket::lookup("127.0.0.1", nextionUDPPort, addr, addrLength); - - if (addrLength > 0U) { - socket = new CUDPSocket("127.0.0.1", nextionUDPPort - 1U); - bool ret = socket->open(addr); - if (!ret) { - delete socket; - socket = nullptr; - } - } - } - - if (socket == nullptr) { - ISerialPort* serial = new IModemSerialPort(modem); - display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout, txFrequency, rxFrequency, displayTempInF); - } else { - ISerialPort* serial = new IModemSerialPort(modem); - display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout, txFrequency, rxFrequency, displayTempInF, socket, addr, addrLength); - } - } else { - unsigned int baudrate = 9600U; - if (screenLayout == 4U) - baudrate = 115200U; - - LogInfo(" Display baudrate: %u ", baudrate); - ISerialPort* serial = new CUARTController(port, baudrate); - display = new CNextion(conf.getCallsign(), dmrid, serial, brightness, displayClock, utc, idleBrightness, screenLayout, txFrequency, rxFrequency, displayTempInF); - } - } 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(); - bool rotate = conf.getOLEDRotate(); - bool logosaver = conf.getOLEDLogoScreensaver(); - - display = new COLED(type, brightness, invert, scroll, rotate, logosaver, conf.getDuplex()); -#endif - } else if (type == "CAST") { - display = new CCASTInfo(modem); - } 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 deleted file mode 100644 index 8d55de9..0000000 --- a/Display.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2016,2017,2018,2020,2021,2023,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#if !defined(DISPLAY_H) -#define DISPLAY_H - -#include "Timer.h" -#include "UserDBentry.h" -#include "Modem.h" - -#include - -#include - -class CConf; -class CModem; - -class CDisplay -{ -public: - CDisplay(); - virtual ~CDisplay() = 0; - - virtual bool open() = 0; - - void setIdle(); - void setLockout(); - void setError(const char* text); - void setQuit(); - void setFM(); - - void writeDStar(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); - void writeDStarRSSI(unsigned char rssi); - void writeDStarBER(float ber); - void clearDStar(); - - void writeDMR(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); - void writeDMR(unsigned int slotNo, const CUserDBentry& src, bool group, const std::string& dst, const char* type); - void writeDMRRSSI(unsigned int slotNo, unsigned char rssi); - void writeDMRBER(unsigned int slotNo, float ber); - void writeDMRTA(unsigned int slotNo, const unsigned char* talkerAlias, const char* type); - void clearDMR(unsigned int slotNo); - - void writeFusion(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin); - void writeFusionRSSI(unsigned char rssi); - void writeFusionBER(float ber); - void clearFusion(); - - void writeP25(const char* source, bool group, unsigned int dest, const char* type); - void writeP25RSSI(unsigned char rssi); - void writeP25BER(float ber); - void clearP25(); - - void writeNXDN(const char* source, bool group, unsigned int dest, const char* type); - void writeNXDN(const CUserDBentry& source, bool group, unsigned int dest, const char* type); - void writeNXDNRSSI(unsigned char rssi); - void writeNXDNBER(float ber); - void clearNXDN(); - - void writePOCSAG(uint32_t ric, const std::string& message); - void clearPOCSAG(); - - void writeCW(); - - virtual void close() = 0; - - void clock(unsigned int ms); - - static CDisplay* createDisplay(const CConf& conf, CModem* modem); - -protected: - virtual void setIdleInt() = 0; - virtual void setLockoutInt() = 0; - virtual void setErrorInt(const char* text) = 0; - virtual void setQuitInt() = 0; - virtual void setFMInt() = 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); - virtual void writeDStarBERInt(float ber); - virtual void clearDStarInt() = 0; - - virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) = 0; - virtual int writeDMRIntEx(unsigned int slotNo, const CUserDBentry& src, bool group, const std::string& dst, const char* type); - virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi); - virtual void writeDMRTAInt(unsigned int slotNo, const unsigned char* talkerAlias, const char* type); - virtual void writeDMRBERInt(unsigned int slotNo, float ber); - virtual void clearDMRInt(unsigned int slotNo) = 0; - - virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) = 0; - virtual void writeFusionRSSIInt(unsigned char rssi); - virtual void writeFusionBERInt(float ber); - virtual void clearFusionInt() = 0; - - virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type) = 0; - virtual void writeP25RSSIInt(unsigned char rssi); - virtual void writeP25BERInt(float ber); - virtual void clearP25Int() = 0; - - virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type) = 0; - virtual int writeNXDNIntEx(const CUserDBentry& source, bool group, unsigned int dest, const char* type); - virtual void writeNXDNRSSIInt(unsigned char rssi); - 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; - - virtual void clockInt(unsigned int ms); - -private: - CTimer m_timer1; - CTimer m_timer2; - unsigned char m_mode1; - unsigned char m_mode2; -}; - -#endif diff --git a/FMControl.cpp b/FMControl.cpp index 478edf9..c492078 100644 --- a/FMControl.cpp +++ b/FMControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2020,2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,13 +17,12 @@ */ #include "FMControl.h" +#include "Utils.h" + +#if defined(USE_FM) #include -#if defined(DUMP_RF_AUDIO) -#include -#endif - #define SWAP_BYTES_16(a) (((a >> 8) & 0x00FFU) | ((a << 8) & 0xFF00U)) const float DEEMPHASIS_GAIN_DB = 8.0F; // Audio gain adjustment @@ -31,12 +30,26 @@ const float PREEMPHASIS_GAIN_DB = 0.0F; // Audio gain adjustment const float FILTER_GAIN_DB = 2.0F; // Audio gain adjustment const unsigned int FM_MASK = 0x00000FFFU; -CFMControl::CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn) : +const unsigned char FS_LISTENING = 0U; +const unsigned char FS_KERCHUNK_RF = 1U; +const unsigned char FS_RELAYING_RF = 2U; +const unsigned char FS_RELAYING_WAIT_RF = 3U; +const unsigned char FS_TIMEOUT_RF = 4U; +const unsigned char FS_TIMEOUT_WAIT_RF = 5U; +const unsigned char FS_KERCHUNK_EXT = 6U; +const unsigned char FS_RELAYING_EXT = 7U; +const unsigned char FS_RELAYING_WAIT_EXT = 8U; +const unsigned char FS_TIMEOUT_EXT = 9U; +const unsigned char FS_TIMEOUT_WAIT_EXT = 10U; +const unsigned char FS_HANG = 11U; + +CFMControl::CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn, CRSSIInterpolator* rssiMapper) : m_network(network), m_txAudioGain(txAudioGain), m_rxAudioGain(rxAudioGain), m_preEmphasisOn(preEmphasisOn), m_deEmphasisOn(deEmphasisOn), +m_rssiMapper(rssiMapper), m_enabled(false), m_incomingRFAudio(1600U, "Incoming RF FM Audio"), m_preEmphasis(nullptr), @@ -45,26 +58,27 @@ m_filterStage1(nullptr), m_filterStage2(nullptr), m_filterStage3(nullptr) { - assert(txAudioGain > 0.0F); - assert(rxAudioGain > 0.0F); + assert(txAudioGain > 0.0F); + assert(rxAudioGain > 0.0F); + assert(rssiMapper != nullptr); - m_preEmphasis = new CIIRDirectForm1Filter(8.315375384336983F, -7.03334621603483F,0.0F,1.0F, 0.282029168302153F,0.0F, PREEMPHASIS_GAIN_DB); - m_deEmphasis = new CIIRDirectForm1Filter(0.07708787090460224F, 0.07708787090460224F,0.0F, 1.0F, -0.8458242581907955F,0.0F, DEEMPHASIS_GAIN_DB); + m_preEmphasis = new CIIRDirectForm1Filter(8.315375384336983F, -7.03334621603483F,0.0F,1.0F, 0.282029168302153F,0.0F, PREEMPHASIS_GAIN_DB); + m_deEmphasis = new CIIRDirectForm1Filter(0.07708787090460224F, 0.07708787090460224F,0.0F, 1.0F, -0.8458242581907955F,0.0F, DEEMPHASIS_GAIN_DB); - // Chebyshev type 1 0.2dB cheby type 1 3rd order 300-2700Hz fs=8000 - m_filterStage1 = new CIIRDirectForm1Filter(0.29495028f, 0.0f, -0.29495028f, 1.0f, -0.61384624f, -0.057158668f, FILTER_GAIN_DB); - m_filterStage2 = new CIIRDirectForm1Filter(1.0f, 2.0f, 1.0f, 1.0f, 0.9946123f, 0.6050482f, FILTER_GAIN_DB); - m_filterStage3 = new CIIRDirectForm1Filter(1.0f, -2.0f, 1.0f, 1.0f, -1.8414584f, 0.8804949f, FILTER_GAIN_DB); + // Chebyshev type 1 0.2dB cheby type 1 3rd order 300-2700Hz fs=8000 + m_filterStage1 = new CIIRDirectForm1Filter(0.29495028f, 0.0f, -0.29495028f, 1.0f, -0.61384624f, -0.057158668f, FILTER_GAIN_DB); + m_filterStage2 = new CIIRDirectForm1Filter(1.0f, 2.0f, 1.0f, 1.0f, 0.9946123f, 0.6050482f, FILTER_GAIN_DB); + m_filterStage3 = new CIIRDirectForm1Filter(1.0f, -2.0f, 1.0f, 1.0f, -1.8414584f, 0.8804949f, FILTER_GAIN_DB); } CFMControl::~CFMControl() { - delete m_preEmphasis; - delete m_deEmphasis; + delete m_preEmphasis; + delete m_deEmphasis; - delete m_filterStage1; - delete m_filterStage2; - delete m_filterStage3; + delete m_filterStage1; + delete m_filterStage2; + delete m_filterStage3; } bool CFMControl::writeModem(const unsigned char* data, unsigned int length) @@ -72,121 +86,174 @@ bool CFMControl::writeModem(const unsigned char* data, unsigned int length) assert(data != nullptr); assert(length > 0U); - if (m_network == nullptr) - return true; + if (data[0U] == TAG_HEADER) { + switch (data[1U]) { + case FS_LISTENING: writeJSON("listening"); break; + case FS_KERCHUNK_RF: writeJSON("kerchunk_rf"); break; + case FS_RELAYING_RF: writeJSON("relaying_rf"); break; + case FS_RELAYING_WAIT_RF: writeJSON("relaying_wait_rf"); break; + case FS_TIMEOUT_RF: writeJSON("timeout_rf"); break; + case FS_TIMEOUT_WAIT_RF: writeJSON("timeout_wait_rf"); break; + case FS_KERCHUNK_EXT: writeJSON("kerchunk_ext"); break; + case FS_RELAYING_EXT: writeJSON("relaying_ext"); break; + case FS_RELAYING_WAIT_EXT: writeJSON("relaying_wait_ext"); break; + case FS_TIMEOUT_EXT: writeJSON("timeout_ext"); break; + case FS_TIMEOUT_WAIT_EXT: writeJSON("timeout_wait_ext"); break; + case FS_HANG: writeJSON("hang"); break; + default: writeJSON("unknown"); break; + } - if (data[0U] == TAG_HEADER) - return true; + return true; + } - if (data[0U] == TAG_EOT) - return m_network->writeEnd(); + if (data[0U] == TAG_RSSI) { + uint16_t raw = 0U; + raw |= (data[0U] << 8) & 0xFF00U; + raw |= (data[1U] << 0) & 0x00FFU; - if (data[0U] != TAG_DATA) - return false; + // Convert the raw RSSI to dBm + int rssi = m_rssiMapper->interpolate(raw); + if (rssi != 0) { + LogDebug("FM, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi); + writeJSONRSSI(rssi); + } - m_incomingRFAudio.addData(data + 1U, length - 1U); - unsigned int bufferLength = m_incomingRFAudio.dataSize(); - if (bufferLength > 240U) // 160 samples 12-bit - bufferLength = 240U; // 160 samples 12-bit + return true; + } - if (bufferLength >= 3U) { - bufferLength = bufferLength - bufferLength % 3U; // Round down to nearest multiple of 3 - unsigned char bufferData[240U]; // 160 samples 12-bit - m_incomingRFAudio.getData(bufferData, bufferLength); + if (m_network == nullptr) + return true; - unsigned int pack = 0U; - unsigned char* packPointer = (unsigned char*)&pack; - float out[160U]; // 160 samples 12-bit - unsigned int nOut = 0U; - short unpackedSamples[2U]; + if (data[0U] == TAG_EOT) + return m_network->writeEnd(); - for (unsigned int i = 0U; i < bufferLength; i += 3U) { - // Extract unsigned 12 bit unsigned sample pairs packed into 3 bytes to 16 bit signed - packPointer[0U] = bufferData[i + 0U]; - packPointer[1U] = bufferData[i + 1U]; - packPointer[2U] = bufferData[i + 2U]; + if (data[0U] != TAG_DATA) + return false; - unpackedSamples[1U] = short(int(pack & FM_MASK) - 2048); - unpackedSamples[0U] = short(int(pack >> 12 & FM_MASK) - 2048); // + m_incomingRFAudio.addData(data + 1U, length - 1U); + unsigned int bufferLength = m_incomingRFAudio.dataSize(); - // Process unpacked sample pair - for (unsigned char j = 0U; j < 2U; j++) { - // Convert to float (-1.0 to +1.0) - float sampleFloat = (float(unpackedSamples[j]) * m_rxAudioGain) / 2048.0F; + if (bufferLength > 240U) // 160 samples 12-bit + bufferLength = 240U; // 160 samples 12-bit - // De-emphasise and remove CTCSS - if (m_deEmphasisOn) - sampleFloat = m_deEmphasis->filter(sampleFloat); + if (bufferLength >= 3U) { + bufferLength = bufferLength - bufferLength % 3U; // Round down to nearest multiple of 3 - out[nOut++] = m_filterStage3->filter(m_filterStage2->filter(m_filterStage1->filter(sampleFloat))); - } - } + unsigned char bufferData[240U]; // 160 samples 12-bit + m_incomingRFAudio.getData(bufferData, bufferLength); -#if defined(DUMP_RF_AUDIO) - FILE * audiofile = fopen("./audiodump.bin", "ab"); - if (audiofile != nullptr) { - fwrite(out, sizeof(float), nOut, audiofile); - fclose(audiofile); - } -#endif - return m_network->writeData(out, nOut); - } + unsigned int pack = 0U; + unsigned char* packPointer = (unsigned char*)&pack; - return true; + float out[160U]; // 160 samples 12-bit + unsigned int nOut = 0U; + + for (unsigned int i = 0U; i < bufferLength; i += 3U) { + // Extract unsigned 12 bit unsigned sample pairs packed into 3 bytes to 16 bit signed + packPointer[0U] = bufferData[i + 0U]; + packPointer[1U] = bufferData[i + 1U]; + packPointer[2U] = bufferData[i + 2U]; + + short unpackedSamples[2U]; + unpackedSamples[1U] = short(int((pack >> 0) & FM_MASK) - 2048); + unpackedSamples[0U] = short(int((pack >> 12) & FM_MASK) - 2048); // + + // Process unpacked sample pair + for (unsigned char j = 0U; j < 2U; j++) { + // Convert to float (-1.0 to +1.0) + float sampleFloat = (float(unpackedSamples[j]) * m_rxAudioGain) / 2048.0F; + + // De-emphasise and remove CTCSS + if (m_deEmphasisOn) + sampleFloat = m_deEmphasis->filter(sampleFloat); + + out[nOut++] = m_filterStage3->filter(m_filterStage2->filter(m_filterStage1->filter(sampleFloat))); + } + } + + return m_network->writeData(out, nOut); + } + + return true; } unsigned int CFMControl::readModem(unsigned char* data, unsigned int space) { - assert(data != nullptr); - assert(space > 0U); + assert(data != nullptr); + assert(space > 0U); - if (m_network == nullptr) - return 0U; + if (m_network == nullptr) + return 0U; - if (space > 240U) // 160 samples 12-bit - space = 240U; // 160 samples 12-bit + if (space > 240U) // 160 samples 12-bit + space = 240U; // 160 samples 12-bit - float netData[160U]; // Modem can handle up to 160 samples at a time - unsigned int length = m_network->readData(netData, 160U); // 160 samples 12-bit - if (length == 0U) - return 0U; + float netData[160U]; // Modem can handle up to 160 samples at a time + unsigned int length = m_network->readData(netData, 160U); // 160 samples 12-bit + if (length == 0U) + return 0U; - unsigned int pack = 0U; - unsigned char* packPointer = (unsigned char*)&pack; - unsigned int nData = 0U; + unsigned int pack = 0U; + unsigned char* packPointer = (unsigned char*)&pack; + unsigned int nData = 0U; - for (unsigned int i = 0; i < length; i++) { - float sampleFloat = netData[i] * m_txAudioGain; + for (unsigned int i = 0; i < length; i++) { + float sampleFloat = netData[i] * m_txAudioGain; - // Pre-emphasis - if (m_preEmphasisOn) - sampleFloat = m_preEmphasis->filter(sampleFloat); + // Pre-emphasis + if (m_preEmphasisOn) + sampleFloat = m_preEmphasis->filter(sampleFloat); - // Convert float to 12-bit samples (0 to 4095) - unsigned int sample12bit = (unsigned int)((sampleFloat + 1.0F) * 2048.0F + 0.5F); + // Convert float to 12-bit samples (0 to 4095) + unsigned int sample12bit = (unsigned int)((sampleFloat + 1.0F) * 2048.0F + 0.5F); - // Pack 2 samples into 3 bytes - if ((i & 1U) == 0) { - pack = 0U; - pack = sample12bit << 12; - } else { - pack |= sample12bit; + // Pack 2 samples into 3 bytes + if ((i & 1U) == 0U) { + pack = 0U; + pack = sample12bit << 12; + } else { + pack |= sample12bit; - data[nData++] = packPointer[0U]; - data[nData++] = packPointer[1U]; - data[nData++] = packPointer[2U]; - } - } + data[nData++] = packPointer[0U]; + data[nData++] = packPointer[1U]; + data[nData++] = packPointer[2U]; + } + } - return nData; + return nData; } void CFMControl::clock(unsigned int ms) { - // May not be needed + // May not be needed } void CFMControl::enable(bool enabled) { - // May not be needed + // May not be needed } + +void CFMControl::writeJSON(const char* state) +{ + assert(state != nullptr); + + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["state"] = state; + + WriteJSON("FM", json); +} + +void CFMControl::writeJSONRSSI(int rssi) +{ + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "FM"; + json["value"] = rssi; + + WriteJSON("RSSI", json); +} +#endif + diff --git a/FMControl.h b/FMControl.h index 9344f77..f15902f 100644 --- a/FMControl.h +++ b/FMControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020,2021 by Jonathan Naylor G4KLX + * Copyright (C) 2020,2021,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,11 @@ #include "FMNetwork.h" #include "Defines.h" #include "IIRDirectForm1Filter.h" +#include "RSSIInterpolator.h" + +#if defined(USE_FM) + +#include // Uncomment this to dump audio to a raw audio file // The file will be written in same folder as executable @@ -30,7 +35,7 @@ class CFMControl { public: - CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn); + CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn, CRSSIInterpolator* rssiMapper); ~CFMControl(); bool writeModem(const unsigned char* data, unsigned int length); @@ -42,18 +47,25 @@ public: void enable(bool enabled); private: - CFMNetwork* m_network; - float m_txAudioGain; - float m_rxAudioGain; - bool m_preEmphasisOn; - bool m_deEmphasisOn; - bool m_enabled; + CFMNetwork* m_network; + float m_txAudioGain; + float m_rxAudioGain; + bool m_preEmphasisOn; + bool m_deEmphasisOn; + CRSSIInterpolator* m_rssiMapper; + bool m_enabled; CRingBuffer m_incomingRFAudio; CIIRDirectForm1Filter* m_preEmphasis; CIIRDirectForm1Filter* m_deEmphasis; CIIRDirectForm1Filter* m_filterStage1; CIIRDirectForm1Filter* m_filterStage2; CIIRDirectForm1Filter* m_filterStage3; + + void writeJSON(const char* state); + void writeJSONRSSI(int rssi); }; #endif + +#endif + diff --git a/FMNetwork.cpp b/FMNetwork.cpp index 0927b5d..45d5563 100644 --- a/FMNetwork.cpp +++ b/FMNetwork.cpp @@ -17,10 +17,11 @@ */ #include "FMNetwork.h" -#include "Defines.h" #include "Utils.h" #include "Log.h" +#if defined(USE_FM) + #include #include #include @@ -29,56 +30,34 @@ #include #include -const unsigned int MMDVM_SAMPLERATE = 8000U; - const unsigned int BUFFER_LENGTH = 1500U; -CFMNetwork::CFMNetwork(const std::string& callsign, const std::string& protocol, const std::string& localAddress, unsigned short localPort, const std::string& gatewayAddress, unsigned short gatewayPort, unsigned int sampleRate, const std::string& squelchFile, bool debug) : +CFMNetwork::CFMNetwork(const std::string& callsign, const std::string& localAddress, unsigned short localPort, const std::string& gatewayAddress, unsigned short gatewayPort, bool debug) : m_callsign(callsign), -m_protocol(FM_NETWORK_PROTOCOL::USRP), m_socket(localAddress, localPort), m_addr(), m_addrLen(0U), -m_sampleRate(sampleRate), -m_squelchFile(squelchFile), m_debug(debug), m_enabled(false), m_buffer(2000U, "FM Network"), m_seqNo(0U), -#if defined(HAS_SRC) -m_resampler(nullptr), -#endif -m_error(0), -m_fp(nullptr) +m_timer(1000U, 5U) { assert(!callsign.empty()); assert(gatewayPort > 0U); assert(!gatewayAddress.empty()); - assert(sampleRate > 0U); if (CUDPSocket::lookup(gatewayAddress, gatewayPort, m_addr, m_addrLen) != 0) m_addrLen = 0U; - // Remove any trailing letters in the callsign + // Remove any trailing spaces/letters from the callsign size_t pos = callsign.find_first_of(' '); if (pos != std::string::npos) m_callsign = callsign.substr(0U, pos); - - if (protocol == "RAW") - m_protocol = FM_NETWORK_PROTOCOL::RAW; - else - m_protocol = FM_NETWORK_PROTOCOL::USRP; - -#if defined(HAS_SRC) - m_resampler = ::src_new(SRC_SINC_FASTEST, 1, &m_error); -#endif } CFMNetwork::~CFMNetwork() { -#if defined(HAS_SRC) - ::src_delete(m_resampler); -#endif } bool CFMNetwork::open() @@ -90,28 +69,13 @@ bool CFMNetwork::open() LogMessage("Opening FM network connection"); - if (m_protocol == FM_NETWORK_PROTOCOL::RAW) { - if (!m_squelchFile.empty()) { - m_fp = ::fopen(m_squelchFile.c_str(), "wb"); - if (m_fp == nullptr) { -#if !defined(_WIN32) && !defined(_WIN64) - LogError("Cannot open the squelch file: %s, errno=%d", m_squelchFile.c_str(), errno); -#else - LogError("Cannot open the squelch file: %s, errno=%lu", m_squelchFile.c_str(), ::GetLastError()); -#endif - return false; - } - } - } - -#if !defined(HAS_SRC) - if ((m_protocol == FM_NETWORK_PROTOCOL::RAW) && (m_sampleRate != MMDVM_SAMPLERATE)) { - LogError("The resampler needed for non-native sample rates has not been included"); + bool ret = m_socket.open(m_addr); + if (!ret) return false; - } -#endif - return m_socket.open(m_addr); + m_timer.start(); + + return true; } bool CFMNetwork::writeData(const float* data, unsigned int nSamples) @@ -119,72 +83,23 @@ bool CFMNetwork::writeData(const float* data, unsigned int nSamples) assert(data != nullptr); assert(nSamples > 0U); - if (m_protocol == FM_NETWORK_PROTOCOL::USRP) - return writeUSRPData(data, nSamples); - else if (m_protocol == FM_NETWORK_PROTOCOL::RAW) - return writeRawData(data, nSamples); - else - return false; -} - -bool CFMNetwork::writeUSRPData(const float* data, unsigned int nSamples) -{ - assert(data != nullptr); - assert(nSamples > 0U); - if (m_seqNo == 0U) { - bool ret = writeUSRPStart(); + bool ret = writeStart(); if (!ret) return false; } - unsigned char buffer[500U]; - ::memset(buffer, 0x00U, 500U); + assert(data != nullptr); + assert(nSamples > 0U); + + uint8_t buffer[BUFFER_LENGTH]; + ::memset(buffer, 0x00U, BUFFER_LENGTH); unsigned int length = 0U; - buffer[length++] = 'U'; - buffer[length++] = 'S'; - buffer[length++] = 'R'; - buffer[length++] = 'P'; - - // Sequence number - buffer[length++] = (m_seqNo >> 24) & 0xFFU; - buffer[length++] = (m_seqNo >> 16) & 0xFFU; - buffer[length++] = (m_seqNo >> 8) & 0xFFU; - buffer[length++] = (m_seqNo >> 0) & 0xFFU; - - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - // PTT on - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x01U; - - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - // Type, 0 for audio - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; + buffer[length++] = 'F'; + buffer[length++] = 'M'; + buffer[length++] = 'D'; for (unsigned int i = 0U; i < nSamples; i++) { short val = short(data[i] * 32767.0F + 0.5F); // Changing audio format from float to S16LE @@ -193,170 +108,73 @@ bool CFMNetwork::writeUSRPData(const float* data, unsigned int nSamples) buffer[length++] = (val >> 8) & 0xFFU; } + m_seqNo++; + if (m_debug) CUtils::dump(1U, "FM Network Data Sent", buffer, length); - m_seqNo++; - return m_socket.write(buffer, length, m_addr, m_addrLen); } -bool CFMNetwork::writeRawData(const float* in, unsigned int nIn) +bool CFMNetwork::writeStart() { - assert(in != nullptr); - assert(nIn > 0U); - - if (m_seqNo == 0U) { - bool ret = writeRawStart(); - if (!ret) - return false; - } - - unsigned char buffer[2000U]; + uint8_t buffer[5U]; unsigned int length = 0U; -#if defined(HAS_SRC) - if (m_sampleRate != MMDVM_SAMPLERATE) { - unsigned int nOut = (nIn * m_sampleRate) / MMDVM_SAMPLERATE; + buffer[length++] = 'F'; + buffer[length++] = 'M'; + buffer[length++] = 'S'; - float out[1000U]; + for (unsigned int i = 0U; i < m_callsign.size(); i++) + buffer[length++] = m_callsign.at(i); - SRC_DATA data; - data.data_in = in; - data.data_out = out; - data.input_frames = nIn; - data.output_frames = nOut; - data.end_of_input = 0; - data.src_ratio = float(m_sampleRate) / float(MMDVM_SAMPLERATE); - - int ret = ::src_process(m_resampler, &data); - if (ret != 0) { - LogError("Error from the write resampler - %d - %s", ret, ::src_strerror(ret)); - return false; - } - - for (unsigned int i = 0U; i < nOut; i++) { - short val = short(out[i] * 32767.0F + 0.5F); // Changing audio format from float to S16LE - - buffer[length++] = (val >> 0) & 0xFFU; - buffer[length++] = (val >> 8) & 0xFFU; - } - } else { -#endif - for (unsigned int i = 0U; i < nIn; i++) { - short val = short(in[i] * 32767.0F + 0.5F); // Changing audio format from float to S16LE - - buffer[length++] = (val >> 0) & 0xFFU; - buffer[length++] = (val >> 8) & 0xFFU; - } -#if defined(HAS_SRC) - } -#endif + buffer[length++] = '\0'; if (m_debug) CUtils::dump(1U, "FM Network Data Sent", buffer, length); - m_seqNo++; - return m_socket.write(buffer, length, m_addr, m_addrLen); } bool CFMNetwork::writeEnd() { - if (m_protocol == FM_NETWORK_PROTOCOL::USRP) - return writeUSRPEnd(); - else - return writeRawEnd(); -} + uint8_t buffer[5U]; -bool CFMNetwork::writeUSRPEnd() -{ - unsigned char buffer[500U]; - ::memset(buffer, 0x00U, 500U); - - unsigned int length = 0U; - - buffer[length++] = 'U'; - buffer[length++] = 'S'; - buffer[length++] = 'R'; - buffer[length++] = 'P'; - - // Sequence number - buffer[length++] = (m_seqNo >> 24) & 0xFFU; - buffer[length++] = (m_seqNo >> 16) & 0xFFU; - buffer[length++] = (m_seqNo >> 8) & 0xFFU; - buffer[length++] = (m_seqNo >> 0) & 0xFFU; - - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - // PTT off - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - // Type, 0 for audio - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - length += 320U; + buffer[0U] = 'F'; + buffer[1U] = 'M'; + buffer[2U] = 'E'; m_seqNo = 0U; - if (length > 0U) { - if (m_debug) - CUtils::dump(1U, "FM Network Data Sent", buffer, length); + if (m_debug) + CUtils::dump(1U, "FM Network Data Sent", buffer, 3U); - return m_socket.write(buffer, length, m_addr, m_addrLen); - } else { - return true; - } + return m_socket.write(buffer, 3U, m_addr, m_addrLen); } -bool CFMNetwork::writeRawEnd() +bool CFMNetwork::writePing() { - m_seqNo = 0U; + uint8_t buffer[5U]; - if (m_fp != nullptr) { - size_t n = ::fwrite("Z", 1, 1, m_fp); - if (n != 1) { -#if !defined(_WIN32) && !defined(_WIN64) - LogError("Cannot write to the squelch file: %s, errno=%d", m_squelchFile.c_str(), errno); -#else - LogError("Cannot write to the squelch file: %s, errno=%lu", m_squelchFile.c_str(), ::GetLastError()); -#endif - return false; - } + buffer[0U] = 'F'; + buffer[1U] = 'M'; + buffer[2U] = 'P'; - ::fflush(m_fp); - } + if (m_debug) + CUtils::dump(1U, "FM Network Data Sent", buffer, 3U); - return true; + return m_socket.write(buffer, 3U, m_addr, m_addrLen); } void CFMNetwork::clock(unsigned int ms) { + m_timer.clock(ms); + if (m_timer.isRunning() && m_timer.hasExpired()) { + writePing(); + m_timer.start(); + } + unsigned char buffer[BUFFER_LENGTH]; sockaddr_storage addr; @@ -366,43 +184,28 @@ void CFMNetwork::clock(unsigned int ms) return; // Check if the data is for us - if (m_protocol == FM_NETWORK_PROTOCOL::USRP) { - if (!CUDPSocket::match(addr, m_addr, IPMATCHTYPE::ADDRESS_AND_PORT)) { - LogMessage("FM packet received from an invalid source"); - return; - } - } else { - if (!CUDPSocket::match(addr, m_addr, IPMATCHTYPE::ADDRESS_ONLY)) { - LogMessage("FM packet received from an invalid source"); - return; - } + if (!CUDPSocket::match(addr, m_addr, IPMATCHTYPE::ADDRESS_AND_PORT)) { + LogMessage("FM packet received from an invalid source"); + return; } if (!m_enabled) return; + // Invalid packet type? + if (::memcmp(buffer, "FM", 2U) != 0) + return; + + if (::memcmp(buffer, "FMP", 3U) == 0) + return; + if (m_debug) CUtils::dump(1U, "FM Network Data Received", buffer, length); - if (m_protocol == FM_NETWORK_PROTOCOL::USRP) { - // Invalid packet type? - if (::memcmp(buffer, "USRP", 4U) != 0) - return; + if (::memcmp(buffer, "FMD", 3U) != 0) + return; - if (length < 32) - return; - - // The type is a big-endian 4-byte integer - unsigned int type = (buffer[20U] << 24) + - (buffer[21U] << 16) + - (buffer[22U] << 8) + - (buffer[23U] << 0); - - if (type == 0U) - m_buffer.addData(buffer + 32U, length - 32U); - } else if (m_protocol == FM_NETWORK_PROTOCOL::RAW) { - m_buffer.addData(buffer, length); - } + m_buffer.addData(buffer + 3U, length - 3U); } unsigned int CFMNetwork::readData(float* out, unsigned int nOut) @@ -414,53 +217,16 @@ unsigned int CFMNetwork::readData(float* out, unsigned int nOut) if (bytes == 0U) return 0U; -#if defined(HAS_SRC) - if ((m_protocol == FM_NETWORK_PROTOCOL::RAW) && (m_sampleRate != MMDVM_SAMPLERATE)) { - unsigned int nIn = (nOut * m_sampleRate) / MMDVM_SAMPLERATE; + if (bytes < nOut) + nOut = bytes; - if (bytes < nIn) { - nIn = bytes; - nOut = (nIn * MMDVM_SAMPLERATE) / m_sampleRate; - } + unsigned char buffer[BUFFER_LENGTH]; + m_buffer.getData(buffer, nOut * sizeof(unsigned short)); - unsigned char buffer[2000U]; - m_buffer.getData(buffer, nIn * sizeof(unsigned short)); - - float in[1000U]; - - for (unsigned int i = 0U; i < nIn; i++) { - short val = ((buffer[i * 2U + 0U] & 0xFFU) << 0) + ((buffer[i * 2U + 1U] & 0xFFU) << 8); - in[i] = float(val) / 65536.0F; - } - - SRC_DATA data; - data.data_in = in; - data.data_out = out; - data.input_frames = nIn; - data.output_frames = nOut; - data.end_of_input = 0; - data.src_ratio = float(MMDVM_SAMPLERATE) / float(m_sampleRate); - - int ret = ::src_process(m_resampler, &data); - if (ret != 0) { - LogError("Error from the read resampler - %d - %s", ret, ::src_strerror(ret)); - return false; - } - } else { -#endif - if (bytes < nOut) - nOut = bytes; - - unsigned char buffer[1500U]; - m_buffer.getData(buffer, nOut * sizeof(unsigned short)); - - for (unsigned int i = 0U; i < nOut; i++) { - short val = ((buffer[i * 2U + 0U] & 0xFFU) << 0) + ((buffer[i * 2U + 1U] & 0xFFU) << 8); - out[i] = float(val) / 65536.0F; - } -#if defined(HAS_SRC) + for (unsigned int i = 0U; i < nOut; i++) { + short val = ((buffer[i * 2U + 0U] & 0xFFU) << 0) + ((buffer[i * 2U + 1U] & 0xFFU) << 8); + out[i] = float(val) / 65536.0F; } -#endif return nOut; } @@ -474,11 +240,6 @@ void CFMNetwork::close() { m_socket.close(); - if (m_fp != nullptr) { - ::fclose(m_fp); - m_fp = nullptr; - } - LogMessage("Closing FM network connection"); } @@ -492,119 +253,5 @@ void CFMNetwork::enable(bool enabled) m_enabled = enabled; } -bool CFMNetwork::writeUSRPStart() -{ - unsigned char buffer[500U]; - ::memset(buffer, 0x00U, 500U); - - unsigned int length = 0U; - - buffer[length++] = 'U'; - buffer[length++] = 'S'; - buffer[length++] = 'R'; - buffer[length++] = 'P'; - - // Sequence number - buffer[length++] = (m_seqNo >> 24) & 0xFFU; - buffer[length++] = (m_seqNo >> 16) & 0xFFU; - buffer[length++] = (m_seqNo >> 8) & 0xFFU; - buffer[length++] = (m_seqNo >> 0) & 0xFFU; - - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - // PTT off - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - // Type, 2 for metadata - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x02U; - - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - // TLV TAG for Metadata - buffer[length++] = 0x08U; - - // TLV Length - buffer[length++] = 3U + 4U + 3U + 1U + 1U + (unsigned char)m_callsign.size() + 1U; - - // DMR Id - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - // Rpt Id - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - // Talk Group - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - buffer[length++] = 0x00U; - - // Time Slot - buffer[length++] = 0x00U; - - // Color Code - buffer[length++] = 0x00U; - - // Callsign - for (std::string::const_iterator it = m_callsign.cbegin(); it != m_callsign.cend(); ++it) - buffer[length++] = *it; - - // End of Metadata - buffer[length++] = 0x00U; - - length = 70U; - - if (length > 0U) { - if (m_debug) - CUtils::dump(1U, "FM Network Data Sent", buffer, length); - - return m_socket.write(buffer, length, m_addr, m_addrLen); - } else { - return true; - } -} - -bool CFMNetwork::writeRawStart() -{ - if (m_fp != nullptr) { - size_t n = ::fwrite("O", 1, 1, m_fp); - if (n != 1) { -#if !defined(_WIN32) && !defined(_WIN64) - LogError("Cannot write to the squelch file: %s, errno=%d", m_squelchFile.c_str(), errno); -#else - LogError("Cannot write to the squelch file: %s, errno=%lu", m_squelchFile.c_str(), ::GetLastError()); #endif - return false; - } - - ::fflush(m_fp); - } - - return true; -} diff --git a/FMNetwork.h b/FMNetwork.h index cd2cc4e..98c0dad 100644 --- a/FMNetwork.h +++ b/FMNetwork.h @@ -21,22 +21,18 @@ #include "RingBuffer.h" #include "UDPSocket.h" +#include "Defines.h" +#include "Timer.h" -#if defined(HAS_SRC) -#include -#endif +#if defined(USE_FM) #include #include -enum class FM_NETWORK_PROTOCOL { - USRP, - RAW -}; class CFMNetwork { public: - CFMNetwork(const std::string& callsign, const std::string& protocol, const std::string& localAddress, unsigned short localPort, const std::string& gatewayAddress, unsigned short gatewayPort, unsigned int sampleRate, const std::string& squelchFile, bool debug); + CFMNetwork(const std::string& callsign, const std::string& localAddress, unsigned short localPort, const std::string& gatewayAddress, unsigned short gatewayPort, bool debug); ~CFMNetwork(); bool open(); @@ -57,31 +53,20 @@ public: private: std::string m_callsign; - FM_NETWORK_PROTOCOL m_protocol; CUDPSocket m_socket; sockaddr_storage m_addr; unsigned int m_addrLen; - unsigned int m_sampleRate; - std::string m_squelchFile; bool m_debug; bool m_enabled; CRingBuffer m_buffer; unsigned int m_seqNo; -#if defined(HAS_SRC) - SRC_STATE* m_resampler; -#endif - int m_error; - FILE* m_fp; + CTimer m_timer; - bool writeUSRPStart(); - bool writeRawStart(); - - bool writeUSRPData(const float* data, unsigned int nSamples); - bool writeRawData(const float* in, unsigned int nIn); - - bool writeUSRPEnd(); - bool writeRawEnd(); + bool writeStart(); + bool writePing(); }; #endif +#endif + diff --git a/Golay2087.cpp b/Golay2087.cpp index d68ec6a..a08d0d1 100644 --- a/Golay2087.cpp +++ b/Golay2087.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,8 @@ #include "Golay2087.h" +#if defined(USE_DMR) + #include #include @@ -260,3 +262,6 @@ void CGolay2087::encode(unsigned char* data) data[1U] = cksum & 0xFFU; data[2U] = cksum >> 8; } + +#endif + diff --git a/Golay2087.h b/Golay2087.h index d54daed..c8ce210 100644 --- a/Golay2087.h +++ b/Golay2087.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #ifndef Golay2087_H #define Golay2087_H +#include "Defines.h" + +#if defined(USE_DMR) + class CGolay2087 { public: static void encode(unsigned char* data); @@ -30,3 +34,6 @@ private: }; #endif + +#endif + diff --git a/HD44780.cpp b/HD44780.cpp deleted file mode 100644 index 7a98a2b..0000000 --- a/HD44780.cpp +++ /dev/null @@ -1,1093 +0,0 @@ -/* - * Copyright (C) 2016,2017,2018,2020,2021,2025 by Jonathan Naylor G4KLX & Tony Corbett G0WFV - * - * 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 "HD44780.h" -#include "Log.h" - -#include -#include -#include -#include - -#include -#include -#include - -const char* LISTENING = "Listening "; -const char* DEADSPACE = " "; - -char m_buffer1[128U]; -char m_buffer2[128U]; -char m_buffer3[128U]; -char m_buffer4[128U]; - -const unsigned int DSTAR_RSSI_COUNT = 3U; // 3 * 420ms = 1260ms -const unsigned int DMR_RSSI_COUNT = 4U; // 4 * 360ms = 1440ms -const unsigned int YSF_RSSI_COUNT = 13U; // 13 * 100ms = 1300ms -const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms -const unsigned int NXDN_RSSI_COUNT = 28U; // 28 * 40ms = 1120ms - -CHD44780::CHD44780(unsigned int rows, unsigned int cols, const std::string& callsign, unsigned int dmrid, const std::vector& pins, unsigned int i2cAddress, bool pwm, unsigned int pwmPin, unsigned int pwmBright, unsigned int pwmDim, bool displayClock, bool utc, bool duplex) : -CDisplay(), -m_rows(rows), -m_cols(cols), -m_callsign(callsign), -m_dmrid(dmrid), -m_rb(pins.at(0U)), -m_strb(pins.at(1U)), -m_d0(pins.at(2U)), -m_d1(pins.at(3U)), -m_d2(pins.at(4U)), -m_d3(pins.at(5U)), -m_i2cAddress(i2cAddress), -m_pwm(pwm), -m_pwmPin(pwmPin), -m_pwmBright(pwmBright), -m_pwmDim(pwmDim), -m_displayClock(displayClock), -m_utc(utc), -m_duplex(duplex), -//m_duplex(true), // uncomment to force duplex display for testing! -m_fd(-1), -m_dmr(false), -m_clockDisplayTimer(1000U, 0U, 250U), // Update the clock display every 250ms -m_rssiCount1(0U), -m_rssiCount2(0U) -{ - assert(rows > 1U); - assert(cols > 15U); -} - -// Text-based custom character for "from" -unsigned char fmChar[8] = -{ - 0b11100, - 0b10000, - 0b11000, - 0b10000, - 0b00101, - 0b00111, - 0b00101, - 0b00101 -}; - -// Text-based custom character for "to" -unsigned char toChar[8] = -{ - 0b11100, - 0b01000, - 0b01000, - 0b01000, - 0b00010, - 0b00101, - 0b00101, - 0b00010 -}; - -// Icon-based custom character for RF traffic -unsigned char rfChar[8] = -{ - 0b11111, - 0b10101, - 0b01110, - 0b00100, - 0b00100, - 0b00100, - 0b00100, - 0b00000 -}; - -// Icon-based custom character for network traffic -unsigned char ipChar[8] = -{ - 0b00000, - 0b01110, - 0b10001, - 0b00100, - 0b01010, - 0b00000, - 0b00100, - 0b00000 -}; - -// Icon-based custom character for call to talkgroup -unsigned char tgChar[8] = -{ - 0b01110, - 0b10001, - 0b10001, - 0b10001, - 0b01010, - 0b01100, - 0b10000, - 0b00000 -}; - -// Icon-based custom character for private call -unsigned char privChar[8] = -{ - 0b00100, - 0b00000, - 0b11111, - 0b01110, - 0b01110, - 0b01010, - 0b01010, - 0b00000 -}; - -CHD44780::~CHD44780() -{ -} - -bool CHD44780::open() -{ - ::wiringPiSetup(); - - if (m_pwm) { - if (m_pwmPin != 1U) { - ::softPwmCreate(m_pwmPin, 0, 100); - ::softPwmWrite(m_pwmPin, m_pwmDim); - } else { - ::pinMode(m_pwmPin, PWM_OUTPUT); - ::pwmWrite(m_pwmPin, (m_pwmDim / 100) * 1024); - } - } - -#ifdef ADAFRUIT_DISPLAY - adafruitLCDSetup(); -#endif - -#ifdef PCF8574_DISPLAY - pcf8574LCDSetup(); -#endif - - m_fd = ::lcdInit(m_rows, m_cols, 4, m_rb, m_strb, m_d0, m_d1, m_d2, m_d3, 0, 0, 0, 0); - if (m_fd == -1) { - LogError("Unable to open the HD44780"); - return false; - } - - ::lcdDisplay(m_fd, 1); - ::lcdCursor(m_fd, 0); - ::lcdCursorBlink(m_fd, 0); - ::lcdCharDef(m_fd, 0, fmChar); - ::lcdCharDef(m_fd, 1, toChar); - ::lcdCharDef(m_fd, 2, rfChar); - ::lcdCharDef(m_fd, 3, ipChar); - ::lcdCharDef(m_fd, 4, privChar); - ::lcdCharDef(m_fd, 5, tgChar); - - return true; -} - -#ifdef ADAFRUIT_DISPLAY -void CHD44780::adafruitLCDSetup() -{ - // The other control pins are initialised with lcdInit() - ::mcp23017Setup(AF_BASE, m_i2cAddress); - - // Backlight LEDs - ::pinMode(AF_RED, OUTPUT); - ::pinMode(AF_GREEN, OUTPUT); - ::pinMode(AF_BLUE, OUTPUT); - - // Control signals - ::pinMode(AF_RW, OUTPUT); - ::digitalWrite(AF_RW, LOW); - - m_rb = AF_RS; - m_strb = AF_E; - m_d0 = AF_D0; - m_d1 = AF_D1; - m_d2 = AF_D2; - m_d3 = AF_D3; -} - -void CHD44780::adafruitLCDColour(ADAFRUIT_COLOUR colour) -{ - switch (colour) { - case AC_OFF: - ::digitalWrite(AF_RED, AF_OFF); - ::digitalWrite(AF_GREEN, AF_OFF); - ::digitalWrite(AF_BLUE, AF_OFF); - break; - case AC_WHITE: - ::digitalWrite(AF_RED, AF_ON); - ::digitalWrite(AF_GREEN, AF_ON); - ::digitalWrite(AF_BLUE, AF_ON); - break; - case AC_RED: - ::digitalWrite(AF_RED, AF_ON); - ::digitalWrite(AF_GREEN, AF_OFF); - ::digitalWrite(AF_BLUE, AF_OFF); - break; - case AC_GREEN: - ::digitalWrite(AF_RED, AF_OFF); - ::digitalWrite(AF_GREEN, AF_ON); - ::digitalWrite(AF_BLUE, AF_OFF); - break; - case AC_BLUE: - ::digitalWrite(AF_RED, AF_OFF); - ::digitalWrite(AF_GREEN, AF_OFF); - ::digitalWrite(AF_BLUE, AF_ON); - break; - case AC_PURPLE: - ::digitalWrite(AF_RED, AF_ON); - ::digitalWrite(AF_GREEN, AF_OFF); - ::digitalWrite(AF_BLUE, AF_ON); - break; - case AC_YELLOW: - ::digitalWrite(AF_RED, AF_ON); - ::digitalWrite(AF_GREEN, AF_ON); - ::digitalWrite(AF_BLUE, AF_OFF); - break; - case AC_ICE: - ::digitalWrite(AF_RED, AF_OFF); - ::digitalWrite(AF_GREEN, AF_ON); - ::digitalWrite(AF_BLUE, AF_ON); - break; - default: - break; - } -} -#endif - -#ifdef PCF8574_DISPLAY -void CHD44780::pcf8574LCDSetup() -{ - // Initalize PFC8574 - ::pcf8574Setup(AF_BASE, m_i2cAddress); - - // Turn on backlight - ::pinMode(AF_BL, OUTPUT); - ::digitalWrite(AF_BL, 1); - - // Set LCD to write mode. - ::pinMode(AF_RW, OUTPUT); - ::digitalWrite(AF_RW, 0); - - m_rb = AF_RS; - m_strb = AF_E; - m_d0 = AF_D0; - m_d1 = AF_D1; - m_d2 = AF_D2; - m_d3 = AF_D3; -} -#endif - -void CHD44780::setIdleInt() -{ - m_clockDisplayTimer.start(); // Start the clock display in IDLE only - ::lcdClear(m_fd); - -#ifdef ADAFRUIT_DISPLAY - adafruitLCDColour(AC_WHITE); -#endif - - if (m_pwm) { - if (m_pwmPin != 1U) - ::softPwmWrite(m_pwmPin, m_pwmDim); - else - ::pwmWrite(m_pwmPin, (m_pwmDim / 100) * 1024); - } - - // Print callsign and ID at on top row for all screen sizes - ::lcdPosition(m_fd, 0, 0); - ::lcdPrintf(m_fd, "%-6s", m_callsign.c_str()); - ::lcdPosition(m_fd, m_cols - 7, 0); - ::lcdPrintf(m_fd, "%7u", m_dmrid); - - // Print MMDVM and Idle on bottom row for all screen sizes - ::lcdPosition(m_fd, 0, m_rows - 1); - ::lcdPuts(m_fd, "MMDVM"); - ::lcdPosition(m_fd, m_cols - 4, m_rows - 1); - ::lcdPuts(m_fd, "Idle"); // Gets overwritten by clock on 2 line screen - - m_dmr = false; -} - -void CHD44780::setErrorInt(const char* text) -{ - assert(text != NULL); - -#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); - ::lcdPrintf(m_fd, "%s ERROR", text); - - m_dmr = false; -} - -void CHD44780::setLockoutInt() -{ -#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, "Lockout"); - - 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::setFMInt() -{ - m_clockDisplayTimer.stop(); - ::lcdClear(m_fd); - -#ifdef ADAFRUIT_DISPLAY - adafruitLCDColour(AC_WHITE); -#endif - - if (m_pwm) { - if (m_pwmPin != 1U) - ::softPwmWrite(m_pwmPin, m_pwmDim); - else - ::pwmWrite(m_pwmPin, (m_pwmDim / 100) * 1024); - } - - // Print callsign and ID at on top row for all screen sizes - ::lcdPosition(m_fd, 0, 0); - ::lcdPrintf(m_fd, "%-6s", m_callsign.c_str()); - ::lcdPosition(m_fd, m_cols - 7, 0); - ::lcdPrintf(m_fd, "%7u", m_dmrid); - - // Print MMDVM and Idle on bottom row for all screen sizes - ::lcdPosition(m_fd, 0, m_rows - 1); - ::lcdPuts(m_fd, "MMDVM"); - ::lcdPosition(m_fd, m_cols - 4, m_rows - 1); - ::lcdPuts(m_fd, "FM"); // Gets overwritten by clock on 2 line screen - - m_dmr = false; -} - -void CHD44780::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) -{ - assert(my1 != NULL); - assert(my2 != NULL); - assert(your != NULL); - assert(type != NULL); - assert(reflector != NULL); - -#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); - } - - if (m_rows > 2U) { - ::lcdPosition(m_fd, 0, (m_rows / 2) - 2); - ::sprintf(m_buffer1, "%s%s", "D-Star", DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } - - ::lcdPosition(m_fd, 0, (m_rows / 2) - 1); - ::lcdPutchar(m_fd, 0); - ::lcdPrintf(m_fd, " %.8s/%.4s", my1, my2); - ::lcdPosition(m_fd, m_cols - 1, (m_rows / 2) - 1); - - if (strcmp(type, "R") == 0) - ::lcdPutchar(m_fd, 2); - else - ::lcdPutchar(m_fd, 3); - - ::sprintf(m_buffer1, "%.8s", your); - - char *p = m_buffer1; - for (; *p; ++p) { - if (*p == ' ') - *p = '_'; - } - - if (strcmp(reflector, " ") != 0) { - if (m_rows == 2 && m_cols == 40) { - ::sprintf(m_buffer3, " via %.8s", reflector); - strcat(m_buffer1, m_buffer3); - } else if (m_rows > 2) { - ::sprintf(m_buffer3, "via %.8s", reflector); - ::lcdPosition(m_fd, 0, (m_rows / 2) + 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer3); - } - } - - ::lcdPosition(m_fd, 0, (m_rows / 2)); - ::lcdPutchar(m_fd, 1); - ::lcdPrintf(m_fd, " %.*s", m_cols, m_buffer1); - - m_dmr = false; - m_rssiCount1 = 0U; -} - -void CHD44780::writeDStarRSSIInt(unsigned char rssi) -{ - if (m_rssiCount1 == 0U && m_rows > 2) { - ::lcdPosition(m_fd, 0, 3); - ::lcdPrintf(m_fd, "-%3udBm", rssi); - } - - m_rssiCount1++; - if (m_rssiCount1 >= DSTAR_RSSI_COUNT) - m_rssiCount1 = 0U; -} - -void CHD44780::clearDStarInt() -{ -#ifdef ADAFRUIT_DISPLAY - adafruitLCDColour(AC_PURPLE); -#endif - - m_clockDisplayTimer.stop(); // Stop the clock display - ::lcdClear(m_fd); - - ::lcdPosition(m_fd, 0, (m_rows / 2) - 1); - ::sprintf(m_buffer2, "%s%s", "D-Star", DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer2); - ::lcdPosition(m_fd, 0, (m_rows / 2)); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); -} - -void CHD44780::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) -{ - assert(type != NULL); - - if (!m_dmr) { - m_clockDisplayTimer.stop(); // Stop the clock display - ::lcdClear(m_fd); - -#ifdef ADAFRUIT_DISPLAY - adafruitLCDColour(AC_GREEN); -#endif - - if (m_pwm) { - if (m_pwmPin != 1U) - ::softPwmWrite(m_pwmPin, m_pwmBright); - else - ::pwmWrite(m_pwmPin, (m_pwmBright / 100) * 1024); - } - - if (m_duplex) { - if (m_rows > 2U) { - ::lcdPosition(m_fd, 0, (m_rows / 2) - 2); - ::sprintf(m_buffer1, "%s%s", "DMR", DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } - - if (slotNo == 1U) { - //m_dmrScrollTimer2.stop(); - ::lcdPosition(m_fd, 0, (m_rows / 2)); - ::lcdPrintf(m_fd, "2 %.*s", m_cols - 2U, LISTENING); - } else { - //m_dmrScrollTimer1.stop(); - ::lcdPosition(m_fd, 0, (m_rows / 2) - 1); - ::lcdPrintf(m_fd, "1 %.*s", m_cols - 2U, LISTENING); - } - } else { - //m_dmrScrollTimer2.stop(); - - if (m_rows > 2U) { - ::lcdPosition(m_fd, 0, (m_rows / 2) - 2); - ::sprintf(m_buffer1, "%s", DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } - - ::lcdPosition(m_fd, 0, (m_rows / 2) - 1); - ::sprintf(m_buffer1, "%s%s", "DMR", DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - ::lcdPosition(m_fd, 0, (m_rows / 2)); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - } - } - -#ifdef ADAFRUIT_DISPLAY - adafruitLCDColour(AC_RED); -#endif - - if (m_duplex) { - if (m_rows > 2U) { - ::lcdPosition(m_fd, 0, (m_rows / 2) - 2); - ::sprintf(m_buffer1, "%s%s", "DMR", DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } - - if (slotNo == 1U) { - ::lcdPosition(m_fd, 0, (m_rows / 2) - 1); - ::lcdPuts(m_fd, "1 "); - if (m_cols > 16 ) - ::sprintf(m_buffer1, "%s > %s%s%s", src.c_str(), group ? "TG" : "", dst.c_str(), DEADSPACE); - else - ::sprintf(m_buffer1, "%s>%s%s", src.c_str(), dst.c_str(), DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols - 2U, m_buffer1); - - ::lcdPosition(m_fd, m_cols - 3U, (m_rows / 2) - 1); - ::lcdPuts(m_fd, " "); - - if (group) - ::lcdPutchar(m_fd, 5); - else - ::lcdPutchar(m_fd, 4); - - if (strcmp(type, "R") == 0) - ::lcdPutchar(m_fd, 2); - else - ::lcdPutchar(m_fd, 3); - } else { - ::lcdPosition(m_fd, 0, (m_rows / 2)); - ::lcdPuts(m_fd, "2 "); - - if (m_cols > 16) - ::sprintf(m_buffer2, "%s > %s%s%s", src.c_str(), group ? "TG" : "", dst.c_str(), DEADSPACE); - else - ::sprintf(m_buffer2, "%s>%s%s", src.c_str(), dst.c_str(), DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols - 2U, m_buffer2); - - ::lcdPosition(m_fd, m_cols - 3U, (m_rows / 2)); - ::lcdPuts(m_fd, " "); - - if (group) - ::lcdPutchar(m_fd, 5); - else - ::lcdPutchar(m_fd, 4); - - if (strcmp(type, "R") == 0) - ::lcdPutchar(m_fd, 2); - else - ::lcdPutchar(m_fd, 3); - } - } else { - if (m_rows > 2U) { - ::lcdPosition(m_fd, 0, (m_rows / 2) - 2); - ::sprintf(m_buffer1, "%s%s", "DMR", DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } - - ::lcdPosition(m_fd, 0, (m_rows / 2) - 1); - ::lcdPutchar(m_fd, 0); - ::sprintf(m_buffer2, " %s%s", src.c_str(), DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols - 4U, m_buffer2); - ::lcdPosition(m_fd, m_cols - 1U, (m_rows / 2) - 1); - - if (strcmp(type, "R") == 0) - ::lcdPutchar(m_fd, 2); - else - ::lcdPutchar(m_fd, 3); - - ::lcdPosition(m_fd, 0, (m_rows / 2)); - ::lcdPutchar(m_fd, 1); - ::sprintf(m_buffer2, " %s%s%s", group ? "TG" : "", dst.c_str(), DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols - 4U, m_buffer2); - ::lcdPosition(m_fd, m_cols - 1U, (m_rows / 2)); - - if (group) - ::lcdPutchar(m_fd, 5); - else - ::lcdPutchar(m_fd, 4); - } - - m_dmr = true; - m_rssiCount1 = 0U; - m_rssiCount2 = 0U; -} - -void CHD44780::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi) -{ - if (m_rows > 2) { - if (slotNo == 1U) { - if (m_rssiCount1 == 0U) { - ::lcdPosition(m_fd, 0, 3); - ::lcdPrintf(m_fd, "-%3udBm", rssi); - } - - m_rssiCount1++; - if (m_rssiCount1 >= DMR_RSSI_COUNT) - m_rssiCount1 = 0U; - } else { - if (m_rssiCount2 == 0U) { - ::lcdPosition(m_fd, (m_cols / 2), 3); - ::lcdPrintf(m_fd, "-%3udBm", rssi); - } - - m_rssiCount2++; - if (m_rssiCount2 >= DMR_RSSI_COUNT) - m_rssiCount2 = 0U; - } - } -} - -void CHD44780::clearDMRInt(unsigned int slotNo) -{ -#ifdef ADAFRUIT_DISPLAY - adafruitLCDColour(AC_PURPLE); -#endif - - m_clockDisplayTimer.stop(); // Stop the clock display - - if (m_duplex) { - if (slotNo == 1U) { - ::lcdPosition(m_fd, 0, (m_rows / 2) - 1); - ::lcdPrintf(m_fd, "1 %.*s", m_cols - 2U, LISTENING); - - if (m_rows > 2) { // clear slot 1 RSSI - ::lcdPosition(m_fd, 0, 3); - ::lcdPrintf(m_fd, "%.*s", m_cols / 2, DEADSPACE); - } - } else { - ::lcdPosition(m_fd, 0, (m_rows / 2)); - ::lcdPrintf(m_fd, "2 %.*s", m_cols - 2U, LISTENING); - - if (m_rows > 2) { // cleat slot 2 RSSI - ::lcdPosition(m_fd, m_cols / 2, 3); - ::lcdPrintf(m_fd, "%.*s", m_cols / 2, DEADSPACE); - } - } - } else { - if (m_rows > 2U) { - ::lcdPosition(m_fd, 0, (m_rows / 2) - 2); - ::sprintf(m_buffer1, "%s", DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } - - ::lcdPosition(m_fd, 0, (m_rows / 2) - 1); - ::sprintf(m_buffer2, "%s%s", "DMR", DEADSPACE); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer2); - ::lcdPosition(m_fd, 0, (m_rows / 2)); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - } -} - -void CHD44780::writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) -{ - assert(source != NULL); - assert(dest != NULL); - assert(type != NULL); - assert(origin != NULL); - -#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, "System Fusion"); - - if (m_rows == 2U && m_cols == 16U) { - ::sprintf(m_buffer1, "%.10s >", source); - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } else if (m_rows == 4U && m_cols == 16U) { - ::sprintf(m_buffer1, "%.10s >", source); - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - - ::sprintf(m_buffer1, "DG-ID %u", dgid); - ::lcdPosition(m_fd, 0, 2); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } else if (m_rows == 4U && m_cols == 20U) { - ::sprintf(m_buffer1, "%.10s >", source); - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - - ::sprintf(m_buffer1, "DG-ID %u", dgid); - ::lcdPosition(m_fd, 0, 2); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } else if (m_rows == 2 && m_cols == 40U) { - ::sprintf(m_buffer1, "%.10s > DG-ID %u", source, dgid); - - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } - - m_dmr = false; - m_rssiCount1 = 0U; -} - -void CHD44780::writeFusionRSSIInt(unsigned char rssi) -{ - if (m_rssiCount1 == 0U && m_rows > 2) { - ::lcdPosition(m_fd, 0, 3); - ::lcdPrintf(m_fd, "-%3udBm", rssi); - } - - m_rssiCount1++; - if (m_rssiCount1 >= YSF_RSSI_COUNT) - m_rssiCount1 = 0U; -} - -void CHD44780::clearFusionInt() -{ -#ifdef ADAFRUIT_DISPLAY - adafruitLCDColour(AC_PURPLE); -#endif - m_clockDisplayTimer.stop(); // Stop the clock display - - if (m_rows == 2U && m_cols == 16U) { - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - } else if (m_rows == 4U && m_cols == 16U) { - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - - ::lcdPosition(m_fd, 0, 2); - ::lcdPrintf(m_fd, "%.*s", m_cols, " "); - - ::lcdPosition(m_fd, 0, 3); - ::lcdPrintf(m_fd, "%.*s", m_cols, " "); - } else if (m_rows == 4U && m_cols == 20U) { - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - - ::lcdPosition(m_fd, 0, 2); - ::lcdPrintf(m_fd, "%.*s", m_cols, " "); - - ::lcdPosition(m_fd, 0, 3); - ::lcdPrintf(m_fd, "%.*s", m_cols, " "); - } else if (m_rows == 2 && m_cols == 40U) { - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - } -} - -void CHD44780::writeP25Int(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(source != NULL); - assert(type != NULL); - -#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, "P25"); - - if (m_rows == 2U && m_cols == 16U) { - ::sprintf(m_buffer1, "%.10s >", source); - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } else if (m_rows == 4U && m_cols == 16U) { - ::sprintf(m_buffer1, "%.10s >", source); - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - - ::sprintf(m_buffer1, "%s%u", group ? "TG" : "", dest); - ::lcdPosition(m_fd, 0, 2); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } else if (m_rows == 4U && m_cols == 20U) { - ::sprintf(m_buffer1, "%.10s >", source); - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - - ::sprintf(m_buffer1, "%s%u", group ? "TG" : "", dest); - ::lcdPosition(m_fd, 0, 2); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } else if (m_rows == 2 && m_cols == 40U) { - ::sprintf(m_buffer1, "%.10s > %s%u", source, group ? "TG" : "", dest); - - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } - - m_dmr = false; - m_rssiCount1 = 0U; -} - -void CHD44780::writeP25RSSIInt(unsigned char rssi) -{ - if (m_rssiCount1 == 0U && m_rows > 2) { - ::lcdPosition(m_fd, 0, 3); - ::lcdPrintf(m_fd, "-%3udBm", rssi); - } - - m_rssiCount1++; - if (m_rssiCount1 >= P25_RSSI_COUNT) - m_rssiCount1 = 0U; -} - -void CHD44780::clearP25Int() -{ -#ifdef ADAFRUIT_DISPLAY - adafruitLCDColour(AC_PURPLE); -#endif - - m_clockDisplayTimer.stop(); // Stop the clock display - - if (m_rows == 2U && m_cols == 16U) { - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - } else if (m_rows == 4U && m_cols == 16U) { - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - - ::lcdPosition(m_fd, 0, 2); - ::lcdPrintf(m_fd, "%.*s", m_cols, " "); - - ::lcdPosition(m_fd, 0, 3); - ::lcdPrintf(m_fd, "%.*s", m_cols, " "); - } else if (m_rows == 4U && m_cols == 20U) { - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - - ::lcdPosition(m_fd, 0, 2); - ::lcdPrintf(m_fd, "%.*s", m_cols, " "); - - ::lcdPosition(m_fd, 0, 3); - ::lcdPrintf(m_fd, "%.*s", m_cols, " "); - } else if (m_rows == 2 && m_cols == 40U) { - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - } -} - -void CHD44780::writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(source != NULL); - assert(type != NULL); - -#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, "NXDN"); - - if (m_rows == 2U && m_cols == 16U) { - ::sprintf(m_buffer1, "%.10s >", source); - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } else if (m_rows == 4U && m_cols == 16U) { - ::sprintf(m_buffer1, "%.10s >", source); - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - - ::sprintf(m_buffer1, "%s%u", group ? "TG" : "", dest); - ::lcdPosition(m_fd, 0, 2); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } else if (m_rows == 4U && m_cols == 20U) { - ::sprintf(m_buffer1, "%.10s >", source); - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - - ::sprintf(m_buffer1, "%s%u", group ? "TG" : "", dest); - ::lcdPosition(m_fd, 0, 2); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } else if (m_rows == 2 && m_cols == 40U) { - ::sprintf(m_buffer1, "%.10s > %s%u", source, group ? "TG" : "", dest); - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, m_buffer1); - } - - m_dmr = false; - m_rssiCount1 = 0U; -} - -void CHD44780::writeNXDNRSSIInt(unsigned char rssi) -{ - if (m_rssiCount1 == 0U && m_rows > 2) { - ::lcdPosition(m_fd, 0, 3); - ::lcdPrintf(m_fd, "-%3udBm", rssi); - } - - m_rssiCount1++; - if (m_rssiCount1 >= NXDN_RSSI_COUNT) - m_rssiCount1 = 0U; -} - -void CHD44780::clearNXDNInt() -{ -#ifdef ADAFRUIT_DISPLAY - adafruitLCDColour(AC_PURPLE); -#endif - - m_clockDisplayTimer.stop(); // Stop the clock display - - if (m_rows == 2U && m_cols == 16U) { - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - } else if (m_rows == 4U && m_cols == 16U) { - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - - ::lcdPosition(m_fd, 0, 2); - ::lcdPrintf(m_fd, "%.*s", m_cols, " "); - - ::lcdPosition(m_fd, 0, 3); - ::lcdPrintf(m_fd, "%.*s", m_cols, " "); - } else if (m_rows == 4U && m_cols == 20U) { - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - - ::lcdPosition(m_fd, 0, 2); - ::lcdPrintf(m_fd, "%.*s", m_cols, " "); - - ::lcdPosition(m_fd, 0, 3); - ::lcdPrintf(m_fd, "%.*s", m_cols, " "); - } else if (m_rows == 2 && m_cols == 40U) { - ::lcdPosition(m_fd, 0, 1); - ::lcdPrintf(m_fd, "%.*s", m_cols, LISTENING); - } -} - -void CHD44780::writePOCSAGInt(uint32_t ric, const std::string& message) -{ - ::lcdPosition(m_fd, m_cols - 5, m_rows - 1); - ::lcdPuts(m_fd, "POCSG"); // Shortened "POCSAG TX" to 5 characters because it wraps around onto the next line (or on 16x2 displays the 1st line). -} - -void CHD44780::clearPOCSAGInt() -{ - ::lcdPosition(m_fd, m_cols - 5, m_rows - 1); - ::lcdPuts(m_fd, " Idle"); // Reverted back to 5 character implementation. -} - -void CHD44780::writeCWInt() -{ - ::lcdPosition(m_fd, m_cols - 5, m_rows - 1); - ::lcdPuts(m_fd, "CW TX"); -} - -void CHD44780::clearCWInt() -{ - ::lcdPosition(m_fd, m_cols - 5, m_rows - 1); - ::lcdPuts(m_fd, " Idle"); -} - -void CHD44780::clockInt(unsigned int ms) -{ - m_clockDisplayTimer.clock(ms); - - // Idle clock display - if (m_displayClock && m_clockDisplayTimer.isRunning() && m_clockDisplayTimer.hasExpired()) { - time_t currentTime; - struct tm *Time; - ::time(¤tTime); - - if (m_utc) - Time = ::gmtime(¤tTime); - else - Time = ::localtime(¤tTime); - - setlocale(LC_TIME,""); - ::strftime(m_buffer1, 128, "%X", Time); // Time - ::strftime(m_buffer2, 128, "%x", Time); // Date - - if (m_cols == 16U && m_rows == 2U) { - ::lcdPosition(m_fd, m_cols - 10, 1); - ::lcdPrintf(m_fd, "%s%.*s", strlen(m_buffer1) > 8 ? "" : " ", 10, m_buffer1); - } else { - ::lcdPosition(m_fd, (m_cols - (strlen(m_buffer1) == 8 ? 8 : 10)) / 2, m_rows == 2 ? 1 : 2); - ::lcdPrintf(m_fd, "%.*s", strlen(m_buffer1) == 8 ? 8 : 10, m_buffer1); - ::lcdPosition(m_fd, (m_cols - strlen(m_buffer2)) / 2, m_rows == 2 ? 0 : 1); - ::lcdPrintf(m_fd, "%s", m_buffer2); - } - - m_clockDisplayTimer.start(); - } -} - -void CHD44780::close() -{ -} diff --git a/HD44780.h b/HD44780.h deleted file mode 100644 index 7eef5be..0000000 --- a/HD44780.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2016,2017,2018,2020,2021,2025 by Jonathan Naylor G4KLX & Tony Corbett G0WFV - * - * 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(HD44780_H) -#define HD44780_H - -#include "Display.h" -#include "Timer.h" - -#include -#include - -#include -#include - -enum ADAFRUIT_COLOUR { - AC_OFF, - AC_WHITE, - AC_RED, - AC_GREEN, - AC_BLUE, - AC_PURPLE, - AC_YELLOW, - AC_ICE -}; - -// Defines for the Adafruit Pi LCD interface board -#ifdef ADAFRUIT_DISPLAY -#define AF_BASE 100 - -/* Not yet used defines (for possible future use) - * - * #define AF_SELECT (AF_BASE + 0) - * #define AF_RIGHT (AF_BASE + 1) - * #define AF_DOWN (AF_BASE + 2) - * #define AF_UP (AF_BASE + 3) - * #define AF_LEFT (AF_BASE + 4) - */ - -#define AF_RED (AF_BASE + 6) -#define AF_GREEN (AF_BASE + 7) -#define AF_BLUE (AF_BASE + 8) - -#define AF_D3 (AF_BASE + 9) -#define AF_D2 (AF_BASE + 10) -#define AF_D1 (AF_BASE + 11) -#define AF_D0 (AF_BASE + 12) -#define AF_E (AF_BASE + 13) -#define AF_RW (AF_BASE + 14) -#define AF_RS (AF_BASE + 15) - -#define AF_ON LOW -#define AF_OFF HIGH - -// #define MCP23017 0x20 -#endif - -// Define for HD44780 connected via a PCF8574 GPIO extender -#ifdef PCF8574_DISPLAY -#define AF_BASE 100 - -#define AF_RS (AF_BASE + 0) -#define AF_RW (AF_BASE + 1) -#define AF_E (AF_BASE + 2) -#define AF_BL (AF_BASE + 3) -#define AF_D0 (AF_BASE + 4) -#define AF_D1 (AF_BASE + 5) -#define AF_D2 (AF_BASE + 6) -#define AF_D3 (AF_BASE + 7) - -// #define PCF8574 0x27 -#endif - -class CHD44780 : public CDisplay -{ -public: - CHD44780(unsigned int rows, unsigned int cols, const std::string& callsign, unsigned int dmrid, const std::vector& pins, unsigned int i2cAddress, bool pwm, unsigned int pwmPin, unsigned int pwmBright, unsigned int pwmDim, bool displayClock, bool utc, bool duplex); - virtual ~CHD44780(); - - virtual bool open(); - - virtual void close(); - -protected: - virtual void setIdleInt(); - virtual void setErrorInt(const char* text); - virtual void setLockoutInt(); - virtual void setQuitInt(); - virtual void setFMInt(); - - virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); - virtual void writeDStarRSSIInt(unsigned char rssi); - virtual void clearDStarInt(); - - virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); - virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi); - virtual void clearDMRInt(unsigned int slotNo); - - virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin); - virtual void writeFusionRSSIInt(unsigned char rssi); - virtual void clearFusionInt(); - - virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type); - virtual void writeP25RSSIInt(unsigned char rssi); - virtual void clearP25Int(); - - virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); - 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(); - - virtual void clockInt(unsigned int ms); - -private: - unsigned int m_rows; - unsigned int m_cols; - std::string m_callsign; - unsigned int m_dmrid; - unsigned int m_rb; - unsigned int m_strb; - unsigned int m_d0; - unsigned int m_d1; - unsigned int m_d2; - unsigned int m_d3; - unsigned int m_i2cAddress; - bool m_pwm; - unsigned int m_pwmPin; - unsigned int m_pwmBright; - unsigned int m_pwmDim; - bool m_displayClock; - bool m_utc; - bool m_duplex; - int m_fd; - bool m_dmr; - CTimer m_clockDisplayTimer; - unsigned int m_rssiCount1; - unsigned int m_rssiCount2; -/* - CTimer m_dmrScrollTimer1; - CTimer m_dmrScrollTimer2; - CTimer m_dstarScrollTimer; -*/ - -#ifdef ADAFRUIT_DISPLAY - void adafruitLCDSetup(); - void adafruitLCDColour(ADAFRUIT_COLOUR colour); -#endif - -#ifdef PCF8574_DISPLAY - void pcf8574LCDSetup(); -#endif -}; - -#endif diff --git a/HD44780.layouts b/HD44780.layouts deleted file mode 100644 index 08b6224..0000000 --- a/HD44780.layouts +++ /dev/null @@ -1,107 +0,0 @@ -IDLE SCREEN LAYOUTS -------------------- - -16 x 2 ------- - - With clock Without clock - ---------- ------------- - - 0 1 0 1 - 0123456789012345 0123456789012345 - +----------------+ +----------------+ -0|AAAAAA NNNNNNN| 0|AAAAAA NNNNNNN| -1|MMDVM HH:MM:SS| 1|MMDVM Idle| - +----------------+ +----------------+ - -40 x 2 ------- - - With clock Without clock - ---------- ------------- - - 0 1 2 3 0 1 2 3 - 0123456789012345678901234567890123456789 0123456789012345678901234567890123456789 - +----------------------------------------+ +----------------------------------------+ -0|AAAAAA DD/MM/YY NNNNNNN| 0|AAAAAA NNNNNNN| -1|MMDVM HH:MM:SS Idle| 1|MMDVM Idle| - +----------------------------------------+ +----------------------------------------+ - -16 x 4 ------- - - With clock Without clock - ---------- ------------- - - 0 1 0 1 - 0123456789012345 0123456789012345 - +----------------+ +----------------+ -0|AAAAAA NNNNNNN| 0|AAAAAA NNNNNNN| -1| DD/MM/YY | 1| | -2| HH:MM:SS | 2| | -3|MMDVM Idle| 3|MMDVM Idle| - +----------------+ +----------------+ - -20 x 4 ------- - - With clock Without clock - ---------- ------------- - - 0 1 0 1 - 01234567890123456479 01234567890123456789 - +--------------------+ +--------------------+ -0|AAAAAA NNNNNNN| 0|AAAAAA NNNNNNN| -1| DD/MM/YY | 1| | -2| HH:MM:SS | 2| | -3|MMDVM Idle| 3|MMDVM Idle| - +--------------------+ +--------------------+ - -D-STAR LAYOUTS -------------- - -16 x 2 ------- - - 0 1 - 0123456789012345 - +----------------+ -0|F AAAAAAAA/AAAAX| -1|T AAAAAAAA | - +----------------+ - -40 x 2 ------- - - 0 1 2 3 - 0123456789012345678901234567890123456789 - +----------------------------------------+ -0|F AAAAAAAA/AAAA X| -1|T AAAAAAAA via AAAAAAAA | - +----------------------------------------+ - -16 x 4 ------- - - - 0 1 - 0123456789012345 - +----------------+ -0|D-Star | -1|F AAAAAAAA/AAAAX| -2|T AAAAAAAA | -3|via AAAAAAAA | - +----------------+ - - -20 x 4 ------- - - 0 1 - 01234567890123456479 - +--------------------+ -0|D-Star | -1|F AAAAAAAA/AAAA X| -2|T AAAAAAAA | -3|via AAAAAAAA | - +--------------------+ diff --git a/IIRDirectForm1Filter.cpp b/IIRDirectForm1Filter.cpp index 946acdd..871b6ce 100644 --- a/IIRDirectForm1Filter.cpp +++ b/IIRDirectForm1Filter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2020,2023 by Jonathan Naylor G4KLX * Copyright (C) 2020 by Geoffrey Merck - F4FXL KC3FRA * * This program is free software; you can redistribute it and/or modify @@ -18,7 +18,9 @@ */ #include "IIRDirectForm1Filter.h" -#include "math.h" +#include + +#if defined(USE_FM) CIIRDirectForm1Filter::CIIRDirectForm1Filter(float b0, float b1, float b2, float , float a1, float a2, float addtionalGaindB) : m_x2(0.0F), @@ -58,3 +60,6 @@ void CIIRDirectForm1Filter::reset() m_y1 = 0.0f; m_y2 = 0.0f; } + +#endif + diff --git a/IIRDirectForm1Filter.h b/IIRDirectForm1Filter.h index f575f7f..79ec76a 100644 --- a/IIRDirectForm1Filter.h +++ b/IIRDirectForm1Filter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2020,2023 by Jonathan Naylor G4KLX * Copyright (C) 2020 by Geoffrey Merck - F4FXL KC3FRA * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,10 @@ #if !defined(IIRDIRECTFORM1FILTER_H) #define IIRDIRECTFORM1FILTER_H +#include "Defines.h" + +#if defined(USE_FM) + class CIIRDirectForm1Filter { public: @@ -46,5 +50,7 @@ private: float m_additionalGainLin; }; +#endif + +#endif -#endif \ No newline at end of file diff --git a/Images/DMR.bmp b/Images/DMR.bmp deleted file mode 100644 index e537438..0000000 Binary files a/Images/DMR.bmp and /dev/null differ diff --git a/Images/DStar.bmp b/Images/DStar.bmp deleted file mode 100644 index abb23f6..0000000 Binary files a/Images/DStar.bmp and /dev/null differ diff --git a/Images/MMDVM.bmp b/Images/MMDVM.bmp deleted file mode 100644 index 9d25d9f..0000000 Binary files a/Images/MMDVM.bmp and /dev/null differ diff --git a/Images/NXDN.bmp b/Images/NXDN.bmp deleted file mode 100644 index ec022b0..0000000 Binary files a/Images/NXDN.bmp and /dev/null differ diff --git a/Images/P25.bmp b/Images/P25.bmp deleted file mode 100644 index 8e87af3..0000000 Binary files a/Images/P25.bmp and /dev/null differ diff --git a/Images/POCSAG.bmp b/Images/POCSAG.bmp deleted file mode 100644 index bd80cdd..0000000 Binary files a/Images/POCSAG.bmp and /dev/null differ diff --git a/Images/YSF.bmp b/Images/YSF.bmp deleted file mode 100644 index d1a8292..0000000 Binary files a/Images/YSF.bmp and /dev/null differ diff --git a/LCDproc.cpp b/LCDproc.cpp deleted file mode 100644 index 33d368e..0000000 --- a/LCDproc.cpp +++ /dev/null @@ -1,870 +0,0 @@ -/* - * Copyright (C) 2016,2017,2018 by Tony Corbett G0WFV - * Copyright (C) 2018,2020,2024,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* -* Some LCD displays include additional LEDs for status. -* If they exist, the LDCproc server will use the output command. -* If the LEDs do not exist, the command is ignored. -* to control these LEDs Below are the values for the Crystalfontz CFA-635. -* N4IRS - -* LED 1 (DMR) -* Green 1 0000 0001 -* Red 16 0001 0000 -* Yellow 17 0001 0001 - -* LED 2 (P25) -* Green 2 0000 0010 -* Red 32 0010 0000 -* Yellow 34 0010 0010 - -* LED 3 (Fusion) -* Green 4 0000 0100 -* Red 64 0100 0000 -* Yellow 68 1000 0100 - -* LED 4 (D-Star) -* Green 8 0000 1000 -* Red 128 1000 0000 -* Yellow 136 1000 1000 - -* LED 5 (NXDN) -* Green 16 0001 0000 -* Red 255 1111 1111 -* Yellow 255 1111 1111 - -*/ - -#include "LCDproc.h" -#include "Log.h" - -#include -#include -#include -#include -#include -#include - -#if !defined(_WIN32) && !defined(_WIN64) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#else -#include -#include -#endif - -#define BUFFER_MAX_LEN 128 - -#if defined(_WIN32) || defined(_WIN64) -SOCKET m_socketfd; -#else -int m_socketfd; -#endif -char m_buffer[BUFFER_MAX_LEN]; -fd_set m_readfds, m_writefds; -struct timeval m_timeout; -int m_recvsize; -unsigned int m_rows(0); -unsigned int m_cols(0); -bool m_screensDefined(false); -bool m_connected(false); - -char m_displayBuffer1[BUFFER_MAX_LEN]; -char m_displayBuffer2[BUFFER_MAX_LEN]; - -const unsigned int DSTAR_RSSI_COUNT = 3U; // 3 * 420ms = 1260ms -const unsigned int DMR_RSSI_COUNT = 4U; // 4 * 360ms = 1440ms -const unsigned int YSF_RSSI_COUNT = 13U; // 13 * 100ms = 1300ms -const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms -const unsigned int NXDN_RSSI_COUNT = 28U; // 28 * 40ms = 1120ms - -CLCDproc::CLCDproc(std::string address, unsigned int port, unsigned short localPort, const std::string& callsign, unsigned int dmrid, bool displayClock, bool utc, bool duplex, bool dimOnIdle) : -CDisplay(), -m_address(address), -m_port(port), -m_localPort(localPort), -m_callsign(callsign), -m_dmrid(dmrid), -m_displayClock(displayClock), -m_utc(utc), -m_duplex(duplex), -//m_duplex(true), // uncomment to force duplex display for testing! -m_dimOnIdle(dimOnIdle), -m_dmr(false), -m_clockDisplayTimer(1000U, 0U, 250U), // Update the clock display every 250ms -m_rssiCount1(0U), -m_rssiCount2(0U) -{ -} - -CLCDproc::~CLCDproc() -{ -} - -bool CLCDproc::open() -{ - int err; - unsigned int addrlen; - std::string port, localPort; - struct sockaddr_storage serverAddress, clientAddress; - struct addrinfo hints, *res; - - port = std::to_string(m_port); - localPort = std::to_string(m_localPort); - memset(&hints, 0, sizeof(hints)); - - /* Lookup the hostname address */ - hints.ai_flags = AI_NUMERICSERV; - hints.ai_socktype = SOCK_STREAM; - err = getaddrinfo(m_address.c_str(), port.c_str(), &hints, &res); - if (err) { - LogError("LCDproc, cannot lookup server"); - return false; - } - memcpy(&serverAddress, res->ai_addr, addrlen = (unsigned int)res->ai_addrlen); - freeaddrinfo(res); - - /* Lookup the client address (random port - need to specify manual port from ini file) */ - hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE; - hints.ai_family = serverAddress.ss_family; - err = getaddrinfo(nullptr, localPort.c_str(), &hints, &res); - if (err) { - LogError("LCDproc, cannot lookup client"); - return false; - } - memcpy(&clientAddress, res->ai_addr, res->ai_addrlen); - freeaddrinfo(res); - - /* Create TCP socket */ - m_socketfd = socket(clientAddress.ss_family, SOCK_STREAM, 0); -#if defined(_WIN32) || defined(_WIN64) - if (m_socketfd == INVALID_SOCKET) { -#else - if (m_socketfd == -1) { -#endif - LogError("LCDproc, failed to create socket"); - return false; - } - - /* Bind the address to the socket */ - if (bind(m_socketfd, (struct sockaddr *)&clientAddress, addrlen) == -1) { - LogError("LCDproc, error whilst binding address"); - return false; - } - - /* Connect to server */ - if (connect(m_socketfd, (struct sockaddr *)&serverAddress, addrlen) == -1) { - LogError("LCDproc, cannot connect to server"); - return false; - } - - socketPrintf(m_socketfd, "hello"); // Login to the LCD server - socketPrintf(m_socketfd, "output 0"); // Clear all LEDs - - return true; -} - -void CLCDproc::setIdleInt() -{ - m_clockDisplayTimer.start(); // Start the clock display in IDLE only - - 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 Idle", m_cols - 3, m_rows); - socketPrintf(m_socketfd, "output 0"); // Clear all LEDs - } - - m_dmr = false; -} - -void CLCDproc::setErrorInt(const char* text) -{ - assert(text != nullptr); - - 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 Error", m_cols - 4, m_rows); - socketPrintf(m_socketfd, "output 0"); // Clear all LEDs - } - - m_dmr = false; -} - -void CLCDproc::setLockoutInt() -{ - 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 Lockout", m_cols - 6, m_rows); - socketPrintf(m_socketfd, "output 0"); // Clear all LEDs - } - - m_dmr = false; -} - -// 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::setFMInt() -{ - 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 FM", 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 != nullptr); - assert(my2 != nullptr); - assert(your != nullptr); - assert(type != nullptr); - assert(reflector != nullptr); - - m_clockDisplayTimer.stop(); // Stop the clock display - - socketPrintf(m_socketfd, "screen_set DStar -priority foreground"); - socketPrintf(m_socketfd, "widget_set DStar Mode 1 1 \"D-Star\""); - - ::sprintf(m_displayBuffer1, "%.8s", your); - - char *p = m_displayBuffer1; - for (; *p; ++p) { - if (*p == ' ') - *p = '_'; - } - - if (strcmp(reflector, " ") != 0) - sprintf(m_displayBuffer2, " via %.8s", reflector); - else - memset(m_displayBuffer2, 0, BUFFER_MAX_LEN); - - if (m_rows == 2U) { - socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 %u 2 h 3 \"%.8s/%.4s to %s%s\"", m_cols - 1, my1, my2, m_displayBuffer1, m_displayBuffer2); - } else { - socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 %u 2 h 3 \"%.8s/%.4s\"", m_cols - 1, my1, my2); - socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 %u 3 h 3 \"%s%s\"", m_cols - 1, m_displayBuffer1, m_displayBuffer2); - socketPrintf(m_socketfd, "output 128"); // Set LED4 color red - } - - m_dmr = false; - m_rssiCount1 = 0U; -} - -void CLCDproc::writeDStarRSSIInt(unsigned char rssi) -{ - if (m_rssiCount1 == 0U) { - socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi); - } - - m_rssiCount1++; - if (m_rssiCount1 >= DSTAR_RSSI_COUNT) - m_rssiCount1 = 0U; -} - -void CLCDproc::clearDStarInt() -{ - m_clockDisplayTimer.stop(); // Stop the clock display - - socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 15 2 h 3 \"Listening\""); - socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 15 3 h 3 \"\""); - socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 15 4 h 3 \"\""); - socketPrintf(m_socketfd, "output 8"); // Set LED4 color green -} - -// LED 1 Green 1 Red 16 Yellow 17 - -void CLCDproc::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) -{ - assert(type != nullptr); - - if (!m_dmr) { - m_clockDisplayTimer.stop(); // Stop the clock display - - socketPrintf(m_socketfd, "screen_set DMR -priority foreground"); - - if (m_duplex) { - if (m_rows > 2U) - socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR"); - if (slotNo == 1U) - socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"Listening\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1); - else - socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2); - } else { - socketPrintf(m_socketfd, "widget_set DMR Slot1_ 1 %u \"\"", m_rows / 2); - socketPrintf(m_socketfd, "widget_set DMR Slot2_ 1 %u \"\"", m_rows / 2 + 1); - - socketPrintf(m_socketfd, "widget_set DMR Slot1 1 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2); - socketPrintf(m_socketfd, "widget_set DMR Slot2 1 %u %u %u h 3 \"\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1); - } - } - - if (m_duplex) { - if (m_rows > 2U) - socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR"); - - if (slotNo == 1U) - socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"%s > %s%s\"", m_rows / 2, m_cols - 1, m_rows / 2, src.c_str(), group ? "TG" : "", dst.c_str()); - else - socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"%s > %s%s\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1, src.c_str(), group ? "TG" : "", dst.c_str()); - } else { - socketPrintf(m_socketfd, "widget_set DMR Mode 1 1 DMR"); - - if (m_rows == 2U) { - socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 %u 2 h 3 \"%s > %s%s\"", m_cols - 1, src.c_str(), group ? "TG" : "", dst.c_str()); - } else { - socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 %u 2 h 3 \"%s >\"", m_cols - 1, src.c_str()); - socketPrintf(m_socketfd, "widget_set DMR Slot2 1 3 %u 3 h 3 \"%s%s\"", m_cols - 1, group ? "TG" : "", dst.c_str()); - } - } - socketPrintf(m_socketfd, "output 16"); // Set LED1 color red - m_dmr = true; - m_rssiCount1 = 0U; - m_rssiCount2 = 0U; -} - -void CLCDproc::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi) -{ - if (m_rows > 2) { - if (slotNo == 1U) { - if (m_rssiCount1 == 0U) - socketPrintf(m_socketfd, "widget_set DMR Slot1RSSI %u %u -%3udBm", 1, 4, rssi); - - m_rssiCount1++; - - if (m_rssiCount1 >= DMR_RSSI_COUNT) - m_rssiCount1 = 0U; - } else { - if (m_rssiCount2 == 0U) - socketPrintf(m_socketfd, "widget_set DMR Slot2RSSI %u %u -%3udBm", (m_cols / 2) + 1, 4, rssi); - - m_rssiCount2++; - - if (m_rssiCount2 >= DMR_RSSI_COUNT) - m_rssiCount2 = 0U; - } - } -} - -void CLCDproc::clearDMRInt(unsigned int slotNo) -{ - m_clockDisplayTimer.stop(); // Stop the clock display - - if (m_duplex) { - if (slotNo == 1U) { - socketPrintf(m_socketfd, "widget_set DMR Slot1 3 %u %u %u h 3 \"Listening\"", m_rows / 2, m_cols - 1, m_rows / 2); - socketPrintf(m_socketfd, "widget_set DMR Slot1RSSI %u %u %*.s", 1, 4, m_cols / 2, " "); - } else { - socketPrintf(m_socketfd, "widget_set DMR Slot2 3 %u %u %u h 3 \"Listening\"", m_rows / 2 + 1, m_cols - 1, m_rows / 2 + 1); - socketPrintf(m_socketfd, "widget_set DMR Slot2RSSI %u %u %*.s", (m_cols / 2) + 1, 4, m_cols / 2, " "); - } - } else { - socketPrintf(m_socketfd, "widget_set DMR Slot1 1 2 15 2 h 3 \"Listening\""); - socketPrintf(m_socketfd, "widget_set DMR Slot2 1 3 15 3 h 3 \"\""); - socketPrintf(m_socketfd, "widget_set DMR Slot2RSSI %u %u %*.s", (m_cols / 2) + 1, 4, m_cols / 2, " "); - } - socketPrintf(m_socketfd, "output 1"); // Set LED1 color green -} - -// LED 3 Green 4 Red 64 Yellow 68 - -void CLCDproc::writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) -{ - assert(source != nullptr); - assert(dest != nullptr); - assert(type != nullptr); - assert(origin != nullptr); - - m_clockDisplayTimer.stop(); // Stop the clock display - - socketPrintf(m_socketfd, "screen_set YSF -priority foreground"); - socketPrintf(m_socketfd, "widget_set YSF Mode 1 1 \"System Fusion\""); - - if (m_rows == 2U) { - socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 \"%.10s > DG-ID %u\"", source, dgid); - } else { - socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 \"%.10s >\"", source); - socketPrintf(m_socketfd, "widget_set YSF Line3 1 3 15 3 h 3 \"DG-ID %u\"", dgid); - socketPrintf(m_socketfd, "output 64"); // Set LED3 color red - } - - m_dmr = false; - m_rssiCount1 = 0U; -} - -void CLCDproc::writeFusionRSSIInt(unsigned char rssi) -{ - if (m_rssiCount1 == 0U) - socketPrintf(m_socketfd, "widget_set YSF Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi); - - m_rssiCount1++; - if (m_rssiCount1 >= YSF_RSSI_COUNT) - m_rssiCount1 = 0U; -} - -void CLCDproc::clearFusionInt() -{ - m_clockDisplayTimer.stop(); // Stop the clock display - - socketPrintf(m_socketfd, "widget_set YSF Line2 1 2 15 2 h 3 \"Listening\""); - socketPrintf(m_socketfd, "widget_set YSF Line3 1 3 15 3 h 3 \"\""); - socketPrintf(m_socketfd, "widget_set YSF Line4 1 4 15 4 h 3 \"\""); - socketPrintf(m_socketfd, "output 4"); // Set LED3 color green -} - -// LED 2 Green 2 Red 32 Yellow 34 - -void CLCDproc::writeP25Int(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(source != nullptr); - assert(type != nullptr); - - m_clockDisplayTimer.stop(); // Stop the clock display - - socketPrintf(m_socketfd, "screen_set P25 -priority foreground"); - socketPrintf(m_socketfd, "widget_set P25 Mode 1 1 P25"); - - if (m_rows == 2U) { - socketPrintf(m_socketfd, "widget_set P25 Line2 1 2 15 2 h 3 \"%.10s > %s%u\"", source, group ? "TG" : "", dest); - } else { - socketPrintf(m_socketfd, "widget_set P25 Line2 1 2 15 2 h 3 \"%.10s >\"", source); - socketPrintf(m_socketfd, "widget_set P25 Line3 1 3 15 3 h 3 \"%s%u\"", group ? "TG" : "", dest); - socketPrintf(m_socketfd, "output 32"); // Set LED2 color red - } - - m_dmr = false; - m_rssiCount1 = 0U; -} - -void CLCDproc::writeP25RSSIInt(unsigned char rssi) -{ - if (m_rssiCount1 == 0U) { - socketPrintf(m_socketfd, "widget_set P25 Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi); - } - - m_rssiCount1++; - if (m_rssiCount1 >= P25_RSSI_COUNT) - m_rssiCount1 = 0U; -} - -void CLCDproc::clearP25Int() -{ - m_clockDisplayTimer.stop(); // Stop the clock display - - socketPrintf(m_socketfd, "widget_set P25 Line2 1 2 15 2 h 3 \"Listening\""); - socketPrintf(m_socketfd, "widget_set P25 Line3 1 3 15 3 h 3 \"\""); - socketPrintf(m_socketfd, "widget_set P25 Line4 1 4 15 4 h 3 \"\""); - socketPrintf(m_socketfd, "output 2"); // Set LED2 color green -} - -// LED 5 Green 16 Red 255 Yellow 255 - -void CLCDproc::writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(source != nullptr); - assert(type != nullptr); - - m_clockDisplayTimer.stop(); // Stop the clock display - - socketPrintf(m_socketfd, "screen_set NXDN -priority foreground"); - socketPrintf(m_socketfd, "widget_set NXDN Mode 1 1 NXDN"); - - if (m_rows == 2U) { - socketPrintf(m_socketfd, "widget_set NXDN Line2 1 2 15 2 h 3 \"%.10s > %s%u\"", source, group ? "TG" : "", dest); - } else { - socketPrintf(m_socketfd, "widget_set NXDN Line2 1 2 15 2 h 3 \"%.10s >\"", source); - socketPrintf(m_socketfd, "widget_set NXDN Line3 1 3 15 3 h 3 \"%s%u\"", group ? "TG" : "", dest); - socketPrintf(m_socketfd, "output 255"); // Set LED5 color red - } - - m_dmr = false; - m_rssiCount1 = 0U; -} - -void CLCDproc::writeNXDNRSSIInt(unsigned char rssi) -{ - if (m_rssiCount1 == 0U) { - socketPrintf(m_socketfd, "widget_set NXDN Line4 1 4 %u 4 h 3 \"-%3udBm\"", m_cols - 1, rssi); - } - - m_rssiCount1++; - if (m_rssiCount1 >= NXDN_RSSI_COUNT) - m_rssiCount1 = 0U; -} - -void CLCDproc::clearNXDNInt() -{ - m_clockDisplayTimer.stop(); // Stop the clock display - - socketPrintf(m_socketfd, "widget_set NXDN Line2 1 2 15 2 h 3 \"Listening\""); - socketPrintf(m_socketfd, "widget_set NXDN Line3 1 3 15 3 h 3 \"\""); - socketPrintf(m_socketfd, "widget_set NXDN Line4 1 4 15 4 h 3 \"\""); - socketPrintf(m_socketfd, "output 16"); // Set LED5 color green -} - -void CLCDproc::writePOCSAGInt(uint32_t ric, const std::string& message) -{ -} - -void CLCDproc::clearPOCSAGInt() -{ -} - -void CLCDproc::writeCWInt() -{ -} - -void CLCDproc::clearCWInt() -{ -} - -void CLCDproc::clockInt(unsigned int ms) -{ - m_clockDisplayTimer.clock(ms); - - // Idle clock display - if (m_displayClock && m_clockDisplayTimer.isRunning() && m_clockDisplayTimer.hasExpired()) { - time_t currentTime; - struct tm *Time; - time(¤tTime); - - if (m_utc) - Time = gmtime(¤tTime); - else - Time = localtime(¤tTime); - - setlocale(LC_TIME, ""); - strftime(m_displayBuffer1, 128, "%X", Time); // Time - strftime(m_displayBuffer2, 128, "%x", Time); // Date - - if (m_cols < 26U && m_rows == 2U) { - socketPrintf(m_socketfd, "widget_set Status Time %u 2 \"%s%s\"", m_cols - 9, strlen(m_displayBuffer1) > 8 ? "" : " ", m_displayBuffer1); - } else { - socketPrintf(m_socketfd, "widget_set Status Time %u %u \"%s\"", (m_cols - (strlen(m_displayBuffer1) == 8 ? 6 : 8)) / 2, m_rows / 2, m_displayBuffer1); - socketPrintf(m_socketfd, "widget_set Status Date %u %u \"%s\"", (m_cols - (strlen(m_displayBuffer1) == 8 ? 6 : 8)) / 2, m_rows / 2 + 1, m_displayBuffer2); - } - - m_clockDisplayTimer.start(); - } - - // We must set all this information on each select we do - FD_ZERO(&m_readfds); // empty readfds - - // Then we put all the descriptors we want to wait for in a mask = m_readfds - FD_SET(m_socketfd, &m_readfds); - - // Timeout, we will stop waiting for information - m_timeout.tv_sec = 0; - m_timeout.tv_usec = 0; - - /* The first parameter is the biggest descriptor + 1. The first one was 0, so - * every other descriptor will be bigger - * - * readfds = &m_readfds - * writefds = we are not waiting for writefds - * exceptfds = we are not waiting for exception fds - */ - - if (select(int(m_socketfd) + 1, &m_readfds, nullptr, nullptr, &m_timeout) == -1) { - LogError("LCDproc, error on select"); - return; - } - - // If something was received from the server... - if (FD_ISSET(m_socketfd, &m_readfds)) { - m_recvsize = recv(m_socketfd, m_buffer, BUFFER_MAX_LEN, 0); - - if (m_recvsize == -1) { - LogError("LCDproc, cannot receive information"); - return; - } - - m_buffer[m_recvsize] = '\0'; - - char *argv[256]; - size_t len = strlen(m_buffer); - - // Now split the string into tokens... - int argc = 0; - int newtoken = 1; - - for (size_t i = 0U; i < len; i++) { - switch (m_buffer[i]) { - case ' ': - newtoken = 1; - m_buffer[i] = 0; - break; - default: /* regular chars, keep tokenizing */ - if (newtoken) - argv[argc++] = m_buffer + i; - newtoken = 0; - break; - case '\0': - case '\n': - m_buffer[i] = 0; - if (argc > 0) { - if (0 == strcmp(argv[0], "listen")) { - LogDebug("LCDproc, the %s screen is displayed", argv[1]); - } else if (0 == strcmp(argv[0], "ignore")) { - LogDebug("LCDproc, the %s screen is hidden", argv[1]); - } else if (0 == strcmp(argv[0], "key")) { - LogDebug("LCDproc, Key %s", argv[1]); - } else if (0 == strcmp(argv[0], "menu")) { - } else if (0 == strcmp(argv[0], "connect")) { - // connect LCDproc 0.5.7 protocol 0.3 lcd wid 16 hgt 2 cellwid 5 cellhgt 8 - int a; - - for (a = 1; a < argc; a++) { - if (0 == strcmp(argv[a], "wid")) - m_cols = atoi(argv[++a]); - else if (0 == strcmp(argv[a], "hgt")) - m_rows = atoi(argv[++a]); - else if (0 == strcmp(argv[a], "cellwid")) { - //lcd_cellwid = atoi(argv[++a]); - } else if (0 == strcmp(argv[a], "cellhgt")) { - //lcd_cellhgt = atoi(argv[++a]); - } - } - - m_connected = true; - socketPrintf(m_socketfd, "client_set -name MMDVMHost"); - } else if (0 == strcmp(argv[0], "bye")) { - //close the socket- todo - } else if (0 == strcmp(argv[0], "success")) { - //LogDebug("LCDproc, command successful"); - } else if (0 == strcmp(argv[0], "huh?")) { - sprintf(m_displayBuffer1, "LCDproc, command failed:"); - sprintf(m_displayBuffer2, " "); - - int j; - for (j = 1; j < argc; j++) { - strcat(m_displayBuffer1, m_displayBuffer2); - strcat(m_displayBuffer1, argv[j]); - } - LogDebug("%s", m_displayBuffer1); - } - } - - /* Restart tokenizing */ - argc = 0; - newtoken = 1; - break; - } /* switch( m_buffer[i] ) */ - } - } - - if (!m_screensDefined && m_connected) - defineScreens(); -} - -void CLCDproc::close() -{ -} - -#if defined(_WIN32) || defined(_WIN64) -int CLCDproc::socketPrintf(SOCKET fd, const char* format, ...) -#else -int CLCDproc::socketPrintf(int fd, const char *format, ...) -#endif -{ - char buf[BUFFER_MAX_LEN]; - va_list ap; - - va_start(ap, format); - int size = vsnprintf(buf, BUFFER_MAX_LEN, format, ap); - va_end(ap); - - if (size < 0) { - LogError("LCDproc, socketPrintf: vsnprintf failed"); - return -1; - } - - if (size > BUFFER_MAX_LEN) - LogWarning("LCDproc, socketPrintf: vsnprintf truncated message"); - - FD_ZERO(&m_writefds); // empty writefds - FD_SET(m_socketfd, &m_writefds); - - m_timeout.tv_sec = 0; - m_timeout.tv_usec = 0; - - if (select(int(m_socketfd) + 1, nullptr, &m_writefds, nullptr, &m_timeout) == -1) - LogError("LCDproc, error on select"); - - if (FD_ISSET(m_socketfd, &m_writefds)) { - if (send(m_socketfd, buf, int(strlen(buf) + 1U), 0) == -1) { - LogError("LCDproc, cannot send data"); - return -1; - } - } - - return 0; -} - -void CLCDproc::defineScreens() -{ - // The Status Screen - - socketPrintf(m_socketfd, "screen_add Status"); - socketPrintf(m_socketfd, "screen_set Status -name Status -heartbeat on -priority info -backlight %s", m_dimOnIdle ? "off" : "on"); - - socketPrintf(m_socketfd, "widget_add Status Callsign string"); - socketPrintf(m_socketfd, "widget_add Status DMRNumber string"); - socketPrintf(m_socketfd, "widget_add Status Title string"); - socketPrintf(m_socketfd, "widget_add Status Status string"); - socketPrintf(m_socketfd, "widget_add Status Time string"); - socketPrintf(m_socketfd, "widget_add Status Date string"); - - socketPrintf(m_socketfd, "widget_set Status Callsign 1 1 %s", m_callsign.c_str()); - socketPrintf(m_socketfd, "widget_set Status DMRNumber %u 1 %u", m_cols - 7, m_dmrid); - socketPrintf(m_socketfd, "widget_set Status Title 1 %u MMDVM", m_rows); - socketPrintf(m_socketfd, "widget_set Status Status %u %u Idle", m_cols - 3, m_rows); - - // The DStar Screen - - socketPrintf(m_socketfd, "screen_add DStar"); - socketPrintf(m_socketfd, "screen_set DStar -name DStar -heartbeat on -priority hidden -backlight on"); - - socketPrintf(m_socketfd, "widget_add DStar Mode string"); - socketPrintf(m_socketfd, "widget_add DStar Line2 scroller"); - socketPrintf(m_socketfd, "widget_add DStar Line3 scroller"); - socketPrintf(m_socketfd, "widget_add DStar Line4 scroller"); - -/* Do we need to pre-populate the values?? - socketPrintf(m_socketfd, "widget_set DStar Line2 1 2 15 2 h 3 \"Listening\""); - socketPrintf(m_socketfd, "widget_set DStar Line3 1 3 15 3 h 3 \"\""); - socketPrintf(m_socketfd, "widget_set DStar Line4 1 4 15 4 h 3 \"\""); -*/ - - // The DMR Screen - - socketPrintf(m_socketfd, "screen_add DMR"); - socketPrintf(m_socketfd, "screen_set DMR -name DMR -heartbeat on -priority hidden -backlight on"); - - socketPrintf(m_socketfd, "widget_add DMR Mode string"); - socketPrintf(m_socketfd, "widget_add DMR Slot1_ string"); - socketPrintf(m_socketfd, "widget_add DMR Slot2_ string"); - socketPrintf(m_socketfd, "widget_add DMR Slot1 scroller"); - socketPrintf(m_socketfd, "widget_add DMR Slot2 scroller"); - socketPrintf(m_socketfd, "widget_add DMR Slot1RSSI string"); - socketPrintf(m_socketfd, "widget_add DMR Slot2RSSI string"); - -/* Do we need to pre-populate the values?? - socketPrintf(m_socketfd, "widget_set DMR Slot1_ 1 %u 1", m_rows / 2); - socketPrintf(m_socketfd, "widget_set DMR Slot2_ 1 %u 2", m_rows / 2 + 1); - socketPrintf(m_socketfd, "widget_set DMR Slot1 3 1 15 1 h 3 \"Listening\""); - socketPrintf(m_socketfd, "widget_set DMR Slot2 3 2 15 2 h 3 \"Listening\""); -*/ - - // The YSF Screen - - socketPrintf(m_socketfd, "screen_add YSF"); - socketPrintf(m_socketfd, "screen_set YSF -name YSF -heartbeat on -priority hidden -backlight on"); - - socketPrintf(m_socketfd, "widget_add YSF Mode string"); - socketPrintf(m_socketfd, "widget_add YSF Line2 scroller"); - socketPrintf(m_socketfd, "widget_add YSF Line3 scroller"); - socketPrintf(m_socketfd, "widget_add YSF Line4 scroller"); - -/* Do we need to pre-populate the values?? - socketPrintf(m_socketfd, "widget_set YSF Line2 2 1 15 1 h 3 \"Listening\""); - socketPrintf(m_socketfd, "widget_set YSF Line3 3 1 15 1 h 3 \" \""); - socketPrintf(m_socketfd, "widget_set YSF Line4 4 2 15 2 h 3 \" \""); -*/ - - // The P25 Screen - - socketPrintf(m_socketfd, "screen_add P25"); - socketPrintf(m_socketfd, "screen_set P25 -name P25 -heartbeat on -priority hidden -backlight on"); - - socketPrintf(m_socketfd, "widget_add P25 Mode string"); - socketPrintf(m_socketfd, "widget_add P25 Line2 scroller"); - socketPrintf(m_socketfd, "widget_add P25 Line3 scroller"); - socketPrintf(m_socketfd, "widget_add P25 Line4 scroller"); - -/* Do we need to pre-populate the values?? - socketPrintf(m_socketfd, "widget_set P25 Line3 2 1 15 1 h 3 \"Listening\""); - socketPrintf(m_socketfd, "widget_set P25 Line3 3 1 15 1 h 3 \" \""); - socketPrintf(m_socketfd, "widget_set P25 Line4 4 2 15 2 h 3 \" \""); -*/ - - // The NXDN Screen - - socketPrintf(m_socketfd, "screen_add NXDN"); - socketPrintf(m_socketfd, "screen_set NXDN -name NXDN -heartbeat on -priority hidden -backlight on"); - - socketPrintf(m_socketfd, "widget_add NXDN Mode string"); - socketPrintf(m_socketfd, "widget_add NXDN Line2 scroller"); - socketPrintf(m_socketfd, "widget_add NXDN Line3 scroller"); - socketPrintf(m_socketfd, "widget_add NXDN Line4 scroller"); - -/* Do we need to pre-populate the values?? - socketPrintf(m_socketfd, "widget_set NXDN Line3 2 1 15 1 h 3 \"Listening\""); - socketPrintf(m_socketfd, "widget_set NXDN Line3 3 1 15 1 h 3 \" \""); - socketPrintf(m_socketfd, "widget_set NXDN Line4 4 2 15 2 h 3 \" \""); -*/ - m_screensDefined = true; -} diff --git a/LCDproc.h b/LCDproc.h deleted file mode 100644 index 1aafc5d..0000000 --- a/LCDproc.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2016,2017 by Tony Corbett G0WFV - * Copyright (C) 2018,2020,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#if !defined(LCDproc_H) -#define LCDproc_H - -#include "Display.h" -#include "Timer.h" - -#include - -#if defined(_WIN32) || defined(_WIN64) -#include -#include -#endif - -class CLCDproc : public CDisplay -{ -public: - CLCDproc(std::string address, unsigned int port, unsigned short localPort, const std::string& callsign, unsigned int dmrid, bool displayClock, bool utc, bool duplex, bool dimOnIdle); - virtual ~CLCDproc(); - - virtual bool open(); - - virtual void close(); - -protected: - virtual void setIdleInt(); - virtual void setErrorInt(const char* text); - virtual void setLockoutInt(); - virtual void setQuitInt(); - virtual void setFMInt(); - - virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); - virtual void writeDStarRSSIInt(unsigned char rssi); - virtual void clearDStarInt(); - - virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); - virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi); - virtual void clearDMRInt(unsigned int slotNo); - - virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin); - virtual void writeFusionRSSIInt(unsigned char rssi); - virtual void clearFusionInt(); - - virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type); - virtual void writeP25RSSIInt(unsigned char rssi); - virtual void clearP25Int(); - - virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); - 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(); - - virtual void clockInt(unsigned int ms); - -private: - std::string m_address; - unsigned int m_port; - unsigned short m_localPort; - std::string m_callsign; - unsigned int m_dmrid; - bool m_displayClock; - bool m_utc; - bool m_duplex; - bool m_dimOnIdle; - bool m_dmr; - CTimer m_clockDisplayTimer; - unsigned int m_rssiCount1; - unsigned int m_rssiCount2; - -#if defined(_WIN32) || defined(_WIN64) - int socketPrintf(SOCKET fd, const char* format, ...); -#else - int socketPrintf(int fd, const char *format, ...); -#endif - void defineScreens(); -}; - -#endif diff --git a/Log.cpp b/Log.cpp index 9aa2f96..222d18e 100644 --- a/Log.cpp +++ b/Log.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2020,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2020,2022,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,6 +17,7 @@ */ #include "Log.h" +#include "MQTTConnection.h" #if defined(_WIN32) || defined(_WIN64) #include @@ -32,117 +33,27 @@ #include #include -static unsigned int m_fileLevel = 2U; -static std::string m_filePath; -static std::string m_fileRoot; -static bool m_fileRotate = true; +CMQTTConnection* m_mqtt = nullptr; -static FILE* m_fpLog = nullptr; -static bool m_daemon = false; +static unsigned int m_mqttLevel = 2U; static unsigned int m_displayLevel = 2U; -static struct tm m_tm; - static char LEVELS[] = " DMIWEF"; -static bool logOpenRotate() +void LogInitialise(unsigned int displayLevel, unsigned int mqttLevel) { - bool status = false; - - if (m_fileLevel == 0U) - return true; - - time_t now; - ::time(&now); - - struct tm* tm = ::gmtime(&now); - - if (tm->tm_mday == m_tm.tm_mday && tm->tm_mon == m_tm.tm_mon && tm->tm_year == m_tm.tm_year) { - if (m_fpLog != nullptr) - return true; - } else { - if (m_fpLog != nullptr) - ::fclose(m_fpLog); - } - - char filename[200U]; -#if defined(_WIN32) || defined(_WIN64) - ::sprintf(filename, "%s\\%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); -#else - ::sprintf(filename, "%s/%s-%04d-%02d-%02d.log", m_filePath.c_str(), m_fileRoot.c_str(), tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); -#endif - - if ((m_fpLog = ::fopen(filename, "a+t")) != nullptr) { - status = true; - -#if !defined(_WIN32) && !defined(_WIN64) - if (m_daemon) - dup2(fileno(m_fpLog), fileno(stderr)); -#endif - } - - m_tm = *tm; - - return status; -} - -static bool logOpenNoRotate() -{ - bool status = false; - - if (m_fileLevel == 0U) - return true; - - if (m_fpLog != nullptr) - return true; - - char filename[200U]; -#if defined(_WIN32) || defined(_WIN64) - ::sprintf(filename, "%s\\%s.log", m_filePath.c_str(), m_fileRoot.c_str()); -#else - ::sprintf(filename, "%s/%s.log", m_filePath.c_str(), m_fileRoot.c_str()); -#endif - - if ((m_fpLog = ::fopen(filename, "a+t")) != nullptr) { - status = true; - -#if !defined(_WIN32) && !defined(_WIN64) - if (m_daemon) - dup2(fileno(m_fpLog), fileno(stderr)); -#endif - } - - return status; -} - -bool LogOpen() -{ - if (m_fileRotate) - return logOpenRotate(); - else - return logOpenNoRotate(); -} - -bool LogInitialise(bool daemon, const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel, bool rotate) -{ - m_filePath = filePath; - m_fileRoot = fileRoot; - m_fileLevel = fileLevel; + m_mqttLevel = mqttLevel; m_displayLevel = displayLevel; - m_daemon = daemon; - m_fileRotate = rotate; - - if (m_daemon) - m_displayLevel = 0U; - - return ::LogOpen(); } void LogFinalise() { - if (m_fpLog != nullptr) - ::fclose(m_fpLog); + if (m_mqtt != nullptr) { + m_mqtt->close(); + delete m_mqtt; + m_mqtt = nullptr; + } } void Log(unsigned int level, const char* fmt, ...) @@ -171,22 +82,26 @@ void Log(unsigned int level, const char* fmt, ...) va_end(vl); - if (level >= m_fileLevel && m_fileLevel != 0U) { - bool ret = ::LogOpen(); - if (!ret) - return; - - ::fprintf(m_fpLog, "%s\n", buffer); - ::fflush(m_fpLog); - } + if (m_mqtt != nullptr && level >= m_mqttLevel && m_mqttLevel != 0U) + m_mqtt->publish("log", buffer); if (level >= m_displayLevel && m_displayLevel != 0U) { ::fprintf(stdout, "%s\n", buffer); ::fflush(stdout); } - if (level == 6U) { // Fatal - ::fclose(m_fpLog); + if (level == 6U) // Fatal exit(1); +} + +void WriteJSON(const std::string& topLevel, nlohmann::json& json) +{ + if (m_mqtt != nullptr) { + nlohmann::json top; + + top[topLevel] = json; + + m_mqtt->publish("json", top.dump()); } } + diff --git a/Log.h b/Log.h index ae95b60..7392164 100644 --- a/Log.h +++ b/Log.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2020 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2020,2022,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,8 @@ #include +#include + #define LogDebug(fmt, ...) Log(1U, fmt, ##__VA_ARGS__) #define LogMessage(fmt, ...) Log(2U, fmt, ##__VA_ARGS__) #define LogInfo(fmt, ...) Log(3U, fmt, ##__VA_ARGS__) @@ -30,7 +32,9 @@ extern void Log(unsigned int level, const char* fmt, ...); -extern bool LogInitialise(bool daemon, const std::string& filePath, const std::string& fileRoot, unsigned int fileLevel, unsigned int displayLevel, bool rotate); +extern void LogInitialise(unsigned int displayLevel, unsigned int mqttLevel); extern void LogFinalise(); +extern void WriteJSON(const std::string& topLevel, nlohmann::json& json); + #endif diff --git a/MMDVM.ini b/MMDVM.ini index 9b199c4..73bd730 100644 --- a/MMDVM.ini +++ b/MMDVM.ini @@ -6,7 +6,6 @@ Duplex=1 # ModeHang=10 RFModeHang=10 NetModeHang=3 -Display=None Daemon=0 [Info] @@ -23,11 +22,17 @@ URL=www.google.co.uk [Log] # Logging levels, 0=No logging +MQTTLevel=1 DisplayLevel=1 -FileLevel=1 -FilePath=. -FileRoot=MMDVM -FileRotate=1 + +[MQTT] +Host=127.0.0.1 +Port=1883 +Auth=0 +Username=mmdvm +Password=mmdvm +Keepalive=60 +Name=mmdvm [CW Id] Enable=1 @@ -44,7 +49,7 @@ Time=24 [Modem] # Valid values are "null", "uart", "udp", and (on Linux) "i2c" -Protocol=uart +Protocol=null # The port and speed used for a UART connection # UARTPort=\\.\COM4 # UARTPort=/dev/ttyACM0 @@ -197,7 +202,7 @@ ExtAudioBoost=1 # ModeHang=10 [D-Star Network] -Enable=1 +Enable=0 LocalAddress=127.0.0.1 LocalPort=20011 GatewayAddress=127.0.0.1 @@ -206,24 +211,19 @@ GatewayPort=20010 Debug=0 [DMR Network] -Enable=1 -# Type may be either 'Direct' or 'Gateway'. When Direct you must provide the Master's -# address as well as the Password, and for DMR+, Options also. -Type=Gateway +Enable=0 LocalAddress=127.0.0.1 LocalPort=62032 -RemoteAddress=127.0.0.1 -RemotePort=62031 -# Password=P@ssw0rd1234 +GatewayAddress=127.0.0.1 +GatewayPort=62031 Jitter=360 Slot1=1 Slot2=1 -# Options= # ModeHang=3 Debug=0 [System Fusion Network] -Enable=1 +Enable=0 LocalAddress=127.0.0.1 LocalPort=3200 GatewayAddress=127.0.0.1 @@ -232,7 +232,7 @@ GatewayPort=4200 Debug=0 [P25 Network] -Enable=1 +Enable=0 LocalAddress=127.0.0.1 LocalPort=32010 GatewayAddress=127.0.0.1 @@ -241,7 +241,7 @@ GatewayPort=42020 Debug=0 [NXDN Network] -Enable=1 +Enable=0 Protocol=Icom LocalAddress=127.0.0.1 LocalPort=14021 @@ -251,7 +251,7 @@ GatewayPort=14020 Debug=0 [POCSAG Network] -Enable=1 +Enable=0 LocalAddress=127.0.0.1 LocalPort=3800 GatewayAddress=127.0.0.1 @@ -261,12 +261,6 @@ Debug=0 [FM Network] Enable=1 -# Protocol may be USRP or RAW -Protocol=USRP -# SampleRate is only used in RAW mode -SampleRate=48000 -# The squelch file is optional and only used in RAW mode -SquelchFile=/tmp/sql LocalAddress=127.0.0.1 LocalPort=3810 GatewayAddress=127.0.0.1 @@ -278,67 +272,9 @@ RXAudioGain=1.0 # ModeHang=3 Debug=0 -[TFT Serial] -# Port=modem -Port=/dev/ttyAMA0 -Brightness=50 -ScreenLayout=0 - -[HD44780] -Rows=2 -Columns=16 - -# For basic HD44780 displays (4-bit connection) -# rs, strb, d0, d1, d2, d3 -Pins=11,10,0,1,2,3 - -# Device address for I2C -I2CAddress=0x20 - -# PWM backlight -PWM=0 -PWMPin=21 -PWMBright=100 -PWMDim=16 - -DisplayClock=1 -UTC=0 - -[Nextion] -# Port=modem -Port=/dev/ttyAMA0 -Brightness=50 -DisplayClock=1 -UTC=0 -#Screen Layout: 0=G4KLX 2=ON7LDS -ScreenLayout=2 -IdleBrightness=20 -# Output data from the Nextion -NextionOutput=0 -NextionPort=6759 - -[OLED] -Type=3 -Brightness=0 -Invert=0 -Scroll=1 -Rotate=0 -Cast=0 -LogoScreensaver=1 - -[LCDproc] -Address=localhost -Port=13666 -#LocalPort=13667 -DimOnIdle=0 -DisplayClock=1 -UTC=0 - [Lock File] Enable=0 File=/tmp/MMDVM_Active.lck [Remote Control] Enable=0 -Address=127.0.0.1 -Port=7642 diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index 10fb079..868118a 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -17,8 +17,6 @@ */ #include "MMDVMHost.h" -#include "DMRDirectNetwork.h" -#include "DMRGatewayNetwork.h" #include "NXDNKenwoodNetwork.h" #include "NXDNIcomNetwork.h" #include "RSSIInterpolator.h" @@ -28,11 +26,12 @@ #include "I2CController.h" #endif #include "UDPController.h" +#include "MQTTConnection.h" #include "DStarDefines.h" #include "Version.h" #include "StopWatch.h" -#include "Defines.h" #include "Thread.h" +#include "Utils.h" #include "Log.h" #include "GitVersion.h" @@ -41,6 +40,8 @@ #include +#include + #if !defined(_WIN32) && !defined(_WIN64) #include #include @@ -59,6 +60,9 @@ static bool m_killed = false; static int m_signal = 0; static bool m_reload = false; +// In Log.cpp +extern CMQTTConnection* m_mqtt; + #if !defined(_WIN32) && !defined(_WIN64) static void sigHandler(int signum) { @@ -67,6 +71,8 @@ static void sigHandler(int signum) } #endif +static CMMDVMHost* host = nullptr; + const char* HEADER1 = "This software is for use on amateur radio networks only,"; const char* HEADER2 = "it is to be used for educational purposes only. Its use on"; const char* HEADER3 = "commercial networks is strictly prohibited."; @@ -102,10 +108,11 @@ int main(int argc, char** argv) m_signal = 0; m_killed = false; - CMMDVMHost* host = new CMMDVMHost(std::string(iniFile)); + host = new CMMDVMHost(std::string(iniFile)); ret = host->run(); delete host; + host = nullptr; switch (m_signal) { case 0: @@ -134,37 +141,92 @@ int main(int argc, char** argv) CMMDVMHost::CMMDVMHost(const std::string& confFile) : m_conf(confFile), m_modem(nullptr), +#if defined(USE_DSTAR) m_dstar(nullptr), +#endif +#if defined(USE_DMR) m_dmr(nullptr), +#endif +#if defined(USE_YSF) m_ysf(nullptr), +#endif +#if defined(USE_P25) m_p25(nullptr), +#endif +#if defined(USE_NXDN) m_nxdn(nullptr), +#endif +#if defined(USE_POCSAG) m_pocsag(nullptr), +#endif +#if defined(USE_FM) m_fm(nullptr), +#endif +#if defined(USE_DSTAR) m_dstarNetwork(nullptr), +#endif +#if defined(USE_DMR) m_dmrNetwork(nullptr), +#endif +#if defined(USE_YSF) m_ysfNetwork(nullptr), +#endif +#if defined(USE_P25) m_p25Network(nullptr), +#endif +#if defined(USE_NXDN) m_nxdnNetwork(nullptr), +#endif +#if defined(USE_POCSAG) m_pocsagNetwork(nullptr), +#endif +#if defined(USE_FM) m_fmNetwork(nullptr), -m_display(nullptr), +#endif m_mode(MODE_IDLE), +#if defined(USE_DSTAR) m_dstarRFModeHang(10U), +#endif +#if defined(USE_DMR) m_dmrRFModeHang(10U), +#endif +#if defined(USE_YSF) m_ysfRFModeHang(10U), +#endif +#if defined(USE_P25) m_p25RFModeHang(10U), +#endif +#if defined(USE_NXDN) m_nxdnRFModeHang(10U), +#endif +#if defined(USE_FM) m_fmRFModeHang(10U), +#endif +#if defined(USE_DSTAR) m_dstarNetModeHang(3U), +#endif +#if defined(USE_DMR) m_dmrNetModeHang(3U), +#endif +#if defined(USE_YSF) m_ysfNetModeHang(3U), +#endif +#if defined(USE_P25) m_p25NetModeHang(3U), +#endif +#if defined(USE_NXDN) m_nxdnNetModeHang(3U), +#endif +#if defined(USE_POCSAG) m_pocsagNetModeHang(3U), +#endif +#if defined(USE_FM) m_fmNetModeHang(3U), +#endif m_modeTimer(1000U), +#if defined(USE_DMR) m_dmrTXTimer(1000U), +#endif m_cwIdTimer(1000U), m_duplex(false), m_timeout(180U), @@ -176,22 +238,34 @@ m_nxdnEnabled(false), m_pocsagEnabled(false), m_fmEnabled(false), m_cwIdTime(0U), +#if defined(USE_DMR) || defined(USE_P25) m_dmrLookup(nullptr), +#endif +#if defined(USE_NXDN) m_nxdnLookup(nullptr), +#endif m_callsign(), m_id(0U), m_cwCallsign(), m_lockFileEnabled(false), m_lockFileName(), m_remoteControl(nullptr), -m_fixedMode(false) +m_fixedMode(false), +m_serialTimer(1000U, 0U, 210U), // 252 bytes at 9600 bps +m_serialBuffer(nullptr), +m_serialStart(0U), +m_serialLength(0U) { CUDPSocket::startup(); + + m_serialBuffer = new unsigned char[5000U]; } CMMDVMHost::~CMMDVMHost() { CUDPSocket::shutdown(); + + delete[] m_serialBuffer; } int CMMDVMHost::run() @@ -261,15 +335,20 @@ int CMMDVMHost::run() } #endif #endif + ::LogInitialise(m_conf.getLogDisplayLevel(), m_conf.getLogMQTTLevel()); -#if !defined(_WIN32) && !defined(_WIN64) - ret = ::LogInitialise(m_daemon, m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel(), m_conf.getLogFileRotate()); -#else - ret = ::LogInitialise(false, m_conf.getLogFilePath(), m_conf.getLogFileRoot(), m_conf.getLogFileLevel(), m_conf.getLogDisplayLevel(), m_conf.getLogFileRotate()); -#endif + std::vector> subscriptions; + subscriptions.push_back(std::make_pair("display-in", CMMDVMHost::onDisplay)); + + if (m_conf.getRemoteControlEnabled()) + subscriptions.push_back(std::make_pair("command", CMMDVMHost::onCommand)); + + m_mqtt = new CMQTTConnection(m_conf.getMQTTHost(), m_conf.getMQTTPort(), m_conf.getMQTTName(), m_conf.getMQTTAuthEnabled(), m_conf.getMQTTUsername(), m_conf.getMQTTPassword(), subscriptions, m_conf.getMQTTKeepalive()); + ret = m_mqtt->open(); if (!ret) { - ::fprintf(stderr, "MMDVMHost: unable to open the log file\n"); - return 1; + ::fprintf(stderr, "MMDVMHost: unable to start the MQTT Publisher\n"); + delete m_mqtt; + m_mqtt = nullptr; } #if !defined(_WIN32) && !defined(_WIN64) @@ -287,92 +366,121 @@ int CMMDVMHost::run() LogInfo("MMDVMHost-%s is starting", VERSION); LogInfo("Built %s %s (GitID #%.7s)", __TIME__, __DATE__, gitversion); + writeJSONMessage("MMDVMHost is starting"); + readParams(); ret = createModem(); if (!ret) return 1; +#if defined(USE_DSTAR) if (m_dstarEnabled && !m_modem->hasDStar()) { LogWarning("D-Star enabled in the host but not in the modem firmware, disabling"); m_dstarEnabled = false; } +#endif +#if defined(USE_DMR) if (m_dmrEnabled && !m_modem->hasDMR()) { LogWarning("DMR enabled in the host but not in the modem firmware, disabling"); m_dmrEnabled = false; } +#endif +#if defined(USE_YSF) if (m_ysfEnabled && !m_modem->hasYSF()) { LogWarning("YSF enabled in the host but not in the modem firmware, disabling"); m_ysfEnabled = false; } +#endif +#if defined(USE_P25) if (m_p25Enabled && !m_modem->hasP25()) { LogWarning("P25 enabled in the host but not in the modem firmware, disabling"); m_p25Enabled = false; } +#endif +#if defined(USE_NXDN) if (m_nxdnEnabled && !m_modem->hasNXDN()) { LogWarning("NXDN enabled in the host but not in the modem firmware, disabling"); m_nxdnEnabled = false; } +#endif +#if defined(USE_FM) if (m_fmEnabled && !m_modem->hasFM()) { LogWarning("FM enabled in the host but not in the modem firmware, disabling"); m_fmEnabled = false; } +#endif +#if defined(USE_POCSAG) if (m_pocsagEnabled && !m_modem->hasPOCSAG()) { LogWarning("POCSAG enabled in the host but not in the modem firmware, disabling"); m_pocsagEnabled = false; } - - m_display = CDisplay::createDisplay(m_conf, m_modem); +#endif LogInfo("Opening network connections"); + writeJSONMessage("Opening network connections"); +#if defined(USE_DSTAR) if (m_dstarEnabled && m_conf.getDStarNetworkEnabled()) { ret = createDStarNetwork(); if (!ret) return 1; } +#endif +#if defined(USE_DMR) if (m_dmrEnabled && m_conf.getDMRNetworkEnabled()) { ret = createDMRNetwork(); if (!ret) return 1; } +#endif +#if defined(USE_YSF) if (m_ysfEnabled && m_conf.getFusionNetworkEnabled()) { ret = createYSFNetwork(); if (!ret) return 1; } +#endif +#if defined(USE_P25) if (m_p25Enabled && m_conf.getP25NetworkEnabled()) { ret = createP25Network(); if (!ret) return 1; } +#endif +#if defined(USE_NXDN) if (m_nxdnEnabled && m_conf.getNXDNNetworkEnabled()) { ret = createNXDNNetwork(); if (!ret) return 1; } +#endif +#if defined(USE_POCSAG) if (m_pocsagEnabled && m_conf.getPOCSAGNetworkEnabled()) { ret = createPOCSAGNetwork(); if (!ret) return 1; } +#endif +#if defined(USE_FM) if (m_fmEnabled && m_conf.getFMNetworkEnabled()) { ret = createFMNetwork(); if (!ret) return 1; } +#endif sockaddr_storage transparentAddress; unsigned int transparentAddrLen; @@ -441,6 +549,7 @@ int CMMDVMHost::run() rssi->load(rssiMappingFile); } +#if defined(USE_DMR) || defined(USE_P25) // For DMR and P25 we try to map IDs to callsigns if (m_dmrEnabled || m_p25Enabled) { std::string lookupFile = m_conf.getDMRIdLookupFile(); @@ -454,12 +563,15 @@ int CMMDVMHost::run() m_dmrLookup = new CDMRLookup(lookupFile, reloadTime); m_dmrLookup->read(); } +#endif LogInfo("Starting protocol handlers"); + writeJSONMessage("Starting protocol handlers"); CStopWatch stopWatch; stopWatch.start(); +#if defined(USE_DSTAR) if (m_dstarEnabled) { std::string module = m_conf.getDStarModule(); bool selfOnly = m_conf.getDStarSelfOnly(); @@ -487,9 +599,11 @@ int CMMDVMHost::run() if (whiteList.size() > 0U) LogInfo(" White List: %u", whiteList.size()); - m_dstar = new CDStarControl(m_callsign, module, selfOnly, ackReply, ackTime, ackMessage, errorReply, blackList, whiteList, m_dstarNetwork, m_display, m_timeout, m_duplex, remoteGateway, rssi); + m_dstar = new CDStarControl(m_callsign, module, selfOnly, ackReply, ackTime, ackMessage, errorReply, blackList, whiteList, m_dstarNetwork, m_timeout, m_duplex, remoteGateway, rssi); } +#endif +#if defined(USE_DMR) DMR_BEACONS dmrBeacons = DMR_BEACONS::OFF; CTimer dmrBeaconIntervalTimer(1000U); CTimer dmrBeaconDurationTimer(1000U); @@ -587,11 +701,13 @@ int CMMDVMHost::run() break; } - m_dmr = new CDMRControl(id, colorCode, callHang, selfOnly, embeddedLCOnly, dumpTAData, prefixes, blackList, whiteList, slot1TGWhiteList, slot2TGWhiteList, m_timeout, m_modem, m_dmrNetwork, m_display, m_duplex, m_dmrLookup, rssi, jitter, ovcm, protect); + m_dmr = new CDMRControl(id, colorCode, callHang, selfOnly, embeddedLCOnly, dumpTAData, prefixes, blackList, whiteList, slot1TGWhiteList, slot2TGWhiteList, m_timeout, m_modem, m_dmrNetwork, m_duplex, m_dmrLookup, rssi, jitter, ovcm, protect); m_dmrTXTimer.setTimeout(txHang); } +#endif +#if defined(USE_YSF) if (m_ysfEnabled) { bool lowDeviation = m_conf.getFusionLowDeviation(); bool remoteGateway = m_conf.getFusionRemoteGateway(); @@ -606,9 +722,11 @@ int CMMDVMHost::run() LogInfo(" Self Only: %s", selfOnly ? "yes" : "no"); LogInfo(" Mode Hang: %us", m_ysfRFModeHang); - m_ysf = new CYSFControl(m_callsign, selfOnly, m_ysfNetwork, m_display, m_timeout, m_duplex, lowDeviation, remoteGateway, rssi); + m_ysf = new CYSFControl(m_callsign, selfOnly, m_ysfNetwork, m_timeout, m_duplex, lowDeviation, remoteGateway, rssi); } +#endif +#if defined(USE_P25) if (m_p25Enabled) { unsigned int id = m_conf.getP25Id(); unsigned int nac = m_conf.getP25NAC(); @@ -627,9 +745,11 @@ int CMMDVMHost::run() LogInfo(" TX Hang: %us", txHang); LogInfo(" Mode Hang: %us", m_p25RFModeHang); - m_p25 = new CP25Control(nac, id, selfOnly, uidOverride, m_p25Network, m_display, m_timeout, m_duplex, m_dmrLookup, remoteGateway, rssi); + m_p25 = new CP25Control(nac, id, selfOnly, uidOverride, m_p25Network, m_timeout, m_duplex, m_dmrLookup, remoteGateway, rssi); } +#endif +#if defined(USE_NXDN) if (m_nxdnEnabled) { std::string lookupFile = m_conf.getNXDNIdLookupFile(); unsigned int reloadTime = m_conf.getNXDNIdLookupTime(); @@ -657,9 +777,11 @@ int CMMDVMHost::run() LogInfo(" TX Hang: %us", txHang); LogInfo(" Mode Hang: %us", m_nxdnRFModeHang); - m_nxdn = new CNXDNControl(ran, id, selfOnly, m_nxdnNetwork, m_display, m_timeout, m_duplex, remoteGateway, m_nxdnLookup, rssi); + m_nxdn = new CNXDNControl(ran, id, selfOnly, m_nxdnNetwork, m_timeout, m_duplex, remoteGateway, m_nxdnLookup, rssi); } +#endif +#if defined(USE_POCSAG) CTimer pocsagTimer(1000U, 30U); if (m_pocsagEnabled) { @@ -668,12 +790,14 @@ int CMMDVMHost::run() LogInfo("POCSAG RF Parameters"); LogInfo(" Frequency: %uHz", frequency); - m_pocsag = new CPOCSAGControl(m_pocsagNetwork, m_display); + m_pocsag = new CPOCSAGControl(m_pocsagNetwork); if (m_pocsagNetwork != nullptr) pocsagTimer.start(); } +#endif +#if defined(USE_FM) if (m_fmEnabled) { bool preEmphasis = m_conf.getFMPreEmphasis(); bool deEmphasis = m_conf.getFMDeEmphasis(); @@ -681,31 +805,16 @@ int CMMDVMHost::run() float rxAudioGain = m_conf.getFMRXAudioGain(); m_fmRFModeHang = m_conf.getFMModeHang(); - m_fm = new CFMControl(m_fmNetwork, txAudioGain, rxAudioGain, preEmphasis, deEmphasis); + m_fm = new CFMControl(m_fmNetwork, txAudioGain, rxAudioGain, preEmphasis, deEmphasis, rssi); } +#endif bool remoteControlEnabled = m_conf.getRemoteControlEnabled(); - if (remoteControlEnabled) { - std::string address = m_conf.getRemoteControlAddress(); - unsigned short port = m_conf.getRemoteControlPort(); - - LogInfo("Remote Control Parameters"); - LogInfo(" Address: %s", address.c_str()); - LogInfo(" Port: %hu", port); - - m_remoteControl = new CRemoteControl(this, address, port); - - ret = m_remoteControl->open(); - if (!ret) { - delete m_remoteControl; - m_remoteControl = nullptr; - } - } + if (remoteControlEnabled) + m_remoteControl = new CRemoteControl(this, m_mqtt); setMode(MODE_IDLE); - LogInfo("MMDVMHost-%s is running", VERSION); - while (!m_killed) { bool lockout = m_modem->hasLockout(); @@ -724,6 +833,7 @@ int CMMDVMHost::run() unsigned int len; bool ret; +#if defined(USE_DSTAR) len = m_modem->readDStarData(data); if (m_dstar != nullptr && m_dstarEnabled && len > 0U) { if (m_mode == MODE_IDLE) { @@ -740,7 +850,9 @@ int CMMDVMHost::run() LogWarning("D-Star modem data received when in mode %u", m_mode); } } +#endif +#if defined(USE_DMR) len = m_modem->readDMRData1(data); if (m_dmr != nullptr && m_dmrEnabled && len > 0U) { if (m_mode == MODE_IDLE) { @@ -814,7 +926,9 @@ int CMMDVMHost::run() LogWarning("DMR modem data received when in mode %u", m_mode); } } +#endif +#if defined(USE_YSF) len = m_modem->readYSFData(data); if (m_ysf != nullptr && m_ysfEnabled && len > 0U) { if (m_mode == MODE_IDLE) { @@ -831,7 +945,9 @@ int CMMDVMHost::run() LogWarning("System Fusion modem data received when in mode %u", m_mode); } } +#endif +#if defined(USE_P25) len = m_modem->readP25Data(data); if (m_p25 != nullptr && m_p25Enabled && len > 0U) { if (m_mode == MODE_IDLE) { @@ -848,7 +964,9 @@ int CMMDVMHost::run() LogWarning("P25 modem data received when in mode %u", m_mode); } } +#endif +#if defined(USE_NXDN) len = m_modem->readNXDNData(data); if (m_nxdn != nullptr && m_nxdnEnabled && len > 0U) { if (m_mode == MODE_IDLE) { @@ -865,7 +983,9 @@ int CMMDVMHost::run() LogWarning("NXDN modem data received when in mode %u", m_mode); } } +#endif +#if defined(USE_FM) len = m_modem->readFMData(data); if (m_fm != nullptr && m_fmEnabled && len > 0U) { if (m_mode == MODE_IDLE) { @@ -882,6 +1002,7 @@ int CMMDVMHost::run() LogWarning("FM modem data received when in mode %u", m_mode); } } +#endif len = m_modem->readTransparentData(data); if (transparentSocket != nullptr && len > 0U) @@ -892,7 +1013,8 @@ int CMMDVMHost::run() setMode(MODE_IDLE); } - if (m_dstar != nullptr && m_dstarEnabled) { +#if defined(USE_DSTAR) + if (m_dstar != nullptr) { ret = m_modem->hasDStarSpace(); if (ret) { len = m_dstar->readModem(data); @@ -910,8 +1032,10 @@ int CMMDVMHost::run() } } } +#endif - if (m_dmr != nullptr && m_dmrEnabled) { +#if defined(USE_DMR) + if (m_dmr != nullptr) { ret = m_modem->hasDMRSpace1(); if (ret) { len = m_dmr->readModemSlot1(data); @@ -956,8 +1080,10 @@ int CMMDVMHost::run() } } } +#endif - if (m_ysf != nullptr && m_ysfEnabled) { +#if defined(USE_YSF) + if (m_ysf != nullptr) { ret = m_modem->hasYSFSpace(); if (ret) { len = m_ysf->readModem(data); @@ -975,8 +1101,10 @@ int CMMDVMHost::run() } } } +#endif - if (m_p25 != nullptr && m_p25Enabled) { +#if defined(USE_P25) + if (m_p25 != nullptr) { ret = m_modem->hasP25Space(); if (ret) { len = m_p25->readModem(data); @@ -994,8 +1122,10 @@ int CMMDVMHost::run() } } } +#endif - if (m_nxdn != nullptr && m_nxdnEnabled) { +#if defined(USE_NXDN) + if (m_nxdn != nullptr) { ret = m_modem->hasNXDNSpace(); if (ret) { len = m_nxdn->readModem(data); @@ -1013,8 +1143,10 @@ int CMMDVMHost::run() } } } +#endif - if (m_pocsag != nullptr && m_pocsagEnabled) { +#if defined(USE_POCSAG) + if (m_pocsag != nullptr) { ret = m_modem->hasPOCSAGSpace(); if (ret) { len = m_pocsag->readModem(data); @@ -1032,8 +1164,10 @@ int CMMDVMHost::run() } } } +#endif - if (m_fm != nullptr && m_fmEnabled) { +#if defined(USE_FM) + if (m_fm != nullptr) { unsigned int space = m_modem->getFMSpace(); if (space > 0U) { len = m_fm->readModem(data, space); @@ -1051,6 +1185,7 @@ int CMMDVMHost::run() } } } +#endif if (transparentSocket != nullptr) { sockaddr_storage address; @@ -1060,63 +1195,110 @@ int CMMDVMHost::run() m_modem->writeTransparentData(data, len); } - remoteControl(); + len = m_modem->readSerialData(data); + if (len > 0U) + m_mqtt->publish("display-out", data, len); unsigned int ms = stopWatch.elapsed(); stopWatch.start(); - m_display->clock(ms); - m_modem->clock(ms); + m_serialTimer.clock(ms); + if (m_serialTimer.isRunning() && m_serialTimer.hasExpired()) { + unsigned int length = m_serialLength - m_serialStart; + if (length > 252U) { + m_modem->writeSerialData(m_serialBuffer + m_serialStart, 252U); + + m_serialStart += 252U; + m_serialTimer.start(); + } else { + if (length > 0U) + m_modem->writeSerialData(m_serialBuffer + m_serialStart, length); + + m_serialLength = 0U; + m_serialStart = 0U; + m_serialTimer.stop(); + } + } + if (!m_fixedMode) m_modeTimer.clock(ms); if (m_reload) { +#if defined(USE_DMR) || defined(USE_P25) if (m_dmrLookup != nullptr) m_dmrLookup->reload(); - +#endif +#if defined(USE_NXDN) if (m_nxdnLookup != nullptr) m_nxdnLookup->reload(); - +#endif m_reload = false; } - + +#if defined(USE_DSTAR) if (m_dstar != nullptr) m_dstar->clock(); +#endif +#if defined(USE_DMR) if (m_dmr != nullptr) m_dmr->clock(); +#endif +#if defined(USE_YSF) if (m_ysf != nullptr) m_ysf->clock(ms); +#endif +#if defined(USE_P25) if (m_p25 != nullptr) m_p25->clock(ms); +#endif +#if defined(USE_NXDN) if (m_nxdn != nullptr) m_nxdn->clock(ms); +#endif +#if defined(USE_POCSAG) if (m_pocsag != nullptr) m_pocsag->clock(ms); +#endif +#if defined(USE_FM) if (m_fm != nullptr) m_fm->clock(ms); +#endif +#if defined(USE_DSTAR) if (m_dstarNetwork != nullptr) m_dstarNetwork->clock(ms); +#endif +#if defined(USE_DMR) if (m_dmrNetwork != nullptr) m_dmrNetwork->clock(ms); +#endif +#if defined(USE_YSF) if (m_ysfNetwork != nullptr) m_ysfNetwork->clock(ms); +#endif +#if defined(USE_P25) if (m_p25Network != nullptr) m_p25Network->clock(ms); +#endif +#if defined(USE_NXDN) if (m_nxdnNetwork != nullptr) m_nxdnNetwork->clock(ms); +#endif +#if defined(USE_POCSAG) if (m_pocsagNetwork != nullptr) m_pocsagNetwork->clock(ms); +#endif +#if defined(USE_FM) if (m_fmNetwork != nullptr) m_fmNetwork->clock(ms); - +#endif m_cwIdTimer.clock(ms); if (m_cwIdTimer.isRunning() && m_cwIdTimer.hasExpired()) { if (!m_modem->hasTX()){ LogDebug("sending CW ID"); - m_display->writeCW(); + writeJSONMode("CW"); m_modem->sendCWId(m_cwCallsign); m_cwIdTimer.setTimeout(m_cwIdTime); @@ -1124,6 +1306,7 @@ int CMMDVMHost::run() } } +#if defined(USE_DMR) switch (dmrBeacons) { case DMR_BEACONS::TIMED: dmrBeaconIntervalTimer.clock(ms); @@ -1168,90 +1351,129 @@ int CMMDVMHost::run() m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } +#endif +#if defined(USE_POCSAG) pocsagTimer.clock(ms); if (pocsagTimer.isRunning() && pocsagTimer.hasExpired()) { assert(m_pocsagNetwork != nullptr); m_pocsagNetwork->enable((m_mode == MODE_IDLE || m_mode == MODE_POCSAG) && m_pocsagEnabled); pocsagTimer.start(); } +#endif if (ms < 5U) CThread::sleep(5U); } + LogInfo("MMDVMHost is stopping"); + writeJSONMessage("MMDVMHost is stopping"); + setMode(MODE_QUIT); +#if defined(USE_DMR) || defined(USE_P25) if (m_dmrLookup != nullptr) m_dmrLookup->stop(); - +#endif +#if defined(USE_NXDN) if (m_nxdnLookup != nullptr) m_nxdnLookup->stop(); +#endif LogInfo("Closing network connections"); + writeJSONMessage("Closing network connections"); +#if defined(USE_DSTAR) if (m_dstarNetwork != nullptr) { m_dstarNetwork->close(); delete m_dstarNetwork; } +#endif +#if defined(USE_DMR) if (m_dmrNetwork != nullptr) { m_dmrNetwork->close(true); delete m_dmrNetwork; } +#endif +#if defined(USE_YSF) if (m_ysfNetwork != nullptr) { m_ysfNetwork->close(); delete m_ysfNetwork; } +#endif +#if defined(USE_P25) if (m_p25Network != nullptr) { m_p25Network->close(); delete m_p25Network; } +#endif +#if defined(USE_NXDN) if (m_nxdnNetwork != nullptr) { m_nxdnNetwork->close(); delete m_nxdnNetwork; } +#endif +#if defined(USE_POCSAG) if (m_pocsagNetwork != nullptr) { m_pocsagNetwork->close(); delete m_pocsagNetwork; } +#endif +#if defined(USE_FM) if (m_fmNetwork != nullptr) { m_fmNetwork->close(); delete m_fmNetwork; } +#endif if (transparentSocket != nullptr) { transparentSocket->close(); delete transparentSocket; } - if (m_remoteControl != nullptr) { - m_remoteControl->close(); - delete m_remoteControl; - } + delete m_remoteControl; LogInfo("Stopping protocol handlers"); + writeJSONMessage("Stopping protocol handlers"); +#if defined(USE_DSTAR) delete m_dstar; +#endif +#if defined(USE_DMR) delete m_dmr; +#endif +#if defined(USE_YSF) delete m_ysf; +#endif +#if defined(USE_P25) delete m_p25; +#endif +#if defined(USE_NXDN) delete m_nxdn; +#endif +#if defined(USE_POCSAG) delete m_pocsag; +#endif +#if defined(USE_FM) delete m_fm; +#endif LogInfo("MMDVMHost-%s has stopped", VERSION); + writeJSONMessage("MMDVMHost has stopped"); m_modem->close(); delete m_modem; - m_display->close(); - delete m_display; + if (m_mqtt != nullptr) { + m_mqtt->close(); + delete m_mqtt; + } return 0; } @@ -1273,26 +1495,70 @@ bool CMMDVMHost::createModem() bool txInvert = m_conf.getModemTXInvert(); bool pttInvert = m_conf.getModemPTTInvert(); unsigned int txDelay = m_conf.getModemTXDelay(); +#if defined(USE_DMR) unsigned int dmrDelay = m_conf.getModemDMRDelay(); +#else + unsigned int dmrDelay = 0U; +#endif float rxLevel = m_conf.getModemRXLevel(); float cwIdTXLevel = m_conf.getModemCWIdTXLevel(); +#if defined(USE_DSTAR) float dstarTXLevel = m_conf.getModemDStarTXLevel(); +#else + float dstarTXLevel = 0.0F; +#endif +#if defined(USE_DMR) float dmrTXLevel = m_conf.getModemDMRTXLevel(); +#else + float dmrTXLevel = 0.0F; +#endif +#if defined(USE_YSF) float ysfTXLevel = m_conf.getModemYSFTXLevel(); +#else + float ysfTXLevel = 0.0F; +#endif +#if defined(USE_P25) float p25TXLevel = m_conf.getModemP25TXLevel(); +#else + float p25TXLevel = 0.0F; +#endif +#if defined(USE_NXDN) float nxdnTXLevel = m_conf.getModemNXDNTXLevel(); +#else + float nxdnTXLevel = 0.0F; +#endif +#if defined(USE_POCSAG) float pocsagTXLevel = m_conf.getModemPOCSAGTXLevel(); +#else + float pocsagTXLevel = 0.0F; +#endif +#if defined(USE_FM) float fmTXLevel = m_conf.getModemFMTXLevel(); +#else + float fmTXLevel = 0.0F; +#endif bool trace = m_conf.getModemTrace(); bool debug = m_conf.getModemDebug(); +#if defined(USE_DMR) unsigned int colorCode = m_conf.getDMRColorCode(); +#endif +#if defined(USE_YSF) bool lowDeviation = m_conf.getFusionLowDeviation(); unsigned int ysfTXHang = m_conf.getFusionTXHang(); +#endif +#if defined(USE_P25) unsigned int p25TXHang = m_conf.getP25TXHang(); +#endif +#if defined(USE_NXDN) unsigned int nxdnTXHang = m_conf.getNXDNTXHang(); +#endif unsigned int rxFrequency = m_conf.getRXFrequency(); unsigned int txFrequency = m_conf.getTXFrequency(); +#if defined(USE_POCSAG) unsigned int pocsagFrequency = m_conf.getPOCSAGFrequency(); +#else + unsigned int pocsagFrequency = txFrequency; +#endif int rxOffset = m_conf.getModemRXOffset(); int txOffset = m_conf.getModemTXOffset(); int rxDCOffset = m_conf.getModemRXDCOffset(); @@ -1328,16 +1594,32 @@ bool CMMDVMHost::createModem() LogInfo(" RX DC Offset: %d", rxDCOffset); LogInfo(" TX DC Offset: %d", txDCOffset); LogInfo(" RF Level: %.1f%%", rfLevel); +#if defined(USE_DMR) LogInfo(" DMR Delay: %u (%.1fms)", dmrDelay, float(dmrDelay) * 0.0416666F); +#endif LogInfo(" RX Level: %.1f%%", rxLevel); LogInfo(" CW Id TX Level: %.1f%%", cwIdTXLevel); +#if defined(USE_DSTAR) LogInfo(" D-Star TX Level: %.1f%%", dstarTXLevel); +#endif +#if defined(USE_DMR) LogInfo(" DMR TX Level: %.1f%%", dmrTXLevel); +#endif +#if defined(USE_YSF) LogInfo(" YSF TX Level: %.1f%%", ysfTXLevel); +#endif +#if defined(USE_P25) LogInfo(" P25 TX Level: %.1f%%", p25TXLevel); +#endif +#if defined(USE_NXDN) LogInfo(" NXDN TX Level: %.1f%%", nxdnTXLevel); +#endif +#if defined(USE_POCSAG) LogInfo(" POCSAG TX Level: %.1f%%", pocsagTXLevel); +#endif +#if defined(USE_FM) LogInfo(" FM TX Level: %.1f%%", fmTXLevel); +#endif LogInfo(" TX Frequency: %uHz (%uHz)", txFrequency, txFrequency + txOffset); LogInfo(" Use COS as Lockout: %s", useCOSAsLockout ? "yes" : "no"); @@ -1361,11 +1643,20 @@ bool CMMDVMHost::createModem() m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled, m_fmEnabled); m_modem->setLevels(rxLevel, cwIdTXLevel, dstarTXLevel, dmrTXLevel, ysfTXLevel, p25TXLevel, nxdnTXLevel, pocsagTXLevel, fmTXLevel); m_modem->setRFParams(rxFrequency, rxOffset, txFrequency, txOffset, txDCOffset, rxDCOffset, rfLevel, pocsagFrequency); +#if defined(USE_DMR) m_modem->setDMRParams(colorCode); +#endif +#if defined(USE_YSF) m_modem->setYSFParams(lowDeviation, ysfTXHang); +#endif +#if defined(USE_P25) m_modem->setP25Params(p25TXHang); +#endif +#if defined(USE_NXDN) m_modem->setNXDNParams(nxdnTXHang); +#endif +#if defined(USE_FM) if (m_fmEnabled) { std::string callsign = m_conf.getFMCallsign(); unsigned int callsignSpeed = m_conf.getFMCallsignSpeed(); @@ -1454,7 +1745,7 @@ bool CMMDVMHost::createModem() m_modem->setFMExtParams(extAck, extAudioBoost); } } - +#endif bool ret = m_modem->open(); if (!ret) { delete m_modem; @@ -1465,6 +1756,7 @@ bool CMMDVMHost::createModem() return true; } +#if defined(USE_DSTAR) bool CMMDVMHost::createDStarNetwork() { std::string gatewayAddress = m_conf.getDStarGatewayAddress(); @@ -1494,29 +1786,26 @@ bool CMMDVMHost::createDStarNetwork() return true; } +#endif +#if defined(USE_DMR) bool CMMDVMHost::createDMRNetwork() { - std::string remoteAddress = m_conf.getDMRNetworkRemoteAddress(); - unsigned short remotePort = m_conf.getDMRNetworkRemotePort(); + std::string gatewayAddress = m_conf.getDMRNetworkGatewayAddress(); + unsigned short gatewayPort = m_conf.getDMRNetworkGatewayPort(); std::string localAddress = m_conf.getDMRNetworkLocalAddress(); unsigned short localPort = m_conf.getDMRNetworkLocalPort(); unsigned int id = m_conf.getDMRId(); - std::string password = m_conf.getDMRNetworkPassword(); bool debug = m_conf.getDMRNetworkDebug(); unsigned int jitter = m_conf.getDMRNetworkJitter(); bool slot1 = m_conf.getDMRNetworkSlot1(); bool slot2 = m_conf.getDMRNetworkSlot2(); HW_TYPE hwType = m_modem->getHWType(); m_dmrNetModeHang = m_conf.getDMRNetworkModeHang(); - std::string options = m_conf.getDMRNetworkOptions(); - - std::string type = m_conf.getDMRNetworkType(); LogInfo("DMR Network Parameters"); - LogInfo(" Type: %s", type.c_str()); - LogInfo(" Remote Address: %s", remoteAddress.c_str()); - LogInfo(" Remote Port: %hu", remotePort); + LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); + LogInfo(" Gateway Port: %hu", gatewayPort); LogInfo(" Local Address: %s", localAddress.c_str()); LogInfo(" Local Port: %hu", localPort); LogInfo(" Jitter: %ums", jitter); @@ -1524,10 +1813,7 @@ bool CMMDVMHost::createDMRNetwork() LogInfo(" Slot 2: %s", slot2 ? "enabled" : "disabled"); LogInfo(" Mode Hang: %us", m_dmrNetModeHang); - if (type == "Direct") - m_dmrNetwork = new CDMRDirectNetwork(remoteAddress, remotePort, localAddress, localPort, id, password, m_duplex, VERSION, slot1, slot2, hwType, debug); - else - m_dmrNetwork = new CDMRGatewayNetwork(remoteAddress, remotePort, localAddress, localPort, id, m_duplex, VERSION, slot1, slot2, hwType, debug); + m_dmrNetwork = new CDMRNetwork(gatewayAddress, gatewayPort, localAddress, localPort, id, m_duplex, VERSION, slot1, slot2, hwType, debug); unsigned int rxFrequency = m_conf.getRXFrequency(); unsigned int txFrequency = m_conf.getTXFrequency(); @@ -1540,32 +1826,8 @@ bool CMMDVMHost::createDMRNetwork() LogInfo(" TX Frequency: %uHz", txFrequency); LogInfo(" Power: %uW", power); - if (type == "Direct") { - float latitude = m_conf.getLatitude(); - float longitude = m_conf.getLongitude(); - int height = m_conf.getHeight(); - std::string location = m_conf.getLocation(); - std::string description = m_conf.getDescription(); - std::string url = m_conf.getURL(); - - LogInfo(" Latitude: %fdeg N", latitude); - LogInfo(" Longitude: %fdeg E", longitude); - LogInfo(" Height: %um", height); - LogInfo(" Location: \"%s\"", location.c_str()); - LogInfo(" Description: \"%s\"", description.c_str()); - LogInfo(" URL: \"%s\"", url.c_str()); - - m_dmrNetwork->setConfig(m_callsign, rxFrequency, txFrequency, power, colorCode, latitude, longitude, height, location, description, url); - } else { - m_dmrNetwork->setConfig(m_callsign, rxFrequency, txFrequency, power, colorCode, 0.0F, 0.0F, 0, "", "", ""); - } + m_dmrNetwork->setConfig(m_callsign, rxFrequency, txFrequency, power, colorCode); - if (!options.empty()) { - LogInfo(" Options: %s", options.c_str()); - - m_dmrNetwork->setOptions(options); - } - bool ret = m_dmrNetwork->open(); if (!ret) { delete m_dmrNetwork; @@ -1577,7 +1839,9 @@ bool CMMDVMHost::createDMRNetwork() return true; } +#endif +#if defined(USE_YSF) bool CMMDVMHost::createYSFNetwork() { std::string localAddress = m_conf.getFusionNetworkLocalAddress(); @@ -1607,7 +1871,9 @@ bool CMMDVMHost::createYSFNetwork() return true; } +#endif +#if defined(USE_P25) bool CMMDVMHost::createP25Network() { std::string gatewayAddress = m_conf.getP25GatewayAddress(); @@ -1637,7 +1903,9 @@ bool CMMDVMHost::createP25Network() return true; } +#endif +#if defined(USE_NXDN) bool CMMDVMHost::createNXDNNetwork() { std::string protocol = m_conf.getNXDNNetworkProtocol(); @@ -1672,7 +1940,9 @@ bool CMMDVMHost::createNXDNNetwork() return true; } +#endif +#if defined(USE_POCSAG) bool CMMDVMHost::createPOCSAGNetwork() { std::string gatewayAddress = m_conf.getPOCSAGGatewayAddress(); @@ -1702,13 +1972,12 @@ bool CMMDVMHost::createPOCSAGNetwork() return true; } +#endif +#if defined(USE_FM) bool CMMDVMHost::createFMNetwork() { std::string callsign = m_conf.getFMCallsign(); - std::string protocol = m_conf.getFMNetworkProtocol(); - unsigned int sampleRate = m_conf.getFMNetworkSampleRate(); - std::string squelchFile = m_conf.getFMNetworkSquelchFile(); std::string gatewayAddress = m_conf.getFMGatewayAddress(); unsigned short gatewayPort = m_conf.getFMGatewayPort(); std::string localAddress = m_conf.getFMLocalAddress(); @@ -1721,11 +1990,6 @@ bool CMMDVMHost::createFMNetwork() bool debug = m_conf.getFMNetworkDebug(); LogInfo("FM Network Parameters"); - LogInfo(" Protocol: %s", protocol.c_str()); - if (protocol == "RAW") { - LogInfo(" Sample Rate: %u", sampleRate); - LogInfo(" Squelch File: %s", squelchFile.empty() ? "(none)" : squelchFile.c_str()); - } LogInfo(" Gateway Address: %s", gatewayAddress.c_str()); LogInfo(" Gateway Port: %hu", gatewayPort); LogInfo(" Local Address: %s", localAddress.c_str()); @@ -1736,7 +2000,7 @@ bool CMMDVMHost::createFMNetwork() LogInfo(" RX Audio Gain: %.2f", rxAudioGain); LogInfo(" Mode Hang: %us", m_fmNetModeHang); - m_fmNetwork = new CFMNetwork(callsign, protocol, localAddress, localPort, gatewayAddress, gatewayPort, sampleRate, squelchFile, debug); + m_fmNetwork = new CFMNetwork(callsign, localAddress, localPort, gatewayAddress, gatewayPort, debug); bool ret = m_fmNetwork->open(); if (!ret) { @@ -1749,16 +2013,31 @@ bool CMMDVMHost::createFMNetwork() return true; } +#endif void CMMDVMHost::readParams() { +#if defined(USE_DSTAR) m_dstarEnabled = m_conf.getDStarEnabled(); +#endif +#if defined(USE_DMR) m_dmrEnabled = m_conf.getDMREnabled(); +#endif +#if defined(USE_YSF) m_ysfEnabled = m_conf.getFusionEnabled(); +#endif +#if defined(USE_P25) m_p25Enabled = m_conf.getP25Enabled(); +#endif +#if defined(USE_NXDN) m_nxdnEnabled = m_conf.getNXDNEnabled(); +#endif +#if defined(USE_POCSAG) m_pocsagEnabled = m_conf.getPOCSAGEnabled(); +#endif +#if defined(USE_FM) m_fmEnabled = m_conf.getFMEnabled(); +#endif m_duplex = m_conf.getDuplex(); m_callsign = m_conf.getCallsign(); m_id = m_conf.getId(); @@ -1766,180 +2045,157 @@ void CMMDVMHost::readParams() LogInfo("General Parameters"); LogInfo(" Callsign: %s", m_callsign.c_str()); +#if defined(USE_DMR) || defined(USE_P25) LogInfo(" Id: %u", m_id); +#endif LogInfo(" Duplex: %s", m_duplex ? "yes" : "no"); LogInfo(" Timeout: %us", m_timeout); +#if defined(USE_DSTAR) LogInfo(" D-Star: %s", m_dstarEnabled ? "enabled" : "disabled"); +#endif +#if defined(USE_DMR) LogInfo(" DMR: %s", m_dmrEnabled ? "enabled" : "disabled"); +#endif +#if defined(USE_YSF) LogInfo(" YSF: %s", m_ysfEnabled ? "enabled" : "disabled"); +#endif +#if defined(USE_P25) LogInfo(" P25: %s", m_p25Enabled ? "enabled" : "disabled"); +#endif +#if defined(USE_NXDN) LogInfo(" NXDN: %s", m_nxdnEnabled ? "enabled" : "disabled"); +#endif +#if defined(USE_POCSAG) LogInfo(" POCSAG: %s", m_pocsagEnabled ? "enabled" : "disabled"); +#endif +#if defined(USE_FM) LogInfo(" FM: %s", m_fmEnabled ? "enabled" : "disabled"); -} - -void CMMDVMHost::enableModemMode(bool& mode, bool enabled) -{ - LogDebug("Setting mode current=%s new=%s", mode ? "true" : "false", enabled ? "true" : "false"); - - mode = enabled; - - m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled, m_fmEnabled); - if (!m_modem->writeConfig()) - LogError("Cannot write Config to MMDVM"); -} - -void CMMDVMHost::processEnableModeCommand(unsigned char mode, bool hasController, bool& modeEnabled, bool enableMode) -{ - if (hasController && (modeEnabled != enableMode)) { - unsigned char data[500U]; - bool switchToOrInIDLE = (enableMode ? (m_mode == MODE_IDLE) : (m_mode == mode)); - - // Enable or disable Controller and Network. - // When re-enabling, it tries to recover ASAP when mode is currently set to IDLE, - // as Controller and Network will ::clock() immediatly after. - if (switchToOrInIDLE) { - switch (mode) { - case MODE_DSTAR: - if (m_dstarNetwork != nullptr) - m_dstarNetwork->enable(enableMode); - m_dstar->enable(enableMode); - break; - case MODE_DMR: - if (m_dmrNetwork != nullptr) - m_dmrNetwork->enable(enableMode); - m_dmr->enable(enableMode); - break; - case MODE_YSF: - if (m_ysfNetwork != nullptr) - m_ysfNetwork->enable(enableMode); - m_ysf->enable(enableMode); - break; - case MODE_P25: - if (m_p25Network != nullptr) - m_p25Network->enable(enableMode); - m_p25->enable(enableMode); - break; - case MODE_NXDN: - if (m_nxdnNetwork != nullptr) - m_nxdnNetwork->enable(enableMode); - m_nxdn->enable(enableMode); - break; - case MODE_FM: - if (m_fmNetwork != nullptr) - m_fmNetwork->enable(enableMode); - m_fm->enable(enableMode); - break; - default: - break; - } - } - - // Flush modem data for the given mode - switch (mode) { - case MODE_DSTAR: - while (m_modem->readDStarData(data) > 0U); - break; - case MODE_DMR: - while (m_modem->readDMRData1(data) > 0U || m_modem->readDMRData2(data) > 0U); - break; - case MODE_YSF: - while (m_modem->readYSFData(data) > 0U); - break; - case MODE_P25: - while (m_modem->readP25Data(data) > 0U); - break; - case MODE_NXDN: - while (m_modem->readNXDNData(data) > 0U); - break; - case MODE_FM: - while (m_modem->readFMData(data) > 0U); - break; - default: - break; - } - - enableModemMode(modeEnabled, enableMode); - - if (!enableMode && switchToOrInIDLE) - setMode(MODE_IDLE); - } +#endif } void CMMDVMHost::setMode(unsigned char mode) { assert(m_modem != nullptr); - assert(m_display != nullptr); switch (mode) { +#if defined(USE_DSTAR) case MODE_DSTAR: if (m_dstarNetwork != nullptr && m_dstarEnabled) m_dstarNetwork->enable(true); +#if defined(USE_DMR) if (m_dmrNetwork != nullptr) m_dmrNetwork->enable(false); +#endif +#if defined(USE_YSF) if (m_ysfNetwork != nullptr) m_ysfNetwork->enable(false); +#endif +#if defined(USE_P25) if (m_p25Network != nullptr) m_p25Network->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdnNetwork != nullptr) m_nxdnNetwork->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsagNetwork != nullptr) m_pocsagNetwork->enable(false); +#endif +#if defined(USE_FM) if (m_fmNetwork != nullptr) m_fmNetwork->enable(false); +#endif if (m_dstar != nullptr && m_dstarEnabled) m_dstar->enable(true); +#if defined(USE_DMR) if (m_dmr != nullptr) m_dmr->enable(false); +#endif +#if defined(USE_YSF) if (m_ysf != nullptr) m_ysf->enable(false); +#endif +#if defined(USE_P25) if (m_p25 != nullptr) m_p25->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdn != nullptr) m_nxdn->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsag != nullptr) m_pocsag->enable(false); +#endif +#if defined(USE_FM) if (m_fm != nullptr) m_fm->enable(false); +#endif if (m_dstarEnabled) { m_modem->setMode(MODE_DSTAR); m_mode = MODE_DSTAR; m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("D-Star"); - LogMessage("Mode set to D-Star"); + writeJSONMode("D-Star"); } break; - +#endif +#if defined(USE_DMR) case MODE_DMR: +#if defined(USE_DSTAR) if (m_dstarNetwork != nullptr) m_dstarNetwork->enable(false); +#endif if (m_dmrNetwork != nullptr && m_dmrEnabled) m_dmrNetwork->enable(true); +#if defined(USE_YSF) if (m_ysfNetwork != nullptr) m_ysfNetwork->enable(false); +#endif +#if defined(USE_P25) if (m_p25Network != nullptr) m_p25Network->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdnNetwork != nullptr) m_nxdnNetwork->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsagNetwork != nullptr) m_pocsagNetwork->enable(false); +#endif +#if defined(USE_FM) if (m_fmNetwork != nullptr) m_fmNetwork->enable(false); +#endif +#if defined(USE_DSTAR) if (m_dstar != nullptr) m_dstar->enable(false); +#endif if (m_dmr != nullptr && m_dmrEnabled) m_dmr->enable(true); +#if defined(USE_YSF) if (m_ysf != nullptr) m_ysf->enable(false); +#endif +#if defined(USE_P25) if (m_p25 != nullptr) m_p25->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdn != nullptr) m_nxdn->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsag != nullptr) m_pocsag->enable(false); +#endif +#if defined(USE_FM) if (m_fm != nullptr) m_fm->enable(false); +#endif if (m_dmrEnabled) { m_modem->setMode(MODE_DMR); if (m_duplex) { @@ -1950,325 +2206,540 @@ void CMMDVMHost::setMode(unsigned char mode) m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("DMR"); - LogMessage("Mode set to DMR"); + writeJSONMode("DMR"); } break; - +#endif +#if defined(USE_YSF) case MODE_YSF: +#if defined(USE_DSTAR) if (m_dstarNetwork != nullptr) m_dstarNetwork->enable(false); +#endif +#if defined(USE_DMR) if (m_dmrNetwork != nullptr) m_dmrNetwork->enable(false); +#endif if (m_ysfNetwork != nullptr && m_ysfEnabled) m_ysfNetwork->enable(true); +#if defined(USE_P25) if (m_p25Network != nullptr) m_p25Network->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdnNetwork != nullptr) m_nxdnNetwork->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsagNetwork != nullptr) m_pocsagNetwork->enable(false); +#endif +#if defined(USE_FM) if (m_fmNetwork != nullptr) m_fmNetwork->enable(false); +#endif +#if defined(USE_DSTAR) if (m_dstar != nullptr) m_dstar->enable(false); +#endif +#if defined(USE_DMR) if (m_dmr != nullptr) m_dmr->enable(false); +#endif if (m_ysf != nullptr && m_ysfEnabled) m_ysf->enable(true); +#if defined(USE_P25) if (m_p25 != nullptr) m_p25->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdn != nullptr) m_nxdn->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsag != nullptr) m_pocsag->enable(false); +#endif +#if defined(USE_FM) if (m_fm != nullptr) m_fm->enable(false); +#endif if (m_ysfEnabled) { m_modem->setMode(MODE_YSF); m_mode = MODE_YSF; m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("System Fusion"); - LogMessage("Mode set to System Fusion"); + writeJSONMode("YSF"); } break; - +#endif +#if defined(USE_P25) case MODE_P25: +#if defined(USE_DSTAR) if (m_dstarNetwork != nullptr) m_dstarNetwork->enable(false); +#endif +#if defined(USE_DMR) if (m_dmrNetwork != nullptr) m_dmrNetwork->enable(false); +#endif +#if defined(USE_YSF) if (m_ysfNetwork != nullptr) m_ysfNetwork->enable(false); +#endif if (m_p25Network != nullptr && m_p25Enabled) m_p25Network->enable(true); +#if defined(USE_NXDN) if (m_nxdnNetwork != nullptr) m_nxdnNetwork->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsagNetwork != nullptr) m_pocsagNetwork->enable(false); +#endif +#if defined(USE_FM) if (m_fmNetwork != nullptr) m_fmNetwork->enable(false); +#endif +#if defined(USE_DSTAR) if (m_dstar != nullptr) m_dstar->enable(false); +#endif +#if defined(USE_DMR) if (m_dmr != nullptr) m_dmr->enable(false); +#endif +#if defined(USE_YSF) if (m_ysf != nullptr) m_ysf->enable(false); +#endif if (m_p25 != nullptr && m_p25Enabled) m_p25->enable(true); +#if defined(USE_NXDN) if (m_nxdn != nullptr) m_nxdn->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsag != nullptr) m_pocsag->enable(false); +#endif +#if defined(USE_FM) if (m_fm != nullptr) m_fm->enable(false); +#endif if (m_p25Enabled) { m_modem->setMode(MODE_P25); m_mode = MODE_P25; m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("P25"); - LogMessage("Mode set to P25"); + writeJSONMode("P25"); } break; - +#endif +#if defined(USE_NXDN) case MODE_NXDN: +#if defined(USE_DSTAR) if (m_dstarNetwork != nullptr) m_dstarNetwork->enable(false); +#endif +#if defined(USE_DMR) if (m_dmrNetwork != nullptr) m_dmrNetwork->enable(false); +#endif +#if defined(USE_YSF) if (m_ysfNetwork != nullptr) m_ysfNetwork->enable(false); +#endif +#if defined(USE_P25) if (m_p25Network != nullptr) m_p25Network->enable(false); +#endif if (m_nxdnNetwork != nullptr && m_nxdnEnabled) m_nxdnNetwork->enable(true); +#if defined(USE_POCSAG) if (m_pocsagNetwork != nullptr) m_pocsagNetwork->enable(false); +#endif +#if defined(USE_FM) if (m_fmNetwork != nullptr) m_fmNetwork->enable(false); +#endif +#if defined(USE_DSTAR) if (m_dstar != nullptr) m_dstar->enable(false); +#endif +#if defined(USE_DMR) if (m_dmr != nullptr) m_dmr->enable(false); +#endif +#if defined(USE_YSF) if (m_ysf != nullptr) m_ysf->enable(false); +#endif +#if defined(USE_P25) if (m_p25 != nullptr) m_p25->enable(false); +#endif if (m_nxdn != nullptr && m_nxdnEnabled) m_nxdn->enable(true); +#if defined(USE_POCSAG) if (m_pocsag != nullptr) m_pocsag->enable(false); +#endif +#if defined(USE_FM) if (m_fm != nullptr) m_fm->enable(false); +#endif if (m_nxdnEnabled) { m_modem->setMode(MODE_NXDN); m_mode = MODE_NXDN; m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("NXDN"); - LogMessage("Mode set to NXDN"); + writeJSONMode("NXDN"); } break; - +#endif +#if defined(USE_POCSAG) case MODE_POCSAG: +#if defined(USE_DSTAR) if (m_dstarNetwork != nullptr) m_dstarNetwork->enable(false); +#endif +#if defined(USE_DMR) if (m_dmrNetwork != nullptr) m_dmrNetwork->enable(false); +#endif +#if defined(USE_YSF) if (m_ysfNetwork != nullptr) m_ysfNetwork->enable(false); +#endif +#if defined(USE_P25) if (m_p25Network != nullptr) m_p25Network->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdnNetwork != nullptr) m_nxdnNetwork->enable(false); - if (m_pocsagNetwork != nullptr && m_pocsagEnabled) +#endif + if (m_pocsagNetwork != nullptr) m_pocsagNetwork->enable(true); +#if defined(USE_FM) if (m_fmNetwork != nullptr) m_fmNetwork->enable(false); +#endif +#if defined(USE_DSTAR) if (m_dstar != nullptr) m_dstar->enable(false); +#endif +#if defined(USE_DMR) if (m_dmr != nullptr) m_dmr->enable(false); +#endif +#if defined(USE_YSF) if (m_ysf != nullptr) m_ysf->enable(false); +#endif +#if defined(USE_P25) if (m_p25 != nullptr) m_p25->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdn != nullptr) m_nxdn->enable(false); - if (m_pocsag != nullptr && m_pocsagEnabled) +#endif + if (m_pocsag != nullptr) m_pocsag->enable(true); +#if defined(USE_FM) if (m_fm != nullptr) m_fm->enable(false); +#endif m_modem->setMode(MODE_POCSAG); m_mode = MODE_POCSAG; m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("POCSAG"); - LogMessage("Mode set to POCSAG"); + writeJSONMode("POCSAG"); break; - +#endif +#if defined(USE_FM) case MODE_FM: +#if defined(USE_DSTAR) if (m_dstarNetwork != nullptr) m_dstarNetwork->enable(false); +#endif +#if defined(USE_DMR) if (m_dmrNetwork != nullptr) m_dmrNetwork->enable(false); +#endif +#if defined(USE_YSF) if (m_ysfNetwork != nullptr) m_ysfNetwork->enable(false); +#endif +#if defined(USE_P25) if (m_p25Network != nullptr) m_p25Network->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdnNetwork != nullptr) m_nxdnNetwork->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsagNetwork != nullptr) m_pocsagNetwork->enable(false); +#endif if (m_fmNetwork != nullptr && m_fmEnabled) m_fmNetwork->enable(true); +#if defined(USE_DSTAR) if (m_dstar != nullptr) m_dstar->enable(false); +#endif +#if defined(USE_DMR) if (m_dmr != nullptr) m_dmr->enable(false); +#endif +#if defined(USE_YSF) if (m_ysf != nullptr) m_ysf->enable(false); +#endif +#if defined(USE_P25) if (m_p25 != nullptr) m_p25->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdn != nullptr) m_nxdn->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsag != nullptr) m_pocsag->enable(false); if (m_fm != nullptr && m_fmEnabled) m_fm->enable(true); +#endif +#if defined(USE_DMR) if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } +#endif if (m_fmEnabled) { m_modem->setMode(MODE_FM); - m_display->setFM(); m_mode = MODE_FM; m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("FM"); - LogMessage("Mode set to FM"); + writeJSONMode("FM"); } break; +#endif case MODE_LOCKOUT: +#if defined(USE_DSTAR) if (m_dstarNetwork != nullptr) m_dstarNetwork->enable(false); +#endif +#if defined(USE_DMR) if (m_dmrNetwork != nullptr) m_dmrNetwork->enable(false); +#endif +#if defined(USE_YSF) if (m_ysfNetwork != nullptr) m_ysfNetwork->enable(false); +#endif +#if defined(USE_P25) if (m_p25Network != nullptr) m_p25Network->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdnNetwork != nullptr) m_nxdnNetwork->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsagNetwork != nullptr) m_pocsagNetwork->enable(false); +#endif +#if defined(USE_FM) if (m_fmNetwork != nullptr) m_fmNetwork->enable(false); +#endif +#if defined(USE_DSTAR) if (m_dstar != nullptr) m_dstar->enable(false); +#endif +#if defined(USE_DMR) if (m_dmr != nullptr) m_dmr->enable(false); +#endif +#if defined(USE_YSF) if (m_ysf != nullptr) m_ysf->enable(false); +#endif +#if defined(USE_P25) if (m_p25 != nullptr) m_p25->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdn != nullptr) m_nxdn->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsag != nullptr) m_pocsag->enable(false); +#endif +#if defined(USE_FM) if (m_fm != nullptr) m_fm->enable(false); +#endif +#if defined(USE_DMR) if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } +#endif m_modem->setMode(MODE_IDLE); - m_display->setLockout(); m_mode = MODE_LOCKOUT; m_modeTimer.stop(); m_cwIdTimer.stop(); removeLockFile(); - LogMessage("Mode set to Lockout"); + writeJSONMode("lockout"); break; case MODE_ERROR: LogMessage("Mode set to Error"); +#if defined(USE_DSTAR) if (m_dstarNetwork != nullptr) m_dstarNetwork->enable(false); +#endif +#if defined(USE_DMR) if (m_dmrNetwork != nullptr) m_dmrNetwork->enable(false); +#endif +#if defined(USE_YSF) if (m_ysfNetwork != nullptr) m_ysfNetwork->enable(false); +#endif +#if defined(USE_P25) if (m_p25Network != nullptr) m_p25Network->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdnNetwork != nullptr) m_nxdnNetwork->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsagNetwork != nullptr) m_pocsagNetwork->enable(false); +#endif +#if defined(USE_FM) if (m_fmNetwork != nullptr) m_fmNetwork->enable(false); +#endif +#if defined(USE_DSTAR) if (m_dstar != nullptr) m_dstar->enable(false); +#endif +#if defined(USE_DMR) if (m_dmr != nullptr) m_dmr->enable(false); +#endif +#if defined(USE_YSF) if (m_ysf != nullptr) m_ysf->enable(false); +#endif +#if defined(USE_P25) if (m_p25 != nullptr) m_p25->enable(false); +#endif +#if defined(USE_NXDN) if (m_nxdn != nullptr) m_nxdn->enable(false); +#endif +#if defined(USE_POCSAG) if (m_pocsag != nullptr) m_pocsag->enable(false); +#endif +#if defined(USE_FM) if (m_fm != nullptr) m_fm->enable(false); +#endif +#if defined(USE_DMR) if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX()) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } - m_display->setError("MODEM"); +#endif m_mode = MODE_ERROR; m_modeTimer.stop(); m_cwIdTimer.stop(); removeLockFile(); - LogMessage("Mode set to Error"); + writeJSONMode("error"); break; default: +#if defined(USE_DSTAR) if (m_dstarNetwork != nullptr && m_dstarEnabled) m_dstarNetwork->enable(true); +#endif +#if defined(USE_DMR) if (m_dmrNetwork != nullptr && m_dmrEnabled) m_dmrNetwork->enable(true); +#endif +#if defined(USE_YSF) if (m_ysfNetwork != nullptr && m_ysfEnabled) m_ysfNetwork->enable(true); +#endif +#if defined(USE_P25) if (m_p25Network != nullptr && m_p25Enabled) m_p25Network->enable(true); +#endif +#if defined(USE_NXDN) if (m_nxdnNetwork != nullptr && m_nxdnEnabled) m_nxdnNetwork->enable(true); - if (m_pocsagNetwork != nullptr && m_pocsagEnabled) +#endif +#if defined(USE_POCSAG) + if (m_pocsagNetwork != nullptr) m_pocsagNetwork->enable(true); +#endif +#if defined(USE_FM) if (m_fmNetwork != nullptr && m_fmEnabled) m_fmNetwork->enable(true); +#endif +#if defined(USE_DSTAR) if (m_dstar != nullptr && m_dstarEnabled) m_dstar->enable(true); +#endif +#if defined(USE_DMR) if (m_dmr != nullptr && m_dmrEnabled) m_dmr->enable(true); +#endif +#if defined(USE_YSF) if (m_ysf != nullptr && m_ysfEnabled) m_ysf->enable(true); +#endif +#if defined(USE_P25) if (m_p25 != nullptr && m_p25Enabled) m_p25->enable(true); +#endif +#if defined(USE_NXDN) if (m_nxdn != nullptr && m_nxdnEnabled) m_nxdn->enable(true); - if (m_pocsag != nullptr && m_pocsagEnabled) +#endif +#if defined(USE_POCSAG) + if (m_pocsag != nullptr) m_pocsag->enable(true); +#endif +#if defined(USE_FM) if (m_fm != nullptr && m_fmEnabled) m_fm->enable(true); +#endif +#if defined(USE_DMR) if (m_mode == MODE_DMR && m_duplex && m_modem->hasTX() && m_dmrEnabled) { m_modem->writeDMRStart(false); m_dmrTXTimer.stop(); } +#endif m_modem->setMode(MODE_IDLE); if (m_mode == MODE_ERROR) { m_modem->sendCWId(m_callsign); @@ -2278,18 +2749,15 @@ void CMMDVMHost::setMode(unsigned char mode) m_cwIdTimer.setTimeout(m_cwIdTime / 4U); m_cwIdTimer.start(); } - m_display->setIdle(); - if (mode == MODE_QUIT) - m_display->setQuit(); m_mode = MODE_IDLE; m_modeTimer.stop(); removeLockFile(); - LogMessage("Mode set to Idle"); + writeJSONMode("idle"); break; } } -void CMMDVMHost::createLockFile(const char* mode) const +void CMMDVMHost::createLockFile(const char* mode) const { if (m_lockFileEnabled) { FILE* fp = ::fopen(m_lockFileName.c_str(), "wt"); @@ -2300,147 +2768,217 @@ void CMMDVMHost::createLockFile(const char* mode) const } } -void CMMDVMHost::removeLockFile() const +void CMMDVMHost::removeLockFile() const { if (m_lockFileEnabled) ::remove(m_lockFileName.c_str()); } -void CMMDVMHost::remoteControl() +void CMMDVMHost::remoteControl(const std::string& commandString) { if (m_remoteControl == nullptr) return; - REMOTE_COMMAND command = m_remoteControl->getCommand(); + REMOTE_COMMAND command = m_remoteControl->getCommand(commandString); switch (command) { - case REMOTE_COMMAND::MODE_IDLE: - m_fixedMode = false; - setMode(MODE_IDLE); - break; - case REMOTE_COMMAND::MODE_LOCKOUT: - m_fixedMode = false; - setMode(MODE_LOCKOUT); - break; - case REMOTE_COMMAND::MODE_DSTAR: - if (m_dstar != nullptr) - processModeCommand(MODE_DSTAR, m_dstarRFModeHang); - break; - case REMOTE_COMMAND::MODE_DMR: - if (m_dmr != nullptr) - processModeCommand(MODE_DMR, m_dmrRFModeHang); - break; - case REMOTE_COMMAND::MODE_YSF: - if (m_ysf != nullptr) - processModeCommand(MODE_YSF, m_ysfRFModeHang); - break; - case REMOTE_COMMAND::MODE_P25: - if (m_p25 != nullptr) - processModeCommand(MODE_P25, m_p25RFModeHang); - break; - case REMOTE_COMMAND::MODE_NXDN: - if (m_nxdn != nullptr) - processModeCommand(MODE_NXDN, m_nxdnRFModeHang); - break; - case REMOTE_COMMAND::MODE_FM: - if (m_fmEnabled) - processModeCommand(MODE_FM, 0); - break; - case REMOTE_COMMAND::ENABLE_DSTAR: - processEnableModeCommand(MODE_DSTAR, (m_dstar != nullptr), m_dstarEnabled, true); - break; - case REMOTE_COMMAND::ENABLE_DMR: - processEnableModeCommand(MODE_DMR, (m_dmr != nullptr), m_dmrEnabled, true); - break; - case REMOTE_COMMAND::ENABLE_YSF: - processEnableModeCommand(MODE_YSF, (m_ysf != nullptr), m_ysfEnabled, true); - break; - case REMOTE_COMMAND::ENABLE_P25: - processEnableModeCommand(MODE_P25, (m_p25 != nullptr), m_p25Enabled, true); - break; - case REMOTE_COMMAND::ENABLE_NXDN: - processEnableModeCommand(MODE_NXDN, (m_nxdn != nullptr), m_nxdnEnabled, true); - break; - case REMOTE_COMMAND::ENABLE_FM: - processEnableModeCommand(MODE_FM, (m_fm != nullptr), m_fmEnabled, true); - break; - case REMOTE_COMMAND::DISABLE_DSTAR: - processEnableModeCommand(MODE_DSTAR, (m_dstar != nullptr), m_dstarEnabled, false); - break; - case REMOTE_COMMAND::DISABLE_DMR: - processEnableModeCommand(MODE_DMR, (m_dmr != nullptr), m_dmrEnabled, false); - break; - case REMOTE_COMMAND::DISABLE_YSF: - processEnableModeCommand(MODE_YSF, (m_ysf != nullptr), m_ysfEnabled, false); - break; - case REMOTE_COMMAND::DISABLE_P25: - processEnableModeCommand(MODE_P25, (m_p25), m_p25Enabled, false); - break; - case REMOTE_COMMAND::DISABLE_NXDN: - processEnableModeCommand(MODE_NXDN, (m_nxdn != nullptr), m_nxdnEnabled, false); - break; - case REMOTE_COMMAND::DISABLE_FM: - processEnableModeCommand(MODE_FM, (m_fm != nullptr), m_fmEnabled, false); - break; - case REMOTE_COMMAND::PAGE: - if (m_pocsag != nullptr) { - unsigned int ric = m_remoteControl->getArgUInt(0U); - std::string text; - for (unsigned int i = 1U; i < m_remoteControl->getArgCount(); i++) { - if (i > 1U) - text += " "; - text += m_remoteControl->getArgString(i); + case REMOTE_COMMAND::MODE_IDLE: + m_fixedMode = false; + setMode(MODE_IDLE); + break; + case REMOTE_COMMAND::MODE_LOCKOUT: + m_fixedMode = false; + setMode(MODE_LOCKOUT); + break; +#if defined(USE_DSTAR) + case REMOTE_COMMAND::MODE_DSTAR: + if (m_dstar != nullptr) + processModeCommand(MODE_DSTAR, m_dstarRFModeHang); + break; +#endif +#if defined(USE_DMR) + case REMOTE_COMMAND::MODE_DMR: + if (m_dmr != nullptr) + processModeCommand(MODE_DMR, m_dmrRFModeHang); + break; +#endif +#if defined(USE_YSF) + case REMOTE_COMMAND::MODE_YSF: + if (m_ysf != nullptr) + processModeCommand(MODE_YSF, m_ysfRFModeHang); + break; +#endif +#if defined(USE_P25) + case REMOTE_COMMAND::MODE_P25: + if (m_p25 != nullptr) + processModeCommand(MODE_P25, m_p25RFModeHang); + break; +#endif +#if defined(USE_NXDN) + case REMOTE_COMMAND::MODE_NXDN: + if (m_nxdn != nullptr) + processModeCommand(MODE_NXDN, m_nxdnRFModeHang); + break; +#endif +#if defined(USE_FM) + case REMOTE_COMMAND::MODE_FM: + if (m_fmEnabled) + processModeCommand(MODE_FM, 0); + break; +#endif +#if defined(USE_DSTAR) + case REMOTE_COMMAND::ENABLE_DSTAR: + if (m_dstar != nullptr && !m_dstarEnabled) + processEnableCommand(m_dstarEnabled, true); + if (m_dstarNetwork != nullptr) + m_dstarNetwork->enable(true); + break; +#endif +#if defined(USE_DMR) + case REMOTE_COMMAND::ENABLE_DMR: + if (m_dmr != nullptr && !m_dmrEnabled) + processEnableCommand(m_dmrEnabled, true); + if (m_dmrNetwork != nullptr) + m_dmrNetwork->enable(true); + break; +#endif +#if defined(USE_YSF) + case REMOTE_COMMAND::ENABLE_YSF: + if (m_ysf != nullptr && !m_ysfEnabled) + processEnableCommand(m_ysfEnabled, true); + if (m_ysfNetwork != nullptr) + m_ysfNetwork->enable(true); + break; +#endif +#if defined(USE_P25) + case REMOTE_COMMAND::ENABLE_P25: + if (m_p25 != nullptr && !m_p25Enabled) + processEnableCommand(m_p25Enabled, true); + if (m_p25Network != nullptr) + m_p25Network->enable(true); + break; +#endif +#if defined(USE_NXDN) + case REMOTE_COMMAND::ENABLE_NXDN: + if (m_nxdn != nullptr && !m_nxdnEnabled) + processEnableCommand(m_nxdnEnabled, true); + if (m_nxdnNetwork != nullptr) + m_nxdnNetwork->enable(true); + break; +#endif +#if defined(USE_FM) + case REMOTE_COMMAND::ENABLE_FM: + if (!m_fmEnabled) + processEnableCommand(m_fmEnabled, true); + break; +#endif +#if defined(USE_DSTAR) + case REMOTE_COMMAND::DISABLE_DSTAR: + if (m_dstar != nullptr && m_dstarEnabled) + processEnableCommand(m_dstarEnabled, false); + if (m_dstarNetwork != nullptr) + m_dstarNetwork->enable(false); + break; +#endif +#if defined(USE_DMR) + case REMOTE_COMMAND::DISABLE_DMR: + if (m_dmr != nullptr && m_dmrEnabled) + processEnableCommand(m_dmrEnabled, false); + if (m_dmrNetwork != nullptr) + m_dmrNetwork->enable(false); + break; +#endif +#if defined(USE_YSF) + case REMOTE_COMMAND::DISABLE_YSF: + if (m_ysf != nullptr && m_ysfEnabled) + processEnableCommand(m_ysfEnabled, false); + if (m_ysfNetwork != nullptr) + m_ysfNetwork->enable(false); + break; +#endif +#if defined(USE_P25) + case REMOTE_COMMAND::DISABLE_P25: + if (m_p25 != nullptr && m_p25Enabled) + processEnableCommand(m_p25Enabled, false); + if (m_p25Network != nullptr) + m_p25Network->enable(false); + break; +#endif +#if defined(USE_NXDN) + case REMOTE_COMMAND::DISABLE_NXDN: + if (m_nxdn != nullptr && m_nxdnEnabled) + processEnableCommand(m_nxdnEnabled, false); + if (m_nxdnNetwork != nullptr) + m_nxdnNetwork->enable(false); + break; +#endif +#if defined(USE_FM) + case REMOTE_COMMAND::DISABLE_FM: + if (m_fmEnabled) + processEnableCommand(m_fmEnabled, false); + break; +#endif +#if defined(USE_POCSAG) + case REMOTE_COMMAND::PAGE: + if (m_pocsag != nullptr) { + unsigned int ric = m_remoteControl->getArgUInt(0U); + std::string text; + for (unsigned int i = 1U; i < m_remoteControl->getArgCount(); i++) { + if (i > 1U) + text += " "; + text += m_remoteControl->getArgString(i); + } + m_pocsag->sendPage(ric, text); } - m_pocsag->sendPage(ric, text); - } - break; - case REMOTE_COMMAND::PAGE_BCD: - if (m_pocsag != nullptr) { - unsigned int ric = m_remoteControl->getArgUInt(0U); - std::string text; - for (unsigned int i = 1U; i < m_remoteControl->getArgCount(); i++) { - if (i > 1U) - text += " "; - text += m_remoteControl->getArgString(i); + break; + case REMOTE_COMMAND::PAGE_BCD: + if (m_pocsag != nullptr) { + unsigned int ric = m_remoteControl->getArgUInt(0U); + std::string text; + for (unsigned int i = 1U; i < m_remoteControl->getArgCount(); i++) { + if (i > 1U) + text += " "; + text += m_remoteControl->getArgString(i); + } + m_pocsag->sendPageBCD(ric, text); } - m_pocsag->sendPageBCD(ric, text); - } - break; - case REMOTE_COMMAND::PAGE_A1: - if (m_pocsag != nullptr) { - unsigned int ric = m_remoteControl->getArgUInt(0U); - m_pocsag->sendPageAlert1(ric); - } - break; - case REMOTE_COMMAND::PAGE_A2: - if (m_pocsag != nullptr) { - unsigned int ric = m_remoteControl->getArgUInt(0U); - std::string text; - for (unsigned int i = 1U; i < m_remoteControl->getArgCount(); i++) { - if (i > 1U) - text += " "; - text += m_remoteControl->getArgString(i); + break; + case REMOTE_COMMAND::PAGE_A1: + if (m_pocsag != nullptr) { + unsigned int ric = m_remoteControl->getArgUInt(0U); + m_pocsag->sendPageAlert1(ric); } - m_pocsag->sendPageAlert2(ric, text); - } - break; - case REMOTE_COMMAND::CW: - setMode(MODE_IDLE); // Force the modem to go idle so that we can send the CW text. - if (!m_modem->hasTX()) { - std::string cwtext; - for (unsigned int i = 0U; i < m_remoteControl->getArgCount(); i++) { - if (i > 0U) - cwtext += " "; - cwtext += m_remoteControl->getArgString(i); + break; + case REMOTE_COMMAND::PAGE_A2: + if (m_pocsag != nullptr) { + unsigned int ric = m_remoteControl->getArgUInt(0U); + std::string text; + for (unsigned int i = 1U; i < m_remoteControl->getArgCount(); i++) { + if (i > 1U) + text += " "; + text += m_remoteControl->getArgString(i); + } + m_pocsag->sendPageAlert2(ric, text); } - m_display->writeCW(); - m_modem->sendCWId(cwtext); - } - break; - case REMOTE_COMMAND::RELOAD: - m_reload = true; - break; - default: - break; + break; +#endif + case REMOTE_COMMAND::CW: + setMode(MODE_IDLE); // Force the modem to go idle so that we can send the CW text. + if (!m_modem->hasTX()) { + std::string cwtext; + for (unsigned int i = 0U; i < m_remoteControl->getArgCount(); i++) { + if (i > 0U) + cwtext += " "; + cwtext += m_remoteControl->getArgString(i); + } + writeJSONMode("CW"); + m_modem->sendCWId(cwtext); + } + break; + case REMOTE_COMMAND::RELOAD: + m_reload = true; + break; + default: + break; } } @@ -2462,21 +3000,46 @@ void CMMDVMHost::processModeCommand(unsigned char mode, unsigned int timeout) setMode(mode); } +void CMMDVMHost::processEnableCommand(bool& mode, bool enabled) +{ + LogDebug("Setting mode current=%s new=%s", mode ? "true" : "false", enabled ? "true" : "false"); + + mode = enabled; + + m_modem->setModeParams(m_dstarEnabled, m_dmrEnabled, m_ysfEnabled, m_p25Enabled, m_nxdnEnabled, m_pocsagEnabled, m_fmEnabled); + if (!m_modem->writeConfig()) + LogError("Cannot write Config to MMDVM"); +} + void CMMDVMHost::buildNetworkStatusString(std::string &str) { str = ""; - str += std::string("dstar:") + (((m_dstarNetwork == nullptr) || (m_dstarEnabled == false)) ? "n/a" : (m_dstarNetwork->isConnected() ? "conn" : "disc")); - str += std::string(" dmr:") + (((m_dmrNetwork == nullptr) || (m_dmrEnabled == false)) ? "n/a" : (m_dmrNetwork->isConnected() ? "conn" : "disc")); - str += std::string(" ysf:") + (((m_ysfNetwork == nullptr) || (m_ysfEnabled == false)) ? "n/a" : (m_ysfNetwork->isConnected() ? "conn" : "disc")); - str += std::string(" p25:") + (((m_p25Network == nullptr) || (m_p25Enabled == false)) ? "n/a" : (m_p25Network->isConnected() ? "conn" : "disc")); - str += std::string(" nxdn:") + (((m_nxdnNetwork == nullptr) || (m_nxdnEnabled == false)) ? "n/a" : (m_nxdnNetwork->isConnected() ? "conn" : "disc")); + +#if defined(USE_DSTAR) + str += std::string("dstar:") + (((m_dstarNetwork == nullptr) || !m_dstarEnabled) ? "n/a" : (m_dstarNetwork->isConnected() ? "conn" : "disc")); +#endif +#if defined(USE_DMR) + str += std::string(" dmr:") + (((m_dmrNetwork == nullptr) || !m_dmrEnabled) ? "n/a" : (m_dmrNetwork->isConnected() ? "conn" : "disc")); +#endif +#if defined(USE_YSF) + str += std::string(" ysf:") + (((m_ysfNetwork == nullptr) || !m_ysfEnabled) ? "n/a" : (m_ysfNetwork->isConnected() ? "conn" : "disc")); +#endif +#if defined(USE_P25) + str += std::string(" p25:") + (((m_p25Network == nullptr) || !m_p25Enabled) ? "n/a" : (m_p25Network->isConnected() ? "conn" : "disc")); +#endif +#if defined(USE_NXDN) + str += std::string(" nxdn:") + (((m_nxdnNetwork == nullptr) || !m_nxdnEnabled) ? "n/a" : (m_nxdnNetwork->isConnected() ? "conn" : "disc")); +#endif +#if defined(USE_FM) str += std::string(" fm:") + (m_fmEnabled ? "conn" : "n/a"); +#endif } void CMMDVMHost::buildNetworkHostsString(std::string &str) { str = ""; +#if defined(USE_DSTAR) std::string dstarReflector; if (m_dstarEnabled && (m_dstarNetwork != nullptr)) { unsigned char ref[DSTAR_LONG_CALLSIGN_LENGTH + 1]; @@ -2498,10 +3061,77 @@ void CMMDVMHost::buildNetworkHostsString(std::string &str) break; } } + str += std::string("dstar:\"") + ((dstarReflector.length() == 0) ? "NONE" : dstarReflector) + "\""; - str += std::string(" dmr:\"") + ((m_dmrEnabled && (m_dmrNetwork != nullptr)) ? m_conf.getDMRNetworkRemoteAddress() : "NONE") + "\""; +#endif +#if defined(USE_DMR) + str += std::string(" dmr:\"") + ((m_dmrEnabled && (m_dmrNetwork != nullptr)) ? m_conf.getDMRNetworkGatewayAddress() : "NONE") + "\""; +#endif +#if defined(USE_YSF) str += std::string(" ysf:\"") + ((m_ysfEnabled && (m_ysfNetwork != nullptr)) ? m_conf.getFusionNetworkGatewayAddress() : "NONE") + "\""; +#endif +#if defined(USE_P25) str += std::string(" p25:\"") + ((m_p25Enabled && (m_p25Network != nullptr)) ? m_conf.getP25GatewayAddress() : "NONE") + "\""; +#endif +#if defined(USE_NXDN) str += std::string(" nxdn:\"") + ((m_nxdnEnabled && (m_nxdnNetwork != nullptr)) ? m_conf.getNXDNGatewayAddress() : "NONE") + "\""; +#endif +#if defined(USE_FM) str += std::string(" fm:\"") + ((m_fmEnabled && (m_fmNetwork != nullptr)) ? m_conf.getFMGatewayAddress() : "NONE") + "\""; +#endif +} + +void CMMDVMHost::writeJSONMode(const std::string& mode) +{ + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = mode; + + WriteJSON("MMDVM", json); +} + +void CMMDVMHost::writeJSONMessage(const std::string& message) +{ + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["message"] = message; + + WriteJSON("MMDVM", json); +} + +void CMMDVMHost::writeSerial(const unsigned char* message, unsigned int length) +{ + assert(m_modem != nullptr); + assert(message != nullptr); + + if (length <= 252U) { + // Simple case of a short message, send it immediately to the modem + m_modem->writeSerialData(message, length); + } else { + ::memcpy(m_serialBuffer, message, length); + m_serialLength = length; + + m_modem->writeSerialData(m_serialBuffer, 252U); + m_serialStart = 252U; + + m_serialTimer.start(); + } +} + +void CMMDVMHost::onCommand(const unsigned char* command, unsigned int length) +{ + assert(host != nullptr); + assert(command != nullptr); + + host->remoteControl(std::string((char*)command, length)); +} + +void CMMDVMHost::onDisplay(const unsigned char* message, unsigned int length) +{ + assert(host != nullptr); + assert(message != nullptr); + + host->writeSerial(message, length); } diff --git a/MMDVMHost.h b/MMDVMHost.h index 2c9cbb7..b6c940a 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2015-2021,2023 by Jonathan Naylor G4KLX * Copyright (C) 2015-2021,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify @@ -36,99 +37,181 @@ #include "FMNetwork.h" #include "DMRLookup.h" #include "FMControl.h" -#include "Display.h" +#include "Defines.h" #include "Timer.h" #include "Modem.h" #include "Conf.h" #include - class CMMDVMHost { public: - CMMDVMHost(const std::string& confFile); - ~CMMDVMHost(); + CMMDVMHost(const std::string& confFile); + ~CMMDVMHost(); - int run(); + int run(); - void buildNetworkStatusString(std::string &str); - void buildNetworkHostsString(std::string &str); + void buildNetworkStatusString(std::string &str); + void buildNetworkHostsString(std::string &str); private: - CConf m_conf; - CModem* m_modem; - CDStarControl* m_dstar; - CDMRControl* m_dmr; - CYSFControl* m_ysf; - CP25Control* m_p25; - CNXDNControl* m_nxdn; - CPOCSAGControl* m_pocsag; - CFMControl* m_fm; - CDStarNetwork* m_dstarNetwork; - IDMRNetwork* m_dmrNetwork; - CYSFNetwork* m_ysfNetwork; - CP25Network* m_p25Network; - INXDNNetwork* m_nxdnNetwork; - CPOCSAGNetwork* m_pocsagNetwork; - CFMNetwork* m_fmNetwork; - CDisplay* m_display; - unsigned char m_mode; - unsigned int m_dstarRFModeHang; - unsigned int m_dmrRFModeHang; - unsigned int m_ysfRFModeHang; - unsigned int m_p25RFModeHang; - unsigned int m_nxdnRFModeHang; - unsigned int m_fmRFModeHang; - unsigned int m_dstarNetModeHang; - unsigned int m_dmrNetModeHang; - unsigned int m_ysfNetModeHang; - unsigned int m_p25NetModeHang; - unsigned int m_nxdnNetModeHang; - unsigned int m_pocsagNetModeHang; - unsigned int m_fmNetModeHang; - CTimer m_modeTimer; - CTimer m_dmrTXTimer; - CTimer m_cwIdTimer; - bool m_duplex; - unsigned int m_timeout; - bool m_dstarEnabled; - bool m_dmrEnabled; - bool m_ysfEnabled; - bool m_p25Enabled; - bool m_nxdnEnabled; - bool m_pocsagEnabled; - bool m_fmEnabled; - unsigned int m_cwIdTime; - CDMRLookup* m_dmrLookup; - CNXDNLookup* m_nxdnLookup; - std::string m_callsign; - unsigned int m_id; - std::string m_cwCallsign; - bool m_lockFileEnabled; - std::string m_lockFileName; - CRemoteControl* m_remoteControl; - bool m_fixedMode; + CConf m_conf; + CModem* m_modem; +#if defined(USE_DSTAR) + CDStarControl* m_dstar; +#endif +#if defined(USE_DMR) + CDMRControl* m_dmr; +#endif +#if defined(USE_YSF) + CYSFControl* m_ysf; +#endif +#if defined(USE_P25) + CP25Control* m_p25; +#endif +#if defined(USE_NXDN) + CNXDNControl* m_nxdn; +#endif +#if defined(USE_POCSAG) + CPOCSAGControl* m_pocsag; +#endif +#if defined(USE_FM) + CFMControl* m_fm; +#endif +#if defined(USE_DSTAR) + CDStarNetwork* m_dstarNetwork; +#endif +#if defined(USE_DMR) + CDMRNetwork* m_dmrNetwork; +#endif +#if defined(USE_YSF) + CYSFNetwork* m_ysfNetwork; +#endif +#if defined(USE_P25) + CP25Network* m_p25Network; +#endif +#if defined(USE_NXDN) + INXDNNetwork* m_nxdnNetwork; +#endif +#if defined(USE_POCSAG) + CPOCSAGNetwork* m_pocsagNetwork; +#endif +#if defined(USE_FM) + CFMNetwork* m_fmNetwork; +#endif + unsigned char m_mode; +#if defined(USE_DSTAR) + unsigned int m_dstarRFModeHang; +#endif +#if defined(USE_DMR) + unsigned int m_dmrRFModeHang; +#endif +#if defined(USE_YSF) + unsigned int m_ysfRFModeHang; +#endif +#if defined(USE_P25) + unsigned int m_p25RFModeHang; +#endif +#if defined(USE_NXDN) + unsigned int m_nxdnRFModeHang; +#endif +#if defined(USE_FM) + unsigned int m_fmRFModeHang; +#endif +#if defined(USE_DSTAR) + unsigned int m_dstarNetModeHang; +#endif +#if defined(USE_DMR) + unsigned int m_dmrNetModeHang; +#endif +#if defined(USE_YSF) + unsigned int m_ysfNetModeHang; +#endif +#if defined(USE_P25) + unsigned int m_p25NetModeHang; +#endif +#if defined(USE_NXDN) + unsigned int m_nxdnNetModeHang; +#endif +#if defined(USE_POCSAG) + unsigned int m_pocsagNetModeHang; +#endif +#if defined(USE_FM) + unsigned int m_fmNetModeHang; +#endif + CTimer m_modeTimer; +#if defined(USE_DMR) + CTimer m_dmrTXTimer; +#endif + CTimer m_cwIdTimer; + bool m_duplex; + unsigned int m_timeout; + bool m_dstarEnabled; + bool m_dmrEnabled; + bool m_ysfEnabled; + bool m_p25Enabled; + bool m_nxdnEnabled; + bool m_pocsagEnabled; + bool m_fmEnabled; + unsigned int m_cwIdTime; +#if defined(USE_DMR) || defined(USE_P25) + CDMRLookup* m_dmrLookup; +#endif +#if defined(USE_NXDN) + CNXDNLookup* m_nxdnLookup; +#endif + std::string m_callsign; + unsigned int m_id; + std::string m_cwCallsign; + bool m_lockFileEnabled; + std::string m_lockFileName; + CRemoteControl* m_remoteControl; + bool m_fixedMode; - void readParams(); - bool createModem(); - bool createDStarNetwork(); - bool createDMRNetwork(); - bool createYSFNetwork(); - bool createP25Network(); - bool createNXDNNetwork(); - bool createPOCSAGNetwork(); - bool createFMNetwork(); + CTimer m_serialTimer; + unsigned char* m_serialBuffer; + unsigned int m_serialStart; + unsigned int m_serialLength; - void remoteControl(); - void processModeCommand(unsigned char mode, unsigned int timeout); + void readParams(); + bool createModem(); +#if defined(USE_DSTAR) + bool createDStarNetwork(); +#endif +#if defined(USE_DMR) + bool createDMRNetwork(); +#endif +#if defined(USE_YSF) + bool createYSFNetwork(); +#endif +#if defined(USE_P25) + bool createP25Network(); +#endif +#if defined(USE_NXDN) + bool createNXDNNetwork(); +#endif +#if defined(USE_POCSAG) + bool createPOCSAGNetwork(); +#endif +#if defined(USE_FM) + bool createFMNetwork(); +#endif + void writeSerial(const unsigned char* message, unsigned int length); + void remoteControl(const std::string& commandString); + void processModeCommand(unsigned char mode, unsigned int timeout); + void processEnableCommand(bool& mode, bool enabled); - void setMode(unsigned char mode); - void enableModemMode(bool& mode, bool enabled); - void processEnableModeCommand(unsigned char mode, bool hasController, bool& modeEnabled, bool enableMode); + void setMode(unsigned char mode); - void createLockFile(const char* mode) const; - void removeLockFile() const; + void createLockFile(const char* mode) const; + void removeLockFile() const; + + void writeJSONMode(const std::string& mode); + void writeJSONMessage(const std::string& message); + + static void onDisplay(const unsigned char* message, unsigned int length); + static void onCommand(const unsigned char* command, unsigned int length); }; #endif diff --git a/MMDVMHost.sln b/MMDVMHost.sln index 4d9518c..fec801b 100644 --- a/MMDVMHost.sln +++ b/MMDVMHost.sln @@ -5,8 +5,6 @@ VisualStudioVersion = 15.0.28307.271 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MMDVMHost", "MMDVMHost.vcxproj", "{1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RemoteCommand", "RemoteCommand.vcxproj", "{5A61AB93-58BB-413A-BBD9-A26284CB37AE}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -23,14 +21,6 @@ Global {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x64.Build.0 = Release|x64 {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x86.ActiveCfg = Release|Win32 {1D34E8C1-CFA5-4D60-B509-9DB58DC4AE92}.Release|x86.Build.0 = Release|Win32 - {5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Debug|x64.ActiveCfg = Debug|x64 - {5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Debug|x64.Build.0 = Debug|x64 - {5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Debug|x86.ActiveCfg = Debug|Win32 - {5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Debug|x86.Build.0 = Debug|Win32 - {5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Release|x64.ActiveCfg = Release|x64 - {5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Release|x64.Build.0 = Release|x64 - {5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Release|x86.ActiveCfg = Release|Win32 - {5A61AB93-58BB-413A-BBD9-A26284CB37AE}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/MMDVMHost.vcxproj b/MMDVMHost.vcxproj index 27c0ff8..0bb9837 100644 --- a/MMDVMHost.vcxproj +++ b/MMDVMHost.vcxproj @@ -87,15 +87,14 @@ Level3 Disabled - HAVE_LOG_H;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - - + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + C:\Program Files\mosquitto\devel;C:\Program Files;%(AdditionalIncludeDirectories) Console true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - %(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;mosquitto.lib;%(AdditionalDependencies) + C:\Program Files\mosquitto\devel;%(AdditionalLibraryDirectories) @@ -104,15 +103,14 @@ Level3 Disabled - HAVE_LOG_H;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - - + _DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + C:\Program Files\mosquitto\devel;C:\Program Files;%(AdditionalIncludeDirectories) Console true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - %(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;mosquitto.lib;%(AdditionalDependencies) + C:\Program Files\mosquitto\devel;%(AdditionalLibraryDirectories) "$(ProjectDir)prebuild.cmd" $(ProjectDir) @@ -129,17 +127,16 @@ MaxSpeed true true - HAVE_LOG_H;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - - + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + C:\Program Files\mosquitto\devel;C:\Program Files;%(AdditionalIncludeDirectories) Console true true true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - %(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;mosquitto.lib;%(AdditionalDependencies) + C:\Program Files\mosquitto\devel;%(AdditionalLibraryDirectories) @@ -150,39 +147,34 @@ MaxSpeed true true - HAVE_LOG_H;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - - + NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + C:\Program Files\mosquitto\devel;C:\Program Files;%(AdditionalIncludeDirectories) Console true true true - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;%(AdditionalDependencies) - %(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;ws2_32.lib;mosquitto.lib;%(AdditionalDependencies) + C:\Program Files\mosquitto\devel;%(AdditionalLibraryDirectories) - - - - @@ -202,16 +194,12 @@ - - + - - - @@ -237,7 +225,6 @@ - @@ -249,11 +236,8 @@ - - - @@ -273,20 +257,16 @@ - - - - @@ -305,16 +285,12 @@ - - + - - - @@ -338,7 +314,6 @@ - @@ -347,11 +322,8 @@ - - - diff --git a/MMDVMHost.vcxproj.filters b/MMDVMHost.vcxproj.filters index 2670f71..db9235e 100644 --- a/MMDVMHost.vcxproj.filters +++ b/MMDVMHost.vcxproj.filters @@ -23,9 +23,6 @@ Header Files - - Header Files - Header Files @@ -56,9 +53,6 @@ Header Files - - Header Files - Header Files @@ -143,9 +137,6 @@ Header Files - - Header Files - Header Files @@ -194,21 +185,12 @@ Header Files - - Header Files - Header Files - - Header Files - Header Files - - Header Files - Header Files @@ -260,12 +242,6 @@ Header Files - - Header Files - - - Header Files - Header Files @@ -287,18 +263,6 @@ Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files @@ -314,7 +278,7 @@ Header Files - + Header Files @@ -334,9 +298,6 @@ Source Files - - Source Files - Source Files @@ -361,9 +322,6 @@ Source Files - - Source Files - Source Files @@ -439,9 +397,6 @@ Source Files - - Source Files - Source Files @@ -487,21 +442,12 @@ Source Files - - Source Files - Source Files - - Source Files - Source Files - - Source Files - Source Files @@ -547,12 +493,6 @@ Source Files - - Source Files - - - Source Files - Source Files @@ -574,18 +514,6 @@ Source Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - Source Files @@ -601,7 +529,7 @@ Source Files - + Source Files diff --git a/MQTTConnection.cpp b/MQTTConnection.cpp new file mode 100644 index 0000000..c4bb87d --- /dev/null +++ b/MQTTConnection.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2022,2023,2025 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "MQTTConnection.h" + +#include +#include +#include +#include + + +CMQTTConnection::CMQTTConnection(const std::string& host, unsigned short port, const std::string& name, const bool authEnabled, const std::string& username, const std::string& password, const std::vector>& subs, unsigned int keepalive, MQTT_QOS qos) : +m_host(host), +m_port(port), +m_name(name), +m_authEnabled(authEnabled), +m_username(username), +m_password(password), +m_subs(subs), +m_keepalive(keepalive), +m_qos(qos), +m_mosq(nullptr), +m_connected(false) +{ + assert(!host.empty()); + assert(port > 0U); + assert(!name.empty()); + assert(keepalive >= 5U); + + ::mosquitto_lib_init(); +} + +CMQTTConnection::~CMQTTConnection() +{ + ::mosquitto_lib_cleanup(); +} + +bool CMQTTConnection::open() +{ + char name[50U]; + ::sprintf(name, "MMDVMHost.%ld", ::time(nullptr)); + + ::fprintf(stdout, "MMDVMHost (%s) connecting to MQTT as %s\n", m_name.c_str(), name); + + m_mosq = ::mosquitto_new(name, true, this); + if (m_mosq == nullptr) { + ::fprintf(stderr, "MQTT Error newing: Out of memory.\n"); + return false; + } + + if (m_authEnabled) + ::mosquitto_username_pw_set(m_mosq, m_username.c_str(), m_password.c_str()); + + ::mosquitto_connect_callback_set(m_mosq, onConnect); + ::mosquitto_subscribe_callback_set(m_mosq, onSubscribe); + ::mosquitto_message_callback_set(m_mosq, onMessage); + ::mosquitto_disconnect_callback_set(m_mosq, onDisconnect); + + int rc = ::mosquitto_connect(m_mosq, m_host.c_str(), m_port, m_keepalive); + if (rc != MOSQ_ERR_SUCCESS) { + ::mosquitto_destroy(m_mosq); + m_mosq = nullptr; + ::fprintf(stderr, "MQTT Error connecting: %s\n", ::mosquitto_strerror(rc)); + return false; + } + + rc = ::mosquitto_loop_start(m_mosq); + if (rc != MOSQ_ERR_SUCCESS) { + ::mosquitto_disconnect(m_mosq); + ::mosquitto_destroy(m_mosq); + m_mosq = nullptr; + ::fprintf(stderr, "MQTT Error loop starting: %s\n", ::mosquitto_strerror(rc)); + return false; + } + + return true; +} + +bool CMQTTConnection::publish(const char* topic, const char* text) +{ + assert(topic != nullptr); + assert(text != nullptr); + + return publish(topic, (unsigned char*)text, (unsigned int)::strlen(text)); +} + +bool CMQTTConnection::publish(const char* topic, const std::string& text) +{ + assert(topic != nullptr); + + return publish(topic, (unsigned char*)text.c_str(), (unsigned int)text.size()); +} + +bool CMQTTConnection::publish(const char* topic, const unsigned char* data, unsigned int len) +{ + assert(topic != nullptr); + assert(data != nullptr); + + if (!m_connected) + return false; + + if (::strchr(topic, '/') == nullptr) { + char topicEx[100U]; + ::sprintf(topicEx, "%s/%s", m_name.c_str(), topic); + + int rc = ::mosquitto_publish(m_mosq, nullptr, topicEx, len, data, static_cast(m_qos), false); + if (rc != MOSQ_ERR_SUCCESS) { + ::fprintf(stderr, "MQTT Error publishing: %s\n", ::mosquitto_strerror(rc)); + return false; + } + } else { + int rc = ::mosquitto_publish(m_mosq, nullptr, topic, len, data, static_cast(m_qos), false); + if (rc != MOSQ_ERR_SUCCESS) { + ::fprintf(stderr, "MQTT Error publishing: %s\n", ::mosquitto_strerror(rc)); + return false; + } + } + + return true; +} + +void CMQTTConnection::close() +{ + if (m_mosq != nullptr) { + ::mosquitto_disconnect(m_mosq); + ::mosquitto_destroy(m_mosq); + m_mosq = nullptr; + } +} + +void CMQTTConnection::onConnect(mosquitto* mosq, void* obj, int rc) +{ + assert(mosq != nullptr); + assert(obj != nullptr); + + ::fprintf(stdout, "MQTT: on_connect: %s\n", ::mosquitto_connack_string(rc)); + if (rc != 0) { + ::mosquitto_disconnect(mosq); + return; + } + + CMQTTConnection* p = static_cast(obj); + p->m_connected = true; + + for (std::vector>::const_iterator it = p->m_subs.cbegin(); it != p->m_subs.cend(); ++it) { + std::string topic = (*it).first; + + if (topic.find_first_of('/') == std::string::npos) { + char topicEx[100U]; + ::sprintf(topicEx, "%s/%s", p->m_name.c_str(), topic.c_str()); + + rc = ::mosquitto_subscribe(mosq, nullptr, topicEx, static_cast(p->m_qos)); + if (rc != MOSQ_ERR_SUCCESS) { + ::fprintf(stderr, "MQTT: error subscribing to %s - %s\n", topicEx, ::mosquitto_strerror(rc)); + ::mosquitto_disconnect(mosq); + } + } else { + rc = ::mosquitto_subscribe(mosq, nullptr, topic.c_str(), static_cast(p->m_qos)); + if (rc != MOSQ_ERR_SUCCESS) { + ::fprintf(stderr, "MQTT: error subscribing to %s - %s\n", topic.c_str(), ::mosquitto_strerror(rc)); + ::mosquitto_disconnect(mosq); + } + } + } +} + +void CMQTTConnection::onSubscribe(mosquitto* mosq, void* obj, int mid, int qosCount, const int* grantedQOS) +{ + assert(mosq != nullptr); + assert(obj != nullptr); + assert(grantedQOS != nullptr); + + for (int i = 0; i < qosCount; i++) + ::fprintf(stdout, "MQTT: on_subscribe: %d:%d\n", i, grantedQOS[i]); +} + +void CMQTTConnection::onMessage(mosquitto* mosq, void* obj, const mosquitto_message* message) +{ + assert(mosq != nullptr); + assert(obj != nullptr); + assert(message != nullptr); + + CMQTTConnection* p = static_cast(obj); + + for (std::vector>::const_iterator it = p->m_subs.cbegin(); it != p->m_subs.cend(); ++it) { + std::string topic = (*it).first; + + char topicEx[100U]; + ::sprintf(topicEx, "%s/%s", p->m_name.c_str(), topic.c_str()); + + if (::strcmp(topicEx, message->topic) == 0) { + (*it).second((unsigned char*)message->payload, message->payloadlen); + break; + } + } +} + +void CMQTTConnection::onDisconnect(mosquitto* mosq, void* obj, int rc) +{ + assert(mosq != nullptr); + assert(obj != nullptr); + + ::fprintf(stdout, "MQTT: on_disconnect: %s\n", ::mosquitto_reason_string(rc)); + + CMQTTConnection* p = static_cast(obj); + p->m_connected = false; +} + diff --git a/MQTTConnection.h b/MQTTConnection.h new file mode 100644 index 0000000..9f718da --- /dev/null +++ b/MQTTConnection.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022,2023,2025 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(MQTTPUBLISHER_H) +#define MQTTPUBLISHER_H + +#include + +#include +#include + +enum class MQTT_QOS : int { + AT_MODE_ONCE = 0, + AT_LEAST_ONCE = 1, + EXACTLY_ONCE = 2 +}; + +class CMQTTConnection { +public: + CMQTTConnection(const std::string& host, unsigned short port, const std::string& name, const bool authEnabled, const std::string& username, const std::string& password, const std::vector>& subs, unsigned int keepalive, MQTT_QOS qos = MQTT_QOS::EXACTLY_ONCE); + ~CMQTTConnection(); + + bool open(); + + bool publish(const char* topic, const char* text); + bool publish(const char* topic, const std::string& text); + bool publish(const char* topic, const unsigned char* data, unsigned int len); + + void close(); + +private: + std::string m_host; + unsigned short m_port; + std::string m_name; + bool m_authEnabled; + std::string m_username; + std::string m_password; + std::vector> m_subs; + unsigned int m_keepalive; + MQTT_QOS m_qos; + mosquitto* m_mosq; + bool m_connected; + + static void onConnect(mosquitto* mosq, void* obj, int rc); + static void onSubscribe(mosquitto* mosq, void* obj, int mid, int qosCount, const int* grantedQOS); + static void onMessage(mosquitto* mosq, void* obj, const mosquitto_message* message); + static void onDisconnect(mosquitto* mosq, void* obj, int rc); +}; + +#endif + diff --git a/Makefile b/Makefile index 3eed7a8..ecbb135 100644 --- a/Makefile +++ b/Makefile @@ -1,39 +1,32 @@ -# This makefile is for all platforms, but doesn't include support for the HD44780, OLED, or PCF8574 displays on the Raspberry Pi. - -# If you have the resampler library installed, add -DHAS_SRC to the CFLAGS line, and -lsamplerate to the LIBS line. +# This makefile is for all platforms. CC = cc CXX = c++ -CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -I/usr/local/include -LIBS = -lpthread -lutil +CFLAGS = -g -O3 -Wall -std=c++0x -pthread -I/usr/local/include +LIBS = -lpthread -lutil -lsamplerate -lmosquitto LDFLAGS = -g -L/usr/local/lib OBJECTS = \ - AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \ - DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \ - DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \ - Hamming.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o MMDVMHost.o \ - Modem.o ModemPort.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullController.o NullDisplay.o NXDNAudio.o NXDNControl.o \ - NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o \ - NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o POCSAGControl.o \ - POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o SMeter.o StopWatch.o Sync.o SHA256.o TFTSurenoo.o Thread.o \ - Timer.o UARTController.o UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o + AMBEFEC.o BCH.o BPTC19696.o Conf.o CRC.o DMRAccessControl.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o DMREMB.o \ + DMREmbeddedData.o DMRFullLC.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o \ + DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o Hamming.o I2CController.o IIRDirectForm1Filter.o Log.o \ + MMDVMHost.o MQTTConnection.o Modem.o ModemPort.o Mutex.o NullController.o NXDNAudio.o \ + NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \ + NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o POCSAGControl.o \ + POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o StopWatch.o Sync.o Thread.o Timer.o UARTController.o \ + UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o -all: MMDVMHost RemoteCommand +all: MMDVMHost MMDVMHost: GitVersion.h $(OBJECTS) $(CXX) $(OBJECTS) $(LDFLAGS) $(LIBS) -o MMDVMHost -RemoteCommand: Log.o RemoteCommand.o UDPSocket.o - $(CXX) Log.o RemoteCommand.o UDPSocket.o $(LDFLAGS) $(LIBS) -o RemoteCommand - %.o: %.cpp $(CXX) $(CFLAGS) -c -o $@ $< .PHONY install: install: all install -m 755 MMDVMHost /usr/local/bin/ - install -m 755 RemoteCommand /usr/local/bin/ .PHONY install-service: install-service: install /etc/MMDVM.ini @@ -58,7 +51,7 @@ uninstall-service: @rm -f /lib/systemd/system/mmdvmhost.service || true clean: - $(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h + $(RM) MMDVMHost *.o *.d *.bak *~ GitVersion.h # Export the current git version if the index file exists, else 000... GitVersion.h: diff --git a/Makefile.Pi.Adafruit b/Makefile.Pi.Adafruit deleted file mode 100644 index 72f46cf..0000000 --- a/Makefile.Pi.Adafruit +++ /dev/null @@ -1,71 +0,0 @@ -# This makefile is for use with the Raspberry Pi when using an HD44780 compatible display. The wiringpi library is needed. -# Support for the Adafruit i2c 16 x 2 RGB LCD Pi Plate - -# If you have the resampler library installed, add -DHAS_SRC to the CFLAGS line, and -lsamplerate to the LIBS line. - -CC = cc -CXX = c++ -CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DHD44780 -DADAFRUIT_DISPLAY -I/usr/local/include -LIBS = -lwiringPi -lwiringPiDev -lpthread -lutil -LDFLAGS = -g -L/usr/local/lib - -OBJECTS = \ - AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \ - DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \ - DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \ - Hamming.o HD44780.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o \ - MMDVMHost.o Modem.o ModemPort.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullController.o NullDisplay.o NXDNAudio.o \ - NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \ - NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o \ - POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o SMeter.o StopWatch.o Sync.o SHA256.o \ - TFTSurenoo.o Thread.o Timer.o UARTController.o UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \ - YSFNetwork.o YSFPayload.o - -all: MMDVMHost RemoteCommand - -MMDVMHost: GitVersion.h $(OBJECTS) - $(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost - -RemoteCommand: Log.o RemoteCommand.o UDPSocket.o - $(CXX) Log.o RemoteCommand.o UDPSocket.o $(CFLAGS) $(LIBS) -o RemoteCommand - -%.o: %.cpp - $(CXX) $(CFLAGS) -c -o $@ $< - -.PHONY install: -install: all - install -m 755 MMDVMHost /usr/local/bin/ - install -m 755 RemoteCommand /usr/local/bin/ - -.PHONY install-service: -install-service: install /etc/MMDVM.ini - @useradd --user-group -M --system mmdvm --shell /bin/false || true - @usermod --groups dialout --append mmdvm || true - @mkdir /var/log/mmdvm || true - @chown mmdvm:mmdvm /var/log/mmdvm - @cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/ - @systemctl enable mmdvmhost.service - -/etc/MMDVM.ini: - @cp -n MMDVM.ini /etc/MMDVM.ini - @sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini - @sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini - @chown mmdvm:mmdvm /etc/MMDVM.ini - -.PHONY uninstall-service: -uninstall-service: - @systemctl stop mmdvmhost.service || true - @systemctl disable mmdvmhost.service || true - @rm -f /usr/local/bin/MMDVMHost || true - @rm -f /lib/systemd/system/mmdvmhost.service || true - -clean: - $(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h - -# Export the current git version if the index file exists, else 000... -GitVersion.h: -ifneq ("$(wildcard .git/index)","") - echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@ -else - echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@ -endif diff --git a/Makefile.Pi.HD44780 b/Makefile.Pi.HD44780 deleted file mode 100644 index 26755d2..0000000 --- a/Makefile.Pi.HD44780 +++ /dev/null @@ -1,70 +0,0 @@ -# This makefile is for use with the Raspberry Pi when using an HD44780 compatible display. The wiringpi library is needed. - -# If you have the resampler library installed, add -DHAS_SRC to the CFLAGS line, and -lsamplerate to the LIBS line. - -CC = cc -CXX = c++ -CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DHD44780 -I/usr/local/include -LIBS = -lwiringPi -lwiringPiDev -lpthread -lutil -LDFLAGS = -g -L/usr/local/lib - -OBJECTS = \ - AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \ - DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \ - DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \ - Hamming.o HD44780.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o \ - MMDVMHost.o Modem.o ModemPort.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullController.o NullDisplay.o NXDNAudio.o \ - NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \ - NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o \ - POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o SMeter.o StopWatch.o Sync.o SHA256.o \ - TFTSurenoo.o Thread.o Timer.o UARTController.o UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \ - YSFNetwork.o YSFPayload.o - -all: MMDVMHost RemoteCommand - -MMDVMHost: GitVersion.h $(OBJECTS) - $(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost - -RemoteCommand: Log.o RemoteCommand.o UDPSocket.o - $(CXX) Log.o RemoteCommand.o UDPSocket.o $(CFLAGS) $(LIBS) -o RemoteCommand - -%.o: %.cpp - $(CXX) $(CFLAGS) -c -o $@ $< - -.PHONY install: -install: all - install -m 755 MMDVMHost /usr/local/bin/ - install -m 755 RemoteCommand /usr/local/bin/ - -.PHONY install-service: -install-service: install /etc/MMDVM.ini - @useradd --user-group -M --system mmdvm --shell /bin/false || true - @usermod --groups dialout --append mmdvm || true - @mkdir /var/log/mmdvm || true - @chown mmdvm:mmdvm /var/log/mmdvm - @cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/ - @systemctl enable mmdvmhost.service - -/etc/MMDVM.ini: - @cp -n MMDVM.ini /etc/MMDVM.ini - @sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini - @sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini - @chown mmdvm:mmdvm /etc/MMDVM.ini - -.PHONY uninstall-service: -uninstall-service: - @systemctl stop mmdvmhost.service || true - @systemctl disable mmdvmhost.service || true - @rm -f /usr/local/bin/MMDVMHost || true - @rm -f /lib/systemd/system/mmdvmhost.service || true - -clean: - $(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h - -# Export the current git version if the index file exists, else 000... -GitVersion.h: -ifneq ("$(wildcard .git/index)","") - echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@ -else - echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@ -endif diff --git a/Makefile.Pi.I2C b/Makefile.Pi.I2C deleted file mode 100644 index 762b5f7..0000000 --- a/Makefile.Pi.I2C +++ /dev/null @@ -1,69 +0,0 @@ -# This makefile is for use with the Raspberry Pi. The wiringpi library is needed. - -# If you have the resampler library installed, add -DHAS_SRC to the CFLAGS line, and -lsamplerate to the LIBS line. - -CC = cc -CXX = c++ -CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DRASPBERRY_PI -I/usr/local/include -LIBS = -lwiringPi -lwiringPiDev -lpthread -lutil -LDFLAGS = -g -L/usr/local/lib - -OBJECTS = \ - AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \ - DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \ - DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \ - Hamming.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o MMDVMHost.o \ - Modem.o ModemPort.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullController.o NullDisplay.o NXDNAudio.o NXDNControl.o \ - NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o \ - NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o POCSAGControl.o \ - POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o SMeter.o StopWatch.o Sync.o SHA256.o TFTSurenoo.o Thread.o \ - Timer.o UARTController.o UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o YSFNetwork.o YSFPayload.o - -all: MMDVMHost RemoteCommand - -MMDVMHost: GitVersion.h $(OBJECTS) - $(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost - -RemoteCommand: Log.o RemoteCommand.o UDPSocket.o - $(CXX) Log.o RemoteCommand.o UDPSocket.o $(CFLAGS) $(LIBS) -o RemoteCommand - -%.o: %.cpp - $(CXX) $(CFLAGS) -c -o $@ $< - -.PHONY install: -install: all - install -m 755 MMDVMHost /usr/local/bin/ - install -m 755 RemoteCommand /usr/local/bin/ - -.PHONY install-service: -install-service: install /etc/MMDVM.ini - @useradd --user-group -M --system mmdvm --shell /bin/false || true - @usermod --groups dialout --append mmdvm || true - @mkdir /var/log/mmdvm || true - @chown mmdvm:mmdvm /var/log/mmdvm - @cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/ - @systemctl enable mmdvmhost.service - -/etc/MMDVM.ini: - @cp -n MMDVM.ini /etc/MMDVM.ini - @sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini - @sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini - @chown mmdvm:mmdvm /etc/MMDVM.ini - -.PHONY uninstall-service: -uninstall-service: - @systemctl stop mmdvmhost.service || true - @systemctl disable mmdvmhost.service || true - @rm -f /usr/local/bin/MMDVMHost || true - @rm -f /lib/systemd/system/mmdvmhost.service || true - -clean: - $(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h - -# Export the current git version if the index file exists, else 000... -GitVersion.h: -ifneq ("$(wildcard .git/index)","") - echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@ -else - echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@ -endif diff --git a/Makefile.Pi.OLED b/Makefile.Pi.OLED deleted file mode 100644 index 3e96989..0000000 --- a/Makefile.Pi.OLED +++ /dev/null @@ -1,75 +0,0 @@ -# This makefile is for use with the Raspberry Pi when using an OLED display. The wiringpi library is not needed. - -# If you have the resampler library installed, add -DHAS_SRC to the CFLAGS line, and -lsamplerate to the LIBS line. - -CC = cc -CXX = c++ -CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DOLED -I/usr/local/include -LIBS = -lArduiPi_OLED -lpthread -lutil - -# If you use NetBSD, add following CFLAGS -#CFLAGS += -L/usr/local/lib -Wl,-rpath=/usr/local/lib - -LDFLAGS = -g -L/usr/local/lib - -OBJECTS = \ - AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \ - DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \ - DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \ - Hamming.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o MMDVMHost.o \ - Modem.o ModemPort.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullController.o NullDisplay.o NXDNAudio.o NXDNControl.o \ - NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o NXDNSACCH.o \ - NXDNUDCH.o OLED.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o \ - POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o SMeter.o StopWatch.o Sync.o SHA256.o \ - TFTSurenoo.o Thread.o Timer.o UARTController.o UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \ - YSFNetwork.o YSFPayload.o - -all: MMDVMHost RemoteCommand - -MMDVMHost: GitVersion.h $(OBJECTS) - $(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost - -RemoteCommand: Log.o RemoteCommand.o UDPSocket.o - $(CXX) Log.o RemoteCommand.o UDPSocket.o $(CFLAGS) $(LIBS) -o RemoteCommand - -%.o: %.cpp - $(CXX) $(CFLAGS) -c -o $@ $< - -.PHONY install: -install: all - install -m 755 MMDVMHost /usr/local/bin/ - install -m 755 RemoteCommand /usr/local/bin/ - -.PHONY install-service: -install-service: install /etc/MMDVM.ini - @useradd --user-group -M --system mmdvm --shell /bin/false || true - @usermod --groups dialout --append mmdvm || true - @mkdir /var/log/mmdvm || true - @chown mmdvm:mmdvm /var/log/mmdvm - @cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/ - @systemctl enable mmdvmhost.service - -/etc/MMDVM.ini: - @cp -n MMDVM.ini /etc/MMDVM.ini - @sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini - @sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini - @chown mmdvm:mmdvm /etc/MMDVM.ini - -.PHONY uninstall-service: -uninstall-service: - @systemctl stop mmdvmhost.service || true - @systemctl disable mmdvmhost.service || true - @rm -f /usr/local/bin/MMDVMHost || true - @rm -f /lib/systemd/system/mmdvmhost.service || true - -clean: - $(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h - -# Export the current git version if the index file exists, else 000... -GitVersion.h: -ifneq ("$(wildcard .git/index)","") - echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@ -else - echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@ -endif - diff --git a/Makefile.Pi.PCF8574 b/Makefile.Pi.PCF8574 deleted file mode 100644 index 7a86500..0000000 --- a/Makefile.Pi.PCF8574 +++ /dev/null @@ -1,71 +0,0 @@ -# This makefile is for use with the Raspberry Pi when using an HD44780 compatible display. The wiringpi library is needed. -# Support for the HD44780 connected via a PCF8574 8-bit GPIO expander IC - -# If you have the resampler library installed, add -DHAS_SRC to the CFLAGS line, and -lsamplerate to the LIBS line. - -CC = cc -CXX = c++ -CFLAGS = -g -O3 -Wall -std=c++0x -pthread -DHAVE_LOG_H -DHD44780 -DPCF8574_DISPLAY -I/usr/local/include -LIBS = -lwiringPi -lwiringPiDev -lpthread -lutil -LDFLAGS = -g -L/usr/local/lib - -OBJECTS = \ - AMBEFEC.o BCH.o BPTC19696.o CASTInfo.o Conf.o CRC.o Display.o DMRControl.o DMRCSBK.o DMRData.o DMRDataHeader.o \ - DMRDirectNetwork.o DMREMB.o DMREmbeddedData.o DMRFullLC.o DMRGatewayNetwork.o DMRLookup.o DMRLC.o DMRNetwork.o DMRShortLC.o DMRSlot.o DMRSlotType.o \ - DMRAccessControl.o DMRTA.o DMRTrellis.o DStarControl.o DStarHeader.o DStarNetwork.o DStarSlowData.o FMControl.o FMNetwork.o Golay2087.o Golay24128.o \ - Hamming.o HD44780.o I2CController.o IIRDirectForm1Filter.o LCDproc.o Log.o \ - MMDVMHost.o Modem.o ModemPort.o ModemSerialPort.o Mutex.o NetworkInfo.o Nextion.o NullController.o NullDisplay.o NXDNAudio.o \ - NXDNControl.o NXDNConvolution.o NXDNCRC.o NXDNFACCH1.o NXDNIcomNetwork.o NXDNKenwoodNetwork.o NXDNLayer3.o NXDNLICH.o NXDNLookup.o NXDNNetwork.o \ - NXDNSACCH.o NXDNUDCH.o P25Audio.o P25Control.o P25Data.o P25LowSpeedData.o P25Network.o P25NID.o P25Trellis.o P25Utils.o PseudoTTYController.o \ - POCSAGControl.o POCSAGNetwork.o QR1676.o RemoteControl.o RS129.o RS634717.o RSSIInterpolator.o SerialPort.o SMeter.o StopWatch.o Sync.o SHA256.o \ - TFTSurenoo.o Thread.o Timer.o UARTController.o UDPController.o UDPSocket.o UserDB.o UserDBentry.o Utils.o YSFControl.o YSFConvolution.o YSFFICH.o \ - YSFNetwork.o YSFPayload.o - -all: MMDVMHost RemoteCommand - -MMDVMHost: GitVersion.h $(OBJECTS) - $(CXX) $(OBJECTS) $(CFLAGS) $(LIBS) -o MMDVMHost - -RemoteCommand: Log.o RemoteCommand.o UDPSocket.o - $(CXX) Log.o RemoteCommand.o UDPSocket.o $(CFLAGS) $(LIBS) -o RemoteCommand - -%.o: %.cpp - $(CXX) $(CFLAGS) -c -o $@ $< - -.PHONY install: -install: all - install -m 755 MMDVMHost /usr/local/bin/ - install -m 755 RemoteCommand /usr/local/bin/ - -.PHONY install-service: -install-service: install /etc/MMDVM.ini - @useradd --user-group -M --system mmdvm --shell /bin/false || true - @usermod --groups dialout --append mmdvm || true - @mkdir /var/log/mmdvm || true - @chown mmdvm:mmdvm /var/log/mmdvm - @cp ./linux/systemd/mmdvmhost.service /lib/systemd/system/ - @systemctl enable mmdvmhost.service - -/etc/MMDVM.ini: - @cp -n MMDVM.ini /etc/MMDVM.ini - @sed -i 's/FilePath=./FilePath=\/var\/log\/mmdvm\//' /etc/MMDVM.ini - @sed -i 's/Daemon=0/Daemon=1/' /etc/MMDVM.ini - @chown mmdvm:mmdvm /etc/MMDVM.ini - -.PHONY uninstall-service: -uninstall-service: - @systemctl stop mmdvmhost.service || true - @systemctl disable mmdvmhost.service || true - @rm -f /usr/local/bin/MMDVMHost || true - @rm -f /lib/systemd/system/mmdvmhost.service || true - -clean: - $(RM) MMDVMHost RemoteCommand *.o *.d *.bak *~ GitVersion.h - -# Export the current git version if the index file exists, else 000... -GitVersion.h: -ifneq ("$(wildcard .git/index)","") - echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@ -else - echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@ -endif diff --git a/Modem.cpp b/Modem.cpp index 5838c5c..efc896d 100644 --- a/Modem.cpp +++ b/Modem.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2018,2020,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2011-2018,2020,2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -49,11 +49,14 @@ const unsigned char MMDVM_SET_FREQ = 0x04U; const unsigned char MMDVM_SEND_CWID = 0x0AU; +#if defined(USE_DSTAR) const unsigned char MMDVM_DSTAR_HEADER = 0x10U; const unsigned char MMDVM_DSTAR_DATA = 0x11U; const unsigned char MMDVM_DSTAR_LOST = 0x12U; const unsigned char MMDVM_DSTAR_EOT = 0x13U; +#endif +#if defined(USE_DMR) const unsigned char MMDVM_DMR_DATA1 = 0x18U; const unsigned char MMDVM_DMR_LOST1 = 0x19U; const unsigned char MMDVM_DMR_DATA2 = 0x1AU; @@ -61,26 +64,38 @@ const unsigned char MMDVM_DMR_LOST2 = 0x1BU; const unsigned char MMDVM_DMR_SHORTLC = 0x1CU; const unsigned char MMDVM_DMR_START = 0x1DU; const unsigned char MMDVM_DMR_ABORT = 0x1EU; +#endif +#if defined(USE_YSF) const unsigned char MMDVM_YSF_DATA = 0x20U; const unsigned char MMDVM_YSF_LOST = 0x21U; +#endif +#if defined(USE_P25) const unsigned char MMDVM_P25_HDR = 0x30U; const unsigned char MMDVM_P25_LDU = 0x31U; const unsigned char MMDVM_P25_LOST = 0x32U; +#endif +#if defined(USE_NXDN) const unsigned char MMDVM_NXDN_DATA = 0x40U; const unsigned char MMDVM_NXDN_LOST = 0x41U; +#endif +#if defined(USE_POCSAG) const unsigned char MMDVM_POCSAG_DATA = 0x50U; +#endif +#if defined(USE_FM) const unsigned char MMDVM_FM_PARAMS1 = 0x60U; const unsigned char MMDVM_FM_PARAMS2 = 0x61U; const unsigned char MMDVM_FM_PARAMS3 = 0x62U; const unsigned char MMDVM_FM_PARAMS4 = 0x63U; const unsigned char MMDVM_FM_DATA = 0x65U; -const unsigned char MMDVM_FM_CONTROL = 0x66U; +const unsigned char MMDVM_FM_STATUS = 0x66U; const unsigned char MMDVM_FM_EOT = 0x67U; +const unsigned char MMDVM_FM_RSSI = 0x68U; +#endif const unsigned char MMDVM_ACK = 0x70U; const unsigned char MMDVM_NAK = 0x7FU; @@ -109,43 +124,82 @@ const unsigned char CAP1_NXDN = 0x10U; const unsigned char CAP1_FM = 0x40U; const unsigned char CAP2_POCSAG = 0x01U; - CModem::CModem(bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug) : m_protocolVersion(0U), +#if defined(USE_DMR) m_dmrColorCode(0U), +#endif +#if defined(USE_YSF) m_ysfLoDev(false), m_ysfTXHang(4U), +#endif +#if defined(USE_P25) m_p25TXHang(5U), +#endif +#if defined(USE_NXDN) m_nxdnTXHang(5U), +#endif m_duplex(duplex), m_rxInvert(rxInvert), m_txInvert(txInvert), m_pttInvert(pttInvert), m_txDelay(txDelay), +#if defined(USE_DMR) m_dmrDelay(dmrDelay), +#endif m_rxLevel(0.0F), m_cwIdTXLevel(0.0F), +#if defined(USE_DSTAR) m_dstarTXLevel(0.0F), +#endif +#if defined(USE_DMR) m_dmrTXLevel(0.0F), +#endif +#if defined(USE_YSF) m_ysfTXLevel(0.0F), +#endif +#if defined(USE_P25) m_p25TXLevel(0.0F), +#endif +#if defined(USE_NXDN) m_nxdnTXLevel(0.0F), +#endif +#if defined(USE_POCSAG) m_pocsagTXLevel(0.0F), +#endif +#if defined(USE_FM) m_fmTXLevel(0.0F), +#endif m_rfLevel(0.0F), m_useCOSAsLockout(useCOSAsLockout), m_trace(trace), m_debug(debug), m_rxFrequency(0U), m_txFrequency(0U), +#if defined(USE_POCSAG) m_pocsagFrequency(0U), +#endif +#if defined(USE_DSTAR) m_dstarEnabled(false), +#endif +#if defined(USE_DMR) m_dmrEnabled(false), +#endif +#if defined(USE_YSF) m_ysfEnabled(false), +#endif +#if defined(USE_P25) m_p25Enabled(false), +#endif +#if defined(USE_NXDN) m_nxdnEnabled(false), +#endif +#if defined(USE_POCSAG) m_pocsagEnabled(false), +#endif +#if defined(USE_FM) m_fmEnabled(false), +#endif m_rxDCOffset(0), m_txDCOffset(0), m_port(nullptr), @@ -154,21 +208,35 @@ m_length(0U), m_offset(0U), m_state(SERIAL_STATE::START), m_type(0U), +#if defined(USE_DSTAR) m_rxDStarData(1000U, "Modem RX D-Star"), m_txDStarData(1000U, "Modem TX D-Star"), +#endif +#if defined(USE_DMR) m_rxDMRData1(1000U, "Modem RX DMR1"), m_rxDMRData2(1000U, "Modem RX DMR2"), m_txDMRData1(1000U, "Modem TX DMR1"), m_txDMRData2(1000U, "Modem TX DMR2"), +#endif +#if defined(USE_YSF) m_rxYSFData(1000U, "Modem RX YSF"), m_txYSFData(1000U, "Modem TX YSF"), +#endif +#if defined(USE_P25) m_rxP25Data(1000U, "Modem RX P25"), m_txP25Data(1000U, "Modem TX P25"), +#endif +#if defined(USE_NXDN) m_rxNXDNData(1000U, "Modem RX NXDN"), m_txNXDNData(1000U, "Modem TX NXDN"), +#endif +#if defined(USE_POCSAG) m_txPOCSAGData(1000U, "Modem TX POCSAG"), +#endif +#if defined(USE_FM) m_rxFMData(5000U, "Modem RX FM"), m_txFMData(5000U, "Modem TX FM"), +#endif m_rxSerialData(1000U, "Modem RX Serial"), m_txSerialData(1000U, "Modem TX Serial"), m_rxTransparentData(1000U, "Modem RX Transparent"), @@ -177,20 +245,35 @@ m_sendTransparentDataFrameType(0U), m_statusTimer(1000U, 0U, 250U), m_inactivityTimer(1000U, 2U), m_playoutTimer(1000U, 0U, 10U), +#if defined(USE_DSTAR) m_dstarSpace(0U), +#endif +#if defined(USE_DMR) m_dmrSpace1(0U), m_dmrSpace2(0U), +#endif +#if defined(USE_YSF) m_ysfSpace(0U), +#endif +#if defined(USE_P25) m_p25Space(0U), +#endif +#if defined(USE_NXDN) m_nxdnSpace(0U), +#endif +#if defined(USE_POCSAG) m_pocsagSpace(0U), +#endif +#if defined(USE_FM) m_fmSpace(0U), +#endif m_tx(false), m_cd(false), m_lockout(false), m_error(false), m_mode(MODE_IDLE), m_hwType(HW_TYPE::UNKNOWN), +#if defined(USE_FM) m_fmCallsign(), m_fmCallsignSpeed(20U), m_fmCallsignFrequency(1000U), @@ -226,6 +309,7 @@ m_fmRFAudioBoost(1U), m_fmExtAudioBoost(1U), m_fmMaxDevLevel(90.0F), m_fmExtEnable(false), +#endif m_capabilities1(0x00U), m_capabilities2(0x00U), m_serialDataLen(0U) @@ -253,55 +337,93 @@ void CModem::setRFParams(unsigned int rxFrequency, int rxOffset, unsigned int tx m_txDCOffset = txDCOffset; m_rxDCOffset = rxDCOffset; m_rfLevel = rfLevel; +#if defined(USE_POCSAG) m_pocsagFrequency = pocsagFrequency + txOffset; +#endif } void CModem::setModeParams(bool dstarEnabled, bool dmrEnabled, bool ysfEnabled, bool p25Enabled, bool nxdnEnabled, bool pocsagEnabled, bool fmEnabled) { +#if defined(USE_DSTAR) m_dstarEnabled = dstarEnabled; +#endif +#if defined(USE_DMR) m_dmrEnabled = dmrEnabled; +#endif +#if defined(USE_YSF) m_ysfEnabled = ysfEnabled; +#endif +#if defined(USE_P25) m_p25Enabled = p25Enabled; +#endif +#if defined(USE_NXDN) m_nxdnEnabled = nxdnEnabled; +#endif +#if defined(USE_POCSAG) m_pocsagEnabled = pocsagEnabled; +#endif +#if defined(USE_FM) m_fmEnabled = fmEnabled; +#endif } void CModem::setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagTXLevel, float fmTXLevel) { m_rxLevel = rxLevel; m_cwIdTXLevel = cwIdTXLevel; +#if defined(USE_DSTAR) m_dstarTXLevel = dstarTXLevel; +#endif +#if defined(USE_DMR) m_dmrTXLevel = dmrTXLevel; +#endif +#if defined(USE_YSF) m_ysfTXLevel = ysfTXLevel; +#endif +#if defined(USE_P25) m_p25TXLevel = p25TXLevel; +#endif +#if defined(USE_NXDN) m_nxdnTXLevel = nxdnTXLevel; +#endif +#if defined(USE_POCSAG) m_pocsagTXLevel = pocsagTXLevel; +#endif +#if defined(USE_FM) m_fmTXLevel = fmTXLevel; +#endif } +#if defined(USE_DMR) void CModem::setDMRParams(unsigned int colorCode) { assert(colorCode < 16U); m_dmrColorCode = colorCode; } +#endif +#if defined(USE_YSF) void CModem::setYSFParams(bool loDev, unsigned int txHang) { m_ysfLoDev = loDev; m_ysfTXHang = txHang; } +#endif +#if defined(USE_P25) void CModem::setP25Params(unsigned int txHang) { m_p25TXHang = txHang; } +#endif +#if defined(USE_NXDN) void CModem::setNXDNParams(unsigned int txHang) { m_nxdnTXHang = txHang; } +#endif void CModem::setTransparentDataParams(unsigned int sendFrameType) { @@ -344,6 +466,7 @@ bool CModem::open() return false; } +#if defined(USE_FM) if (m_fmEnabled) { ret = setFMCallsignParams(); if (!ret) { @@ -379,6 +502,7 @@ bool CModem::open() } } } +#endif m_statusTimer.start(); @@ -419,6 +543,7 @@ void CModem::clock(unsigned int ms) } else { // type == OK switch (m_type) { +#if defined(USE_DSTAR) case MMDVM_DSTAR_HEADER: { if (m_trace) CUtils::dump(1U, "RX D-Star Header", m_buffer, m_length); @@ -470,7 +595,9 @@ void CModem::clock(unsigned int ms) m_rxDStarData.addData(&data, 1U); } break; +#endif +#if defined(USE_DMR) case MMDVM_DMR_DATA1: { if (m_trace) CUtils::dump(1U, "RX DMR Data 1", m_buffer, m_length); @@ -528,7 +655,9 @@ void CModem::clock(unsigned int ms) m_rxDMRData2.addData(&data, 1U); } break; +#endif +#if defined(USE_YSF) case MMDVM_YSF_DATA: { if (m_trace) CUtils::dump(1U, "RX YSF Data", m_buffer, m_length); @@ -554,7 +683,9 @@ void CModem::clock(unsigned int ms) m_rxYSFData.addData(&data, 1U); } break; +#endif +#if defined(USE_P25) case MMDVM_P25_HDR: { if (m_trace) CUtils::dump(1U, "RX P25 Header", m_buffer, m_length); @@ -594,7 +725,9 @@ void CModem::clock(unsigned int ms) m_rxP25Data.addData(&data, 1U); } break; +#endif +#if defined(USE_NXDN) case MMDVM_NXDN_DATA: { if (m_trace) CUtils::dump(1U, "RX NXDN Data", m_buffer, m_length); @@ -620,7 +753,9 @@ void CModem::clock(unsigned int ms) m_rxNXDNData.addData(&data, 1U); } break; +#endif +#if defined(USE_FM) case MMDVM_FM_DATA: { if (m_trace) CUtils::dump(1U, "RX FM Data", m_buffer, m_length); @@ -635,14 +770,14 @@ void CModem::clock(unsigned int ms) } break; - case MMDVM_FM_CONTROL: { + case MMDVM_FM_STATUS: { if (m_trace) - CUtils::dump(1U, "RX FM Control", m_buffer, m_length); + CUtils::dump(1U, "RX FM Status", m_buffer, m_length); unsigned int data1 = m_length - m_offset + 1U; m_rxFMData.addData((unsigned char*)&data1, sizeof(unsigned int)); - unsigned char data2= TAG_HEADER; + unsigned char data2 = TAG_HEADER; m_rxFMData.addData(&data2, 1U); m_rxFMData.addData(m_buffer + m_offset, m_length - m_offset); @@ -663,6 +798,21 @@ void CModem::clock(unsigned int ms) } break; + case MMDVM_FM_RSSI: { + if(m_trace) + CUtils::dump(1U, "RX FM RSSI", m_buffer, m_length); + + unsigned int data1 = m_length - m_offset + 1U; + m_rxFMData.addData((unsigned char*)&data1, sizeof(unsigned int)); + + unsigned char data2 = TAG_RSSI; + m_rxFMData.addData(&data2, 1U); + + m_rxFMData.addData(m_buffer + m_offset, m_length - m_offset); + } + break; +#endif + case MMDVM_GET_STATUS: // if (m_trace) // CUtils::dump(1U, "GET_STATUS", m_buffer, m_length); @@ -687,23 +837,41 @@ void CModem::clock(unsigned int ms) LogError("MMDVM DAC levels have overflowed"); m_cd = (m_buffer[m_offset + 2U] & 0x40U) == 0x40U; +#if defined(USE_P25) m_p25Space = 0U; +#endif +#if defined(USE_NXDN) m_nxdnSpace = 0U; +#endif +#if defined(USE_POCSAG) m_pocsagSpace = 0U; +#endif +#if defined(USE_FM) m_fmSpace = 0U; - +#endif +#if defined(USE_DSTAR) m_dstarSpace = m_buffer[m_offset + 3U]; +#endif +#if defined(USE_DMR) m_dmrSpace1 = m_buffer[m_offset + 4U]; m_dmrSpace2 = m_buffer[m_offset + 5U]; +#endif +#if defined(USE_YSF) m_ysfSpace = m_buffer[m_offset + 6U]; - +#endif // The following depend on the version of the firmware +#if defined(USE_P25) if (m_length > (m_offset + 7U)) m_p25Space = m_buffer[m_offset + 7U]; +#endif +#if defined(USE_NXDN) if (m_length > (m_offset + 8U)) m_nxdnSpace = m_buffer[m_offset + 8U]; +#endif +#if defined(USE_POCSAG) if (m_length > (m_offset + 9U)) m_pocsagSpace = m_buffer[m_offset + 9U]; +#endif } break; @@ -726,26 +894,54 @@ void CModem::clock(unsigned int ms) LogError("MMDVM DAC levels have overflowed"); m_cd = (m_buffer[m_offset + 1U] & 0x40U) == 0x40U; +#if defined(USE_DSTAR) m_dstarSpace = m_buffer[m_offset + 3U]; +#endif +#if defined(USE_DMR) m_dmrSpace1 = m_buffer[m_offset + 4U]; m_dmrSpace2 = m_buffer[m_offset + 5U]; +#endif +#if defined(USE_YSF) m_ysfSpace = m_buffer[m_offset + 6U]; +#endif +#if defined(USE_P25) m_p25Space = m_buffer[m_offset + 7U]; +#endif +#if defined(USE_NXDN) m_nxdnSpace = m_buffer[m_offset + 8U]; +#endif +#if defined(USE_FM) m_fmSpace = m_buffer[m_offset + 10U]; +#endif +#if defined(USE_POCSAG) m_pocsagSpace = m_buffer[m_offset + 11U]; +#endif } break; default: +#if defined(USE_DSTAR) m_dstarSpace = 0U; +#endif +#if defined(USE_DMR) m_dmrSpace1 = 0U; m_dmrSpace2 = 0U; +#endif +#if defined(USE_YSF) m_ysfSpace = 0U; +#endif +#if defined(USE_P25) m_p25Space = 0U; +#endif +#if defined(USE_NXDN) m_nxdnSpace = 0U; +#endif +#if defined(USE_POCSAG) m_pocsagSpace = 0U; +#endif +#if defined(USE_FM) m_fmSpace = 0U; +#endif break; } @@ -784,19 +980,15 @@ void CModem::clock(unsigned int ms) printDebug(); break; - //case MMDVM_SERIAL_DATA: - // if (m_trace) - // CUtils::dump(1U, "RX Serial Data", m_buffer, m_length); - // m_rxSerialData.addData(m_buffer + m_offset, m_length - m_offset); - // break; + case MMDVM_SERIAL_DATA: { + if (m_trace) + CUtils::dump(1U, "RX Serial Data", m_buffer, m_length); - // Code changed to bring the Nextion Button Pushes back to life - case MMDVM_SERIAL_DATA: - if (m_trace) - CUtils::dump(1U, "RX Serial Data", m_buffer, m_length); - - // Original: Add to serial data buffer - m_rxSerialData.addData(m_buffer + m_offset, m_length - m_offset); + unsigned char data = m_length - m_offset; + m_rxSerialData.addData(&data, 1U); + + m_rxSerialData.addData(m_buffer + m_offset, m_length - m_offset); + } // NEW: Buffer serial data and forward complete commands as transparent data { @@ -853,6 +1045,7 @@ void CModem::clock(unsigned int ms) if (!m_playoutTimer.hasExpired()) return; +#if defined(USE_DSTAR) if (m_dstarSpace > 1U && !m_txDStarData.isEmpty()) { unsigned char buffer[4U]; m_txDStarData.peek(buffer, 4U); @@ -889,7 +1082,9 @@ void CModem::clock(unsigned int ms) m_playoutTimer.start(); } } +#endif +#if defined(USE_DMR) if (m_dmrSpace1 > 1U && !m_txDMRData1.isEmpty()) { unsigned char len = 0U; m_txDMRData1.getData(&len, 1U); @@ -923,7 +1118,9 @@ void CModem::clock(unsigned int ms) m_dmrSpace2--; } +#endif +#if defined(USE_YSF) if (m_ysfSpace > 1U && !m_txYSFData.isEmpty()) { unsigned char len = 0U; m_txYSFData.getData(&len, 1U); @@ -940,7 +1137,9 @@ void CModem::clock(unsigned int ms) m_ysfSpace--; } +#endif +#if defined(USE_P25) if (m_p25Space > 1U && !m_txP25Data.isEmpty()) { unsigned char len = 0U; m_txP25Data.getData(&len, 1U); @@ -961,7 +1160,9 @@ void CModem::clock(unsigned int ms) m_p25Space--; } +#endif +#if defined(USE_NXDN) if (m_nxdnSpace > 1U && !m_txNXDNData.isEmpty()) { unsigned char len = 0U; m_txNXDNData.getData(&len, 1U); @@ -978,7 +1179,9 @@ void CModem::clock(unsigned int ms) m_nxdnSpace--; } +#endif +#if defined(USE_POCSAG) if (m_pocsagSpace > 1U && !m_txPOCSAGData.isEmpty()) { unsigned char len = 0U; m_txPOCSAGData.getData(&len, 1U); @@ -995,15 +1198,17 @@ void CModem::clock(unsigned int ms) m_pocsagSpace--; } +#endif +#if defined(USE_FM) if (m_fmSpace > 1U && !m_txFMData.isEmpty()) { unsigned int len = 0U; m_txFMData.getData((unsigned char*)&len, sizeof(unsigned int)); m_txFMData.getData(m_buffer, len); if (m_trace) { - if (m_buffer[2U] == MMDVM_FM_CONTROL) - CUtils::dump(1U, "TX FM Control", m_buffer, len); + if (m_buffer[2U] == MMDVM_FM_STATUS) + CUtils::dump(1U, "TX FM Status", m_buffer, len); else CUtils::dump(1U, "TX FM Data", m_buffer, len); } @@ -1016,6 +1221,7 @@ void CModem::clock(unsigned int ms) m_fmSpace--; } +#endif if (!m_txTransparentData.isEmpty()) { unsigned char len = 0U; @@ -1053,6 +1259,7 @@ void CModem::close() m_port->close(); } +#if defined(USE_DSTAR) unsigned int CModem::readDStarData(unsigned char* data) { assert(data != nullptr); @@ -1066,7 +1273,9 @@ unsigned int CModem::readDStarData(unsigned char* data) return len; } +#endif +#if defined(USE_DMR) unsigned int CModem::readDMRData1(unsigned char* data) { assert(data != nullptr); @@ -1094,7 +1303,9 @@ unsigned int CModem::readDMRData2(unsigned char* data) return len; } +#endif +#if defined(USE_YSF) unsigned int CModem::readYSFData(unsigned char* data) { assert(data != nullptr); @@ -1108,7 +1319,9 @@ unsigned int CModem::readYSFData(unsigned char* data) return len; } +#endif +#if defined(USE_P25) unsigned int CModem::readP25Data(unsigned char* data) { assert(data != nullptr); @@ -1122,7 +1335,9 @@ unsigned int CModem::readP25Data(unsigned char* data) return len; } +#endif +#if defined(USE_NXDN) unsigned int CModem::readNXDNData(unsigned char* data) { assert(data != nullptr); @@ -1136,7 +1351,9 @@ unsigned int CModem::readNXDNData(unsigned char* data) return len; } +#endif +#if defined(USE_FM) unsigned int CModem::readFMData(unsigned char* data) { assert(data != nullptr); @@ -1150,6 +1367,7 @@ unsigned int CModem::readFMData(unsigned char* data) return len; } +#endif unsigned int CModem::readTransparentData(unsigned char* data) { @@ -1165,20 +1383,21 @@ unsigned int CModem::readTransparentData(unsigned char* data) return len; } -unsigned int CModem::readSerial(unsigned char* data, unsigned int length) +unsigned int CModem::readSerialData(unsigned char* data) { assert(data != nullptr); - assert(length > 0U); - unsigned int n = 0U; - while (!m_rxSerialData.isEmpty() && n < length) { - m_rxSerialData.getData(data + n, 1U); - n++; - } + if (m_rxSerialData.isEmpty()) + return 0U; - return n; + unsigned char len = 0U; + m_rxSerialData.getData(&len, 1U); + m_rxSerialData.getData(data, len); + + return len; } +#if defined(USE_DSTAR) bool CModem::hasDStarSpace() const { unsigned int space = m_txDStarData.freeSpace() / (DSTAR_FRAME_LENGTH_BYTES + 4U); @@ -1219,7 +1438,9 @@ bool CModem::writeDStarData(const unsigned char* data, unsigned int length) return true; } +#endif +#if defined(USE_DMR) bool CModem::hasDMRSpace1() const { unsigned int space = m_txDMRData1.freeSpace() / (DMR_FRAME_LENGTH_BYTES + 4U); @@ -1279,7 +1500,9 @@ bool CModem::writeDMRData2(const unsigned char* data, unsigned int length) return true; } +#endif +#if defined(USE_YSF) bool CModem::hasYSFSpace() const { unsigned int space = m_txYSFData.freeSpace() / (YSF_FRAME_LENGTH_BYTES + 4U); @@ -1309,7 +1532,9 @@ bool CModem::writeYSFData(const unsigned char* data, unsigned int length) return true; } +#endif +#if defined(USE_P25) bool CModem::hasP25Space() const { unsigned int space = m_txP25Data.freeSpace() / (P25_LDU_FRAME_LENGTH_BYTES + 4U); @@ -1339,7 +1564,9 @@ bool CModem::writeP25Data(const unsigned char* data, unsigned int length) return true; } +#endif +#if defined(USE_NXDN) bool CModem::hasNXDNSpace() const { unsigned int space = m_txNXDNData.freeSpace() / (NXDN_FRAME_LENGTH_BYTES + 4U); @@ -1369,7 +1596,9 @@ bool CModem::writeNXDNData(const unsigned char* data, unsigned int length) return true; } +#endif +#if defined(USE_POCSAG) bool CModem::hasPOCSAGSpace() const { unsigned int space = m_txPOCSAGData.freeSpace() / (POCSAG_FRAME_LENGTH_BYTES + 4U); @@ -1396,7 +1625,9 @@ bool CModem::writePOCSAGData(const unsigned char* data, unsigned int length) return true; } +#endif +#if defined(USE_FM) unsigned int CModem::getFMSpace() const { return m_txFMData.freeSpace(); @@ -1430,6 +1661,7 @@ bool CModem::writeFMData(const unsigned char* data, unsigned int length) return true; } +#endif bool CModem::writeTransparentData(const unsigned char* data, unsigned int length) { @@ -1465,6 +1697,7 @@ bool CModem::writeTransparentData(const unsigned char* data, unsigned int length return true; } +#if defined(USE_DSTAR) bool CModem::writeDStarInfo(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) { assert(m_port != nullptr); @@ -1493,7 +1726,9 @@ bool CModem::writeDStarInfo(const char* my1, const char* my2, const char* your, return m_port->write(buffer, 33U) != 33; } +#endif +#if defined(USE_DMR) bool CModem::writeDMRInfo(unsigned int slotNo, const std::string& src, bool group, const std::string& dest, const char* type) { assert(m_port != nullptr); @@ -1519,7 +1754,9 @@ bool CModem::writeDMRInfo(unsigned int slotNo, const std::string& src, bool grou return m_port->write(buffer, 47U) != 47; } +#endif +#if defined(USE_YSF) bool CModem::writeYSFInfo(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) { assert(m_port != nullptr); @@ -1547,7 +1784,9 @@ bool CModem::writeYSFInfo(const char* source, const char* dest, unsigned char dg return m_port->write(buffer, 36U) != 36; } +#endif +#if defined(USE_P25) bool CModem::writeP25Info(const char* source, bool group, unsigned int dest, const char* type) { assert(m_port != nullptr); @@ -1572,7 +1811,9 @@ bool CModem::writeP25Info(const char* source, bool group, unsigned int dest, con return m_port->write(buffer, 31U) != 31; } +#endif +#if defined(USE_NXDN) bool CModem::writeNXDNInfo(const char* source, bool group, unsigned int dest, const char* type) { assert(m_port != nullptr); @@ -1597,7 +1838,9 @@ bool CModem::writeNXDNInfo(const char* source, bool group, unsigned int dest, co return m_port->write(buffer, 31U) != 31; } +#endif +#if defined(USE_POCSAG) bool CModem::writePOCSAGInfo(unsigned int ric, const std::string& message) { assert(m_port != nullptr); @@ -1620,6 +1863,7 @@ bool CModem::writePOCSAGInfo(unsigned int ric, const std::string& message) return ret != int(length + 11U); } +#endif bool CModem::writeIPInfo(const std::string& address) { @@ -1642,7 +1886,7 @@ bool CModem::writeIPInfo(const std::string& address) return ret != int(length + 4U); } -bool CModem::writeSerial(const unsigned char* data, unsigned int length) +bool CModem::writeSerialData(const unsigned char* data, unsigned int length) { assert(m_port != nullptr); assert(data != nullptr); @@ -1782,7 +2026,7 @@ bool CModem::readVersion() LogInfo("MMDVM protocol version: 1, description: %.*s", m_length - 4U, m_buffer + 4U); m_capabilities1 = CAP1_DSTAR | CAP1_DMR | CAP1_YSF | CAP1_P25 | CAP1_NXDN; m_capabilities2 = CAP2_POCSAG; - return true; + break; case 2U: LogInfo("MMDVM protocol version: 2, description: %.*s", m_length - 23U, m_buffer + 23U); @@ -1802,30 +2046,31 @@ bool CModem::readVersion() } m_capabilities1 = m_buffer[4U]; m_capabilities2 = m_buffer[5U]; - char modeText[100U]; - ::strcpy(modeText, "Modes:"); - if (hasDStar()) - ::strcat(modeText, " D-Star"); - if (hasDMR()) - ::strcat(modeText, " DMR"); - if (hasYSF()) - ::strcat(modeText, " YSF"); - if (hasP25()) - ::strcat(modeText, " P25"); - if (hasNXDN()) - ::strcat(modeText, " NXDN"); - if (hasFM()) - ::strcat(modeText, " FM"); - if (hasPOCSAG()) - ::strcat(modeText, " POCSAG"); - LogInfo(modeText); - return true; + break; default: LogError("MMDVM protocol version: %u, unsupported by this version of the MMDVM Host", m_protocolVersion); return false; } + char modeText[100U]; + ::strcpy(modeText, "Modes:"); + if (hasDStar()) + ::strcat(modeText, " D-Star"); + if (hasDMR()) + ::strcat(modeText, " DMR"); + if (hasYSF()) + ::strcat(modeText, " YSF"); + if (hasP25()) + ::strcat(modeText, " P25"); + if (hasNXDN()) + ::strcat(modeText, " NXDN"); + if (hasFM()) + ::strcat(modeText, " FM"); + if (hasPOCSAG()) + ::strcat(modeText, " POCSAG"); + LogInfo(modeText); + return true; } } @@ -1884,8 +2129,10 @@ bool CModem::setConfig1() buffer[3U] |= 0x02U; if (m_pttInvert) buffer[3U] |= 0x04U; +#if defined(USE_YSF) if (m_ysfLoDev) buffer[3U] |= 0x08U; +#endif if (m_debug) buffer[3U] |= 0x10U; if (m_useCOSAsLockout) @@ -1894,18 +2141,30 @@ bool CModem::setConfig1() buffer[3U] |= 0x80U; buffer[4U] = 0x00U; +#if defined(USE_DSTAR) if (m_dstarEnabled) buffer[4U] |= 0x01U; +#endif +#if defined(USE_DMR) if (m_dmrEnabled) buffer[4U] |= 0x02U; +#endif +#if defined(USE_YSF) if (m_ysfEnabled) buffer[4U] |= 0x04U; +#endif +#if defined(USE_P25) if (m_p25Enabled) buffer[4U] |= 0x08U; +#endif +#if defined(USE_NXDN) if (m_nxdnEnabled) buffer[4U] |= 0x10U; +#endif +#if defined(USE_POCSAG) if (m_pocsagEnabled) buffer[4U] |= 0x20U; +#endif buffer[5U] = m_txDelay / 10U; // In 10ms units @@ -1915,35 +2174,73 @@ bool CModem::setConfig1() buffer[8U] = (unsigned char)(m_cwIdTXLevel * 2.55F + 0.5F); - buffer[9U] = m_dmrColorCode; - +#if defined(USE_DMR) + buffer[9U] = m_dmrColorCode; buffer[10U] = m_dmrDelay; +#else + buffer[9U] = 0U; + buffer[10U] = 0U; +#endif buffer[11U] = 128U; // Was OscOffset +#if defined(USE_DSTAR) buffer[12U] = (unsigned char)(m_dstarTXLevel * 2.55F + 0.5F); +#else + buffer[12U] = 0U; +#endif +#if defined(USE_DMR) buffer[13U] = (unsigned char)(m_dmrTXLevel * 2.55F + 0.5F); +#else + buffer[13U] = 0U; +#endif +#if defined(USE_YSF) buffer[14U] = (unsigned char)(m_ysfTXLevel * 2.55F + 0.5F); +#else + buffer[14U] = 0U; +#endif +#if defined(USE_P25) buffer[15U] = (unsigned char)(m_p25TXLevel * 2.55F + 0.5F); +#else + buffer[15U] = 0U; +#endif buffer[16U] = (unsigned char)(m_txDCOffset + 128); buffer[17U] = (unsigned char)(m_rxDCOffset + 128); +#if defined(USE_NXDN) buffer[18U] = (unsigned char)(m_nxdnTXLevel * 2.55F + 0.5F); - +#else + buffer[18U] = 0U; +#endif +#if defined(USE_YSF) buffer[19U] = (unsigned char)m_ysfTXHang; - +#else + buffer[19U] = 0U; +#endif +#if defined(USE_POCSAG) buffer[20U] = (unsigned char)(m_pocsagTXLevel * 2.55F + 0.5F); - +#else + buffer[20U] = 0U; +#endif +#if defined(USE_FM) buffer[21U] = (unsigned char)(m_fmTXLevel * 2.55F + 0.5F); - +#else + buffer[21U] = 0U; +#endif +#if defined(USE_P25) buffer[22U] = (unsigned char)m_p25TXHang; - +#else + buffer[22U] = 0U; +#endif +#if defined(USE_NXDN) buffer[23U] = (unsigned char)m_nxdnTXHang; +#else + buffer[23U] = 0U; +#endif - buffer[24U] = 0x00U; - - buffer[25U] = 0x00U; + buffer[24U] = 0U; + buffer[25U] = 0U; // CUtils::dump(1U, "Written", buffer, 26U); @@ -1997,8 +2294,10 @@ bool CModem::setConfig2() buffer[3U] |= 0x02U; if (m_pttInvert) buffer[3U] |= 0x04U; +#if defined(USE_YSF) if (m_ysfLoDev) buffer[3U] |= 0x08U; +#endif if (m_debug) buffer[3U] |= 0x10U; if (m_useCOSAsLockout) @@ -2007,23 +2306,36 @@ bool CModem::setConfig2() buffer[3U] |= 0x80U; buffer[4U] = 0x00U; +#if defined(USE_DSTAR) if (m_dstarEnabled) buffer[4U] |= 0x01U; +#endif +#if defined(USE_DMR) if (m_dmrEnabled) buffer[4U] |= 0x02U; +#endif +#if defined(USE_YSF) if (m_ysfEnabled) buffer[4U] |= 0x04U; +#endif +#if defined(USE_P25) if (m_p25Enabled) buffer[4U] |= 0x08U; +#endif +#if defined(USE_NXDN) if (m_nxdnEnabled) buffer[4U] |= 0x10U; +#endif +#if defined(USE_FM) if (m_fmEnabled) buffer[4U] |= 0x20U; +#endif buffer[5U] = 0x00U; +#if defined(USE_POCSAG) if (m_pocsagEnabled) buffer[5U] |= 0x01U; - +#endif buffer[6U] = m_txDelay / 10U; // In 10ms units buffer[7U] = MODE_IDLE; @@ -2034,32 +2346,82 @@ bool CModem::setConfig2() buffer[10U] = (unsigned char)(m_rxLevel * 2.55F + 0.5F); buffer[11U] = (unsigned char)(m_cwIdTXLevel * 2.55F + 0.5F); + +#if defined(USE_DSTAR) buffer[12U] = (unsigned char)(m_dstarTXLevel * 2.55F + 0.5F); +#else + buffer[12U] = 0U; +#endif +#if defined(USE_DMR) buffer[13U] = (unsigned char)(m_dmrTXLevel * 2.55F + 0.5F); +#else + buffer[13U] = 0U; +#endif +#if defined(USE_YSF) buffer[14U] = (unsigned char)(m_ysfTXLevel * 2.55F + 0.5F); +#else + buffer[14U] = 0U; +#endif +#if defined(USE_P25) buffer[15U] = (unsigned char)(m_p25TXLevel * 2.55F + 0.5F); +#else + buffer[15U] = 0U; +#endif +#if defined(USE_NXDN) buffer[16U] = (unsigned char)(m_nxdnTXLevel * 2.55F + 0.5F); - buffer[17U] = 0x00U; +#else + buffer[16U] = 0U; +#endif + + buffer[17U] = 0U; + +#if defined(USE_POCSAG) buffer[18U] = (unsigned char)(m_pocsagTXLevel * 2.55F + 0.5F); +#else + buffer[18U] = 0U; +#endif +#if defined(USE_FM) buffer[19U] = (unsigned char)(m_fmTXLevel * 2.55F + 0.5F); - buffer[20U] = 0x00U; +#else + buffer[19U] = 0U; +#endif + + buffer[20U] = 0U; buffer[21U] = 0x00U; buffer[22U] = 0x00U; +#if defined(USE_YSF) buffer[23U] = (unsigned char)m_ysfTXHang; +#else + buffer[23U] = 0U; +#endif +#if defined(USE_P25) buffer[24U] = (unsigned char)m_p25TXHang; +#else + buffer[24U] = 0U; +#endif +#if defined(USE_NXDN) buffer[25U] = (unsigned char)m_nxdnTXHang; - buffer[26U] = 0x00U; +#else + buffer[25U] = 0U; +#endif + + buffer[26U] = 0U; buffer[27U] = 0x00U; buffer[28U] = 0x00U; +#if defined(USE_DMR) buffer[29U] = m_dmrColorCode; buffer[30U] = m_dmrDelay; +#else + buffer[29U] = 0U; + buffer[30U] = 0U; +#endif buffer[31U] = 128U; - buffer[32U] = 0x00U; - buffer[33U] = 0x00U; - buffer[34U] = 0x00U; + buffer[32U] = 0U; + buffer[33U] = 0U; + buffer[34U] = 0U; buffer[35U] = 0x00U; buffer[36U] = 0x00U; @@ -2106,20 +2468,29 @@ bool CModem::setFrequency() unsigned char buffer[20U]; unsigned char len; - unsigned int pocsagFrequency = 433000000U; +#if defined(USE_POCSAG) + unsigned int pocsagFrequency = 433000000U; if (m_pocsagEnabled) pocsagFrequency = m_pocsagFrequency; +#endif if (m_hwType == HW_TYPE::DVMEGA) len = 12U; else { buffer[12U] = (unsigned char)(m_rfLevel * 2.55F + 0.5F); +#if defined(USE_POCSAG) buffer[13U] = (pocsagFrequency >> 0) & 0xFFU; buffer[14U] = (pocsagFrequency >> 8) & 0xFFU; buffer[15U] = (pocsagFrequency >> 16) & 0xFFU; buffer[16U] = (pocsagFrequency >> 24) & 0xFFU; +#else + buffer[13U] = 0U; + buffer[14U] = 0U; + buffer[15U] = 0U; + buffer[16U] = 0U; +#endif len = 17U; } @@ -2323,6 +2694,7 @@ bool CModem::sendCWId(const std::string& callsign) return m_port->write(buffer, length + 3U) == int(length + 3U); } +#if defined(USE_DMR) bool CModem::writeDMRStart(bool tx) { assert(m_port != nullptr); @@ -2389,7 +2761,9 @@ bool CModem::writeDMRShortLC(const unsigned char* lc) return m_port->write(buffer, 12U) == 12; } +#endif +#if defined(USE_FM) void CModem::setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch) { m_fmCallsign = callsign; @@ -2679,6 +3053,7 @@ bool CModem::setFMExtParams() return true; } +#endif void CModem::printDebug() { diff --git a/Modem.h b/Modem.h index 0e7e95a..33004d6 100644 --- a/Modem.h +++ b/Modem.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2018,2020,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2011-2018,2020,2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -49,16 +49,27 @@ public: 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, bool fmEnabled); void setLevels(float rxLevel, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagLevel, float fmTXLevel); + +#if defined(USE_DMR) void setDMRParams(unsigned int colorCode); +#endif +#if defined(USE_YSF) void setYSFParams(bool loDev, unsigned int txHang); +#endif +#if defined(USE_P25) void setP25Params(unsigned int txHang); +#endif +#if defined(USE_NXDN) void setNXDNParams(unsigned int txHang); +#endif void setTransparentDataParams(unsigned int sendFrameType); +#if defined(USE_FM) void setFMCallsignParams(const std::string& callsign, unsigned int callsignSpeed, unsigned int callsignFrequency, unsigned int callsignTime, unsigned int callsignHoldoff, float callsignHighLevel, float callsignLowLevel, bool callsignAtStart, bool callsignAtEnd, bool callsignAtLatch); void setFMAckParams(const std::string& rfAck, unsigned int ackSpeed, unsigned int ackFrequency, unsigned int ackMinTime, unsigned int ackDelay, float ackLevel); void setFMMiscParams(unsigned int timeout, float timeoutLevel, float ctcssFrequency, unsigned int ctcssHighThreshold, unsigned int ctcssLowThreshold, float ctcssLevel, unsigned int kerchunkTime, unsigned int hangTime, unsigned int accessMode, bool linkMode, bool cosInvert, bool noiseSquelch, unsigned int squelchHighThreshold, unsigned int squelchLowThreshold, unsigned int rfAudioBoost, float maxDevLevel); void setFMExtParams(const std::string& ack, unsigned int audioBoost); +#endif bool open(); @@ -72,22 +83,48 @@ public: unsigned int getVersion() const; +#if defined(USE_DSTAR) unsigned int readDStarData(unsigned char* data); +#endif +#if defined(USE_DMR) unsigned int readDMRData1(unsigned char* data); unsigned int readDMRData2(unsigned char* data); +#endif +#if defined(USE_YSF) unsigned int readYSFData(unsigned char* data); +#endif +#if defined(USE_P25) unsigned int readP25Data(unsigned char* data); +#endif +#if defined(USE_NXDN) unsigned int readNXDNData(unsigned char* data); +#endif +#if defined(USE_FM) unsigned int readFMData(unsigned char* data); +#endif +#if defined(USE_DSTAR) bool hasDStarSpace() const; +#endif +#if defined(USE_DMR) bool hasDMRSpace1() const; bool hasDMRSpace2() const; +#endif +#if defined(USE_YSF) bool hasYSFSpace() const; +#endif +#if defined(USE_P25) bool hasP25Space() const; +#endif +#if defined(USE_NXDN) bool hasNXDNSpace() const; +#endif +#if defined(USE_POCSAG) bool hasPOCSAGSpace() const; +#endif +#if defined(USE_FM) unsigned int getFMSpace() const; +#endif bool hasTX() const; bool hasCD() const; @@ -96,32 +133,60 @@ public: bool hasError() const; bool writeConfig(); + +#if defined(USE_DSTAR) bool writeDStarData(const unsigned char* data, unsigned int length); +#endif +#if defined(USE_DMR) bool writeDMRData1(const unsigned char* data, unsigned int length); bool writeDMRData2(const unsigned char* data, unsigned int length); +#endif +#if defined(USE_YSF) bool writeYSFData(const unsigned char* data, unsigned int length); +#endif +#if defined(USE_P25) bool writeP25Data(const unsigned char* data, unsigned int length); +#endif +#if defined(USE_NXDN) bool writeNXDNData(const unsigned char* data, unsigned int length); +#endif +#if defined(USE_POCSAG) bool writePOCSAGData(const unsigned char* data, unsigned int length); +#endif +#if defined(USE_FM) bool writeFMData(const unsigned char* data, unsigned int length); +#endif +#if defined(USE_DSTAR) bool writeDStarInfo(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); +#endif +#if defined(USE_DMR) bool writeDMRInfo(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); +#endif +#if defined(USE_YSF) bool writeYSFInfo(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin); +#endif +#if defined(USE_P25) bool writeP25Info(const char* source, bool group, unsigned int dest, const char* type); +#endif +#if defined(USE_NXDN) bool writeNXDNInfo(const char* source, bool group, unsigned int dest, const char* type); +#endif +#if defined(USE_POCSAG) bool writePOCSAGInfo(unsigned int ric, const std::string& message); +#endif bool writeIPInfo(const std::string& address); +#if defined(USE_DMR) bool writeDMRStart(bool tx); bool writeDMRShortLC(const unsigned char* lc); bool writeDMRAbort(unsigned int slotNo); - +#endif bool writeTransparentData(const unsigned char* data, unsigned int length); unsigned int readTransparentData(unsigned char* data); - bool writeSerial(const unsigned char* data, unsigned int length); - unsigned int readSerial(unsigned char* data, unsigned int length); + bool writeSerialData(const unsigned char* data, unsigned int length); + unsigned int readSerialData(unsigned char* data); unsigned char getMode() const; bool setMode(unsigned char mode); @@ -136,40 +201,80 @@ public: private: unsigned int m_protocolVersion; +#if defined(USE_DMR) unsigned int m_dmrColorCode; +#endif +#if defined(USE_YSF) bool m_ysfLoDev; unsigned int m_ysfTXHang; +#endif +#if defined(USE_P25) unsigned int m_p25TXHang; +#endif +#if defined(USE_NXDN) unsigned int m_nxdnTXHang; +#endif bool m_duplex; bool m_rxInvert; bool m_txInvert; bool m_pttInvert; unsigned int m_txDelay; +#if defined(USE_DMR) unsigned int m_dmrDelay; +#endif float m_rxLevel; float m_cwIdTXLevel; +#if defined(USE_DSTAR) float m_dstarTXLevel; +#endif +#if defined(USE_DMR) float m_dmrTXLevel; +#endif +#if defined(USE_YSF) float m_ysfTXLevel; +#endif +#if defined(USE_P25) float m_p25TXLevel; +#endif +#if defined(USE_NXDN) float m_nxdnTXLevel; +#endif +#if defined(USE_POCSAG) float m_pocsagTXLevel; +#endif +#if defined(USE_FM) float m_fmTXLevel; +#endif float m_rfLevel; bool m_useCOSAsLockout; bool m_trace; bool m_debug; unsigned int m_rxFrequency; unsigned int m_txFrequency; +#if defined(USE_POCSAG) unsigned int m_pocsagFrequency; +#endif +#if defined(USE_DSTAR) bool m_dstarEnabled; +#endif +#if defined(USE_DMR) bool m_dmrEnabled; +#endif +#if defined(USE_YSF) bool m_ysfEnabled; +#endif +#if defined(USE_P25) bool m_p25Enabled; +#endif +#if defined(USE_NXDN) bool m_nxdnEnabled; +#endif +#if defined(USE_POCSAG) bool m_pocsagEnabled; +#endif +#if defined(USE_FM) bool m_fmEnabled; +#endif int m_rxDCOffset; int m_txDCOffset; IModemPort* m_port; @@ -178,21 +283,36 @@ private: unsigned int m_offset; SERIAL_STATE m_state; unsigned char m_type; + +#if defined(USE_DSTAR) CRingBuffer m_rxDStarData; CRingBuffer m_txDStarData; +#endif +#if defined(USE_DMR) CRingBuffer m_rxDMRData1; CRingBuffer m_rxDMRData2; CRingBuffer m_txDMRData1; CRingBuffer m_txDMRData2; +#endif +#if defined(USE_YSF) CRingBuffer m_rxYSFData; CRingBuffer m_txYSFData; +#endif +#if defined(USE_P25) CRingBuffer m_rxP25Data; CRingBuffer m_txP25Data; +#endif +#if defined(USE_NXDN) CRingBuffer m_rxNXDNData; CRingBuffer m_txNXDNData; +#endif +#if defined(USE_POCSAG) CRingBuffer m_txPOCSAGData; +#endif +#if defined(USE_FM) CRingBuffer m_rxFMData; CRingBuffer m_txFMData; +#endif CRingBuffer m_rxSerialData; CRingBuffer m_txSerialData; CRingBuffer m_rxTransparentData; @@ -201,21 +321,35 @@ private: CTimer m_statusTimer; CTimer m_inactivityTimer; CTimer m_playoutTimer; +#if defined(USE_DSTAR) unsigned int m_dstarSpace; +#endif +#if defined(USE_DMR) unsigned int m_dmrSpace1; unsigned int m_dmrSpace2; +#endif +#if defined(USE_YSF) unsigned int m_ysfSpace; +#endif +#if defined(USE_P25) unsigned int m_p25Space; +#endif +#if defined(USE_NXDN) unsigned int m_nxdnSpace; +#endif +#if defined(USE_POCSAG) unsigned int m_pocsagSpace; +#endif +#if defined(USE_FM) unsigned int m_fmSpace; +#endif bool m_tx; bool m_cd; bool m_lockout; bool m_error; unsigned char m_mode; HW_TYPE m_hwType; - +#if defined(USE_FM) std::string m_fmCallsign; unsigned int m_fmCallsignSpeed; unsigned int m_fmCallsignFrequency; @@ -251,6 +385,7 @@ private: unsigned int m_fmExtAudioBoost; float m_fmMaxDevLevel; bool m_fmExtEnable; +#endif unsigned char m_capabilities1; unsigned char m_capabilities2; @@ -259,11 +394,12 @@ private: bool setConfig1(); bool setConfig2(); bool setFrequency(); +#if defined(USE_FM) bool setFMCallsignParams(); bool setFMAckParams(); bool setFMMiscParams(); bool setFMExtParams(); - +#endif void printDebug(); RESP_TYPE_MMDVM getResponse(); diff --git a/ModemSerialPort.cpp b/ModemSerialPort.cpp deleted file mode 100644 index 4399774..0000000 --- a/ModemSerialPort.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* -* Copyright (C) 2016,2020,2021,2025 by Jonathan Naylor G4KLX -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include "ModemSerialPort.h" - -#include -#include - -IModemSerialPort::IModemSerialPort(CModem* modem) : -m_modem(modem) -{ - assert(modem != nullptr); -} - -IModemSerialPort::~IModemSerialPort() -{ -} - -bool IModemSerialPort::open() -{ - return true; -} - -int IModemSerialPort::write(const unsigned char* data, unsigned int length) -{ - assert(data != nullptr); - assert(length > 0U); - - bool ret = m_modem->writeSerial(data, length); - - return ret ? int(length) : -1; -} - -int IModemSerialPort::read(unsigned char* data, unsigned int length) -{ - assert(data != nullptr); - assert(length > 0U); - - return m_modem->readSerial(data, length); -} - -void IModemSerialPort::close() -{ -} diff --git a/ModemSerialPort.h b/ModemSerialPort.h deleted file mode 100644 index 9a64426..0000000 --- a/ModemSerialPort.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -* Copyright (C) 2016,2020,2021 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 ModemSerialPort_H -#define ModemSerialPort_H - -#include "SerialPort.h" -#include "Modem.h" - -class IModemSerialPort : public ISerialPort { -public: - IModemSerialPort(CModem* modem); - virtual ~IModemSerialPort(); - - virtual bool open(); - - virtual int read(unsigned char* buffer, unsigned int length); - - virtual int write(const unsigned char* buffer, unsigned int length); - - virtual void close(); - -private: - CModem* m_modem; -}; - -#endif diff --git a/NXDNAudio.cpp b/NXDNAudio.cpp index 742e43c..b7119aa 100644 --- a/NXDNAudio.cpp +++ b/NXDNAudio.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2018,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2018,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,8 @@ #include "NXDNAudio.h" #include "Golay24128.h" +#if defined(USE_NXDN) + #include #include #include @@ -575,3 +577,6 @@ void CNXDNAudio::encode(const unsigned char* in, unsigned char* out, unsigned in WRITE_BIT(out, cPos, cOrig & MASK); } } + +#endif + diff --git a/NXDNAudio.h b/NXDNAudio.h index fd778c7..2f8df81 100644 --- a/NXDNAudio.h +++ b/NXDNAudio.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2018 by Jonathan Naylor G4KLX +* Copyright (C) 2018,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(NXDNAudio_H) #define NXDNAudio_H +#include "Defines.h" + +#if defined(USE_NXDN) + class CNXDNAudio { public: CNXDNAudio(); @@ -35,3 +39,6 @@ private: }; #endif + +#endif + diff --git a/NXDNCRC.cpp b/NXDNCRC.cpp index 132855d..05ca358 100644 --- a/NXDNCRC.cpp +++ b/NXDNCRC.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2018,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,8 @@ #include "NXDNCRC.h" +#if defined(USE_NXDN) + #include #include @@ -183,3 +185,6 @@ uint16_t CNXDNCRC::createCRC15(const unsigned char* in, unsigned int length) return crc & 0x7FFFU; } + +#endif + diff --git a/NXDNCRC.h b/NXDNCRC.h index 12e2e40..c42fd48 100644 --- a/NXDNCRC.h +++ b/NXDNCRC.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 by Jonathan Naylor G4KLX + * Copyright (C) 2018,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(NXDNCRC_H) #define NXDNCRC_H +#include "Defines.h" + +#if defined(USE_NXDN) + #include class CNXDNCRC @@ -40,3 +44,6 @@ private: }; #endif + +#endif + diff --git a/NXDNControl.cpp b/NXDNControl.cpp index 6015ca7..981a42e 100644 --- a/NXDNControl.cpp +++ b/NXDNControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2021,2025 Jonathan Naylor, G4KLX + * Copyright (C) 2015-2021,2023,2025 Jonathan Naylor, G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,8 @@ #include "Sync.h" #include "Log.h" +#if defined(USE_NXDN) + #include #include #include @@ -32,19 +34,19 @@ const unsigned char SCRAMBLER[] = { 0x28U, 0x28U, 0x00U, 0x0AU, 0x02U, 0x82U, 0x20U, 0x28U, 0x82U, 0x2AU, 0xAAU, 0x20U, 0x22U, 0x80U, 0xA8U, 0x8AU, 0x08U, 0xA0U, 0xAAU, 0x02U }; -// #define DUMP_NXDN +const unsigned int RSSI_COUNT = 25U; // 25 * 40ms = 1000ms +const unsigned int BER_COUNT = 25U; // 25 * 40ms = 1000ms const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; #define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) #define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) -CNXDNControl::CNXDNControl(unsigned int ran, unsigned int id, bool selfOnly, INXDNNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CNXDNLookup* lookup, CRSSIInterpolator* rssiMapper) : +CNXDNControl::CNXDNControl(unsigned int ran, unsigned int id, bool selfOnly, INXDNNetwork* network, unsigned int timeout, bool duplex, bool remoteGateway, CNXDNLookup* lookup, CRSSIInterpolator* rssiMapper) : m_ran(ran), m_id(id), m_selfOnly(selfOnly), m_network(network), -m_display(display), m_duplex(duplex), m_remoteGateway(remoteGateway), m_lookup(lookup), @@ -66,15 +68,17 @@ m_netLayer3(), m_rfMask(0x00U), m_netMask(0x00U), m_rssiMapper(rssiMapper), -m_rssi(0U), -m_maxRSSI(0U), -m_minRSSI(0U), -m_aveRSSI(0U), +m_rssi(0), +m_maxRSSI(0), +m_minRSSI(0), +m_aveRSSI(0), +m_rssiCountTotal(0U), +m_rssiAccum(0), m_rssiCount(0U), -m_enabled(true), -m_fp(nullptr) +m_bitsCount(0U), +m_bitErrsAccum(0U), +m_enabled(true) { - assert(display != nullptr); assert(lookup != nullptr); assert(rssiMapper != nullptr); } @@ -95,12 +99,16 @@ bool CNXDNControl::writeModem(unsigned char *data, unsigned int len) if ((type == TAG_LOST) && (m_rfState == RPT_RF_STATE::AUDIO)) { unsigned short dstId = m_rfLayer3.getDestinationGroupId(); bool grp = m_rfLayer3.getIsGroup(); - std::string source = m_lookup->find(m_rfLayer3.getSourceUnitId()); + unsigned short srcId = m_rfLayer3.getSourceUnitId(); + std::string source = m_lookup->find(srcId); - if (m_rssi != 0U) - LogMessage("NXDN, transmission lost from %s to %s%u, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else + if (m_rssi != 0) { + LogMessage("NXDN, transmission lost from %s to %s%u, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%ddBm", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + writeJSONRF("lost", float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + } else { LogMessage("NXDN, transmission lost from %s to %s%u, %.1f seconds, BER: %.1f%%", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeJSONRF("lost", float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits)); + } writeEndRF(); return false; } @@ -124,19 +132,19 @@ bool CNXDNControl::writeModem(unsigned char *data, unsigned int len) raw |= (data[51U] << 0) & 0x00FFU; // Convert the raw RSSI to dBm - int rssi = m_rssiMapper->interpolate(raw); - if (rssi != 0) - LogDebug("NXDN, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi); + m_rssi = m_rssiMapper->interpolate(raw); + if (m_rssi != 0) + LogDebug("NXDN, raw RSSI: %u, reported RSSI: %d dBm", raw, m_rssi); - // RSSI is always reported as positive - m_rssi = (rssi >= 0) ? rssi : -rssi; - - if (m_rssi > m_minRSSI) + if (m_rssi < m_minRSSI) m_minRSSI = m_rssi; - if (m_rssi < m_maxRSSI) + if (m_rssi > m_maxRSSI) m_maxRSSI = m_rssi; m_aveRSSI += m_rssi; + m_rssiCountTotal++; + + m_rssiAccum += m_rssi; m_rssiCount++; } @@ -256,43 +264,51 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne writeNetwork(netData, data[0U] == TAG_EOT ? NXDN_NETWORK_MESSAGE_TYPE::VOICE_TRAILER : NXDN_NETWORK_MESSAGE_TYPE::VOICE_HEADER); -#if defined(DUMP_NXDN) - writeFile(data + 2U); -#endif if (m_duplex) writeQueueRF(data); if (data[0U] == TAG_EOT) { unsigned short dstId = m_rfLayer3.getDestinationGroupId(); bool grp = m_rfLayer3.getIsGroup(); - std::string source = m_lookup->find(m_rfLayer3.getSourceUnitId()); + unsigned short srcId = m_rfLayer3.getSourceUnitId(); + std::string source = m_lookup->find(srcId); m_rfFrames++; - if (m_rssi != 0U) - LogMessage("NXDN, received RF end of transmission from %s to %s%u, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else + if (m_rssi != 0) { + LogMessage("NXDN, received RF end of transmission from %s to %s%u, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + writeJSONRF("end", float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + } else { LogMessage("NXDN, received RF end of transmission from %s to %s%u, %.1f seconds, BER: %.1f%%", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeJSONRF("end", float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits)); + } writeEndRF(); } else { m_rfFrames = 0U; m_rfErrs = 0U; m_rfBits = 1U; + m_rfTimeoutTimer.start(); + m_rfState = RPT_RF_STATE::AUDIO; + m_minRSSI = m_rssi; m_maxRSSI = m_rssi; m_aveRSSI = m_rssi; + m_rssiCountTotal = 1U; + + m_rssiAccum = m_rssi; m_rssiCount = 1U; -#if defined(DUMP_NXDN) - openFile(); -#endif - unsigned short srcId = m_rfLayer3.getSourceUnitId(); + + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + unsigned short dstId = m_rfLayer3.getDestinationGroupId(); bool grp = m_rfLayer3.getIsGroup(); + unsigned short srcId = m_rfLayer3.getSourceUnitId(); + std::string source = m_lookup->find(srcId); - std::string source = m_lookup->find(srcId); LogMessage("NXDN, received RF header from %s to %s%u", source.c_str(), grp ? "TG " : "", dstId); - m_display->writeNXDN(source.c_str(), grp, dstId, "R"); + writeJSONRF("start", srcId, source, grp, dstId); } return true; @@ -382,19 +398,25 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne m_rfFrames = 0U; m_rfErrs = 0U; m_rfBits = 1U; + m_rfTimeoutTimer.start(); + m_rfState = RPT_RF_STATE::AUDIO; m_minRSSI = m_rssi; m_maxRSSI = m_rssi; m_aveRSSI = m_rssi; + m_rssiCountTotal = 1U; + + m_rssiAccum = m_rssi; m_rssiCount = 1U; -#if defined(DUMP_NXDN) - openFile(); -#endif + + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + std::string source = m_lookup->find(srcId); LogMessage("NXDN, received RF late entry from %s to %s%u", source.c_str(), grp ? "TG " : "", dstId); - m_display->writeNXDN(source.c_str(), grp, dstId, "R"); + writeJSONRF("late_entry", srcId, source, grp, dstId); m_rfState = RPT_RF_STATE::AUDIO; @@ -440,9 +462,6 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne writeNetwork(netData, NXDN_NETWORK_MESSAGE_TYPE::VOICE_HEADER); -#if defined(DUMP_NXDN) - writeFile(start + 2U); -#endif if (m_duplex) writeQueueRF(start); } @@ -483,7 +502,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne errors += ambe.regenerateYSFDN(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 27U); m_rfErrs += errors; m_rfBits += 188U; - m_display->writeNXDNBER(float(errors) / 1.88F); + writeJSONBER(188U, errors); LogDebug("NXDN, AMBE FEC %u/188 (%.1f%%)", errors, float(errors) / 1.88F); CNXDNAudio audio; @@ -502,7 +521,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne errors += ambe.regenerateYSFDN(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 27U); m_rfErrs += errors; m_rfBits += 94U; - m_display->writeNXDNBER(float(errors) / 0.94F); + writeJSONBER(94U, errors); LogDebug("NXDN, AMBE FEC %u/94 (%.1f%%)", errors, float(errors) / 0.94F); CNXDNAudio audio; @@ -514,7 +533,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne errors += ambe.regenerateYSFDN(data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 9U); m_rfErrs += errors; m_rfBits += 94U; - m_display->writeNXDNBER(float(errors) / 0.94F); + writeJSONBER(94U, errors); LogDebug("NXDN, AMBE FEC %u/94 (%.1f%%)", errors, float(errors) / 0.94F); CNXDNAudio audio; @@ -546,16 +565,12 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne writeNetwork(netData, NXDN_NETWORK_MESSAGE_TYPE::VOICE_BODY); -#if defined(DUMP_NXDN) - writeFile(data + 2U); -#endif - if (m_duplex) writeQueueRF(data); m_rfFrames++; - m_display->writeNXDNRSSI(m_rssi); + writeJSONRSSI(); } return true; @@ -602,19 +617,15 @@ bool CNXDNControl::processData(unsigned char option, unsigned char *data) std::string source = m_lookup->find(srcId); - m_display->writeNXDN(source.c_str(), grp, dstId, "R"); - m_display->writeNXDNRSSI(m_rssi); + writeJSONRSSI(); LogMessage("NXDN, received RF data header from %s to %s%u, %u blocks", source.c_str(), grp ? "TG " : "", dstId, frames); + writeJSONNet("start", srcId, source, grp, dstId, frames); m_rfLayer3 = layer3; m_rfFrames = 0U; m_rfState = RPT_RF_STATE::DATA; - -#if defined(DUMP_NXDN) - openFile(); -#endif } if (m_rfState != RPT_RF_STATE::DATA) @@ -666,16 +677,14 @@ bool CNXDNControl::processData(unsigned char option, unsigned char *data) m_rfFrames++; -#if defined(DUMP_NXDN) - writeFile(data + 2U); -#endif - if (data[0U] == TAG_EOT) { unsigned short dstId = m_rfLayer3.getDestinationGroupId(); bool grp = m_rfLayer3.getIsGroup(); - std::string source = m_lookup->find(m_rfLayer3.getSourceUnitId()); + unsigned short srcId = m_rfLayer3.getSourceUnitId(); + std::string source = m_lookup->find(srcId); LogMessage("NXDN, ended RF data transmission from %s to %s%u", source.c_str(), grp ? "TG " : "", dstId); + writeJSONNet("end"); writeEndRF(); } @@ -707,15 +716,10 @@ void CNXDNControl::writeEndRF() m_rfTimeoutTimer.stop(); if (m_netState == RPT_NET_STATE::IDLE) { - m_display->clearNXDN(); - if (m_network != nullptr) m_network->reset(); } -#if defined(DUMP_NXDN) - closeFile(); -#endif } void CNXDNControl::writeEndNet() @@ -729,8 +733,6 @@ void CNXDNControl::writeEndNet() m_networkWatchdog.stop(); m_packetTimer.stop(); - m_display->clearNXDN(); - if (m_network != nullptr) m_network->reset(); } @@ -775,8 +777,8 @@ void CNXDNControl::writeNetwork() unsigned char frames = layer3.getDataBlocks(); std::string source = m_lookup->find(srcId); - m_display->writeNXDN(source.c_str(), grp, dstId, "N"); LogMessage("NXDN, received network data header from %s to %s%u, %u blocks", source.c_str(), grp ? "TG " : "", dstId, frames); + writeJSONNet("start", srcId, source, grp, dstId, frames); m_netState = RPT_NET_STATE::DATA; } else { @@ -800,9 +802,11 @@ void CNXDNControl::writeNetwork() if (type == NXDN_MESSAGE_TYPE_TX_REL) { unsigned short dstId = m_netLayer3.getDestinationGroupId(); bool grp = m_netLayer3.getIsGroup(); - std::string source = m_lookup->find(m_netLayer3.getSourceUnitId()); + unsigned short srcId = m_netLayer3.getSourceUnitId(); + std::string source = m_lookup->find(srcId); LogMessage("NXDN, ended network data transmission from %s to %s%u", source.c_str(), grp ? "TG " : "", dstId); + writeJSONNet("start", srcId, source, grp, dstId); writeEndNet(); } } @@ -835,16 +839,18 @@ void CNXDNControl::writeNetwork() unsigned short dstId = m_netLayer3.getDestinationGroupId(); bool grp = m_netLayer3.getIsGroup(); + unsigned short srcId = m_netLayer3.getSourceUnitId(); class CUserDBentry source; - m_lookup->findWithName(m_netLayer3.getSourceUnitId(), &source); + m_lookup->findWithName(srcId, &source); if (type == NXDN_MESSAGE_TYPE_TX_REL) { m_netFrames++; LogMessage("NXDN, received network end of transmission from %s to %s%u, %.1f seconds", source.get(keyCALLSIGN).c_str(), grp ? "TG " : "", dstId, float(m_netFrames) / 12.5F); + writeJSONNet("end", float(m_netFrames) / 12.5F); writeEndNet(); } else if (type == NXDN_MESSAGE_TYPE_VCALL) { LogMessage("NXDN, received network transmission from %s to %s%u", source.get(keyCALLSIGN).c_str(), grp ? "TG " : "", dstId); - m_display->writeNXDN(source, grp, dstId, "N"); + writeJSONNet("start", srcId, source.get(keyCALLSIGN), grp, dstId); m_netTimeoutTimer.start(); m_packetTimer.start(); @@ -896,7 +902,7 @@ void CNXDNControl::writeNetwork() class CUserDBentry source; m_lookup->findWithName(srcId, &source); LogMessage("NXDN, received network transmission from %s to %s%u", source.get(keyCALLSIGN).c_str(), grp ? "TG " : "", dstId); - m_display->writeNXDN(source, grp, dstId, "N"); + writeJSONNet("start", srcId, source.get(keyCALLSIGN), grp, dstId); m_netTimeoutTimer.start(); m_packetTimer.start(); @@ -1000,6 +1006,7 @@ void CNXDNControl::clock(unsigned int ms) if (m_networkWatchdog.hasExpired()) { LogMessage("NXDN, network watchdog has expired, %.1f seconds", float(m_netFrames) / 12.5F); + writeJSONNet("lost", float(m_netFrames) / 12.5F); writeEndNet(); } } @@ -1069,46 +1076,6 @@ void CNXDNControl::scrambler(unsigned char* data) const data[i] ^= SCRAMBLER[i]; } -bool CNXDNControl::openFile() -{ - if (m_fp != nullptr) - return true; - - time_t t; - ::time(&t); - - struct tm* tm = ::localtime(&t); - - char name[100U]; - ::sprintf(name, "NXDN_%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 == nullptr) - return false; - - ::fwrite("NXDN", 1U, 4U, m_fp); - - return true; -} - -bool CNXDNControl::writeFile(const unsigned char* data) -{ - if (m_fp == nullptr) - return false; - - ::fwrite(data, 1U, NXDN_FRAME_LENGTH_BYTES, m_fp); - - return true; -} - -void CNXDNControl::closeFile() -{ - if (m_fp != nullptr) { - ::fclose(m_fp); - m_fp = nullptr; - } -} - bool CNXDNControl::isBusy() const { return (m_rfState != RPT_RF_STATE::LISTENING) || (m_netState != RPT_NET_STATE::IDLE); @@ -1162,3 +1129,164 @@ void CNXDNControl::enable(bool enabled) m_enabled = enabled; } + +void CNXDNControl::writeJSONRSSI() +{ + if (m_rssi == 0) + return; + + if (m_rssiCount >= RSSI_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "NXDN"; + + json["value"] = m_rssiAccum / int(m_rssiCount); + + WriteJSON("RSSI", json); + + m_rssiAccum = 0; + m_rssiCount = 0U; + } +} + +void CNXDNControl::writeJSONBER(unsigned int bits, unsigned int errs) +{ + m_bitErrsAccum += errs; + m_bitsCount += bits; + + if (m_bitsCount >= (BER_COUNT * bits)) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "NXDN"; + + json["value"] = float(m_bitErrsAccum * 100U) / float(m_bitsCount); + + WriteJSON("BER", json); + + m_bitErrsAccum = 0U; + m_bitsCount = 1U; + } +} + +void CNXDNControl::writeJSONRF(const char* action, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, "rf", action, srcId, srcInfo, grp, dstId); + + WriteJSON("NXDN", json); +} + +void CNXDNControl::writeJSONRF(const char* action, float duration, float ber) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, action); + + json["duration"] = duration; + json["ber"] = ber; + + WriteJSON("NXDN", json); +} + +void CNXDNControl::writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, action); + + json["duration"] = duration; + json["ber"] = ber; + + nlohmann::json rssi; + rssi["min"] = minRSSI; + rssi["max"] = maxRSSI; + rssi["ave"] = aveRSSI; + + json["rssi"] = rssi; + + WriteJSON("NXDN", json); +} + +void CNXDNControl::writeJSONNet(const char* action) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, action); + + WriteJSON("NXDN", json); +} + +void CNXDNControl::writeJSONNet(const char* action, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, "network", action, srcId, srcInfo, grp, dstId); + + WriteJSON("NXDN", json); +} + +void CNXDNControl::writeJSONNet(const char* action, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId, unsigned char frames) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, "network", action, srcId, srcInfo, grp, dstId); + + json["frames"] = int(frames); + + WriteJSON("NXDN", json); +} + +void CNXDNControl::writeJSONNet(const char* action, float duration) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, action); + + json["duration"] = duration; + + WriteJSON("NXDN", json); +} + +void CNXDNControl::writeJSON(nlohmann::json& json, const char* action) +{ + assert(action != nullptr); + + json["timestamp"] = CUtils::createTimestamp(); + json["action"] = action; +} + +void CNXDNControl::writeJSON(nlohmann::json& json, const char* source, const char* action, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId) +{ + assert(source != nullptr); + assert(action != nullptr); + + json["timestamp"] = CUtils::createTimestamp(); + json["source"] = source; + json["action"] = action; + json["source_id"] = int(srcId); + json["destination_id"] = int(dstId); + json["destination_type"] = grp ? "group" : "individual"; + + if (!srcInfo.empty()) + json["source_info"] = srcInfo; +} + +#endif + diff --git a/NXDNControl.h b/NXDNControl.h index af2fba1..7fe604d 100644 --- a/NXDNControl.h +++ b/NXDNControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2020,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,16 +27,19 @@ #include "RingBuffer.h" #include "StopWatch.h" #include "NXDNLICH.h" -#include "Display.h" #include "Defines.h" #include "Timer.h" #include "Modem.h" +#if defined(USE_NXDN) + #include +#include + class CNXDNControl { public: - CNXDNControl(unsigned int ran, unsigned int id, bool selfOnly, INXDNNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool remoteGateway, CNXDNLookup* lookup, CRSSIInterpolator* rssiMapper); + CNXDNControl(unsigned int ran, unsigned int id, bool selfOnly, INXDNNetwork* network, unsigned int timeout, bool duplex, bool remoteGateway, CNXDNLookup* lookup, CRSSIInterpolator* rssiMapper); ~CNXDNControl(); bool writeModem(unsigned char* data, unsigned int len); @@ -54,7 +57,6 @@ private: unsigned int m_id; bool m_selfOnly; INXDNNetwork* m_network; - CDisplay* m_display; bool m_duplex; bool m_remoteGateway; CNXDNLookup* m_lookup; @@ -76,13 +78,16 @@ private: unsigned char m_rfMask; unsigned char m_netMask; CRSSIInterpolator* m_rssiMapper; - unsigned char m_rssi; - unsigned char m_maxRSSI; - unsigned char m_minRSSI; - unsigned int m_aveRSSI; + int m_rssi; + int m_maxRSSI; + int m_minRSSI; + int m_aveRSSI; + unsigned int m_rssiCountTotal; + int m_rssiAccum; unsigned int m_rssiCount; + unsigned int m_bitsCount; + unsigned int m_bitErrsAccum; bool m_enabled; - FILE* m_fp; bool processVoice(unsigned char usc, unsigned char option, unsigned char *data); bool processData(unsigned char option, unsigned char *data); @@ -97,9 +102,23 @@ private: void writeEndRF(); void writeEndNet(); - bool openFile(); - bool writeFile(const unsigned char* data); - void closeFile(); + void writeJSONRSSI(); + void writeJSONBER(unsigned int bits, unsigned int errs); + + void writeJSONRF(const char* action, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId); + void writeJSONRF(const char* action, float duration, float ber); + void writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI); + + void writeJSONNet(const char* action, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId); + void writeJSONNet(const char* action, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId, unsigned char frames); + void writeJSONNet(const char* action); + void writeJSONNet(const char* action, float duration); + + void writeJSON(nlohmann::json& json, const char* action); + void writeJSON(nlohmann::json& json, const char* source, const char* action, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId); }; #endif + +#endif + diff --git a/NXDNConvolution.cpp b/NXDNConvolution.cpp index 9da4bb2..d5939e3 100644 --- a/NXDNConvolution.cpp +++ b/NXDNConvolution.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2016,2018,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2016,2018,2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,8 @@ #include "NXDNConvolution.h" +#if defined(USE_NXDN) + #include #include #include @@ -149,3 +151,6 @@ void CNXDNConvolution::encode(const unsigned char* in, unsigned char* out, unsig k++; } } + +#endif + diff --git a/NXDNConvolution.h b/NXDNConvolution.h index 805bc25..a9aa8f7 100644 --- a/NXDNConvolution.h +++ b/NXDNConvolution.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2018,2021 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2018,2021,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(NXDNConvolution_H) #define NXDNConvolution_H +#include "Defines.h" + +#if defined(USE_NXDN) + #include class CNXDNConvolution { @@ -44,3 +48,5 @@ private: #endif +#endif + diff --git a/NXDNFACCH1.cpp b/NXDNFACCH1.cpp index e35024a..ee02fdd 100644 --- a/NXDNFACCH1.cpp +++ b/NXDNFACCH1.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2018,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2018,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,11 +17,12 @@ */ #include "NXDNFACCH1.h" - #include "NXDNConvolution.h" #include "NXDNDefines.h" #include "NXDNCRC.h" +#if defined(USE_NXDN) + #include #include #include @@ -186,3 +187,6 @@ CNXDNFACCH1& CNXDNFACCH1::operator=(const CNXDNFACCH1& facch1) return *this; } + +#endif + diff --git a/NXDNFACCH1.h b/NXDNFACCH1.h index db56ec1..3d1b8fe 100644 --- a/NXDNFACCH1.h +++ b/NXDNFACCH1.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2018 by Jonathan Naylor G4KLX +* Copyright (C) 2018,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(NXDNFACCH1_H) #define NXDNFACCH1_H +#include "Defines.h" + +#if defined(USE_NXDN) + class CNXDNFACCH1 { public: CNXDNFACCH1(const CNXDNFACCH1& facch); @@ -42,3 +46,6 @@ private: }; #endif + +#endif + diff --git a/NXDNIcomNetwork.cpp b/NXDNIcomNetwork.cpp index ec37e04..5e730c6 100644 --- a/NXDNIcomNetwork.cpp +++ b/NXDNIcomNetwork.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014,2016,2018-2020,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2014,2016,2018-2020,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,10 +18,11 @@ #include "NXDNIcomNetwork.h" #include "NXDNDefines.h" -#include "Defines.h" #include "Utils.h" #include "Log.h" +#if defined(USE_NXDN) + #include #include #include @@ -174,3 +175,6 @@ void CNXDNIcomNetwork::enable(bool enabled) m_enabled = enabled; } + +#endif + diff --git a/NXDNIcomNetwork.h b/NXDNIcomNetwork.h index f417574..2eb4ff6 100644 --- a/NXDNIcomNetwork.h +++ b/NXDNIcomNetwork.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014,2016,2018,2020 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2014,2016,2018,2020,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,9 @@ #include "RingBuffer.h" #include "UDPSocket.h" #include "Timer.h" +#include "Defines.h" + +#if defined(USE_NXDN) #include #include @@ -59,3 +62,6 @@ private: }; #endif + +#endif + diff --git a/NXDNKenwoodNetwork.cpp b/NXDNKenwoodNetwork.cpp index a8138b6..e0c8083 100644 --- a/NXDNKenwoodNetwork.cpp +++ b/NXDNKenwoodNetwork.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014,2016,2018,2020,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2014,2016,2018,2020,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,8 @@ #include "Utils.h" #include "Log.h" +#if defined(USE_NXDN) + #include #include #include @@ -1185,3 +1187,6 @@ void CNXDNKenwoodNetwork::enable(bool enabled) m_enabled = enabled; } + +#endif + diff --git a/NXDNKenwoodNetwork.h b/NXDNKenwoodNetwork.h index 216176e..83ae69f 100644 --- a/NXDNKenwoodNetwork.h +++ b/NXDNKenwoodNetwork.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014,2016,2018,2020 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2014,2016,2018,2020,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,9 @@ #include "NXDNNetwork.h" #include "UDPSocket.h" #include "Timer.h" +#include "Defines.h" + +#if defined(USE_NXDN) #include #include @@ -100,3 +103,6 @@ private: }; #endif + +#endif + diff --git a/NXDNLICH.cpp b/NXDNLICH.cpp index c2b962e..5698fcd 100644 --- a/NXDNLICH.cpp +++ b/NXDNLICH.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2018,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,8 @@ #include "NXDNDefines.h" #include "NXDNLICH.h" +#if defined(USE_NXDN) + #include #include #include @@ -160,3 +162,6 @@ bool CNXDNLICH::getParity() const return false; } } + +#endif + diff --git a/NXDNLICH.h b/NXDNLICH.h index c3fe821..c58c71f 100644 --- a/NXDNLICH.h +++ b/NXDNLICH.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 by Jonathan Naylor G4KLX + * Copyright (C) 2018,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(NXDNLICH_H) #define NXDNLICH_H +#include "Defines.h" + +#if defined(USE_NXDN) + class CNXDNLICH { public: CNXDNLICH(const CNXDNLICH& lich); @@ -50,3 +54,6 @@ private: }; #endif + +#endif + diff --git a/NXDNLayer3.cpp b/NXDNLayer3.cpp index d3c45d0..e0ebc6c 100644 --- a/NXDNLayer3.cpp +++ b/NXDNLayer3.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2018,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2018,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,8 @@ #include "NXDNDefines.h" #include "NXDNLayer3.h" +#if defined(USE_NXDN) + #include #include #include @@ -115,3 +117,6 @@ CNXDNLayer3& CNXDNLayer3::operator=(const CNXDNLayer3& layer3) return *this; } + +#endif + diff --git a/NXDNLayer3.h b/NXDNLayer3.h index a152452..befd233 100644 --- a/NXDNLayer3.h +++ b/NXDNLayer3.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2018 by Jonathan Naylor G4KLX +* Copyright (C) 2018,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(NXDNLayer3_H) #define NXDNLayer3_H +#include "Defines.h" + +#if defined(USE_NXDN) + class CNXDNLayer3 { public: CNXDNLayer3(const CNXDNLayer3& layer3); @@ -47,3 +51,6 @@ private: }; #endif + +#endif + diff --git a/NXDNLookup.cpp b/NXDNLookup.cpp index 472f106..e21ddb0 100644 --- a/NXDNLookup.cpp +++ b/NXDNLookup.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2017,2018,2021,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2017,2018,2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,8 @@ #include "Timer.h" #include "Log.h" +#if defined(USE_NXDN) + #include #include #include @@ -134,3 +136,6 @@ bool CNXDNLookup::exists(unsigned int id) { return m_table.lookup(id, nullptr); } + +#endif + diff --git a/NXDNLookup.h b/NXDNLookup.h index c49e114..d7b0011 100644 --- a/NXDNLookup.h +++ b/NXDNLookup.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2017,2018 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2017,2018,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,9 @@ #include "Thread.h" #include "UserDB.h" +#include "Defines.h" + +#if defined(USE_NXDN) #include @@ -51,3 +54,6 @@ private: }; #endif + +#endif + diff --git a/NXDNNetwork.cpp b/NXDNNetwork.cpp index 0f55011..73d52ff 100644 --- a/NXDNNetwork.cpp +++ b/NXDNNetwork.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 by Jonathan Naylor G4KLX + * Copyright (C) 2020,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,11 @@ #include "NXDNNetwork.h" +#if defined(USE_NXDN) + INXDNNetwork::~INXDNNetwork() { } + +#endif + diff --git a/NXDNNetwork.h b/NXDNNetwork.h index 62e8521..b36319f 100644 --- a/NXDNNetwork.h +++ b/NXDNNetwork.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2020,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,9 @@ #define NXDNNetwork_H #include "NXDNDefines.h" +#include "Defines.h" + +#if defined(USE_NXDN) #include @@ -56,3 +59,6 @@ private: }; #endif + +#endif + diff --git a/NXDNSACCH.cpp b/NXDNSACCH.cpp index 1038dc2..d1664a9 100644 --- a/NXDNSACCH.cpp +++ b/NXDNSACCH.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2018,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2018,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,11 +17,12 @@ */ #include "NXDNSACCH.h" - #include "NXDNConvolution.h" #include "NXDNDefines.h" #include "NXDNCRC.h" +#if defined(USE_NXDN) + #include #include #include @@ -212,3 +213,6 @@ CNXDNSACCH& CNXDNSACCH::operator=(const CNXDNSACCH& sacch) return *this; } + +#endif + diff --git a/NXDNSACCH.h b/NXDNSACCH.h index 8d6f44a..3103fc0 100644 --- a/NXDNSACCH.h +++ b/NXDNSACCH.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2018 by Jonathan Naylor G4KLX +* Copyright (C) 2018,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(NXDNSACCH_H) #define NXDNSACCH_H +#include "Defines.h" + +#if defined(USE_NXDN) + class CNXDNSACCH { public: CNXDNSACCH(const CNXDNSACCH& sacch); @@ -48,3 +52,6 @@ private: }; #endif + +#endif + diff --git a/NXDNUDCH.cpp b/NXDNUDCH.cpp index fdc402b..ab8f11a 100644 --- a/NXDNUDCH.cpp +++ b/NXDNUDCH.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2018,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2018,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,11 +17,12 @@ */ #include "NXDNUDCH.h" - #include "NXDNConvolution.h" #include "NXDNDefines.h" #include "NXDNCRC.h" +#if defined(USE_NXDN) + #include #include #include @@ -215,3 +216,6 @@ CNXDNUDCH& CNXDNUDCH::operator=(const CNXDNUDCH& udch) return *this; } + +#endif + diff --git a/NXDNUDCH.h b/NXDNUDCH.h index 0db3bf6..4a5b4a7 100644 --- a/NXDNUDCH.h +++ b/NXDNUDCH.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2018 by Jonathan Naylor G4KLX +* Copyright (C) 2018,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(NXDNUDCH_H) #define NXDNUDCH_H +#include "Defines.h" + +#if defined(USE_NXDN) + class CNXDNUDCH { public: CNXDNUDCH(const CNXDNUDCH& udch); @@ -46,3 +50,6 @@ private: }; #endif + +#endif + diff --git a/NetworkInfo.cpp b/NetworkInfo.cpp deleted file mode 100644 index 514d03b..0000000 --- a/NetworkInfo.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2025 by Jonathan Naylor G4KLX - * Copyright (C) 2017 by Lieven De Samblanx ON7LDS - * - * 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 "NetworkInfo.h" -#include "Log.h" - -#include -#include -#include -#include -#include - -#include -#if defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) -#include -#include -#include -#include -#include -#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) -#include -#include -#include -#endif -#elif defined(_WIN32) || defined(_WIN64) -#include -#include -#include -#pragma comment(lib, "iphlpapi.lib") -#ifndef NO_ERROR -#define NO_ERROR 0 -#endif -#ifndef ERROR_BUFFER_OVERFLOW -#define ERROR_BUFFER_OVERFLOW 111 -#endif -#ifndef ERROR_INSUFFICIENT_BUFFER -#define ERROR_INSUFFICIENT_BUFFER 122 -#endif -#endif - -CNetworkInfo::CNetworkInfo() -{ -} - -CNetworkInfo::~CNetworkInfo() -{ -} - -void CNetworkInfo::getNetworkInterface(unsigned char* info) -{ - LogInfo("Interfaces Info"); - - ::strcpy((char*)info, "(address unknown)"); - -#if defined(__linux__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__APPLE__) - char* dflt = nullptr; - -#if defined(__linux__) - FILE* fp = ::fopen("/proc/net/route" , "r"); // IPv4 routing - if (fp == nullptr) { - LogError("Unabled to open /proc/route"); - return; - } - - char line[100U]; - while (::fgets(line, 100U, fp)) { - char* p1 = strtok(line , " \t"); - char* p2 = strtok(nullptr , " \t"); - - if (p1 != nullptr && p2 != nullptr) { - if (::strcmp(p2, "00000000") == 0) { - dflt = p1; - break; - } - } - } - - ::fclose(fp); - -#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) - int mib[] = { - CTL_NET, - PF_ROUTE, - 0, // protocol - AF_INET, // IPv4 routing - NET_RT_DUMP, - 0, // show all routes -#if defined(__OpenBSD__) || defined(__FreeBSD__) - 0, // table id -#endif - }; - const int cnt = sizeof(mib) / sizeof(int); - size_t size; - char ifname[IF_NAMESIZE] = {}; - - if (::sysctl(mib, cnt, nullptr, &size, nullptr, 0) == -1 || size <= 0) { - LogError("Unable to estimate routing table size"); - return; - } - - char *buf = new char[size]; - if (::sysctl(mib, cnt, buf, &size, nullptr, 0) == -1) { - LogError("Unable to get routing table"); - delete[] buf; - return; - } - - struct rt_msghdr *rtm; - for (char *p = buf; p < buf + size; p += rtm->rtm_msglen) { - rtm = (struct rt_msghdr *)p; - if (rtm->rtm_version != RTM_VERSION) - continue; -#if defined(__OpenBSD__) - struct sockaddr_in *sa = (struct sockaddr_in *)(p + rtm->rtm_hdrlen); -#elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__APPLE__) - struct sockaddr_in *sa = (struct sockaddr_in *)(rtm + 1); -#endif - if (sa->sin_addr.s_addr == INADDR_ANY) { - ::if_indextoname(rtm->rtm_index, ifname); - break; - } - } - - delete[] buf; - if (::strlen(ifname)) - dflt = ifname; -#endif - if (dflt == nullptr) { - LogError("Unable to find the default route"); - return; - } - - const unsigned int IFLISTSIZ = 25U; - char interfacelist[IFLISTSIZ][50+INET6_ADDRSTRLEN]; - for (unsigned int n = 0U; n < IFLISTSIZ; n++) - interfacelist[n][0] = 0; - - struct ifaddrs* ifaddr; - if (::getifaddrs(&ifaddr) == -1) { - LogError("getifaddrs failure"); - return; - } - - unsigned int ifnr = 0U; - for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == nullptr) - continue; - - int family = ifa->ifa_addr->sa_family; - if (family == AF_INET || family == AF_INET6) { - char host[NI_MAXHOST]; - int s = ::getnameinfo(ifa->ifa_addr, family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6), host, NI_MAXHOST, nullptr, 0, NI_NUMERICHOST); - if (s != 0) { - LogError("getnameinfo() failed: %s\n", gai_strerror(s)); - continue; - } - - if (family == AF_INET) { - ::sprintf(interfacelist[ifnr], "%s:%s", ifa->ifa_name, host); - LogInfo(" IPv4: %s", interfacelist[ifnr]); - ifnr++; - } else { - ::sprintf(interfacelist[ifnr], "%s:%s", ifa->ifa_name, host); - LogInfo(" IPv6: %s", interfacelist[ifnr]); - // due to default routing is for IPv4, other - // protocols are not candidate to display. - } - } - } - - ::freeifaddrs(ifaddr); - - LogInfo(" Default interface is : %s" , dflt); - - for (unsigned int n = 0U; n < ifnr; n++) { - char* p = ::strchr(interfacelist[n], '%'); - if (p != nullptr) - *p = 0; - - if (::strstr(interfacelist[n], dflt) != 0) { - ::strcpy((char*)info, interfacelist[n]); - break; - } - } - - LogInfo(" IP to show: %s", info); -#elif defined(_WIN32) || defined(_WIN64) - PMIB_IPFORWARDTABLE pIpForwardTable = (MIB_IPFORWARDTABLE *)::malloc(sizeof(MIB_IPFORWARDTABLE)); - if (pIpForwardTable == nullptr) { - LogError("Error allocating memory"); - return; - } - - DWORD dwSize = 0U; - if (::GetIpForwardTable(pIpForwardTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { - ::free(pIpForwardTable); - pIpForwardTable = (MIB_IPFORWARDTABLE *)::malloc(dwSize); - if (pIpForwardTable == nullptr) { - LogError("Error allocating memory"); - return; - } - } - - DWORD ret = ::GetIpForwardTable(pIpForwardTable, &dwSize, 0); - if (ret != NO_ERROR) { - ::free(pIpForwardTable); - LogError("GetIpForwardTable failed."); - return; - } - - DWORD found = 999U; - for (DWORD i = 0U; i < pIpForwardTable->dwNumEntries; i++) { - if (pIpForwardTable->table[i].dwForwardDest == 0U) { - found = i; - break; - } - } - - if (found == 999U) { - ::free(pIpForwardTable); - LogError("Unable to find the default destination in the routing table."); - return; - } - - DWORD ifnr = pIpForwardTable->table[found].dwForwardIfIndex; - ::free(pIpForwardTable); - - PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *)::malloc(sizeof(IP_ADAPTER_INFO)); - if (pAdapterInfo == nullptr) { - LogError("Error allocating memory"); - return; - } - - ULONG buflen = sizeof(IP_ADAPTER_INFO); - if (::GetAdaptersInfo(pAdapterInfo, &buflen) == ERROR_BUFFER_OVERFLOW) { - ::free(pAdapterInfo); - pAdapterInfo = (IP_ADAPTER_INFO *)::malloc(buflen); - if (pAdapterInfo == nullptr) { - LogError("Error allocating memory"); - return; - } - } - - if (::GetAdaptersInfo(pAdapterInfo, &buflen) != NO_ERROR) { - ::free(pAdapterInfo); - LogError("Call to GetAdaptersInfo failed."); - return; - } - - PIP_ADAPTER_INFO pAdapter = pAdapterInfo; - while (pAdapter != nullptr) { - LogInfo(" IP : %s", pAdapter->IpAddressList.IpAddress.String); - if (pAdapter->Index == ifnr) - ::strcpy((char*)info, pAdapter->IpAddressList.IpAddress.String); - - pAdapter = pAdapter->Next; - } - - ::free(pAdapterInfo); - - LogInfo(" IP to show: %s", info); -#endif -} diff --git a/NetworkInfo.h b/NetworkInfo.h deleted file mode 100644 index 2febcde..0000000 --- a/NetworkInfo.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2017 by Lieven De Samblanx ON7LDS - * - * 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(NETWORKINFO_H) -#define NETWORKINFO_H - -class CNetworkInfo { -public: - CNetworkInfo(); - ~CNetworkInfo(); - - void getNetworkInterface(unsigned char* info); - -private: -}; - -#endif diff --git a/Nextion.cpp b/Nextion.cpp deleted file mode 100644 index f2ce9fb..0000000 --- a/Nextion.cpp +++ /dev/null @@ -1,997 +0,0 @@ -/* - * Copyright (C) 2016,2017,2018,2020,2023,2024,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "NetworkInfo.h" -#include "Nextion.h" -#include "Log.h" - -#include -#include -#include -#include -#include - -const unsigned int DSTAR_RSSI_COUNT = 3U; // 3 * 420ms = 1260ms -const unsigned int DSTAR_BER_COUNT = 63U; // 63 * 20ms = 1260ms -const unsigned int DMR_RSSI_COUNT = 4U; // 4 * 360ms = 1440ms -const unsigned int DMR_BER_COUNT = 24U; // 24 * 60ms = 1440ms -const unsigned int YSF_RSSI_COUNT = 13U; // 13 * 100ms = 1300ms -const unsigned int YSF_BER_COUNT = 13U; // 13 * 100ms = 1300ms -const unsigned int P25_RSSI_COUNT = 7U; // 7 * 180ms = 1260ms -const unsigned int P25_BER_COUNT = 7U; // 7 * 180ms = 1260ms -const unsigned int NXDN_RSSI_COUNT = 28U; // 28 * 40ms = 1120ms -const unsigned int NXDN_BER_COUNT = 28U; // 28 * 40ms = 1120ms - -#define LAYOUT_COMPAT_MASK (7 << 0) // compatibility for old setting -#define LAYOUT_TA_ENABLE (1 << 4) // enable Talker Alias (TA) display -#define LAYOUT_TA_COLOUR (1 << 5) // TA display with font colour change -#define LAYOUT_TA_FONTSIZE (1 << 6) // TA display with font size change -#define LAYOUT_DIY (1 << 7) // use ON7LDS-DIY layout - -// bit[3:2] is used in Display.cpp to set connection speed for LCD panel. -// 00:low, others:high-speed. bit[2] is overlapped with LAYOUT_COMPAT_MASK. -#define LAYOUT_HIGHSPEED (3 << 2) - -CNextion::CNextion(const std::string& callsign, unsigned int dmrid, ISerialPort* serial, unsigned int brightness, bool displayClock, bool utc, unsigned int idleBrightness, unsigned int screenLayout, unsigned int txFrequency, unsigned int rxFrequency, bool displayTempInF, CUDPSocket* socket, struct sockaddr_storage& addr, unsigned int addrLength) : -CDisplay(), -m_callsign(callsign), -m_ipaddress("(ip unknown)"), -m_dmrid(dmrid), -m_serial(serial), -m_brightness(brightness), -m_mode(MODE_IDLE), -m_displayClock(displayClock), -m_utc(utc), -m_idleBrightness(idleBrightness), -m_screenLayout(0), -m_clockDisplayTimer(1000U, 0U, 400U), -m_rssiAccum1(0U), -m_rssiAccum2(0U), -m_berAccum1(0.0F), -m_berAccum2(0.0F), -m_rssiCount1(0U), -m_rssiCount2(0U), -m_berCount1(0U), -m_berCount2(0U), -m_txFrequency(txFrequency), -m_rxFrequency(rxFrequency), -m_fl_txFrequency(0.0F), -m_fl_rxFrequency(0.0F), -m_displayTempInF(displayTempInF), -m_socket(socket), -m_addr(addr), -m_addrLength(addrLength) -{ - assert(serial != nullptr); - assert(brightness >= 0U && brightness <= 100U); - assert(socket != nullptr); - assert(addrLength > 0U); - - static const unsigned int feature_set[] = { - 0, // 0: G4KLX - 0, // 1: (reserved, low speed) - // 2: ON7LDS - LAYOUT_TA_ENABLE | LAYOUT_TA_COLOUR | LAYOUT_TA_FONTSIZE, - LAYOUT_TA_ENABLE | LAYOUT_DIY, // 3: ON7LDS-DIY - LAYOUT_TA_ENABLE | LAYOUT_DIY, // 4: ON7LDS-DIY (high speed) - 0, // 5: (reserved, high speed) - 0, // 6: (reserved, high speed) - 0, // 7: (reserved, high speed) - }; - - if (screenLayout & ~LAYOUT_COMPAT_MASK) - m_screenLayout = screenLayout & ~LAYOUT_COMPAT_MASK; - else - m_screenLayout = feature_set[screenLayout]; -} - -CNextion::CNextion(const std::string& callsign, unsigned int dmrid, ISerialPort* serial, unsigned int brightness, bool displayClock, bool utc, unsigned int idleBrightness, unsigned int screenLayout, unsigned int txFrequency, unsigned int rxFrequency, bool displayTempInF) : -CDisplay(), -m_callsign(callsign), -m_ipaddress("(ip unknown)"), -m_dmrid(dmrid), -m_serial(serial), -m_brightness(brightness), -m_mode(MODE_IDLE), -m_displayClock(displayClock), -m_utc(utc), -m_idleBrightness(idleBrightness), -m_screenLayout(0), -m_clockDisplayTimer(1000U, 0U, 400U), -m_rssiAccum1(0U), -m_rssiAccum2(0U), -m_berAccum1(0.0F), -m_berAccum2(0.0F), -m_rssiCount1(0U), -m_rssiCount2(0U), -m_berCount1(0U), -m_berCount2(0U), -m_txFrequency(txFrequency), -m_rxFrequency(rxFrequency), -m_fl_txFrequency(0.0F), -m_fl_rxFrequency(0.0F), -m_displayTempInF(displayTempInF), -m_socket(nullptr), -m_addr(), -m_addrLength(0U) -{ - assert(serial != nullptr); - assert(brightness >= 0U && brightness <= 100U); - - static const unsigned int feature_set[] = { - 0, // 0: G4KLX - 0, // 1: (reserved, low speed) - // 2: ON7LDS - LAYOUT_TA_ENABLE | LAYOUT_TA_COLOUR | LAYOUT_TA_FONTSIZE, - LAYOUT_TA_ENABLE | LAYOUT_DIY, // 3: ON7LDS-DIY - LAYOUT_TA_ENABLE | LAYOUT_DIY, // 4: ON7LDS-DIY (high speed) - 0, // 5: (reserved, high speed) - 0, // 6: (reserved, high speed) - 0, // 7: (reserved, high speed) - }; - - if (screenLayout & ~LAYOUT_COMPAT_MASK) - m_screenLayout = screenLayout & ~LAYOUT_COMPAT_MASK; - else - m_screenLayout = feature_set[screenLayout]; -} - -CNextion::~CNextion() -{ -} - -bool CNextion::open() -{ - unsigned char info[100U]; - CNetworkInfo* m_network; - - bool ret = m_serial->open(); - if (!ret) { - LogError("Cannot open the port for the Nextion display"); - delete m_serial; - return false; - } - - info[0] = 0; - m_network = new CNetworkInfo; - m_network->getNetworkInterface(info); - m_ipaddress = (char*)info; - - sendCommand("bkcmd=0"); - sendCommandAction(0U); - - m_fl_txFrequency = double(m_txFrequency) / 1000000.0F; - m_fl_rxFrequency = double(m_rxFrequency) / 1000000.0F; - - setIdle(); - - return true; -} - -void CNextion::setIdleInt() -{ - // a few bits borrowed from Lieven De Samblanx ON7LDS, NextionDriver - char command[100U]; - - sendCommand("page MMDVM"); - sendCommandAction(1U); - - if (m_brightness > 0U) { - ::sprintf(command, "dim=%u", m_idleBrightness); - sendCommand(command); - } - - ::sprintf(command, "t0.txt=\"%s/%u\"", m_callsign.c_str(), m_dmrid); - sendCommand(command); - - if (m_screenLayout & LAYOUT_DIY) { - ::sprintf(command, "t4.txt=\"%s\"", m_callsign.c_str()); - sendCommand(command); - ::sprintf(command, "t5.txt=\"%u\"", m_dmrid); - sendCommand(command); - sendCommandAction(17U); - - ::sprintf(command, "t30.txt=\"%3.6f\"",m_fl_rxFrequency); // RX freq - sendCommand(command); - sendCommandAction(20U); - - ::sprintf(command, "t32.txt=\"%3.6f\"",m_fl_txFrequency); // TX freq - sendCommand(command); - sendCommandAction(21U); - - // CPU temperature - FILE* fp = ::fopen("/sys/class/thermal/thermal_zone0/temp", "rt"); - if (fp != nullptr) { - double val = 0.0; - int n = ::fscanf(fp, "%lf", &val); - ::fclose(fp); - - if (n == 1) { - val /= 1000.0; - if (m_displayTempInF) { - val = (1.8 * val) + 32.0; - ::sprintf(command, "t20.txt=\"%2.1f %cF\"", val, 176); - } else { - ::sprintf(command, "t20.txt=\"%2.1f %cC\"", val, 176); - } - sendCommand(command); - sendCommandAction(22U); - } - } - } else { - sendCommandAction(17U); - } - - sendCommand("t1.txt=\"MMDVM IDLE\""); - sendCommandAction(11U); - - ::sprintf(command, "t3.txt=\"%s\"", m_ipaddress.c_str()); - sendCommand(command); - sendCommandAction(16U); - - m_clockDisplayTimer.start(); - - m_mode = MODE_IDLE; -} - -void CNextion::setErrorInt(const char* text) -{ - assert(text != nullptr); - - sendCommand("page MMDVM"); - sendCommandAction(1U); - - char command[20]; - if (m_brightness > 0U) { - ::sprintf(command, "dim=%u", m_brightness); - sendCommand(command); - } - - ::sprintf(command, "t0.txt=\"%s\"", text); - sendCommandAction(13U); - - sendCommand(command); - sendCommand("t1.txt=\"ERROR\""); - sendCommandAction(14U); - - m_clockDisplayTimer.stop(); - - m_mode = MODE_ERROR; -} - -void CNextion::setLockoutInt() -{ - sendCommand("page MMDVM"); - sendCommandAction(1U); - - char command[20]; - if (m_brightness > 0U) { - ::sprintf(command, "dim=%u", m_brightness); - sendCommand(command); - } - - sendCommand("t0.txt=\"LOCKOUT\""); - sendCommandAction(15U); - - m_clockDisplayTimer.stop(); - - m_mode = MODE_LOCKOUT; -} - -void CNextion::setQuitInt() -{ - sendCommand("page MMDVM"); - sendCommandAction(1U); - - char command[100]; - if (m_brightness > 0U) { - ::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::setFMInt() -{ - sendCommand("page MMDVM"); - sendCommandAction(1U); - - char command[20]; - if (m_brightness > 0U) { - ::sprintf(command, "dim=%u", m_brightness); - sendCommand(command); - } - - sendCommand("t0.txt=\"FM\""); - sendCommandAction(18U); - - m_clockDisplayTimer.stop(); - - m_mode = MODE_FM; -} - -void CNextion::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) -{ - assert(my1 != nullptr); - assert(my2 != nullptr); - assert(your != nullptr); - assert(type != nullptr); - assert(reflector != nullptr); - - if (m_mode != MODE_DSTAR) { - sendCommand("page DStar"); - sendCommandAction(2U); - } - - char text[50U]; - if (m_brightness > 0U) { - ::sprintf(text, "dim=%u", m_brightness); - sendCommand(text); - } - - ::sprintf(text, "t0.txt=\"%s %.8s/%4.4s\"", type, my1, my2); - sendCommand(text); - sendCommandAction(42U); - - ::sprintf(text, "t1.txt=\"%.8s\"", your); - sendCommand(text); - sendCommandAction(45U); - - if (::strcmp(reflector, " ") != 0) { - ::sprintf(text, "t2.txt=\"via %.8s\"", reflector); - sendCommand(text); - sendCommandAction(46U); - } - - m_clockDisplayTimer.stop(); - - m_mode = MODE_DSTAR; - m_rssiAccum1 = 0U; - m_berAccum1 = 0.0F; - m_rssiCount1 = 0U; - m_berCount1 = 0U; -} - -void CNextion::writeDStarRSSIInt(unsigned char rssi) -{ - m_rssiAccum1 += rssi; - m_rssiCount1++; - - if (m_rssiCount1 == DSTAR_RSSI_COUNT) { - char text[25U]; - ::sprintf(text, "t3.txt=\"-%udBm\"", m_rssiAccum1 / DSTAR_RSSI_COUNT); - sendCommand(text); - sendCommandAction(47U); - m_rssiAccum1 = 0U; - m_rssiCount1 = 0U; - } -} - -void CNextion::writeDStarBERInt(float ber) -{ - m_berAccum1 += ber; - m_berCount1++; - - if (m_berCount1 == DSTAR_BER_COUNT) { - char text[25U]; - ::sprintf(text, "t4.txt=\"%.1f%%\"", m_berAccum1 / float(DSTAR_BER_COUNT)); - sendCommand(text); - sendCommandAction(48U); - m_berAccum1 = 0.0F; - m_berCount1 = 0U; - } -} - -void CNextion::clearDStarInt() -{ - sendCommand("t0.txt=\"Listening\""); - sendCommandAction(41U); - sendCommand("t1.txt=\"\""); - sendCommand("t2.txt=\"\""); - sendCommand("t3.txt=\"\""); - sendCommand("t4.txt=\"\""); -} - -void CNextion::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) -{ - assert(type != nullptr); - - if (m_mode != MODE_DMR) { - sendCommand("page DMR"); - sendCommandAction(3U); - - - if (slotNo == 1U) { - if (m_screenLayout & LAYOUT_TA_ENABLE) { - if (m_screenLayout & LAYOUT_TA_COLOUR) - sendCommand("t2.pco=0"); - if (m_screenLayout & LAYOUT_TA_FONTSIZE) - sendCommand("t2.font=4"); - } - - sendCommand("t2.txt=\"2 Listening\""); - sendCommandAction(69U); - } else { - if (m_screenLayout & LAYOUT_TA_ENABLE) { - if (m_screenLayout & LAYOUT_TA_COLOUR) - sendCommand("t0.pco=0"); - if (m_screenLayout & LAYOUT_TA_FONTSIZE) - sendCommand("t0.font=4"); - } - - sendCommand("t0.txt=\"1 Listening\""); - sendCommandAction(61U); - } - } - - char text[50U]; - if (m_brightness > 0U) { - ::sprintf(text, "dim=%u", m_brightness); - sendCommand(text); - } - - if (slotNo == 1U) { - ::sprintf(text, "t0.txt=\"1 %s %s\"", type, src.c_str()); - - if (m_screenLayout & LAYOUT_TA_ENABLE) { - if (m_screenLayout & LAYOUT_TA_COLOUR) - sendCommand("t0.pco=0"); - if (m_screenLayout & LAYOUT_TA_FONTSIZE) - sendCommand("t0.font=4"); - } - - sendCommand(text); - sendCommandAction(62U); - - ::sprintf(text, "t1.txt=\"%s%s\"", group ? "TG" : "", dst.c_str()); - sendCommand(text); - sendCommandAction(65U); - } else { - ::sprintf(text, "t2.txt=\"2 %s %s\"", type, src.c_str()); - - if (m_screenLayout & LAYOUT_TA_ENABLE) { - if (m_screenLayout & LAYOUT_TA_COLOUR) - sendCommand("t2.pco=0"); - if (m_screenLayout & LAYOUT_TA_FONTSIZE) - sendCommand("t2.font=4"); - } - - sendCommand(text); - sendCommandAction(70U); - - ::sprintf(text, "t3.txt=\"%s%s\"", group ? "TG" : "", dst.c_str()); - sendCommand(text); - sendCommandAction(73U); - } - - m_clockDisplayTimer.stop(); - - m_mode = MODE_DMR; - m_rssiAccum1 = 0U; - m_rssiAccum2 = 0U; - m_berAccum1 = 0.0F; - m_berAccum2 = 0.0F; - m_rssiCount1 = 0U; - m_rssiCount2 = 0U; - m_berCount1 = 0U; - m_berCount2 = 0U; -} - -void CNextion::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi) -{ - if (slotNo == 1U) { - m_rssiAccum1 += rssi; - m_rssiCount1++; - - if (m_rssiCount1 == DMR_RSSI_COUNT) { - char text[25U]; - ::sprintf(text, "t4.txt=\"-%udBm\"", m_rssiAccum1 / DMR_RSSI_COUNT); - sendCommand(text); - sendCommandAction(66U); - m_rssiAccum1 = 0U; - m_rssiCount1 = 0U; - } - } else { - m_rssiAccum2 += rssi; - m_rssiCount2++; - - if (m_rssiCount2 == DMR_RSSI_COUNT) { - char text[25U]; - ::sprintf(text, "t5.txt=\"-%udBm\"", m_rssiAccum2 / DMR_RSSI_COUNT); - sendCommand(text); - sendCommandAction(74U); - m_rssiAccum2 = 0U; - m_rssiCount2 = 0U; - } - } -} - -void CNextion::writeDMRTAInt(unsigned int slotNo, const unsigned char* talkerAlias, const char* type) -{ - if (!(m_screenLayout & LAYOUT_TA_ENABLE)) - return; - - if (type[0] == ' ') { - if (slotNo == 1U) { - if (m_screenLayout & LAYOUT_TA_COLOUR) - sendCommand("t8.pco=33808"); - sendCommandAction(64U); - } else { - if (m_screenLayout & LAYOUT_TA_COLOUR) - sendCommand("t9.pco=33808"); - sendCommandAction(72U); - } - - return; - } - - if (slotNo == 1U) { - char text[50U]; - ::sprintf(text, "t8.txt=\"%s %s\"", type, talkerAlias); - - if (m_screenLayout & LAYOUT_TA_FONTSIZE) { - if (::strlen((char*)talkerAlias) > (16U-4U)) - sendCommand("t8.font=3"); - if (::strlen((char*)talkerAlias) > (20U-4U)) - sendCommand("t8.font=2"); - if (::strlen((char*)talkerAlias) > (24U-4U)) - sendCommand("t8.font=1"); - } - - if (m_screenLayout & LAYOUT_TA_COLOUR) - sendCommand("t8.pco=1024"); - - sendCommand(text); - sendCommandAction(63U); - } else { - char text[50U]; - ::sprintf(text, "t9.txt=\"%s %s\"", type, talkerAlias); - - if (m_screenLayout & LAYOUT_TA_FONTSIZE) { - if (::strlen((char*)talkerAlias) > (16U-4U)) - sendCommand("t9.font=3"); - if (::strlen((char*)talkerAlias) > (20U-4U)) - sendCommand("t9.font=2"); - if (::strlen((char*)talkerAlias) > (24U-4U)) - sendCommand("t9.font=1"); - } - - if (m_screenLayout & LAYOUT_TA_COLOUR) - sendCommand("t9.pco=1024"); - - sendCommand(text); - sendCommandAction(71U); - } -} - -void CNextion::writeDMRBERInt(unsigned int slotNo, float ber) -{ - if (slotNo == 1U) { - m_berAccum1 += ber; - m_berCount1++; - - if (m_berCount1 == DMR_BER_COUNT) { - char text[25U]; - ::sprintf(text, "t6.txt=\"%.1f%%\"", m_berAccum1 / DMR_BER_COUNT); - sendCommand(text); - sendCommandAction(67U); - m_berAccum1 = 0U; - m_berCount1 = 0U; - } - } else { - m_berAccum2 += ber; - m_berCount2++; - - if (m_berCount2 == DMR_BER_COUNT) { - char text[25U]; - ::sprintf(text, "t7.txt=\"%.1f%%\"", m_berAccum2 / DMR_BER_COUNT); - sendCommand(text); - sendCommandAction(75U); - m_berAccum2 = 0U; - m_berCount2 = 0U; - } - } -} - -void CNextion::clearDMRInt(unsigned int slotNo) -{ - - if (slotNo == 1U) { - sendCommand("t0.txt=\"1 Listening\""); - sendCommandAction(61U); - - if (m_screenLayout & LAYOUT_TA_ENABLE) { - if (m_screenLayout & LAYOUT_TA_COLOUR) - sendCommand("t0.pco=0"); - if (m_screenLayout & LAYOUT_TA_FONTSIZE) - sendCommand("t0.font=4"); - } - - sendCommand("t1.txt=\"\""); - sendCommand("t4.txt=\"\""); - sendCommand("t6.txt=\"\""); - sendCommand("t8.txt=\"\""); - } else { - sendCommand("t2.txt=\"2 Listening\""); - sendCommandAction(69U); - - if (m_screenLayout & LAYOUT_TA_ENABLE) { - if (m_screenLayout & LAYOUT_TA_COLOUR) - sendCommand("t2.pco=0"); - if (m_screenLayout & LAYOUT_TA_FONTSIZE) - sendCommand("t2.font=4"); - } - - sendCommand("t3.txt=\"\""); - sendCommand("t5.txt=\"\""); - sendCommand("t7.txt=\"\""); - sendCommand("t9.txt=\"\""); - } -} - -void CNextion::writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) -{ - assert(source != nullptr); - assert(dest != nullptr); - assert(type != nullptr); - assert(origin != nullptr); - - if (m_mode != MODE_YSF) { - sendCommand("page YSF"); - sendCommandAction(4U); - } - - - char text[30U]; - if (m_brightness > 0U) { - ::sprintf(text, "dim=%u", m_brightness); - sendCommand(text); - } - - ::sprintf(text, "t0.txt=\"%s %.10s\"", type, source); - sendCommand(text); - sendCommandAction(82U); - - ::sprintf(text, "t1.txt=\"DG-ID %u\"", dgid); - sendCommand(text); - sendCommandAction(83U); - - if (::strcmp(origin, " ") != 0) { - ::sprintf(text, "t2.txt=\"at %.10s\"", origin); - sendCommand(text); - sendCommandAction(84U); - } - - m_clockDisplayTimer.stop(); - - m_mode = MODE_YSF; - m_rssiAccum1 = 0U; - m_berAccum1 = 0.0F; - m_rssiCount1 = 0U; - m_berCount1 = 0U; -} - -void CNextion::writeFusionRSSIInt(unsigned char rssi) -{ - m_rssiAccum1 += rssi; - m_rssiCount1++; - - if (m_rssiCount1 == YSF_RSSI_COUNT) { - char text[25U]; - ::sprintf(text, "t3.txt=\"-%udBm\"", m_rssiAccum1 / YSF_RSSI_COUNT); - sendCommand(text); - sendCommandAction(85U); - m_rssiAccum1 = 0U; - m_rssiCount1 = 0U; - } -} - -void CNextion::writeFusionBERInt(float ber) -{ - m_berAccum1 += ber; - m_berCount1++; - - if (m_berCount1 == YSF_BER_COUNT) { - char text[25U]; - ::sprintf(text, "t4.txt=\"%.1f%%\"", m_berAccum1 / float(YSF_BER_COUNT)); - sendCommand(text); - sendCommandAction(86U); - m_berAccum1 = 0.0F; - m_berCount1 = 0U; - } -} - -void CNextion::clearFusionInt() -{ - sendCommand("t0.txt=\"Listening\""); - sendCommandAction(81U); - sendCommand("t1.txt=\"\""); - sendCommand("t2.txt=\"\""); - sendCommand("t3.txt=\"\""); - sendCommand("t4.txt=\"\""); -} - -void CNextion::writeP25Int(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(source != nullptr); - assert(type != nullptr); - - if (m_mode != MODE_P25) { - sendCommand("page P25"); - sendCommandAction(5U); - } - - char text[30U]; - if (m_brightness > 0U) { - ::sprintf(text, "dim=%u", m_brightness); - sendCommand(text); - } - - ::sprintf(text, "t0.txt=\"%s %.10s\"", type, source); - sendCommand(text); - sendCommandAction(102U); - - ::sprintf(text, "t1.txt=\"%s%u\"", group ? "TG" : "", dest); - sendCommand(text); - sendCommandAction(103U); - - m_clockDisplayTimer.stop(); - - m_mode = MODE_P25; - m_rssiAccum1 = 0U; - m_berAccum1 = 0.0F; - m_rssiCount1 = 0U; - m_berCount1 = 0U; -} - -void CNextion::writeP25RSSIInt(unsigned char rssi) -{ - m_rssiAccum1 += rssi; - m_rssiCount1++; - - if (m_rssiCount1 == P25_RSSI_COUNT) { - char text[25U]; - ::sprintf(text, "t2.txt=\"-%udBm\"", m_rssiAccum1 / P25_RSSI_COUNT); - sendCommand(text); - sendCommandAction(104U); - m_rssiAccum1 = 0U; - m_rssiCount1 = 0U; - } -} - -void CNextion::writeP25BERInt(float ber) -{ - m_berAccum1 += ber; - m_berCount1++; - - if (m_berCount1 == P25_BER_COUNT) { - char text[25U]; - ::sprintf(text, "t3.txt=\"%.1f%%\"", m_berAccum1 / float(P25_BER_COUNT)); - sendCommand(text); - sendCommandAction(105U); - m_berAccum1 = 0.0F; - m_berCount1 = 0U; - } -} - -void CNextion::clearP25Int() -{ - sendCommand("t0.txt=\"Listening\""); - sendCommandAction(101U); - sendCommand("t1.txt=\"\""); - sendCommand("t2.txt=\"\""); - sendCommand("t3.txt=\"\""); -} - -void CNextion::writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(source != nullptr); - assert(type != nullptr); - - if (m_mode != MODE_NXDN) { - sendCommand("page NXDN"); - sendCommandAction(6U); - } - - char text[30U]; - if (m_brightness > 0U) { - ::sprintf(text, "dim=%u", m_brightness); - sendCommand(text); - } - - ::sprintf(text, "t0.txt=\"%s %.10s\"", type, source); - sendCommand(text); - sendCommandAction(122U); - - ::sprintf(text, "t1.txt=\"%s%u\"", group ? "TG" : "", dest); - sendCommand(text); - sendCommandAction(123U); - - m_clockDisplayTimer.stop(); - - m_mode = MODE_NXDN; - m_rssiAccum1 = 0U; - m_berAccum1 = 0.0F; - m_rssiCount1 = 0U; - m_berCount1 = 0U; -} - -void CNextion::writeNXDNRSSIInt(unsigned char rssi) -{ - m_rssiAccum1 += rssi; - m_rssiCount1++; - - if (m_rssiCount1 == NXDN_RSSI_COUNT) { - char text[25U]; - ::sprintf(text, "t2.txt=\"-%udBm\"", m_rssiAccum1 / NXDN_RSSI_COUNT); - sendCommand(text); - sendCommandAction(124U); - m_rssiAccum1 = 0U; - m_rssiCount1 = 0U; - } -} - -void CNextion::writeNXDNBERInt(float ber) -{ - m_berAccum1 += ber; - m_berCount1++; - - if (m_berCount1 == NXDN_BER_COUNT) { - char text[25U]; - ::sprintf(text, "t3.txt=\"%.1f%%\"", m_berAccum1 / float(NXDN_BER_COUNT)); - sendCommand(text); - sendCommandAction(125U); - m_berAccum1 = 0.0F; - m_berCount1 = 0U; - } -} - -void CNextion::clearNXDNInt() -{ - sendCommand("t0.txt=\"Listening\""); - sendCommandAction(121U); - sendCommand("t1.txt=\"\""); - sendCommand("t2.txt=\"\""); - sendCommand("t3.txt=\"\""); -} - -void CNextion::writePOCSAGInt(uint32_t ric, const std::string& message) -{ - if (m_mode != MODE_POCSAG) { - sendCommand("page POCSAG"); - sendCommandAction(7U); - } - - char text[200U]; - if (m_brightness > 0U) { - ::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\""); - sendCommandAction(12U); - m_clockDisplayTimer.start(); - - m_mode = MODE_CW; -} - -void CNextion::clearCWInt() -{ - sendCommand("t1.txt=\"MMDVM IDLE\""); - sendCommandAction(11U); -} - -void CNextion::clockInt(unsigned int ms) -{ - // Update the clock display in IDLE mode every 400ms - m_clockDisplayTimer.clock(ms); - if (m_displayClock && (m_mode == MODE_IDLE || m_mode == MODE_CW) && m_clockDisplayTimer.isRunning() && m_clockDisplayTimer.hasExpired()) { - time_t currentTime; - struct tm *Time; - ::time(¤tTime); // Get the current time - - if (m_utc) - Time = ::gmtime(¤tTime); - else - Time = ::localtime(¤tTime); - - setlocale(LC_TIME,""); - char text[50U]; - strftime(text, 50, "t2.txt=\"%x %X\"", Time); - sendCommand(text); - - m_clockDisplayTimer.start(); // restart the clock display timer - } - - if (m_socket != nullptr) { - unsigned char buffer[200U]; - - int len = m_serial->read(buffer, 200U); - if (len > 0) - m_socket->write(buffer, len, m_addr, m_addrLength); - } -} - -void CNextion::close() -{ - m_serial->close(); - delete m_serial; - - if (m_socket != nullptr) { - m_socket->close(); - delete m_socket; - } -} - -void CNextion::sendCommandAction(unsigned int status) -{ - if (!(m_screenLayout & LAYOUT_DIY)) - return; - - char text[30U]; - ::sprintf(text, "MMDVM.status.val=%d", status); - sendCommand(text); - - sendCommand("click S0,1"); -} - -void CNextion::sendCommand(const char* command) -{ - assert(command != nullptr); - - m_serial->write((unsigned char*)command, (unsigned int)::strlen(command)); - m_serial->write((unsigned char*)"\xFF\xFF\xFF", 3U); - - // Since we just firing commands at the display, and not listening for the response, - // we must add a bit of a delay to allow the display to process the commands, else some are getting mangled. - // 10 ms is just a guess, but seems to be sufficient. - CThread::sleep(10U); -} - diff --git a/Nextion.h b/Nextion.h deleted file mode 100644 index cbdfe22..0000000 --- a/Nextion.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2016,2017,2018,2020,2023,2024,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#if !defined(NEXTION_H) -#define NEXTION_H - -#include "Display.h" -#include "Defines.h" -#include "SerialPort.h" -#include "UDPSocket.h" -#include "Timer.h" -#include "Thread.h" -#include - -class CNextion : public CDisplay -{ -public: - CNextion(const std::string& callsign, unsigned int dmrid, ISerialPort* serial, unsigned int brightness, bool displayClock, bool utc, unsigned int idleBrightness, unsigned int screenLayout, unsigned int txFrequency, unsigned int rxFrequency, bool displayTempInF, CUDPSocket* socket, struct sockaddr_storage& addr, unsigned int addrLength); - CNextion(const std::string& callsign, unsigned int dmrid, ISerialPort* serial, unsigned int brightness, bool displayClock, bool utc, unsigned int idleBrightness, unsigned int screenLayout, unsigned int txFrequency, unsigned int rxFrequency, bool displayTempInF); - virtual ~CNextion(); - - virtual bool open(); - - virtual void close(); - -protected: - virtual void setIdleInt(); - virtual void setErrorInt(const char* text); - virtual void setLockoutInt(); - virtual void setQuitInt(); - virtual void setFMInt(); - - virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); - virtual void writeDStarRSSIInt(unsigned char rssi); - virtual void writeDStarBERInt(float ber); - virtual void clearDStarInt(); - - virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); - virtual void writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi); - virtual void writeDMRTAInt(unsigned int slotNo, const unsigned char* talkerAlias, const char* type); - virtual void writeDMRBERInt(unsigned int slotNo, float ber); - virtual void clearDMRInt(unsigned int slotNo); - - virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin); - virtual void writeFusionRSSIInt(unsigned char rssi); - virtual void writeFusionBERInt(float ber); - virtual void clearFusionInt(); - - virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type); - virtual void writeP25RSSIInt(unsigned char rssi); - virtual void writeP25BERInt(float ber); - virtual void clearP25Int(); - - virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); - virtual void writeNXDNRSSIInt(unsigned char rssi); - 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(); - - virtual void clockInt(unsigned int ms); - -private: - std::string m_callsign; - std::string m_ipaddress; - unsigned int m_dmrid; - ISerialPort* m_serial; - unsigned int m_brightness; - unsigned char m_mode; - bool m_displayClock; - bool m_utc; - unsigned int m_idleBrightness; - unsigned int m_screenLayout; - CTimer m_clockDisplayTimer; - unsigned int m_rssiAccum1; - unsigned int m_rssiAccum2; - float m_berAccum1; - float m_berAccum2; - unsigned int m_rssiCount1; - unsigned int m_rssiCount2; - unsigned int m_berCount1; - unsigned int m_berCount2; - unsigned int m_txFrequency; - unsigned int m_rxFrequency; - double m_fl_txFrequency; - double m_fl_rxFrequency; - bool m_displayTempInF; - CUDPSocket* m_socket; - struct sockaddr_storage m_addr; - unsigned int m_addrLength; - - void sendCommand(const char* command); - void sendCommandAction(unsigned int status); -}; - -#endif diff --git a/Nextion_DB2OE/NX3224K024.HMI b/Nextion_DB2OE/NX3224K024.HMI deleted file mode 100644 index 183e5fd..0000000 Binary files a/Nextion_DB2OE/NX3224K024.HMI and /dev/null differ diff --git a/Nextion_DB2OE/NX3224K024.tft b/Nextion_DB2OE/NX3224K024.tft deleted file mode 100644 index 9c44cb5..0000000 Binary files a/Nextion_DB2OE/NX3224K024.tft and /dev/null differ diff --git a/Nextion_DB2OE/NX3224K028.HMI b/Nextion_DB2OE/NX3224K028.HMI deleted file mode 100644 index 41c180a..0000000 Binary files a/Nextion_DB2OE/NX3224K028.HMI and /dev/null differ diff --git a/Nextion_DB2OE/NX3224K028.tft b/Nextion_DB2OE/NX3224K028.tft deleted file mode 100644 index d71ad25..0000000 Binary files a/Nextion_DB2OE/NX3224K028.tft and /dev/null differ diff --git a/Nextion_DB2OE/NX3224T024.HMI b/Nextion_DB2OE/NX3224T024.HMI deleted file mode 100644 index f41718c..0000000 Binary files a/Nextion_DB2OE/NX3224T024.HMI and /dev/null differ diff --git a/Nextion_DB2OE/NX3224T024.tft b/Nextion_DB2OE/NX3224T024.tft deleted file mode 100644 index db4d491..0000000 Binary files a/Nextion_DB2OE/NX3224T024.tft and /dev/null differ diff --git a/Nextion_DB2OE/NX3224T028.HMI b/Nextion_DB2OE/NX3224T028.HMI deleted file mode 100644 index 56f8dc9..0000000 Binary files a/Nextion_DB2OE/NX3224T028.HMI and /dev/null differ diff --git a/Nextion_DB2OE/NX3224T028.tft b/Nextion_DB2OE/NX3224T028.tft deleted file mode 100644 index 7f335de..0000000 Binary files a/Nextion_DB2OE/NX3224T028.tft and /dev/null differ diff --git a/Nextion_DB2OE/NX4024K032.HMI b/Nextion_DB2OE/NX4024K032.HMI deleted file mode 100644 index 96dde1c..0000000 Binary files a/Nextion_DB2OE/NX4024K032.HMI and /dev/null differ diff --git a/Nextion_DB2OE/NX4024K032.tft b/Nextion_DB2OE/NX4024K032.tft deleted file mode 100644 index 3b661fb..0000000 Binary files a/Nextion_DB2OE/NX4024K032.tft and /dev/null differ diff --git a/Nextion_DB2OE/NX4024T032.HMI b/Nextion_DB2OE/NX4024T032.HMI deleted file mode 100644 index 9ba4b5e..0000000 Binary files a/Nextion_DB2OE/NX4024T032.HMI and /dev/null differ diff --git a/Nextion_DB2OE/NX4024T032.tft b/Nextion_DB2OE/NX4024T032.tft deleted file mode 100644 index a18610b..0000000 Binary files a/Nextion_DB2OE/NX4024T032.tft and /dev/null differ diff --git a/Nextion_DB2OE/NX4827K043.HMI b/Nextion_DB2OE/NX4827K043.HMI deleted file mode 100644 index 561300b..0000000 Binary files a/Nextion_DB2OE/NX4827K043.HMI and /dev/null differ diff --git a/Nextion_DB2OE/NX4827K043.tft b/Nextion_DB2OE/NX4827K043.tft deleted file mode 100644 index 5a25191..0000000 Binary files a/Nextion_DB2OE/NX4827K043.tft and /dev/null differ diff --git a/Nextion_DB2OE/NX4827T043.HMI b/Nextion_DB2OE/NX4827T043.HMI deleted file mode 100644 index be9fa45..0000000 Binary files a/Nextion_DB2OE/NX4827T043.HMI and /dev/null differ diff --git a/Nextion_DB2OE/NX4827T043.tft b/Nextion_DB2OE/NX4827T043.tft deleted file mode 100644 index 8ec3ae9..0000000 Binary files a/Nextion_DB2OE/NX4827T043.tft and /dev/null differ diff --git a/Nextion_G4KLX/NX3224K024.HMI b/Nextion_G4KLX/NX3224K024.HMI deleted file mode 100644 index ef8698b..0000000 Binary files a/Nextion_G4KLX/NX3224K024.HMI and /dev/null differ diff --git a/Nextion_G4KLX/NX3224K024.tft b/Nextion_G4KLX/NX3224K024.tft deleted file mode 100644 index b1a66da..0000000 Binary files a/Nextion_G4KLX/NX3224K024.tft and /dev/null differ diff --git a/Nextion_G4KLX/NX3224K028.HMI b/Nextion_G4KLX/NX3224K028.HMI deleted file mode 100644 index f6c1475..0000000 Binary files a/Nextion_G4KLX/NX3224K028.HMI and /dev/null differ diff --git a/Nextion_G4KLX/NX3224K028.tft b/Nextion_G4KLX/NX3224K028.tft deleted file mode 100644 index 28b552d..0000000 Binary files a/Nextion_G4KLX/NX3224K028.tft and /dev/null differ diff --git a/Nextion_G4KLX/NX3224T024.HMI b/Nextion_G4KLX/NX3224T024.HMI deleted file mode 100644 index ae1204d..0000000 Binary files a/Nextion_G4KLX/NX3224T024.HMI and /dev/null differ diff --git a/Nextion_G4KLX/NX3224T024.tft b/Nextion_G4KLX/NX3224T024.tft deleted file mode 100644 index 06ad235..0000000 Binary files a/Nextion_G4KLX/NX3224T024.tft and /dev/null differ diff --git a/Nextion_G4KLX/NX3224T028.HMI b/Nextion_G4KLX/NX3224T028.HMI deleted file mode 100644 index 744dd07..0000000 Binary files a/Nextion_G4KLX/NX3224T028.HMI and /dev/null differ diff --git a/Nextion_G4KLX/NX3224T028.tft b/Nextion_G4KLX/NX3224T028.tft deleted file mode 100644 index 2209493..0000000 Binary files a/Nextion_G4KLX/NX3224T028.tft and /dev/null differ diff --git a/Nextion_G4KLX/NX4024K032.HMI b/Nextion_G4KLX/NX4024K032.HMI deleted file mode 100644 index 85cdc48..0000000 Binary files a/Nextion_G4KLX/NX4024K032.HMI and /dev/null differ diff --git a/Nextion_G4KLX/NX4024K032.tft b/Nextion_G4KLX/NX4024K032.tft deleted file mode 100644 index 04da59a..0000000 Binary files a/Nextion_G4KLX/NX4024K032.tft and /dev/null differ diff --git a/Nextion_G4KLX/NX4024T032.HMI b/Nextion_G4KLX/NX4024T032.HMI deleted file mode 100644 index 10dbda4..0000000 Binary files a/Nextion_G4KLX/NX4024T032.HMI and /dev/null differ diff --git a/Nextion_G4KLX/NX4024T032.tft b/Nextion_G4KLX/NX4024T032.tft deleted file mode 100644 index 31ba971..0000000 Binary files a/Nextion_G4KLX/NX4024T032.tft and /dev/null differ diff --git a/Nextion_G4KLX/NX4832K035.HMI b/Nextion_G4KLX/NX4832K035.HMI deleted file mode 100644 index 3693082..0000000 Binary files a/Nextion_G4KLX/NX4832K035.HMI and /dev/null differ diff --git a/Nextion_G4KLX/NX4832K035.tft b/Nextion_G4KLX/NX4832K035.tft deleted file mode 100644 index ff3678d..0000000 Binary files a/Nextion_G4KLX/NX4832K035.tft and /dev/null differ diff --git a/Nextion_G4KLX/NX4832T035.HMI b/Nextion_G4KLX/NX4832T035.HMI deleted file mode 100644 index cda3ced..0000000 Binary files a/Nextion_G4KLX/NX4832T035.HMI and /dev/null differ diff --git a/Nextion_G4KLX/NX4832T035.tft b/Nextion_G4KLX/NX4832T035.tft deleted file mode 100644 index dd9c7a2..0000000 Binary files a/Nextion_G4KLX/NX4832T035.tft and /dev/null differ diff --git a/Nextion_G4KLX/README.md b/Nextion_G4KLX/README.md deleted file mode 100644 index f0367f5..0000000 --- a/Nextion_G4KLX/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# Update Nextion Displays from the Command Line - -This directory contains a simple python script which you can use to update the -Nextion displays. All you need is a compiled .tft file which is written to the -display's flash memory. The precompiled .tft files with the MMDVMHost default -layout are to be found in this directory as well. - -To update the Nextion display you just need to know the serial port the display -is connected to. It could be /dev/ttyUSBx for USB<->Serial adapters or -/dev/ttyAMA0 for the UART on the Raspberry Pi for example. - -# Prerequisites - -You need to have python installed as well as the python-serial package. That can -normally be found in your distro's package manager. - -# File Naming Convention - -There are compiled .tft files in the repo for basic and enhanced Nextion -displays of sizes 2.4", 2.8", 3.2" and 3.5". Please choose depending on the -model number printed on the back of the display. - -The basic displays are denoted by a "T" as 7th character in the filename and -model number whereas enhanced displays have a "K" in that position. The actual -display size can be derived from the last two digits. The four digits in between -the characters refert to the diplay's resolution. - -For example: A model number NX4832T035 represents a display with: - - - a resolution of 320x480 pixels - - basic command set - - a screen size of 3.5" - -# Updating the Display - -Now comes the easy part. Just execute: - -``` -$ python nextion.py NX4832T035.tft /dev/ttyUSB0 -``` - -And the output should be as follows: - -``` -Trying with baudrate: 2400... -Trying with baudrate: 4800... -Trying with baudrate: 9600... -Connected with baudrate: 9600... -Status: comok -Touchscreen: yes -Model: NX4832T035_011R -Firmware version: 68 -MCU code: 61488 -Serial: D2658880C35D2124 -Flash size: 16777216 -Downloading, 100%... -File transferred successfully -``` - -# Known errors - -Especially when using USB<->Serial adapters there are cases in which the scripts -stops at different times. This is known and due to the very simple update -protocol. In this case you have to fix the display by using a SD-Card to update -the display. The /dev/ttyAMAx ports do not seems to suffer from this issue. - diff --git a/Nextion_G4KLX/nextion.py b/Nextion_G4KLX/nextion.py deleted file mode 100644 index b9e501e..0000000 --- a/Nextion_G4KLX/nextion.py +++ /dev/null @@ -1,136 +0,0 @@ -''' - * Copyright (C) 2016 Alex Koren - * - * 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. -''' - -import serial -import time -import sys -import os -import re - -e = "\xff\xff\xff" - -def getBaudrate(ser, fSize=None, checkModel=None): - for baudrate in (2400, 4800, 9600, 19200, 38400, 57600, 115200): - ser.baudrate = baudrate - ser.timeout = 3000 / baudrate + .2 - print 'Trying with baudrate: ' + str(baudrate) + '...' - ser.write("\xff\xff\xff") - ser.write('connect') - ser.write("\xff\xff\xff") - r = ser.read(128) - if 'comok' in r: - print 'Connected with baudrate: ' + str(baudrate) + '...' - noConnect = False - status, unknown1, model, fwversion, mcucode, serial, flashSize = r.strip("\xff\x00").split(',') - print 'Status: ' + status.split(' ')[0] - if status.split(' ')[1] == "1": - print 'Touchscreen: yes' - else: - print 'Touchscreen: no' - print 'Model: ' + model - print 'Firmware version: ' + fwversion - print 'MCU code: ' + mcucode - print 'Serial: ' + serial - print 'Flash size: ' + flashSize - if fSize and fSize > flashSize: - print 'File too big!' - return False - if checkModel and not checkModel in model: - print 'Wrong Display!' - return False - return True - return False - -def setDownloadBaudrate(ser, fSize, baudrate): - ser.write("") - ser.write("whmi-wri " + str(fSize) + "," + str(baudrate) + ",0" + e) - time.sleep(.05) - ser.baudrate = baudrate - ser.timeout = .5 - r = ser.read(1) - if "\x05" in r: - return True - return False - -def transferFile(ser, filename, fSize): - with open(filename, 'rb') as hmif: - dcount = 0 - while True: - data = hmif.read(4096) - if len(data) == 0: - break - dcount += len(data) - ser.timeout = 5 - ser.write(data) - sys.stdout.write('\rDownloading, %3.1f%%...'% (dcount / float(fSize) * 100.0)) - sys.stdout.flush() - ser.timeout = .5 - time.sleep(.5) - r = ser.read(1) - if "\x05" in r: - continue - else: - print - return False - break - print - return True - -def upload(ser, filename, checkModel=None): - if not getBaudrate(ser, os.path.getsize(filename), checkModel): - print 'Could not find baudrate' - exit(1) - - if not setDownloadBaudrate(ser, os.path.getsize(filename), 115200): - print 'Could not set download baudrate' - exit(1) - - if not transferFile(ser, filename, os.path.getsize(filename)): - print 'Could not transfer file' - exit(1) - - print 'File transferred successfully' - exit(0) - -if __name__ == "__main__": - if len(sys.argv) != 4 and len(sys.argv) != 3: - print 'usage:\npython nextion.py file_to_upload.tft /path/to/dev/ttyDevice [nextion_model_name]\ - \nexample: nextion.py newUI.tft /dev/ttyUSB0 NX3224T024\ - \nnote: model name is optional' - exit(1) - - try: - ser = serial.Serial(sys.argv[2], 9600, timeout=5) - except serial.serialutil.SerialException: - print 'could not open serial device ' + sys.argv[2] - exit(1) - if serial.VERSION <= "3.0": - if not ser.isOpen(): - ser.open() - else: - if not ser.is_open: - ser.open() - - checkModel = None - if len(sys.argv) == 4: - checkModel = sys.argv[3] - pattern = re.compile("^NX\d{4}[TK]\d{3}$") - if not pattern.match(checkModel): - print 'Invalid model name. Please give a correct one (e.g. NX3224T024)' - exit(1) - upload(ser, sys.argv[1], checkModel) diff --git a/Nextion_ON7LDS/NX3224T024-L2.HMI b/Nextion_ON7LDS/NX3224T024-L2.HMI deleted file mode 100644 index f4896dd..0000000 Binary files a/Nextion_ON7LDS/NX3224T024-L2.HMI and /dev/null differ diff --git a/Nextion_ON7LDS/NX3224T024-L2.tft b/Nextion_ON7LDS/NX3224T024-L2.tft deleted file mode 100644 index c929092..0000000 Binary files a/Nextion_ON7LDS/NX3224T024-L2.tft and /dev/null differ diff --git a/Nextion_ON7LDS/NX3224T024-L3.HMI b/Nextion_ON7LDS/NX3224T024-L3.HMI deleted file mode 100644 index f2c1330..0000000 Binary files a/Nextion_ON7LDS/NX3224T024-L3.HMI and /dev/null differ diff --git a/Nextion_ON7LDS/NX3224T024-L3.tft b/Nextion_ON7LDS/NX3224T024-L3.tft deleted file mode 100644 index 4ba4609..0000000 Binary files a/Nextion_ON7LDS/NX3224T024-L3.tft and /dev/null differ diff --git a/Nextion_ON7LDS/NX3224T028-L2.HMI b/Nextion_ON7LDS/NX3224T028-L2.HMI deleted file mode 100644 index 768ed56..0000000 Binary files a/Nextion_ON7LDS/NX3224T028-L2.HMI and /dev/null differ diff --git a/Nextion_ON7LDS/NX3224T028-L2.tft b/Nextion_ON7LDS/NX3224T028-L2.tft deleted file mode 100644 index 2ffd4ce..0000000 Binary files a/Nextion_ON7LDS/NX3224T028-L2.tft and /dev/null differ diff --git a/Nextion_ON7LDS/NX3224T028-L3.HMI b/Nextion_ON7LDS/NX3224T028-L3.HMI deleted file mode 100644 index 31ca688..0000000 Binary files a/Nextion_ON7LDS/NX3224T028-L3.HMI and /dev/null differ diff --git a/Nextion_ON7LDS/NX3224T028-L3.tft b/Nextion_ON7LDS/NX3224T028-L3.tft deleted file mode 100644 index 99647e3..0000000 Binary files a/Nextion_ON7LDS/NX3224T028-L3.tft and /dev/null differ diff --git a/Nextion_ON7LDS/NX4024T032-L2.HMI b/Nextion_ON7LDS/NX4024T032-L2.HMI deleted file mode 100644 index 331aac6..0000000 Binary files a/Nextion_ON7LDS/NX4024T032-L2.HMI and /dev/null differ diff --git a/Nextion_ON7LDS/NX4024T032-L2.tft b/Nextion_ON7LDS/NX4024T032-L2.tft deleted file mode 100644 index abec64c..0000000 Binary files a/Nextion_ON7LDS/NX4024T032-L2.tft and /dev/null differ diff --git a/Nextion_ON7LDS/NX4832T035-L2.HMI b/Nextion_ON7LDS/NX4832T035-L2.HMI deleted file mode 100644 index 9bb0de2..0000000 Binary files a/Nextion_ON7LDS/NX4832T035-L2.HMI and /dev/null differ diff --git a/Nextion_ON7LDS/NX4832T035-L2.tft b/Nextion_ON7LDS/NX4832T035-L2.tft deleted file mode 100644 index 960795c..0000000 Binary files a/Nextion_ON7LDS/NX4832T035-L2.tft and /dev/null differ diff --git a/Nextion_ON7LDS/NX4832T035-L3.HMI b/Nextion_ON7LDS/NX4832T035-L3.HMI deleted file mode 100644 index c7ca2ed..0000000 Binary files a/Nextion_ON7LDS/NX4832T035-L3.HMI and /dev/null differ diff --git a/Nextion_ON7LDS/NX4832T035-L3.tft b/Nextion_ON7LDS/NX4832T035-L3.tft deleted file mode 100644 index 45ee468..0000000 Binary files a/Nextion_ON7LDS/NX4832T035-L3.tft and /dev/null differ diff --git a/Nextion_ON7LDS/README-L2 b/Nextion_ON7LDS/README-L2 deleted file mode 100644 index 46e799d..0000000 --- a/Nextion_ON7LDS/README-L2 +++ /dev/null @@ -1,41 +0,0 @@ -Nextion Display Layouts by ON7LDS (for MMDVMHost) -================================================= - - ################################ - # # - # screenLayout 2 files (-L2) # - # # - ################################ - -The -L2 files in this directory are Nextion screen layouts with better - fonts than the (rather ugly) standard Nextion fonts - -There are some other changes: - -* On the main screen there is a field to display the active ip address of the - device. On Linux it is preceded with the network interface name. -* DMR: if received, the Talker Alias is decoded and displayed. The TA will be - in green, so the user knows it is a decoded TA displaying. - This happens on-the-fly: as the parts of the TA arrive, they will immediately - be shown. -* If you want to change my screen layout, the only thing to keep in mind is - that the MMDVMHost program will select following font numbers: - default font for the TA field : font 4 - TA >16 characters : font 3 - TA >20 characters : font 2 - TA >24 characters : font 1 - There always have to be at least 5 fonts, of which font 1-4 are used for the - TA. If your display is wide enough to show all characters in one font, you - must copy this font to the lesser ('smaller') fonts. - i.e. : the 3.2" the display can show more than 31 characters (the maximum - TA length) in font 2, so this font is also copied to font 1. -* If you want more control about what and how it is displayed, you better - select Layout 3 or 4 (see README-L3) -* The screenlayout has to be selected with the parameter ScreenLayout in the - MMDVM.ini file under the Nextion section. This way, the extra function - to automatically change the font size, is activated. - 0 = auto (future use, for now it's G4KLX layout) - 1 = G4KLX layout - 2 = ON7LDS layout (this README file) - 3 = ON7LDS DIY layout (see README-L3) - 4 = ON7LDS DIY layout High Speed (see README-L3) diff --git a/Nextion_ON7LDS/README-L3 b/Nextion_ON7LDS/README-L3 deleted file mode 100644 index 7846be5..0000000 --- a/Nextion_ON7LDS/README-L3 +++ /dev/null @@ -1,249 +0,0 @@ -Nextion Display Layouts by ON7LDS (for MMDVMHost) -================================================= - - #################################### - # # - # screenLayout 3 & 4 files (-L3) # - # # - # ON7LDS DIY layouts # - # # - #################################### - -The screenlayout has to be selected with the parameter ScreenLayout in the -MMDVM.ini file under the Nextion section. This way, the extra functions -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) - -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. -If you select 4 (115200bps) *you* have to program your Nextion to default to -that baudrate. - -How to set the default Nextion Display baudrate ? -You could use the Nextion editor, connect to your display with the debug - tool (do not forget to select your display and not the simulator) and - give the command 'bauds=115200'. -Another option is to set the command above in the Preinitialization Event - of the first page. Nextion recommends to do the latter in any case, - because 'on rare occasions bauds has become lost' - - -ON7LDS DIY layouts ------------------- -When selecting this layout, all processing can and should be done in the -Nextion display itself. -Whenever MMDVMHost sends new data to the screen, it also sends information -about what was sent as a number in global variable MMDVM.status. -(see further in this document for a table of numbers and their events) - -Then MMDVMHost activates the Touch Press Event of object 'S0' of the -active page. -In this event procedure, all processing can be done. - -Check the Touch Press Event of object 'S0' of the DMR page of the example -HMI file. As a straightforward example, the code there will show you how -to do some tasks when receiving data from MMDVMHost or NextionDriver: -- change colors and fonts of the TA on the DMR page when it arrives. - (TA length calculation is done in the display itself !) -- remove the slot number from t2.txt (since it's always 2) -- in the middle of the DMR screen, the previous user is displayed - for each slot (S1 at the left, S2 at the right) - This is done by copying the data from the ID/TA field to these - fields at call end. - -The examples are there to give you an idea of what can be done. -It is up to your imagination to go even further. -When, for example, you want to change the text 'listening' to some -other text, like 'RX', you just have to monitor the event status and -take action in the Touch Event of S0 of the corresponding page. -Or you could hide/show pictures, switch to other pages, etc. - -Check the NextionDriver program and the display layouts there to see - some other examples of what can be done by programming in the - display. (https://github.com/on7lds/NextionDriver) - - -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 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 - OR - set them invisible when the page is selected (with the 'vis' command) in the - preinitialize event of the page - - This way, you can use the data from the fields to process them - as is done with the GPS data in the example. - - - - -Status codes that will be sent: -------------------------------- -The status code gives more information about which type of information -was sent to the display. - - 1 : page MMDVM - 2 : page D-Star - 3 : page DMR - 4 : page YSF - 5 : page P25 - 6 : page NXDN - 7 : page POCSAG - 8 : page M17 - -MMDVMHOST -11 : IDLE -12 : CW -13 : ERROR text -14 : ERROR -15 : LOCKOUT -16 : IPaddress -17 : ID/Call (t0 and t4,t5 are sent) -19 : END -20 : RX Frequency -21 : TX Frequency -22 : Temperature -23 : Location - -DSTAR -41 : D-Star listening -42 : type/my1/my2 -45 : your -46 : reflector -47 : RSSI -48 : ber - -DMR -61 : DMR listening1 -62 : ID1 -63 : TA1 -64 : Call end1 -65 : TG1 -66 : RSSI1 -67 : ber1 -69 : DMR listening2 -70 : ID2 -71 : TA2 -72 : Call end2 -73 : TG2 -74 : RSSI2 -75 : ber2 -76 : GPS1 (t8,t9,t12) -77 : GPS2 (t10,t11,t13) - -YSF -81 : YSF listening -82 : src -83 : dest -84 : origin -85 : RSSI -86 : ber - -P25 -101 : P25 listening -102 : source -103 : dest -104 : RSSI -105 : ber - -NXDN -121 : NXDN listening -122 : source -123 : dest -124 : RSSI -125 : ber - -POCSAG -132 : RIC -133 : message text -134 : waiting - -M17 -141 : M17 listening -142 : source -143 : dest -144 : RSSI -145 : ber - -Fields (and their numbers) on the pages, used by MMDVMHost ----------------------------------------------------------- - -MMDVM -t0 : owner call & ID / errortext LOCKOUT -t1 : status / ERROR -t2 : date & time - - -screenLayout >2 : -t3 : ip address -t4 : owner call -t5 : owner ID -t20 : CPU Temperature in C. ( Can be changed to F in mmdmvhost config setting: [NEXTION] Section, Setting=DisplayTempInFahrenheit=1) -t30 : RX Frequency -t31 : Location -t32 : TX Frequency - - -D-Star -t0 : type my1 my2 -t1 : your -t2 : reflector -t3 : rssi -t4 : ber - - -DMR -t0 : src1 id / call / TA -t1 : dst -t2 : src2 id / call / TA -t3 : dst -t4 : rssi1 -t5 : rssi2 -t6 : ber1 -t7 : ber2 - -screenLayout >2 : -t8 : GPS1 dec -t9 : GPS1 DMS -t10: GPS2 dec -t11: GPS2 DMS -t12: GPS1 err -t13: GPS2 err - - -YSF -t0 : type,source -t1 : dst -t2 : src -t3 : rssi -t4 : ber - - -P25 -t0 : type,source -t1 : dst -t2 : rssi -t3 : ber - - -NXDN -t0 : type,source -t1 : dst -t2 : rssi -t3 : ber - -POCSAG -t0 : waiting / RIC -t1 : message - -M17 -t0 : source -t1 : dst -t2 : rssi -t3 : ber diff --git a/Nextion_ON7LDS/README.md b/Nextion_ON7LDS/README.md deleted file mode 100644 index 4cc5bb9..0000000 --- a/Nextion_ON7LDS/README.md +++ /dev/null @@ -1,34 +0,0 @@ -Nextion Display Layouts by ON7LDS (for MMDVMHost) -================================================= - -##### WARNING -These layouts should be opened with Nextion Editor LTS (v0.53) -The fonts that are used are made by me at the time (with 'zi edit') and are (in my opinion) very good readable and compact to show as much as possible. -Nextion has changed a lot since then and sadly, these fonts seem not to be compatible with later versions of Nextion Editor. - - - -##### Use - -The screenlayout has to be selected with the parameter **ScreenLayout** in the -MMDVM.ini file under the Nextion section. This way, the extra functions -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 - 4 = ON7LDS DIY layout High Speed - -Layout 2 is a no-nonsense layout. It is the original (G4KLX) layout with the Talker Alias added. TA color and fonts size can not be changed. At least not easily. - -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 (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. -This program sends extra information about the host to the display, can do callsign lookup with extended information (name, city, country) and can do more processing which would not be the task of MMDVMHost (for example: with the help of this program, it is possible to use buttons on the display to do actions on the host itself). -In verbose mode, this program shows you all communication between MMDVMHost and the display. diff --git a/NullDisplay.cpp b/NullDisplay.cpp deleted file mode 100644 index e2e5473..0000000 --- a/NullDisplay.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2016,2018,2020,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "NullDisplay.h" - -#if defined(RASPBERRY_PI) -#include -#endif - -#define LED_STATUS 28 - -CNullDisplay::CNullDisplay() : -CDisplay() -{ -} - -CNullDisplay::~CNullDisplay() -{ -} - -bool CNullDisplay::open() -{ -#if defined(RASPBERRY_PI) - ::wiringPiSetup(); - - ::pinMode(LED_STATUS, OUTPUT); - ::digitalWrite(LED_STATUS, 0); -#endif - return true; -} - -void CNullDisplay::setIdleInt() -{ -} - -void CNullDisplay::setErrorInt(const char* text) -{ -} - -void CNullDisplay::setLockoutInt() -{ -} - -void CNullDisplay::setQuitInt() -{ -} - -void CNullDisplay::setFMInt() -{ -} - -void CNullDisplay::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) -{ -#if defined(RASPBERRY_PI) - ::digitalWrite(LED_STATUS, 1); -#endif -} - -void CNullDisplay::clearDStarInt() -{ -#if defined(RASPBERRY_PI) - ::digitalWrite(LED_STATUS, 0); -#endif -} - -void CNullDisplay::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) -{ -#if defined(RASPBERRY_PI) - ::digitalWrite(LED_STATUS, 1); -#endif -} - -void CNullDisplay::clearDMRInt(unsigned int slotNo) -{ -#if defined(RASPBERRY_PI) - ::digitalWrite(LED_STATUS, 0); -#endif -} - -void CNullDisplay::writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) -{ -#if defined(RASPBERRY_PI) - ::digitalWrite(LED_STATUS, 1); -#endif -} - -void CNullDisplay::clearFusionInt() -{ -#if defined(RASPBERRY_PI) - ::digitalWrite(LED_STATUS, 0); -#endif -} - -void CNullDisplay::writeP25Int(const char* source, bool group, unsigned int dest, const char* type) -{ -#if defined(RASPBERRY_PI) - ::digitalWrite(LED_STATUS, 1); -#endif -} - -void CNullDisplay::clearP25Int() -{ -#if defined(RASPBERRY_PI) - ::digitalWrite(LED_STATUS, 0); -#endif -} - -void CNullDisplay::writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type) -{ -#if defined(RASPBERRY_PI) - ::digitalWrite(LED_STATUS, 1); -#endif -} - -void CNullDisplay::clearNXDNInt() -{ -#if defined(RASPBERRY_PI) - ::digitalWrite(LED_STATUS, 0); -#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() -{ -} - -void CNullDisplay::clearCWInt() -{ -} - -void CNullDisplay::close() -{ -} diff --git a/NullDisplay.h b/NullDisplay.h deleted file mode 100644 index b552175..0000000 --- a/NullDisplay.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2016,2018,2020,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#if !defined(nullptrDISPLAY_H) -#define nullptrDISPLAY_H - -#include "Display.h" - -#include - -class CNullDisplay : public CDisplay -{ -public: - CNullDisplay(); - virtual ~CNullDisplay(); - - virtual bool open(); - - virtual void close(); - -protected: - virtual void setIdleInt(); - virtual void setErrorInt(const char* text); - virtual void setLockoutInt(); - virtual void setQuitInt(); - virtual void setFMInt(); - - virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); - virtual void clearDStarInt(); - - virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); - virtual void clearDMRInt(unsigned int slotNo); - - virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin); - virtual void clearFusionInt(); - - virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type); - virtual void clearP25Int(); - - virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); - virtual void clearNXDNInt(); - - virtual void writePOCSAGInt(uint32_t ric, const std::string& message); - virtual void clearPOCSAGInt(); - - virtual void writeCWInt(); - virtual void clearCWInt(); - -private: -}; - -#endif diff --git a/OLED.cpp b/OLED.cpp deleted file mode 100644 index 2348496..0000000 --- a/OLED.cpp +++ /dev/null @@ -1,793 +0,0 @@ -/* - * Copyright (C) 2016,2017,2018,2020,2023,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "OLED.h" -#include "Log.h" - -static bool networkInfoInitialized = false; -static unsigned char passCounter = 0U; - -//Logo MMDVM for Idle Screen -static unsigned char logo_glcd_bmp[] = -{ -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0xF8, 0x03, 0xFC, 0x7F, 0x80, 0x3F, 0xC7, 0xFF, 0xFC, 0xF8, 0x00, 0xF9, 0xFC, 0x01, 0xFE, -0x01, 0xFC, 0x07, 0xFC, 0x7F, 0xC0, 0x7F, 0xC4, 0x00, 0x02, 0x48, 0x00, 0x91, 0xFE, 0x03, 0xFE, -0x03, 0xFC, 0x07, 0xFC, 0x7F, 0xC0, 0x7F, 0xC5, 0xFF, 0xF1, 0x24, 0x01, 0x23, 0xFE, 0x03, 0xFE, -0x03, 0xFE, 0x0F, 0xBC, 0x7B, 0xE0, 0xFB, 0xC5, 0x00, 0x09, 0x24, 0x01, 0x23, 0xDF, 0x07, 0xDE, -0x07, 0xDE, 0x0F, 0x3C, 0x79, 0xE0, 0xF3, 0xC5, 0x00, 0x05, 0x12, 0x02, 0x47, 0xCF, 0x07, 0x9E, -0x07, 0x9F, 0x1F, 0x3C, 0x79, 0xF1, 0xF3, 0xC5, 0x00, 0x05, 0x12, 0x02, 0x47, 0x8F, 0x8F, 0x9E, -0x0F, 0x8F, 0x1E, 0x3C, 0x78, 0xF1, 0xE3, 0xC5, 0x00, 0x05, 0x09, 0x04, 0x8F, 0x87, 0x8F, 0x1E, -0x0F, 0x0F, 0xBE, 0x3C, 0x78, 0xFB, 0xE3, 0xC5, 0x00, 0x05, 0x09, 0x04, 0x8F, 0x07, 0xDF, 0x1E, -0x1F, 0x07, 0xFC, 0x3C, 0x78, 0x7F, 0xC3, 0xC5, 0x00, 0x05, 0x04, 0x89, 0x1F, 0x03, 0xFE, 0x1E, -0x1E, 0x03, 0xFC, 0x3C, 0x78, 0x7F, 0xC3, 0xC5, 0x00, 0x09, 0x04, 0x89, 0x1E, 0x01, 0xFE, 0x1E, -0x3E, 0x03, 0xF8, 0x3C, 0x78, 0x3F, 0x83, 0xC5, 0xFF, 0xF1, 0x02, 0x72, 0x3E, 0x01, 0xFC, 0x1E, -0x3C, 0x01, 0xF0, 0x3C, 0x78, 0x1F, 0x03, 0xC4, 0x00, 0x02, 0x02, 0x02, 0x3C, 0x00, 0xF8, 0x1E, -0x7C, 0x01, 0xF0, 0x3C, 0x78, 0x1F, 0x03, 0xC7, 0xFF, 0xFC, 0x01, 0xFC, 0x7C, 0x00, 0xF8, 0x1E, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -//Logo D-Star 128x16 px -static unsigned char logo_dstar_bmp[] = -{ -0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x60, 0x03, 0xFF, 0xC0, 0x00, 0x00, 0x1F, 0xF0, 0xFF, 0xFE, 0x07, 0x80, 0x3F, 0xF8, -0x00, 0x00, 0xC0, 0x07, 0xC1, 0xE0, 0x00, 0x00, 0x78, 0x7C, 0xFF, 0xFE, 0x0F, 0xC0, 0x3F, 0xFC, -0x00, 0x01, 0xC0, 0x07, 0x80, 0xF0, 0x00, 0x00, 0xE0, 0x3C, 0x07, 0x80, 0x0F, 0xC0, 0x78, 0x0E, -0x00, 0x03, 0xC0, 0x07, 0x80, 0x70, 0x00, 0x00, 0xE0, 0x38, 0x07, 0x00, 0x1B, 0xC0, 0x78, 0x0E, -0x00, 0x07, 0xC0, 0x07, 0x80, 0x70, 0x00, 0x01, 0xE0, 0x00, 0x07, 0x00, 0x33, 0xC0, 0x70, 0x1E, -0x07, 0xFF, 0xFE, 0x07, 0x00, 0x70, 0x00, 0x01, 0xF8, 0x00, 0x07, 0x00, 0x63, 0xC0, 0x70, 0x3C, -0x01, 0xFF, 0xF8, 0x0F, 0x00, 0x71, 0xFF, 0xE0, 0xFF, 0xF0, 0x0E, 0x00, 0xE1, 0xE0, 0xFF, 0xE0, -0x00, 0x7F, 0xE0, 0x0F, 0x00, 0x60, 0x00, 0x00, 0x03, 0xF8, 0x0E, 0x00, 0xC1, 0xE0, 0xFF, 0xE0, -0x00, 0x3F, 0x80, 0x0E, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xF0, 0x0E, 0x01, 0xFF, 0xE0, 0xE0, 0x70, -0x00, 0x7F, 0x00, 0x1E, 0x00, 0xE0, 0x00, 0x03, 0x80, 0x70, 0x0C, 0x03, 0xFC, 0xE0, 0xE0, 0x30, -0x00, 0xFF, 0x00, 0x1E, 0x01, 0xC0, 0x00, 0x07, 0x80, 0xE0, 0x1C, 0x07, 0x00, 0xE1, 0xE0, 0x38, -0x01, 0xEF, 0x00, 0x1C, 0x07, 0x80, 0x00, 0x07, 0xC1, 0xE0, 0x1C, 0x06, 0x00, 0xF1, 0xC0, 0x38, -0x03, 0x87, 0x00, 0x3F, 0xFF, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x1C, 0x0C, 0x00, 0xF3, 0xC0, 0x38, -0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -//Logo DMR 128x16 px -static unsigned char logo_dmr_bmp[] = -{ -0x00, 0x01, 0xFF, 0xFF, 0xF8, 0x01, 0xF8, 0x00, 0x00, 0x1F, 0x1F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, -0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x81, 0xFC, 0x00, 0x00, 0x3F, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xE1, 0xFE, 0x00, 0x00, 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x01, 0xF8, 0x00, 0x0F, 0xF1, 0xFF, 0x80, 0x01, 0xFF, 0x1F, 0x80, 0x00, 0x1F, 0x00, 0x00, -0x00, 0x01, 0xF8, 0x00, 0x03, 0xF9, 0xFF, 0xC0, 0x03, 0xFF, 0x1F, 0x80, 0x00, 0x0F, 0x00, 0x00, -0x00, 0x01, 0xF8, 0x00, 0x01, 0xF9, 0xFF, 0xE0, 0x07, 0xFF, 0x1F, 0x80, 0x00, 0x0F, 0x00, 0x00, -0x00, 0x01, 0xF8, 0x00, 0x01, 0xFD, 0xF3, 0xF0, 0x1F, 0x9F, 0x1F, 0x80, 0x00, 0x1F, 0x00, 0x00, -0x00, 0x01, 0xF8, 0x00, 0x00, 0xFD, 0xF1, 0xFC, 0x3F, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x01, 0xF8, 0x00, 0x00, 0xFD, 0xF0, 0xFE, 0x7E, 0x1F, 0x1F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x01, 0xF8, 0x00, 0x01, 0xFD, 0xF0, 0x7F, 0xFC, 0x1F, 0x1F, 0xFF, 0xFF, 0xFC, 0x00, 0x00, -0x00, 0x01, 0xF8, 0x00, 0x01, 0xF9, 0xF0, 0x1F, 0xF0, 0x1F, 0x1F, 0x81, 0xFC, 0x00, 0x00, 0x00, -0x00, 0x01, 0xF8, 0x00, 0x07, 0xF9, 0xF0, 0x0F, 0xE0, 0x1F, 0x1F, 0x80, 0x7F, 0x00, 0x00, 0x00, -0x00, 0x01, 0xF8, 0x00, 0x3F, 0xF1, 0xF0, 0x07, 0xC0, 0x1F, 0x1F, 0x80, 0x3F, 0xC0, 0x00, 0x00, -0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xC1, 0xF0, 0x03, 0x80, 0x1F, 0x1F, 0x80, 0x0F, 0xF0, 0x00, 0x00, -0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x01, 0xF0, 0x00, 0x00, 0x1F, 0x1F, 0x80, 0x03, 0xFC, 0x00, 0x00, -0x00, 0x01, 0xFF, 0xFF, 0xF0, 0x01, 0xF0, 0x00, 0x00, 0x1F, 0x1F, 0x80, 0x01, 0xFF, 0x00, 0x00 -}; - -//Logo Fusion 128x16 -const unsigned char logo_fusion_bmp[] = -{ -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0xFC, 0x00, 0x1F, 0xE1, 0xFE, 0x1F, 0xFF, 0xF8, 0x7F, 0xC3, 0xFF, 0xFF, 0x1F, 0xFF, 0xFE, -0x03, 0xFC, 0x00, 0x3F, 0xC3, 0xFC, 0x3F, 0x80, 0x00, 0x7F, 0x87, 0xF0, 0xFF, 0x0F, 0xF1, 0xFF, -0x07, 0xFF, 0xFC, 0x7F, 0x83, 0xF8, 0x7F, 0x80, 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0x1F, 0xE1, 0xFE, -0x0F, 0xFF, 0xF0, 0x7F, 0x07, 0xF0, 0xFF, 0xFF, 0xC1, 0xFF, 0x1F, 0xE1, 0xFE, 0x3F, 0xC3, 0xFC, -0x0F, 0xF0, 0x00, 0xFE, 0x0F, 0xE0, 0x7F, 0xFF, 0xE1, 0xFE, 0x3F, 0xC3, 0xFC, 0x3F, 0xC3, 0xFC, -0x1F, 0xE0, 0x01, 0xFC, 0x1F, 0xE0, 0x1F, 0xFF, 0xE3, 0xFC, 0x3F, 0xC3, 0xF8, 0x7F, 0x87, 0xF8, -0x3F, 0xC0, 0x03, 0xFC, 0x3F, 0xC0, 0x00, 0x3F, 0xC7, 0xF8, 0x7F, 0x87, 0xF8, 0xFF, 0x0F, 0xF0, -0x7F, 0xC0, 0x03, 0xFF, 0xFF, 0xE0, 0x00, 0x7F, 0x07, 0xF8, 0x7F, 0xCF, 0xE1, 0xFF, 0x1F, 0xF8, -0x7F, 0x80, 0x01, 0xFF, 0xFF, 0xC7, 0xFF, 0xFC, 0x0F, 0xF0, 0x3F, 0xFF, 0x81, 0xFE, 0x1F, 0xF0, -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[] = -{ -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 -}; - -// 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 -}; - -COLED::COLED(unsigned char displayType, unsigned char displayBrightness, bool displayInvert, bool displayScroll, bool displayRotate, bool displayLogoScreensaver, bool duplex) : -CDisplay(), -m_displayType(displayType), -m_displayBrightness(displayBrightness), -m_displayInvert(displayInvert), -m_displayScroll(displayScroll), -m_displayRotate(displayRotate), -m_displayLogoScreensaver(displayLogoScreensaver), -m_duplex(duplex), -m_ipaddress(), -m_display() -{ -} - -COLED::~COLED() -{ -} - -bool COLED::open() -{ - // SPI - if (m_display.oled_is_spi_proto(m_displayType)) { - // SPI change parameters to fit to your LCD - if (!m_display.init(OLED_SPI_DC, OLED_SPI_RESET, OLED_SPI_CS, m_displayType)) - return false; - } else { - // I2C change parameters to fit to your LCD - if (!m_display.init(OLED_I2C_RESET, m_displayType)) - return false; - } - - m_display.begin(); - - m_display.invertDisplay(m_displayInvert ? 1 : 0); - if (m_displayBrightness > 0U) - m_display.setBrightness(m_displayBrightness); - - if (m_displayRotate > 0U) { - m_display.sendCommand(0xC0); - m_display.sendCommand(0xA0); - } - - // Init done - m_display.setTextWrap(false); // disable text wrap as default - m_display.clearDisplay(); // clears the screen buffer - m_display.display(); // display it (clear display) - - OLED_statusbar(); - - m_display.setCursor(22, OLED_LINE4); - m_display.setTextSize(1); - m_display.print("-Initializing-"); - m_display.display(); - - return true; -} - -float COLED::readTemperature(const char* filePath) -{ - std::ifstream file(filePath); - if (!file.is_open()) { - std::cerr << "Error: Could not open file " << filePath << std::endl; - return -1.0F; // Return a negative value to indicate that CPU temp is not available - } - - float temperature; - file >> temperature; - - file.close(); - - return temperature / 1000.0F; // The temperature is stored in millidegrees Celsius, so a bit of conversion -} - -void COLED::setIdleInt() -{ - m_mode = MODE_IDLE; - - m_display.clearDisplay(); - - OLED_statusbar(); - - if (m_displayScroll && m_displayLogoScreensaver) - m_display.startscrolldiagleft(0x00, 0x0f); //the MMDVM logo scrolls the whole screen - - 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 = 0U; - } - - // Let's let the users know if they are in Auto-AP mode... - if (m_ipaddress.find("wlan0_ap") != std::string::npos) { - size_t pos = m_ipaddress.find("wlan0_ap"); - if (pos != std::string::npos) - m_ipaddress.erase(pos, 9); // remove redundant/superfluous "wlan0_ap" from string - - // Read ssid value from /etc/hostapd.conf if it exists... - std::string ssid; - std::ifstream configFile("/etc/hostapd/hostapd.conf"); - if (configFile.is_open()) { - std::string line; - while (std::getline(configFile, line)) { - if (line.find("ssid=") != std::string::npos) { - std::istringstream iss(line); - std::string key, value; - if (std::getline(iss, key, '=') && std::getline(iss, value)) { - ssid = value; - break; - } - } - } - - configFile.close(); - } else { - ssid = "Unknown"; // `/etc/hostapd.conf` does not exist... - } - - if (m_displayLogoScreensaver) { - m_display.setCursor(0, OLED_LINE3); - m_display.setTextSize(1); - m_display.printf("Auto-AP Running..."); - m_display.setCursor(0, OLED_LINE5); - m_display.setTextSize(1); - m_display.printf("SSID: %s", ssid.c_str()); - m_display.setCursor(0, OLED_LINE6); - m_display.setTextSize(1); - m_display.printf("IP: %s", m_ipaddress.c_str()); - } - } else { // Connected to network - no Auto-AP mode; normal display layout... - if (m_displayLogoScreensaver) { - m_display.setCursor(42, OLED_LINE2); - m_display.setTextSize(1); - m_display.print("-IDLE-"); - m_display.setCursor(0, OLED_LINE4); - m_display.printf("%s", m_ipaddress.c_str()); - - // Display temperature - float tempCelsius = readTemperature("/sys/class/thermal/thermal_zone0/temp"); - if (tempCelsius >= 0.0F) { - // Convert to Fahrenheit - float tempFahrenheit = (tempCelsius * 9.0F / 5.0F) + 32.0F; - m_display.setCursor(0, OLED_LINE5); - m_display.setTextSize(1); - m_display.printf("Temp: %.0fF / %.0fC ", tempFahrenheit, tempCelsius); - } - } - } - - m_display.display(); -} - -void COLED::setErrorInt(const char* text) -{ - m_mode = MODE_ERROR; - - m_display.clearDisplay(); - OLED_statusbar(); - - m_display.setTextWrap(true); // text wrap temorally enable - m_display.setCursor(0, OLED_LINE1); - m_display.printf("%s\n", text); - m_display.setTextWrap(false); - - m_display.display(); -} - -void COLED::setLockoutInt() -{ - m_mode = MODE_LOCKOUT; - - m_display.clearDisplay(); - OLED_statusbar(); - - m_display.setCursor(0, 30); - m_display.setTextSize(3); - m_display.print("Lockout"); - - m_display.setTextSize(1); - m_display.display(); -} - -void COLED::setQuitInt() -{ - m_mode = MODE_QUIT; - - m_display.clearDisplay(); - OLED_statusbar(); - - m_display.setCursor(28, 30); - m_display.setTextSize(1); - m_display.print("Stopping..."); - - m_display.setTextSize(1); - m_display.display(); - sleep(2); -} - -void COLED::setFMInt() -{ - m_mode = MODE_FM; - - m_display.clearDisplay(); - OLED_statusbar(); - - m_display.setCursor(0, 30); - m_display.setTextSize(3); - m_display.print("FM"); - - m_display.setTextSize(1); - m_display.display(); -} - -void COLED::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) -{ - m_mode = MODE_DSTAR; - - m_display.clearDisplay(); - m_display.fillRect(0, OLED_LINE3, m_display.width(), m_display.height(), BLACK); //clear everything beneath logo - - m_display.setCursor(0, OLED_LINE3); - m_display.printf("%s %.8s/%4.4s", type, my1, my2); - - m_display.setCursor(0, OLED_LINE4); - m_display.printf("-> %.8s", your); - - m_display.setCursor(0, OLED_LINE5); - m_display.printf("via %.8s", reflector); - - m_display.setCursor(0, OLED_LINE6); - m_display.printf("%s", m_ipaddress.c_str()); - - OLED_statusbar(); - - m_display.display(); -} - -void COLED::clearDStarInt() -{ - m_display.fillRect(0, OLED_LINE3, m_display.width(),m_display.height(), BLACK); //clear everything beneath the logo - - m_display.setCursor(37, OLED_LINE3); - m_display.print("Standby"); - - m_display.setCursor(0, OLED_LINE5); - m_display.printf("%s", m_ipaddress.c_str()); - - m_display.display(); -} - -void COLED::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) -{ - CUserDBentry tmp; - - tmp.set(keyCALLSIGN, src); - - writeDMRIntEx(slotNo, tmp, group, dst, type); -} - -#define CALLandNAME(u) ((u).get(keyCALLSIGN) + " " + (u).get(keyFIRST_NAME)) - -int COLED::writeDMRIntEx(unsigned int slotNo, const CUserDBentry& src, bool group, const std::string& dst, const char* type) -{ - if (m_mode != MODE_DMR) { - m_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 2-3 - if (m_duplex) { - if (slotNo == 1U) { - m_display.fillRect(0, OLED_LINE2, m_display.width(), 40, BLACK); - m_display.setCursor(0, OLED_LINE2); - m_display.printf("%s", CALLandNAME(src).c_str()); - m_display.setCursor(0, OLED_LINE3); - m_display.printf("Slot: %i %s %s%s", slotNo, type, group ? "TG: " : "", dst.c_str()); - } else { - m_display.fillRect(0, OLED_LINE4, m_display.width(), 40, BLACK); - m_display.setCursor(0, OLED_LINE4); - m_display.printf("%s", CALLandNAME(src).c_str()); - m_display.setCursor(0, OLED_LINE5); - m_display.printf("Slot: %i %s %s%s", slotNo, type, group ? "TG: " : "", dst.c_str()); - } - - m_display.fillRect(0, OLED_LINE6, m_display.width(), 20, BLACK); - m_display.setCursor(0, OLED_LINE6); - m_display.printf("%s", m_ipaddress.c_str()); - } else { - m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); - m_display.setCursor(0, OLED_LINE2); - m_display.printf("%s", CALLandNAME(src).c_str()); - m_display.setCursor(0, OLED_LINE3); - m_display.printf("Slot: %i %s %s%s", slotNo, type, group ? "TG: " : "", dst.c_str()); - m_display.setCursor(0, OLED_LINE4); - m_display.printf("%s", src.get(keyCITY).c_str()); - m_display.setCursor(0, OLED_LINE5); - m_display.printf("%s", src.get(keySTATE).c_str()); - m_display.setCursor(0, OLED_LINE6); - m_display.printf("%s", src.get(keyCOUNTRY).c_str()); - } - - OLED_statusbar(); - - m_display.display(); - - // must be 0, to avoid calling writeDMRInt() from CDisplay::writeDMR() - return 0; -} - -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 2-3 - if (m_duplex){ - if (slotNo == 1U) { - m_display.fillRect(0, OLED_LINE3, m_display.width(), 40, BLACK); - m_display.setCursor(0, OLED_LINE3); - m_display.print("Slot: 1 Standby"); - } else { - m_display.fillRect(0, OLED_LINE5, m_display.width(), 40, BLACK); - m_display.setCursor(0, OLED_LINE5); - m_display.print("Slot: 2 Standby"); - } - } else { - m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); - m_display.setCursor(0, OLED_LINE3); - m_display.printf("Slot: %i Standby", slotNo); - } - - m_display.fillRect(0, OLED_LINE6, m_display.width(), 20, BLACK); - m_display.setCursor(0, OLED_LINE6); - m_display.printf("%s", m_ipaddress.c_str()); - - m_display.display(); -} - -void COLED::writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) -{ - m_mode = MODE_YSF; - - m_display.clearDisplay(); - m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); - - m_display.setCursor(0, OLED_LINE4); - m_display.printf("%s %.10s", type, source); - - m_display.setCursor(0, OLED_LINE5); - m_display.printf(" DG-ID %u", dgid); - - OLED_statusbar(); - - m_display.display(); -} - -void COLED::clearFusionInt() -{ - m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); - - m_display.setCursor(37, OLED_LINE4); - m_display.print("Standby"); - - m_display.setCursor(0, OLED_LINE6); - m_display.printf("%s", m_ipaddress.c_str()); - - m_display.display(); -} - -void COLED::writeP25Int(const char* source, bool group, unsigned int dest, const char* type) -{ - m_mode = MODE_P25; - - m_display.clearDisplay(); - m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); - - m_display.setCursor(0, OLED_LINE3); - m_display.printf("%s %.10s", type, source); - - m_display.setCursor(0, OLED_LINE4); - m_display.printf(" %s%u", group ? "TG" : "", dest); - - OLED_statusbar(); - - m_display.display(); -} - -void COLED::clearP25Int() -{ - m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); - - m_display.setCursor(37, OLED_LINE4); - m_display.print("Standby"); - - m_display.setCursor(0, OLED_LINE6); - m_display.printf("%s", m_ipaddress.c_str()); - - m_display.display(); -} - -void COLED::writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type) -{ - CUserDBentry tmp; - - tmp.set(keyCALLSIGN, source); - - writeNXDNIntEx(tmp, group, dest, type); -} - -int COLED::writeNXDNIntEx(const CUserDBentry& source, bool group, unsigned int dest, const char* type) -{ - m_mode = MODE_NXDN; - - m_display.clearDisplay(); - m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); - - m_display.setCursor(0, OLED_LINE2); - m_display.printf("%s %s", type, CALLandNAME(source).c_str()); - - m_display.setCursor(0, OLED_LINE3); - m_display.printf(" %s%u", group ? "TG" : "", dest); - - m_display.setCursor(0, OLED_LINE4); - m_display.printf("%s", source.get(keyCITY).c_str()); - - m_display.setCursor(0, OLED_LINE5); - m_display.printf("%s", source.get(keySTATE).c_str()); - - m_display.setCursor(0, OLED_LINE6); - m_display.printf("%s", source.get(keyCOUNTRY).c_str()); - - OLED_statusbar(); - - m_display.display(); - - // must be 0, to avoid calling writeNXDNInt() from CDisplay::writeNXDN() - return 0; -} - -void COLED::clearNXDNInt() -{ - m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); - - m_display.setCursor(37, OLED_LINE3); - m_display.print("Standby"); - - m_display.setCursor(0, OLED_LINE6); - m_display.printf("%s", m_ipaddress.c_str()); - - m_display.display(); -} - -void COLED::writePOCSAGInt(uint32_t ric, const std::string& message) -{ - int pos; - int length = message.length(); - std::string rublic; - - // extract rublic index "(xx-xx)" - switch (ric) { - case 4512U: - case 4520U: - if (length) { - std::string::size_type start = message.find("("); - std::string::size_type end = message.find(") "); - if (start != std::string::npos && end != std::string::npos) { - rublic = message.substr(start, end - start + 1); - pos = end + 2; - break; - } - } - /*FALLTHROUGH*/ - default: - rublic = ""; - pos = 0; - break; - } - - // remove double-quotation leading/trailing message - if (length && message.at(pos) == '\"' && message.at(length - 1) == '\"') { - pos++; - length--; - } - - m_mode = MODE_POCSAG; - - m_display.clearDisplay(); - m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); - - m_display.setCursor(0, OLED_LINE2); - m_display.printf("RIC: %u", ric); - if (!rublic.empty()) - m_display.printf(" / %s", rublic.c_str()); - - m_display.setTextWrap(true); // text wrap temorally enable - m_display.setCursor(0, OLED_LINE3); - // no room to display "MSG: " header - - // due to limitation of AdaFruit_GFX::vprintf() (in ArduiPi_OLED), - // the maximum string length displayed by single printf() call is 63. - // to avoid this, divide POCSAG (max 80 chars) message into some pieces. - while (pos < length) { - int remain = length - pos; - int n = (remain < 40) ? remain : 40; - m_display.printf("%s", message.substr(pos, n).c_str()); - pos += n; - } - m_display.setTextWrap(false); - - OLED_statusbar(); - - m_display.display(); -} - -void COLED::clearPOCSAGInt() -{ - m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); - - m_display.setCursor(37, OLED_LINE3); - m_display.print("Standby"); - - m_display.setCursor(0, OLED_LINE6); - m_display.printf("%s", m_ipaddress.c_str()); - - m_display.display(); -} - -void COLED::writeCWInt() -{ - m_display.clearDisplay(); - - m_display.setCursor(15, 30); - m_display.setTextSize(2); - m_display.print("CW ID TX"); - - m_display.setTextSize(1); - m_display.display(); - - if (m_displayScroll) - m_display.startscrollleft(0x02, 0x0f); -} - -void COLED::clearCWInt() -{ - m_display.clearDisplay(); - - m_display.setCursor(17,OLED_LINE1); - m_display.setTextSize(1); - m_display.print("-IDLE-"); - m_display.setCursor(0,OLED_LINE3); - m_display.printf("%s",m_ipaddress.c_str()); - - // Display temperature - float tempCelsius = readTemperature("/sys/class/thermal/thermal_zone0/temp"); - if (tempCelsius >= 0.0F) { - // Convert to Fahrenheit - float tempFahrenheit = (tempCelsius * 9.0F / 5.0F) + 32.0F; - m_display.setCursor(0, OLED_LINE5); - m_display.setTextSize(1); - m_display.printf("CPU Temp: %.0fF/%.0fC ", tempFahrenheit, tempCelsius); - } - - m_display.setTextSize(1); - m_display.display(); - - if (m_displayScroll) - m_display.startscrolldiagleft(0x00,0x0f); -} - -void COLED::close() -{ - m_display.clearDisplay(); - m_display.fillRect(0, 0, m_display.width(), 16, BLACK); - - if (m_displayScroll) - m_display.startscrollleft(0x00, 0x01); - - m_display.setCursor(11, OLED_LINE3); - m_display.setTextSize(2); - m_display.print("-OFFLINE-"); - m_display.display(); - - m_display.close(); -} - -void COLED::OLED_statusbar() -{ - m_display.stopscroll(); - m_display.fillRect(0, 0, m_display.width(), 16, BLACK); - m_display.setTextColor(WHITE); - - m_display.setCursor(0,0); - if (m_mode == MODE_DMR) - m_display.drawBitmap(0, 0, logo_dmr_bmp, 128, 16, WHITE); - else if (m_mode == MODE_DSTAR) - m_display.drawBitmap(0, 0, logo_dstar_bmp, 128, 16, WHITE); - else if (m_mode == MODE_YSF) - m_display.drawBitmap(0, 0, logo_fusion_bmp, 128, 16, WHITE); - else if (m_mode == MODE_P25) - m_display.drawBitmap(0, 0, logo_P25_bmp, 128, 16, WHITE); - else if (m_mode == MODE_NXDN) - m_display.drawBitmap(0, 0, logo_NXDN_bmp, 128, 16, WHITE); - else if (m_mode == MODE_POCSAG) - m_display.drawBitmap(0, 0, logo_POCSAG_bmp, 128, 16, WHITE); - else if (m_displayLogoScreensaver) - m_display.drawBitmap(0, 0, logo_glcd_bmp, 128, 16, WHITE); - - if (m_displayScroll) - m_display.startscrollleft(0x00, 0x01); -} diff --git a/OLED.h b/OLED.h deleted file mode 100644 index b2616fc..0000000 --- a/OLED.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2016,2017,2018,2020,2023,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#if !defined(OLED_H) -#define OLED_H - -#define OLED_STATUSBAR 0 -#define OLED_LINE1 8 //16 -#define OLED_LINE2 18 //26 -#define OLED_LINE3 28 //36 -#define OLED_LINE4 37 //46 -#define OLED_LINE5 47 //56 -#define OLED_LINE6 57 - -#include "Display.h" -#include "Defines.h" -#include "UserDBentry.h" - -#include -#include -#include -#include // for cpu temp value extraction -#include // for cpu temp value rounding - -#include "ArduiPi_OLED_lib.h" -#include "Adafruit_GFX.h" -#include "ArduiPi_OLED.h" -#include "NetworkInfo.h" - -class COLED : public CDisplay -{ -public: - COLED(unsigned char displayType, unsigned char displayBrighness, bool displayInvert, bool displayScroll, bool displayRotate, bool displayLogoScreensaver, bool duplex); - virtual ~COLED(); - - virtual bool open(); - - virtual void close(); - -protected: - virtual void setIdleInt(); - virtual void setErrorInt(const char* text); - virtual void setLockoutInt(); - virtual void setQuitInt(); - virtual void setFMInt(); - - virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); - virtual void clearDStarInt(); - - virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); - virtual int writeDMRIntEx(unsigned int slotNo, const CUserDBentry& src, bool group, const std::string& dst, const char* type); - virtual void clearDMRInt(unsigned int slotNo); - - virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin); - virtual void clearFusionInt(); - - virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type); - virtual void clearP25Int(); - - virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); - virtual int writeNXDNIntEx(const CUserDBentry& source, bool group, unsigned int dest, const char* type); - virtual void clearNXDNInt(); - - virtual void writePOCSAGInt(uint32_t ric, const std::string& message); - virtual void clearPOCSAGInt(); - - virtual void writeCWInt(); - virtual void clearCWInt(); - -private: - const char* m_slot1_state; - const char* m_slot2_state; - unsigned char m_mode; - unsigned char m_displayType; - unsigned char m_displayBrightness; - bool m_displayInvert; - bool m_displayScroll; - bool m_displayRotate; - bool m_displayLogoScreensaver; - bool m_duplex; - std::string m_ipaddress; - ArduiPi_OLED m_display; - - float readTemperature(const char* filePath); - - void OLED_statusbar(); -}; - -#endif - diff --git a/OLED.md b/OLED.md deleted file mode 100644 index dc15703..0000000 --- a/OLED.md +++ /dev/null @@ -1,47 +0,0 @@ -# Prerequisite - -Enable I2C and SPI modules directly with raspi-config tool, issue a -``` -sudo raspi-config -``` - -Then go to menu Advanced Option, select SPI and under question ” Would you like the SPI kernel module to be loaded by default ?”, select Yes, Do the same thing for I2C Advanced Option. - -As I don’t use monitor or TV connected to Pi, I decreased dedicated video memory, always in raspi-config, go to Advanced Options then Memory Split, then type 16 Mo (the minimal) used for GPU, then select Finish, and select Yes when asked to reboot. - -To be able to compile you will need the compiler and some others tools, issue a : - -``` -sudo apt-get install build-essential git-core libi2c-dev i2c-tools lm-sensors wiringpi -``` -*italic* Sometimes I2C and SPI modules are not started and thus he cannot start the sample code. The solution to start the modules at startup by adding the two following lines into the file /etc/modules - -``` -i2c-dev -spidev -``` -Reboot, then you MUST see SPI and I2C devices with the following command - -``` -root@raspberrypi:~# ls /dev/i2c* -/dev/i2c-0 -root@raspberrypi:~# ls /dev/spi* -/dev/spidev0.0 /dev/spidev0.1 -``` -# Installation of the generic Driver - -The Driver is based on Adafruit Arduino library, I ported the code to be able to compile and run on Raspberry Pi but added also some features. - -Get all the file from github dedicated repo : -``` -git clone https://github.com/hallard/ArduiPi_OLED -cd ArduiPi_OLED -sudo make -``` - -# Building MMDVMHost -``` -make -f Makefile.Pi.OLED -``` - -The initial guide is written by [Charles](http://hallard.me/adafruit-oled-display-driver-for-pi/) diff --git a/P25Audio.cpp b/P25Audio.cpp index ce5b37b..d4ef26d 100644 --- a/P25Audio.cpp +++ b/P25Audio.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,8 @@ #include "Golay24128.h" #include "Hamming.h" +#if defined(USE_P25) + #include #include @@ -337,3 +339,6 @@ void CP25Audio::encode(unsigned char* data, const unsigned char* imbe, unsigned return; } } + +#endif + diff --git a/P25Audio.h b/P25Audio.h index 7d77c5b..83e5dc2 100644 --- a/P25Audio.h +++ b/P25Audio.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,9 @@ #define P25Audio_H #include "AMBEFEC.h" +#include "Defines.h" + +#if defined(USE_P25) class CP25Audio { public: @@ -37,3 +40,6 @@ private: }; #endif + +#endif + diff --git a/P25Control.cpp b/P25Control.cpp index 138db0b..27f2493 100644 --- a/P25Control.cpp +++ b/P25Control.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016-2019,2021,2024,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2016-2019,2021,2023,2024,2025 by Jonathan Naylor G4KLX * Copyright (C) 2018 by Bryan Biedenkapp N2PLL * * This program is free software; you can redistribute it and/or modify @@ -26,26 +26,28 @@ #include "CRC.h" #include "Log.h" +#if defined(USE_P25) + #include #include #include #include -// #define DUMP_P25 +const unsigned int RSSI_COUNT = 6U; // 6 * 180ms = 1080ms +const unsigned int BER_COUNT = 6U * 1233U; // 6 * 180ms = 1080ms const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; #define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) #define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) -CP25Control::CP25Control(unsigned int nac, unsigned int id, bool selfOnly, bool uidOverride, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup, bool remoteGateway, CRSSIInterpolator* rssiMapper) : +CP25Control::CP25Control(unsigned int nac, unsigned int id, bool selfOnly, bool uidOverride, CP25Network* network, unsigned int timeout, bool duplex, CDMRLookup* lookup, bool remoteGateway, CRSSIInterpolator* rssiMapper) : m_nac(nac), m_id(id), m_selfOnly(selfOnly), m_uidOverride(uidOverride), m_remoteGateway(remoteGateway), m_network(network), -m_display(display), m_duplex(duplex), m_lookup(lookup), m_queue(1000U, "P25 Control"), @@ -78,15 +80,17 @@ m_rfPDU(nullptr), m_rfPDUCount(0U), m_rfPDUBits(0U), m_rssiMapper(rssiMapper), -m_rssi(0U), -m_maxRSSI(0U), -m_minRSSI(0U), -m_aveRSSI(0U), +m_rssi(0), +m_maxRSSI(0), +m_minRSSI(0), +m_aveRSSI(0), +m_rssiCountTotal(0U), +m_rssiAccum(0), m_rssiCount(0U), -m_enabled(true), -m_fp(nullptr) +m_bitsCount(0U), +m_bitErrsAccum(0U), +m_enabled(true) { - assert(display != nullptr); assert(lookup != nullptr); assert(rssiMapper != nullptr); @@ -127,39 +131,33 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) if ((data[0U] == TAG_LOST) && (m_rfState == RPT_RF_STATE::AUDIO)) { bool grp = m_rfData.getLCF() == P25_LCF_GROUP; unsigned int dstId = m_rfData.getDstId(); - std::string source = m_lookup->find(m_rfData.getSrcId()); + unsigned int srcId = m_rfData.getSrcId(); + std::string source = m_lookup->find(srcId); - if (m_rssi != 0U) - LogMessage("P25, transmission lost from %s to %s%u, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else + if (m_rssi != 0) { + LogMessage("P25, transmission lost from %s to %s%u, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + writeJSONRF("lost", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + } else { LogMessage("P25, transmission lost from %s to %s%u, %.1f seconds, BER: %.1f%%", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeJSONRF("lost", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits)); + } // LogMessage("P25, total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_rfFrames, m_rfBits, m_rfUndecodableLC, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits)); - if (m_netState == RPT_NET_STATE::IDLE) - m_display->clearP25(); - writeNetwork(m_rfLDU, m_lastDUID, true); writeNetwork(data + 2U, P25_DUID_TERM, true); m_rfState = RPT_RF_STATE::LISTENING; m_rfTimeout.stop(); m_rfData.reset(); -#if defined(DUMP_P25) - closeFile(); -#endif + return false; } if ((data[0U] == TAG_LOST) && (m_rfState == RPT_RF_STATE::DATA)) { - if (m_netState == RPT_NET_STATE::IDLE) - m_display->clearP25(); - m_rfState = RPT_RF_STATE::LISTENING; m_rfPDUCount = 0U; m_rfPDUBits = 0U; -#if defined(DUMP_P25) - closeFile(); -#endif + return false; } @@ -205,19 +203,19 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) raw |= (data[219U] << 0) & 0x00FFU; // Convert the raw RSSI to dBm - int rssi = m_rssiMapper->interpolate(raw); - if (rssi != 0) - LogDebug("P25, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi); + m_rssi = m_rssiMapper->interpolate(raw); + if (m_rssi != 0) + LogDebug("P25, raw RSSI: %u, reported RSSI: %d dBm", raw, m_rssi); - // RSSI is always reported as positive - m_rssi = (rssi >= 0) ? rssi : -rssi; - - if (m_rssi > m_minRSSI) + if (m_rssi < m_minRSSI) m_minRSSI = m_rssi; - if (m_rssi < m_maxRSSI) + if (m_rssi > m_maxRSSI) m_maxRSSI = m_rssi; m_aveRSSI += m_rssi; + m_rssiCountTotal++; + + m_rssiAccum += m_rssi; m_rssiCount++; } @@ -273,15 +271,21 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) std::string source = m_lookup->find(srcId); LogMessage("P25, received RF voice transmission from %s to %s%u", source.c_str(), grp ? "TG " : "", dstId); - m_display->writeP25(source.c_str(), grp, dstId, "R"); + writeJSONRF("start", srcId, source, grp, dstId); m_rfState = RPT_RF_STATE::AUDIO; m_minRSSI = m_rssi; m_maxRSSI = m_rssi; m_aveRSSI = m_rssi; + m_rssiCountTotal = 1U; + + m_rssiAccum = m_rssi; m_rssiCount = 1U; + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + createRFHeader(); writeNetwork(data + 2U, P25_DUID_HEADER, false); } else if (m_rfState == RPT_RF_STATE::AUDIO) { @@ -315,20 +319,18 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) unsigned int errors = m_audio.process(data + 2U); LogDebug("P25, LDU1 audio, errs: %u/1233 (%.1f%%)", errors, float(errors) / 12.33F); - m_display->writeP25BER(float(errors) / 12.33F); - m_rfBits += 1233U; m_rfErrs += errors; m_rfFrames++; m_lastDUID = duid; + m_bitsCount += 1233U; + m_bitErrsAccum += errors; + writeJSONBER(); + // Add busy bits, inbound busy addBusyBits(data + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true); -#if defined(DUMP_P25) - writeFile(data + 2U, len - 2U); -#endif - ::memcpy(m_rfLDU, data + 2U, P25_LDU_FRAME_LENGTH_BYTES); if (m_duplex) { @@ -337,7 +339,7 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) writeQueueRF(data, P25_LDU_FRAME_LENGTH_BYTES + 2U); } - m_display->writeP25RSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -371,20 +373,18 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) unsigned int errors = m_audio.process(data + 2U); LogDebug("P25, LDU2 audio, errs: %u/1233 (%.1f%%)", errors, float(errors) / 12.33F); - m_display->writeP25BER(float(errors) / 12.33F); - m_rfBits += 1233U; m_rfErrs += errors; m_rfFrames++; m_lastDUID = duid; + m_bitsCount += 1233U; + m_bitErrsAccum += errors; + writeJSONBER(); + // Add busy bits, inbound busy addBusyBits(data + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true); -#if defined(DUMP_P25) - writeFile(data + 2U, len - 2U); -#endif - ::memcpy(m_rfLDU, data + 2U, P25_LDU_FRAME_LENGTH_BYTES); if (m_duplex) { @@ -393,7 +393,7 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) writeQueueRF(data, P25_LDU_FRAME_LENGTH_BYTES + 2U); } - m_display->writeP25RSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -418,6 +418,43 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) unsigned char data[P25_TSDU_FRAME_LENGTH_BYTES + 2U]; switch (m_rfData.getLCF()) { + case P25_LCF_GROUP: { + // Handle Group Voice Channel User - respond with Group Voice Channel Grant for talk permit + LogMessage("P25, received RF TSDU transmission, GROUP VOICE CH USER from %s to TG %u", source.c_str(), 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); + + // Build a Group Voice Channel Grant response + // Save original LCF and set Grant LCF for encoding + unsigned char originalLcf = m_rfData.getLCF(); + m_rfData.setLCF(P25_LCF_GROUP); + m_rfData.setServiceType(0x00U); // Service options: 0x00 = routine priority, no emergency + + // Encode TSDU with Grant response + m_rfData.encodeTSDU(data + 2U); + + // Restore original LCF + m_rfData.setLCF(originalLcf); + + // Add busy bits - outbound busy (channel granted) + addBusyBits(data + 2U, P25_TSDU_FRAME_LENGTH_BITS, true, false); + + // Set first busy bits to 1,0 (outbound busy) + setBusyBits(data + 2U, P25_SS0_START, true, false); + + if (m_duplex) { + data[0U] = TAG_DATA; + data[1U] = 0x00U; + + writeQueueRF(data, P25_TSDU_FRAME_LENGTH_BYTES + 2U); + } + } + break; case P25_LCF_TSBK_CALL_ALERT: LogMessage("P25, received RF TSDU transmission, CALL ALERT from %s to %s%u", source.c_str(), grp ? "TG " : "", dstId); ::memset(data + 2U, 0x00U, P25_TSDU_FRAME_LENGTH_BYTES); @@ -494,26 +531,24 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) bool grp = m_rfData.getLCF() == P25_LCF_GROUP; unsigned int dstId = m_rfData.getDstId(); - std::string source = m_lookup->find(m_rfData.getSrcId()); + unsigned int srcId = m_rfData.getSrcId(); + std::string source = m_lookup->find(srcId); m_rfState = RPT_RF_STATE::LISTENING; m_rfTimeout.stop(); m_rfData.reset(); m_lastDUID = duid; - if (m_rssi != 0U) - LogMessage("P25, received RF end of voice transmission from %s to %s%u, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else + if (m_rssi != 0) { + LogMessage("P25, received RF end of voice transmission from %s to %s%u, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + writeJSONRF("end", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + } else { LogMessage("P25, received RF end of voice transmission from %s to %s%u, %.1f seconds, BER: %.1f%%", source.c_str(), grp ? "TG " : "", dstId, float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeJSONRF("end", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits)); + } // LogMessage("P25, total frames: %d, bits: %d, undecodable LC: %d, errors: %d, BER: %.4f%%", m_rfFrames, m_rfBits, m_rfUndecodableLC, m_rfErrs, float(m_rfErrs * 100U) / float(m_rfBits)); - m_display->clearP25(); - -#if defined(DUMP_P25) - closeFile(); -#endif - writeNetwork(data + 2U, P25_DUID_TERM, true); if (m_duplex) { @@ -616,7 +651,6 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) } LogMessage("P25, ended RF data transmission"); - m_display->clearP25(); m_rfPDUCount = 0U; m_rfPDUBits = 0U; @@ -770,7 +804,8 @@ void CP25Control::clock(unsigned int ms) if (m_networkWatchdog.hasExpired()) { LogMessage("P25, network watchdog has expired, %.1f seconds, %u%% packet loss", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames); - m_display->clearP25(); + writeJSONNet("lost", float(m_netFrames) / 50.0F, float(m_netLost * 100U) / float(m_netFrames)); + m_networkWatchdog.stop(); m_netState = RPT_NET_STATE::IDLE; m_netData.reset(); @@ -979,11 +1014,6 @@ void CP25Control::createRFHeader() m_lastDUID = P25_DUID_HEADER; ::memset(m_rfLDU, 0x00U, P25_LDU_FRAME_LENGTH_BYTES); -#if defined(DUMP_P25) - openFile(); - writeFile(buffer + 2U, buffer - 2U); -#endif - if (m_duplex) { buffer[0U] = TAG_HEADER; buffer[1U] = 0x00U; @@ -1018,8 +1048,7 @@ void CP25Control::createNetHeader() std::string source = m_lookup->find(srcId); LogMessage("P25, received network transmission from %s to %s%u", source.c_str(), lcf == P25_LCF_GROUP ? "TG " : "", dstId); - - m_display->writeP25(source.c_str(), lcf == P25_LCF_GROUP, dstId, "N"); + writeJSONNet("start", srcId, source, lcf == P25_LCF_GROUP, dstId); m_netState = RPT_NET_STATE::AUDIO; m_netTimeout.start(); @@ -1188,59 +1217,19 @@ void CP25Control::createNetTerminator() writeQueueNet(buffer, P25_TERM_FRAME_LENGTH_BYTES + 2U); - std::string source = m_lookup->find(m_netData.getSrcId()); + unsigned int dstId = m_netData.getDstId(); + unsigned int srcId = m_netData.getSrcId(); + std::string source = m_lookup->find(srcId); - LogMessage("P25, network end of transmission from %s to %s%u, %.1f seconds, %u%% packet loss", source.c_str(), m_netData.getLCF() == P25_LCF_GROUP ? "TG " : "", m_netData.getDstId(), float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames); + LogMessage("P25, network end of transmission from %s to %s%u, %.1f seconds, %u%% packet loss", source.c_str(), m_netData.getLCF() == P25_LCF_GROUP ? "TG " : "", dstId, float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames); + writeJSONNet("end", float(m_netFrames) / 50.0F, float(m_netLost * 100U) / float(m_netFrames)); - m_display->clearP25(); m_netTimeout.stop(); m_networkWatchdog.stop(); m_netData.reset(); m_netState = RPT_NET_STATE::IDLE; } -bool CP25Control::openFile() -{ - if (m_fp != nullptr) - return true; - - time_t t; - ::time(&t); - - struct tm* tm = ::localtime(&t); - - char name[100U]; - ::sprintf(name, "P25_%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 == nullptr) - return false; - - ::fwrite("P25", 1U, 3U, m_fp); - - return true; -} - -bool CP25Control::writeFile(const unsigned char* data, unsigned char length) -{ - if (m_fp == nullptr) - return false; - - ::fwrite(&length, 1U, 1U, m_fp); - - ::fwrite(data, 1U, length, m_fp); - - return true; -} - -void CP25Control::closeFile() -{ - if (m_fp != nullptr) { - ::fclose(m_fp); - m_fp = nullptr; - } -} - bool CP25Control::isBusy() const { return (m_rfState != RPT_RF_STATE::LISTENING) || (m_netState != RPT_NET_STATE::IDLE); @@ -1252,33 +1241,11 @@ void CP25Control::enable(bool enabled) m_queue.clear(); // Reset the RF section - switch (m_rfState) { - case RPT_RF_STATE::LISTENING: - case RPT_RF_STATE::REJECTED: - case RPT_RF_STATE::INVALID: - break; - - default: - if (m_rfTimeout.isRunning()) { - LogMessage("P25, RF user has timed out"); - } - break; - } m_rfState = RPT_RF_STATE::LISTENING; m_rfTimeout.stop(); m_rfData.reset(); // Reset the networking section - switch(m_netState) { - case RPT_NET_STATE::IDLE: - break; - - default: - if (m_netTimeout.isRunning()) { - LogMessage("P25, network user has timed out"); - } - break; - } m_netTimeout.stop(); m_networkWatchdog.stop(); m_netData.reset(); @@ -1287,3 +1254,138 @@ void CP25Control::enable(bool enabled) m_enabled = enabled; } + +void CP25Control::writeJSONRSSI() +{ + if (m_rssi == 0) + return; + + if (m_rssiCount >= RSSI_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "P25"; + + json["value"] = m_rssiAccum / int(m_rssiCount); + + WriteJSON("RSSI", json); + + m_rssiAccum = 0; + m_rssiCount = 0U; + } +} + +void CP25Control::writeJSONBER() +{ + if (m_bitsCount >= BER_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "P25"; + + json["value"] = float(m_bitErrsAccum * 100U) / float(m_bitsCount); + + WriteJSON("BER", json); + + m_bitErrsAccum = 0U; + m_bitsCount = 1U; + } +} + +void CP25Control::writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, "rf", action, srcId, srcInfo, grp, dstId); + + WriteJSON("P25", json); +} + +void CP25Control::writeJSONRF(const char* action, float duration, float ber) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, action); + + json["duration"] = duration; + json["ber"] = ber; + + WriteJSON("P25", json); +} + +void CP25Control::writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, action); + + json["duration"] = duration; + json["ber"] = ber; + + nlohmann::json rssi; + rssi["min"] = minRSSI; + rssi["max"] = maxRSSI; + rssi["ave"] = aveRSSI; + + json["rssi"] = rssi; + + WriteJSON("P25", json); +} + +void CP25Control::writeJSONNet(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, "network", action, srcId, srcInfo, grp, dstId); + + WriteJSON("P25", json); +} + +void CP25Control::writeJSONNet(const char* action, float duration, float loss) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSON(json, action); + + json["duration"] = duration; + json["loss"] = loss; + + WriteJSON("P25", json); +} + +void CP25Control::writeJSON(nlohmann::json& json, const char* action) +{ + assert(action != nullptr); + + json["timestamp"] = CUtils::createTimestamp(); + json["action"] = action; +} + +void CP25Control::writeJSON(nlohmann::json& json, const char* source, const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(source != nullptr); + assert(action != nullptr); + + json["timestamp"] = CUtils::createTimestamp(); + json["source"] = source; + json["action"] = action; + json["source_id"] = int(srcId); + json["destination_id"] = int(dstId); + json["destination_type"] = grp ? "group" : "individual"; + + if (!srcInfo.empty()) + json["source_info"] = srcInfo; +} + +#endif + diff --git a/P25Control.h b/P25Control.h index 3076624..a3a3af9 100644 --- a/P25Control.h +++ b/P25Control.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016-2019 by Jonathan Naylor G4KLX +* Copyright (C) 2016-2019,2023 by Jonathan Naylor G4KLX * Copyright (C) 2018 by Bryan Biedenkapp N2PLL * * This program is free software; you can redistribute it and/or modify @@ -27,17 +27,20 @@ #include "DMRLookup.h" #include "P25Audio.h" #include "Defines.h" -#include "Display.h" #include "P25Data.h" #include "P25NID.h" #include "Modem.h" #include "Timer.h" +#if defined(USE_P25) + #include +#include + class CP25Control { public: - CP25Control(unsigned int nac, unsigned int id, bool selfOly, bool uidOverride, CP25Network* network, CDisplay* display, unsigned int timeout, bool duplex, CDMRLookup* lookup, bool remoteGateway, CRSSIInterpolator* rssiMapper); + CP25Control(unsigned int nac, unsigned int id, bool selfOly, bool uidOverride, CP25Network* network, unsigned int timeout, bool duplex, CDMRLookup* lookup, bool remoteGateway, CRSSIInterpolator* rssiMapper); ~CP25Control(); bool writeModem(unsigned char* data, unsigned int len); @@ -57,7 +60,6 @@ private: bool m_uidOverride; bool m_remoteGateway; CP25Network* m_network; - CDisplay* m_display; bool m_duplex; CDMRLookup* m_lookup; CRingBuffer m_queue; @@ -90,13 +92,16 @@ private: unsigned int m_rfPDUCount; unsigned int m_rfPDUBits; CRSSIInterpolator* m_rssiMapper; - unsigned char m_rssi; - unsigned char m_maxRSSI; - unsigned char m_minRSSI; - unsigned int m_aveRSSI; + int m_rssi; + int m_maxRSSI; + int m_minRSSI; + int m_aveRSSI; + unsigned int m_rssiCountTotal; + int m_rssiAccum; unsigned int m_rssiCount; + unsigned int m_bitsCount; + unsigned int m_bitErrsAccum; bool m_enabled; - FILE* m_fp; void writeQueueRF(const unsigned char* data, unsigned int length); void writeQueueNet(const unsigned char* data, unsigned int length); @@ -118,9 +123,21 @@ private: void createNetLDU2(); void createNetTerminator(); - bool openFile(); - bool writeFile(const unsigned char* data, unsigned char length); - void closeFile(); + void writeJSONRSSI(); + void writeJSONBER(); + + void writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); + void writeJSONRF(const char* action, float duration, float ber); + void writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI); + + void writeJSONNet(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); + void writeJSONNet(const char* action, float duration, float loss); + + void writeJSON(nlohmann::json& json, const char* action); + void writeJSON(nlohmann::json& json, const char* source, const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); }; #endif + +#endif + diff --git a/P25Data.cpp b/P25Data.cpp index de461b2..d4fa563 100644 --- a/P25Data.cpp +++ b/P25Data.cpp @@ -26,6 +26,8 @@ #include "Utils.h" #include "Log.h" +#if defined(USE_P25) + #include #include #include @@ -385,6 +387,11 @@ bool CP25Data::decodeTSDU(const unsigned char* data) tsbkValue = (tsbkValue << 8) + tsbk[9U]; switch (m_lcf) { + case P25_LCF_GROUP: + m_emergency = ((tsbkValue >> 63) & 0x01U) == 0x01U; // Emergency flag (bit 63) + m_dstId = (unsigned int)((tsbkValue >> 24) & 0xFFFFU); // Group Address (16 bits) + m_srcId = (unsigned int)(tsbkValue & 0xFFFFFFU); // Source Radio Address (24 bits) + break; 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 @@ -416,6 +423,11 @@ void CP25Data::encodeTSDU(unsigned char* data) tsbk[1U] = m_mfId; switch (m_lcf) { + case P25_LCF_GROUP: + tsbkValue = (unsigned long long)(m_serviceType & 0xFFU); // Service Options (8 bits) + tsbkValue = (tsbkValue << 16) + 0U; // Channel ID/Number (16 bits) - 0 for conventional + tsbkValue = (tsbkValue << 16) + (m_dstId & 0xFFFFU); // Group Address (16 bits) + tsbkValue = (tsbkValue << 24) + (m_srcId & 0xFFFFFFU); // Source Radio Address (24 bits) case P25_LCF_TSBK_CALL_ALERT: tsbkValue = 0U; tsbkValue = (tsbkValue << 16) + 0U; @@ -664,3 +676,6 @@ void CP25Data::encodeHeaderGolay(unsigned char* data, const unsigned char* raw) } } } + +#endif + diff --git a/P25Data.h b/P25Data.h index ff5e807..b02b1c5 100644 --- a/P25Data.h +++ b/P25Data.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2017,2024 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2017,2023,2024 by Jonathan Naylor G4KLX * Copyright (C) 2018 by Bryan Biedenkapp N2PLL * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,10 @@ #if !defined(P25Data_H) #define P25Data_H +#include "Defines.h" + +#if defined(USE_P25) + #include "RS634717.h" #include "P25Trellis.h" @@ -92,3 +96,6 @@ private: }; #endif + +#endif + diff --git a/P25LowSpeedData.cpp b/P25LowSpeedData.cpp index a9f0191..32c0c3d 100644 --- a/P25LowSpeedData.cpp +++ b/P25LowSpeedData.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,8 @@ #include "P25LowSpeedData.h" #include "P25Utils.h" +#if defined(USE_P25) + #include #include @@ -128,3 +130,6 @@ unsigned char CP25LowSpeedData::encode(unsigned char in) const { return CCS_PARITY[in]; } + +#endif + diff --git a/P25LowSpeedData.h b/P25LowSpeedData.h index 41ba306..5634879 100644 --- a/P25LowSpeedData.h +++ b/P25LowSpeedData.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(P25LowSpeedData_H) #define P25LowSpeedData_H +#include "Defines.h" + +#if defined(USE_P25) + class CP25LowSpeedData { public: CP25LowSpeedData(); @@ -42,3 +46,6 @@ private: }; #endif + +#endif + diff --git a/P25NID.cpp b/P25NID.cpp index 0d9b4b8..8df8b9c 100644 --- a/P25NID.cpp +++ b/P25NID.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2018,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2018,2023,2025 by Jonathan Naylor G4KLX * Copyright (C) 2018 by Bryan Biedenkapp * * This program is free software; you can redistribute it and/or modify @@ -22,6 +22,8 @@ #include "P25Utils.h" #include "BCH.h" +#if defined(USE_P25) + #include #include @@ -187,3 +189,6 @@ unsigned char CP25NID::getDUID() const { return m_duid; } + +#endif + diff --git a/P25NID.h b/P25NID.h index fe7ee74..88510bc 100644 --- a/P25NID.h +++ b/P25NID.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2018,2023 by Jonathan Naylor G4KLX * Copyright (C) 2018 by Bryan Biedenkapp * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,10 @@ #if !defined(P25NID_H) #define P25NID_H +#include "Defines.h" + +#if defined(USE_P25) + class CP25NID { public: CP25NID(unsigned int nac); @@ -43,3 +47,6 @@ private: }; #endif + +#endif + diff --git a/P25Network.cpp b/P25Network.cpp index 818ddc2..5f310a3 100644 --- a/P25Network.cpp +++ b/P25Network.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014,2016,2019,2020,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2014,2016,2019,2020,2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,10 +18,11 @@ #include "P25Network.h" #include "P25Defines.h" -#include "Defines.h" #include "Utils.h" #include "Log.h" +#if defined(USE_P25) + #include #include #include @@ -443,3 +444,6 @@ void CP25Network::enable(bool enabled) m_enabled = enabled; } + +#endif + diff --git a/P25Network.h b/P25Network.h index 7023c94..c2c2e63 100644 --- a/P25Network.h +++ b/P25Network.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014,2016,2020,2021 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2014,2016,2020,2021,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,9 @@ #include "UDPSocket.h" #include "P25Audio.h" #include "P25Data.h" +#include "Defines.h" + +#if defined(USE_P25) #include #include @@ -60,3 +63,6 @@ private: }; #endif + +#endif + diff --git a/P25Trellis.cpp b/P25Trellis.cpp index 50cea5c..3c4bcde 100644 --- a/P25Trellis.cpp +++ b/P25Trellis.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2018,2024,2025 by Jonathan Naylor, G4KLX +* Copyright (C) 2016,2018,2023,2024,2025 by Jonathan Naylor, G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,6 +14,8 @@ #include "P25Trellis.h" #include "Log.h" +#if defined(USE_P25) || defined(USE_DMR) + #include #include @@ -523,3 +525,6 @@ unsigned int CP25Trellis::checkCode12(const unsigned char* points, unsigned char return 999U; } + +#endif + diff --git a/P25Trellis.h b/P25Trellis.h index 47e7c1d..d5c89a8 100644 --- a/P25Trellis.h +++ b/P25Trellis.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2018 by Jonathan Naylor, G4KLX +* Copyright (C) 2016,2018,2023 by Jonathan Naylor, G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,6 +14,10 @@ #ifndef P25Trellis_H #define P25Trellis_H +#include "Defines.h" + +#if defined(USE_P25) + class CP25Trellis { public: CP25Trellis(); @@ -41,3 +45,6 @@ private: }; #endif + +#endif + diff --git a/P25Utils.cpp b/P25Utils.cpp index 9361456..74ababb 100644 --- a/P25Utils.cpp +++ b/P25Utils.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2018,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2018,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,8 @@ #include "P25Utils.h" #include "P25Defines.h" +#if defined(USE_P25) + #include #include @@ -130,3 +132,6 @@ unsigned int CP25Utils::compare(const unsigned char* data1, const unsigned char* return errs; } + +#endif + diff --git a/P25Utils.h b/P25Utils.h index 75960e5..0062356 100644 --- a/P25Utils.h +++ b/P25Utils.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2018 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2018,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(P25Utils_H) #define P25Utils_H +#include "Defines.h" + +#if defined(USE_P25) + class CP25Utils { public: static unsigned int encode(const unsigned char* in, unsigned char* out, unsigned int start, unsigned int stop); @@ -32,3 +36,6 @@ private: }; #endif + +#endif + diff --git a/POCSAGControl.cpp b/POCSAGControl.cpp index be300ba..5e636ff 100644 --- a/POCSAGControl.cpp +++ b/POCSAGControl.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2018,2019,2020,2025 Jonathan Naylor, G4KLX +* Copyright (C) 2018,2019,2020,2023,2025 Jonathan Naylor, G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,13 +15,13 @@ #include "Utils.h" #include "Log.h" +#if defined(USE_POCSAG) + #include #include #include #include -// #define DUMP_POCSAG - const struct BCD { char m_c; uint32_t m_bcd[5U]; @@ -58,9 +58,8 @@ const unsigned char FUNCTIONAL_ALERT1 = 1U; const unsigned char FUNCTIONAL_ALERT2 = 2U; const unsigned char FUNCTIONAL_ALPHANUMERIC = 3U; -CPOCSAGControl::CPOCSAGControl(CPOCSAGNetwork* network, CDisplay* display) : +CPOCSAGControl::CPOCSAGControl(CPOCSAGNetwork* network) : m_network(network), -m_display(display), m_queue(5000U, "POCSAG Control"), m_frames(0U), m_count(0U), @@ -69,10 +68,8 @@ m_buffer(), m_ric(0U), m_data(), m_state(POCSAG_STATE::NONE), -m_enabled(true), -m_fp(nullptr) +m_enabled(true) { - assert(display != nullptr); } CPOCSAGControl::~CPOCSAGControl() @@ -113,6 +110,7 @@ void CPOCSAGControl::sendPage(unsigned int ric, const std::string& text) addAddress(FUNCTIONAL_ALPHANUMERIC, ric, output->m_buffer); LogDebug("Local message to %07u, func Alphanumeric: \"%s\"", ric, text.c_str()); + writeJSON("local", ric, "alphanumeric", text); packASCII(text, output->m_buffer); @@ -137,6 +135,7 @@ void CPOCSAGControl::sendPageBCD(unsigned int ric, const std::string& text) addAddress(FUNCTIONAL_NUMERIC, ric, output->m_buffer); LogDebug("Local message to %07u, func NUMERIC: \"%s\"", ric, text.c_str()); + writeJSON("local", ric, "numeric", text); packNumeric(text, output->m_buffer); @@ -160,6 +159,7 @@ void CPOCSAGControl::sendPageAlert1(unsigned int ric) addAddress(FUNCTIONAL_ALERT1, ric, output->m_buffer); LogDebug("Local message to %07u, func Alert1", ric); + writeJSON("local", ric, "alert_1"); // Ensure data is an even number of words if ((output->m_buffer.size() % 2U) == 1U) @@ -182,6 +182,7 @@ void CPOCSAGControl::sendPageAlert2(unsigned int ric, const std::string& text) addAddress(FUNCTIONAL_ALERT2, ric, output->m_buffer); LogDebug("Local message to %07u, func Alert2: \"%s\"", ric, text.c_str()); + writeJSON("local", ric, "alert_2", text); packASCII(text, output->m_buffer); @@ -238,22 +239,26 @@ bool CPOCSAGControl::readNetwork() break; } LogDebug("Message to %07u, func Alphanumeric: %s", output->m_ric, output->m_display.c_str()); + writeJSON("network", output->m_ric, "alphanumeric", output->m_display); packASCII(output->m_text, output->m_buffer); break; case FUNCTIONAL_NUMERIC: output->m_text = std::string((char*)(data + 4U), length - 4U); output->m_display = output->m_text; LogDebug("Message to %07u, func Numeric: \"%s\"", output->m_ric, output->m_display.c_str()); + writeJSON("network", output->m_ric, "numeric", output->m_display); packNumeric(output->m_text, output->m_buffer); break; case FUNCTIONAL_ALERT1: output->m_display = "Func alert 1"; LogDebug("Message to %07u, func Alert 1", output->m_ric); + writeJSON("network", output->m_ric, "alert_1"); break; case FUNCTIONAL_ALERT2: output->m_text = std::string((char*)(data + 4U), length - 4U); output->m_display = "Func alert 2: " + output->m_text; LogDebug("Message to %07u, func Alert 2: \"%s\"", output->m_ric, output->m_display.c_str()); + writeJSON("network", output->m_ric, "alert_2", output->m_display); packASCII(output->m_text, output->m_buffer); break; default: @@ -277,8 +282,6 @@ bool CPOCSAGControl::processData() POCSAGData* output = m_data.front(); m_data.pop_front(); - m_display->writePOCSAG(output->m_ric, output->m_display); - m_buffer = output->m_buffer; m_ric = output->m_ric; @@ -301,10 +304,6 @@ void CPOCSAGControl::clock(unsigned int ms) m_state = POCSAG_STATE::WAITING; m_frames = 0U; m_count = 1U; - -#if defined(DUMP_POCSAG) - openFile(); -#endif } m_output.clear(); @@ -358,12 +357,8 @@ void CPOCSAGControl::clock(unsigned int ms) if (m_state == POCSAG_STATE::ENDING) { LogMessage("POCSAG, transmitted %u frame(s) of data from %u message(s)", m_frames, m_count); - m_display->clearPOCSAG(); + writeJSON("network", 0U, "end"); m_state = POCSAG_STATE::NONE; - -#if defined(DUMP_POCSAG) - closeFile(); -#endif } } @@ -500,10 +495,6 @@ void CPOCSAGControl::writeQueue() m_output.clear(); -#if defined(DUMP_POCSAG) - writeFile(data); -#endif - CUtils::dump(1U, "Data to MMDVM", data, len); assert(len == POCSAG_FRAME_LENGTH_BYTES); @@ -518,46 +509,6 @@ void CPOCSAGControl::writeQueue() m_queue.addData(data, len); } -bool CPOCSAGControl::openFile() -{ - if (m_fp != nullptr) - return true; - - time_t t; - ::time(&t); - - struct tm* tm = ::localtime(&t); - - char name[100U]; - ::sprintf(name, "POCSAG_%04d%02d%02d_%02d%02d%02d.dat", 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 == nullptr) - return false; - - ::fwrite("POCSAG", 1U, 6U, m_fp); - - return true; -} - -bool CPOCSAGControl::writeFile(const unsigned char* data) -{ - if (m_fp == nullptr) - return false; - - ::fwrite(data, 1U, POCSAG_FRAME_LENGTH_BYTES, m_fp); - - return true; -} - -void CPOCSAGControl::closeFile() -{ - if (m_fp != nullptr) { - ::fclose(m_fp); - m_fp = nullptr; - } -} - void CPOCSAGControl::enable(bool enabled) { if (!enabled && m_enabled) { @@ -579,3 +530,37 @@ void CPOCSAGControl::decodeROT1(const std::string& in, unsigned int start, std:: for (size_t i = start; i < in.length(); i++) out += in.at(i) - 1U; } + +void CPOCSAGControl::writeJSON(const char* source, unsigned int ric, const char* functional) +{ + assert(source != nullptr); + assert(functional != nullptr); + + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["source"] = source; + json["ric"] = int(ric); + json["functional"] = functional; + + WriteJSON("POCSAG", json); +} + +void CPOCSAGControl::writeJSON(const char* source, unsigned int ric, const char* functional, const std::string& message) +{ + assert(source != nullptr); + assert(functional != nullptr); + + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["source"] = source; + json["ric"] = int(ric); + json["functional"] = functional; + json["message"] = message; + + WriteJSON("POCSAG", json); +} + +#endif + diff --git a/POCSAGControl.h b/POCSAGControl.h index 0e24d12..ffcfcbe 100644 --- a/POCSAGControl.h +++ b/POCSAGControl.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2018,2019,2020,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2018,2019,2020,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,14 +22,17 @@ #include "POCSAGNetwork.h" #include "POCSAGDefines.h" #include "RingBuffer.h" -#include "Display.h" #include "Defines.h" +#if defined(USE_POCSAG) + #include #include #include +#include + struct POCSAGData { unsigned int m_ric; std::string m_text; @@ -39,7 +42,7 @@ struct POCSAGData { class CPOCSAGControl { public: - CPOCSAGControl(CPOCSAGNetwork* network, CDisplay* display); + CPOCSAGControl(CPOCSAGNetwork* network); ~CPOCSAGControl(); void sendPage(unsigned int ric, const std::string& text); @@ -55,7 +58,6 @@ public: private: CPOCSAGNetwork* m_network; - CDisplay* m_display; CRingBuffer m_queue; unsigned int m_frames; unsigned int m_count; @@ -73,7 +75,6 @@ private: std::deque m_data; POCSAG_STATE m_state; bool m_enabled; - FILE* m_fp; bool readNetwork(); void writeQueue(); @@ -82,11 +83,14 @@ private: void packASCII(const std::string& text, std::deque& buffer) const; void packNumeric(const std::string& text, std::deque& buffer) const; void addBCHAndParity(uint32_t& word) const; - bool openFile(); - bool writeFile(const unsigned char* data); - void closeFile(); void decodeROT1(const std::string& in, unsigned int start, std::string& out) const; + + void writeJSON(const char* source, unsigned int ric, const char* functional); + void writeJSON(const char* source, unsigned int ric, const char* functional, const std::string& message); }; #endif + +#endif + diff --git a/POCSAGNetwork.cpp b/POCSAGNetwork.cpp index 8801f29..01a8fab 100644 --- a/POCSAGNetwork.cpp +++ b/POCSAGNetwork.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018,2019,2020,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2018,2019,2020,2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,10 +18,11 @@ #include "POCSAGDefines.h" #include "POCSAGNetwork.h" -#include "Defines.h" #include "Utils.h" #include "Log.h" +#if defined(USE_POCSAG) + #include #include #include @@ -124,3 +125,6 @@ void CPOCSAGNetwork::enable(bool enabled) m_enabled = enabled; } + +#endif + diff --git a/POCSAGNetwork.h b/POCSAGNetwork.h index beceb9b..60e8eb7 100644 --- a/POCSAGNetwork.h +++ b/POCSAGNetwork.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018,2020,2021 by Jonathan Naylor G4KLX + * Copyright (C) 2018,2020,2021,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,8 +22,11 @@ #include "POCSAGDefines.h" #include "RingBuffer.h" #include "UDPSocket.h" +#include "Defines.h" #include "Timer.h" +#if defined(USE_POCSAG) + #include #include @@ -54,3 +57,6 @@ private: }; #endif + +#endif + diff --git a/PseudoTTYController.cpp b/PseudoTTYController.cpp deleted file mode 100644 index 2e7f9b3..0000000 --- a/PseudoTTYController.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2020,2021,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#if !defined(_WIN32) && !defined(_WIN64) - -#include "PseudoTTYController.h" -#include "Log.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#if defined(__linux__) - #include -#elif defined(__FreeBSD__) - #include -#else - #include -#endif - - -CPseudoTTYController::CPseudoTTYController(const std::string& symlink, unsigned int speed, bool assertRTS) : -CUARTController(speed, assertRTS), -m_symlink(symlink) -{ -} - -CPseudoTTYController::~CPseudoTTYController() -{ -} - -bool CPseudoTTYController::open() -{ - assert(m_fd == -1); - - int slavefd; - char slave[300]; - int result = ::openpty(&m_fd, &slavefd, slave, nullptr, nullptr); - if (result < 0) { - LogError("Cannot open the pseudo tty - errno : %d", errno); - return false; - } - - // Remove any previous stale symlink - ::unlink(m_symlink.c_str()); - - int ret = ::symlink(slave, m_symlink.c_str()); - if (ret != 0) { - LogError("Cannot make symlink to %s with %s", slave, m_symlink.c_str()); - close(); - return false; - } - - LogMessage("Made symbolic link from %s to %s", slave, m_symlink.c_str()); - - m_device = std::string(::ttyname(m_fd)); - - return setRaw(); -} - -void CPseudoTTYController::close() -{ - CUARTController::close(); - - ::unlink(m_symlink.c_str()); -} - -#endif diff --git a/PseudoTTYController.h b/PseudoTTYController.h deleted file mode 100644 index 228f153..0000000 --- a/PseudoTTYController.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2020,2021 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef PseudoTTYController_H -#define PseudoTTYController_H - -#if !defined(_WIN32) && !defined(_WIN64) - -#include - -#include "UARTController.h" - -class CPseudoTTYController : public CUARTController { -public: - CPseudoTTYController(const std::string& symlink, unsigned int speed, bool assertRTS = false); - virtual ~CPseudoTTYController(); - - virtual bool open(); - - virtual void close(); - -protected: - std::string m_symlink; -}; - -#endif - -#endif diff --git a/QR1676.cpp b/QR1676.cpp index 7da9c4b..f2f1e63 100644 --- a/QR1676.cpp +++ b/QR1676.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,8 @@ #include "QR1676.h" #include "Log.h" +#if defined(USE_DMR) + #include #include @@ -113,3 +115,6 @@ unsigned char CQR1676::decode(const unsigned char* data) return code >> 7; } + +#endif + diff --git a/QR1676.h b/QR1676.h index dac2c0f..549c143 100644 --- a/QR1676.h +++ b/QR1676.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #ifndef QR1676_H #define QR1676_H +#include "Defines.h" + +#if defined(USE_DMR) + class CQR1676 { public: static void encode(unsigned char* data); @@ -30,3 +34,6 @@ private: }; #endif + +#endif + diff --git a/README.HD44780 b/README.HD44780 deleted file mode 100644 index 35c2dea..0000000 --- a/README.HD44780 +++ /dev/null @@ -1,84 +0,0 @@ - - HD44780 LCD SUPPORT - - -Support for the HD44780 LCD is via the wiringPi library available at -http://wiringpi.com/download-and-install/ which must be installed in all cases. - -The HD44780 in 4-bit mode is probably the most common connection method and -wiring details can be found at http://wiringpi.com/dev-lib/lcd-library/ - -The setup that is commonly used for MMDVM is the 4-bit connection that is also -documented within wiringPi. The HD44780 displays have a standardized 16-pin -connector (14 pins if without backlight). The pin numbers on HD44780 refer to -this standard numbering. - -The pin numbers on Raspberry Pi side are the physical pin numbers of the GPIO -pin header, in brackets the wiringPi pin number as these are referred to in the -MMDVM.ini file. - -The wiring ist as follows: - - HD44780 pin Raspberry Pi GPIO - GND 1 -------- 6 GND - VCC 2 -------- 2 +5V - V0 3 -------- Trimmer between +5V and GND for contrast - RS 4 -------- 26 CE1 (11) - RW 5 -------- 6 GND - E 6 -------- 24 CE0 (10) - D0 7 - D1 8 - D2 9 - D3 10 - D4 11 -------- 11 GPIO0 (0) - D5 12 -------- 12 GPIO1 (1) - D6 13 -------- 13 GPIO2 (2) - D7 14 -------- 15 GPIO3 (3) - VCC 15 -------- 2 +5V - GND 16 -------- 6 GND - -The relevant part in the MMDVM.ini is like outlined below. - -[HD44780] -Rows=4 -Columns=20 - -# For basic HD44780 displays (4-bit connection) -# rs, strb, d0, d1, d2, d3 -Pins=11,10,0,1,2,3 - -To compile MMDVMHost with support for the HD44780 use the following commands. - -# make clean -# make -f Makefile.Pi.HD44780 - -Other HD44780 variations exist that connect via I2C. Support is also via -wiringPi, but to compile for each I2C device requires a different Makefile -depending on the IC attached to the LCD. - - - ADAFRUIT RGB LCD SHIELD - - -The Adafruit RGB LCD Shield is available from https://www.adafruit.com/product/714 -I2C is via the MCP23017 IC and is enabled with Makefile.Pi.Adafruit. The -I2C device address in MMDVM.ini should be set to 0x20. - - - PCF8574 IC - - -HD44780 LCDs connected via a PCF8574 Remote 8-Bit I/O Expander are available on -eBay for very little money! This IC uses Makefile.Pi.PCF8574 and the I2C -device address should be set to 0x27. - - - BEWARE - - - The I2C device address can be manually configured on some devices! - - - CHECK YOUR ACTUAL DEVICE ADDRESS - - -More information on configuring and using I2C on the RPi including how to probe -the I2C bus for device addresses can be found at -https://learn.sparkfun.com/tutorials/raspberry-pi-spi-and-i2c-tutorial#i2c-on-pi - - - PWM BACKLIGHT CONTROL - - -Whilst connected in 4-bit mode or via the PCF8574 IC, the LED backlight can be -wired for control via PWM. Connect the backlight +ve pin to any spare GPIO pin -and configure MMDVM.ini as appropriate. By default, wiringPi pin 21 is -configured. diff --git a/README.md b/README.md index 6d0c4be..2588063 100644 --- a/README.md +++ b/README.md @@ -12,29 +12,7 @@ NXCore talk groups. It uses the DAPNET Gateway to access DAPNET to receive paging messages. It builds on 32-bit and 64-bit Linux as well as on Windows using Visual Studio -2022 on x86 and x64. It can optionally control various Displays. Currently -these are: - -- HD44780 (sizes 2x16, 2x40, 4x16, 4x20) - - 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 (all sizes, both Basic and Enhanced versions) -- OLED 128x64 (SSD1306) -- LCDproc - -The Nextion displays can connect to the UART on the Raspberry Pi, or via a USB -to TTL serial converter like the FT-232RL. It may also be connected to the UART -output of the MMDVM modem (Arduino Due, STM32, Teensy). - -The HD44780 displays are integrated with wiringPi for Raspberry Pi based -platforms. - -The OLED display needs an extra library see OLED.md - -The LCDproc support enables the use of a multitude of other LCD screens. See -the [supported devices](http://lcdproc.omnipotent.net/hardware.php3) page on -the LCDproc website for more info. +2022 on x86 and x64. This software is licenced under the GPL v2 and is primarily intended for amateur and educational use. diff --git a/RS129.cpp b/RS129.cpp index f27face..144f6bd 100644 --- a/RS129.cpp +++ b/RS129.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,8 @@ #include "RS129.h" +#if defined(USE_DMR) + #include #include #include @@ -128,3 +130,5 @@ bool CRS129::check(const unsigned char* in) return in[9U] == parity[2U] && in[10U] == parity[1U] && in[11U] == parity[0U]; } +#endif + diff --git a/RS129.h b/RS129.h index 60f99bd..e21c5e9 100644 --- a/RS129.h +++ b/RS129.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(RS129_H) #define RS129_H +#include "Defines.h" + +#if defined(USE_DMR) + class CRS129 { public: @@ -28,3 +32,6 @@ public: }; #endif + +#endif + diff --git a/RS634717.cpp b/RS634717.cpp index 6c8d607..ed435c4 100644 --- a/RS634717.cpp +++ b/RS634717.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2024,2025 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2023,2024,2025 by Jonathan Naylor G4KLX * Copyright (C) 2018,2023 by Bryan Biedenkapp N2PLL * * This program is free software; you can redistribute it and/or modify @@ -22,6 +22,8 @@ #include +#if defined(USE_P25) + #include #include #include @@ -309,3 +311,5 @@ unsigned char CRS634717::gf6Mult(unsigned char a, unsigned char b) const return p; } + +#endif diff --git a/RS634717.h b/RS634717.h index de1ebd7..a1e7108 100644 --- a/RS634717.h +++ b/RS634717.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2024 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2023,2024 by Jonathan Naylor G4KLX * Copyright (C) 2018,2023 by Bryan Biedenkapp N2PLL * * This program is free software; you can redistribute it and/or modify @@ -20,6 +20,10 @@ #if !defined(RS634717_H) #define RS634717_H +#include "Defines.h" + +#if defined(USE_P25) + class CRS634717 { public: @@ -39,3 +43,6 @@ private: }; #endif + +#endif + diff --git a/RemoteCommand.cpp b/RemoteCommand.cpp deleted file mode 100644 index 1bfea48..0000000 --- a/RemoteCommand.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2019,2020,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "RemoteCommand.h" - -#include "UDPSocket.h" -#include "Log.h" - -#include -#include -#include - -const unsigned int BUFFER_LENGTH = 1024U; - -int main(int argc, char** argv) -{ - if (argc < 3) { - ::fprintf(stderr, "Usage: RemoteCommand [argument]\n"); - return 1; - } - - unsigned int port = (unsigned int)::atoi(argv[1]); - std::string cmd = std::string(argv[2]); - - for (int i = 3; i < argc; i++) { - cmd += " "; - cmd += std::string(argv[i]); - } - - if (port == 0U) { - ::fprintf(stderr, "RemoteCommand: invalid port number - %s\n", argv[1]); - return 1; - } - - CRemoteCommand* command = new CRemoteCommand(port); - - return command->send(cmd); -} - -CRemoteCommand::CRemoteCommand(unsigned int port) : -m_port(port) -{ - CUDPSocket::startup(); - - ::LogInitialise(false, ".", "RemoteCommand", 2U, 2U, false); -} - -CRemoteCommand::~CRemoteCommand() -{ - ::LogFinalise(); - - CUDPSocket::shutdown(); -} - -int CRemoteCommand::send(const std::string& command) -{ - sockaddr_storage addr; - unsigned int addrLen; - char buffer[BUFFER_LENGTH]; - int retStatus = 0; - - if (CUDPSocket::lookup("127.0.0.1", m_port, addr, addrLen) != 0) { - ::fprintf(stderr, "Unable to resolve the address of the host\n"); - return 1; - } - - CUDPSocket socket(0U); - - bool ret = socket.open(addr); - if (!ret) - return 1; - - ret = socket.write((unsigned char*)command.c_str(), (unsigned int)command.length(), addr, addrLen); - if (!ret) { - socket.close(); - return 1; - } - - ::fprintf(stdout, "Command sent: \"%s\" to port: %u\n", command.c_str(), m_port); - - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - - int len = socket.read((unsigned char*)buffer, BUFFER_LENGTH, addr, addrLen); - if (len > 0) { - buffer[len] = '\0'; - ::fprintf(stdout, "%s\n", buffer); - } - else - { - retStatus = 1; - } - - socket.close(); - - return retStatus; -} diff --git a/RemoteCommand.h b/RemoteCommand.h deleted file mode 100644 index afefce7..0000000 --- a/RemoteCommand.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2019 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 RemoteCommand_H -#define RemoteCommand_H - -#include - -class CRemoteCommand -{ -public: - CRemoteCommand(unsigned int port); - ~CRemoteCommand(); - - int send(const std::string& command); - -private: - unsigned int m_port; -}; - -#endif diff --git a/RemoteCommand.vcxproj b/RemoteCommand.vcxproj deleted file mode 100644 index 8c70dd2..0000000 --- a/RemoteCommand.vcxproj +++ /dev/null @@ -1,170 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - 15.0 - {5A61AB93-58BB-413A-BBD9-A26284CB37AE} - Win32Proj - RemoteCommand - 10.0 - - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - true - - - true - - - false - - - false - - - - NotUsing - Level3 - Disabled - true - _CRT_SECURE_NO_WARNINGS - true - pch.h - - - Console - true - ws2_32.lib;%(AdditionalDependencies) - - - - - NotUsing - Level3 - Disabled - true - _CRT_SECURE_NO_WARNINGS - true - pch.h - - - Console - true - ws2_32.lib;%(AdditionalDependencies) - - - - - NotUsing - Level3 - MaxSpeed - true - true - true - _CRT_SECURE_NO_WARNINGS - true - pch.h - - - Console - true - true - true - ws2_32.lib;%(AdditionalDependencies) - - - - - NotUsing - Level3 - MaxSpeed - true - true - true - _CRT_SECURE_NO_WARNINGS - true - pch.h - - - Console - true - true - true - ws2_32.lib;%(AdditionalDependencies) - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/RemoteCommand.vcxproj.filters b/RemoteCommand.vcxproj.filters deleted file mode 100644 index 190b67c..0000000 --- a/RemoteCommand.vcxproj.filters +++ /dev/null @@ -1,35 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/RemoteControl.cpp b/RemoteControl.cpp index 6eaf2c3..54a6587 100644 --- a/RemoteControl.cpp +++ b/RemoteControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019,2020,2021,2024,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2019,2020,2021,2023,2024,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,6 @@ #include #include #include -#include const unsigned int SET_MODE_ARGS = 2U; const unsigned int ENABLE_ARGS = 2U; @@ -31,159 +30,181 @@ const unsigned int DISABLE_ARGS = 2U; const unsigned int PAGE_ARGS = 3U; const unsigned int CW_ARGS = 2U; -const unsigned int BUFFER_LENGTH = 100U; -CRemoteControl::CRemoteControl(CMMDVMHost *host, const std::string address, unsigned int port) : +CRemoteControl::CRemoteControl(CMMDVMHost *host, CMQTTConnection* mqtt) : m_host(host), -m_socket(address, port), -m_addr(), -m_addrLen(0U), +m_mqtt(mqtt), m_command(REMOTE_COMMAND::NONE), m_args() { - assert(port > 0U); - - if (CUDPSocket::lookup(address, port, m_addr, m_addrLen) != 0) - m_addrLen = 0U; + assert(host != nullptr); + assert(mqtt != nullptr); } CRemoteControl::~CRemoteControl() { } -bool CRemoteControl::open() -{ - if (m_addrLen == 0U) { - LogError("Unable to resolve the address of the remote control port"); - return false; - } - - return m_socket.open(m_addr); -} - -REMOTE_COMMAND CRemoteControl::getCommand() +REMOTE_COMMAND CRemoteControl::getCommand(const std::string& command) { m_command = REMOTE_COMMAND::NONE; m_args.clear(); - char command[BUFFER_LENGTH]; - char buffer[BUFFER_LENGTH * 2]; - std::string replyStr = "OK"; - sockaddr_storage address; - unsigned int addrlen; - int ret = m_socket.read((unsigned char*)buffer, BUFFER_LENGTH, address, addrlen); - if (ret > 0) { - buffer[ret] = '\0'; + std::string reply = "OK"; - // Make a copy of the original command for logging. - ::strcpy(command, buffer); + // Parse the original command into a vector of strings. + size_t start = command.find_first_not_of(' '); + while (start != std::string::npos) { + size_t end = command.find_first_of(' ', start); - // Parse the original command into a vector of strings. - char* b = buffer; - char* p = nullptr; - while ((p = ::strtok(b, " ")) != nullptr) { - b = nullptr; - m_args.push_back(std::string(p)); - } + m_args.push_back(command.substr(start, end - start)); - if (m_args.at(0U) == "mode" && m_args.size() >= SET_MODE_ARGS) { - // Mode command is in the form of "mode [|fixed]" - if (m_args.at(1U) == "idle") - m_command = REMOTE_COMMAND::MODE_IDLE; - else if (m_args.at(1U) == "lockout") - m_command = REMOTE_COMMAND::MODE_LOCKOUT; - else if (m_args.at(1U) == "d-star") - m_command = REMOTE_COMMAND::MODE_DSTAR; - else if (m_args.at(1U) == "dmr") - m_command = REMOTE_COMMAND::MODE_DMR; - else if (m_args.at(1U) == "ysf") - m_command = REMOTE_COMMAND::MODE_YSF; - else if (m_args.at(1U) == "p25") - m_command = REMOTE_COMMAND::MODE_P25; - else if (m_args.at(1U) == "nxdn") - m_command = REMOTE_COMMAND::MODE_NXDN; - else - replyStr = "KO"; - } else if (m_args.at(0U) == "enable" && m_args.size() >= ENABLE_ARGS) { - if (m_args.at(1U) == "dstar") - m_command = REMOTE_COMMAND::ENABLE_DSTAR; - else if (m_args.at(1U) == "dmr") - m_command = REMOTE_COMMAND::ENABLE_DMR; - else if (m_args.at(1U) == "ysf") - m_command = REMOTE_COMMAND::ENABLE_YSF; - else if (m_args.at(1U) == "p25") - m_command = REMOTE_COMMAND::ENABLE_P25; - else if (m_args.at(1U) == "nxdn") - m_command = REMOTE_COMMAND::ENABLE_NXDN; - else if (m_args.at(1U) == "fm") - m_command = REMOTE_COMMAND::ENABLE_FM; - else - replyStr = "KO"; - } else if (m_args.at(0U) == "disable" && m_args.size() >= DISABLE_ARGS) { - if (m_args.at(1U) == "dstar") - m_command = REMOTE_COMMAND::DISABLE_DSTAR; - else if (m_args.at(1U) == "dmr") - m_command = REMOTE_COMMAND::DISABLE_DMR; - else if (m_args.at(1U) == "ysf") - m_command = REMOTE_COMMAND::DISABLE_YSF; - else if (m_args.at(1U) == "p25") - m_command = REMOTE_COMMAND::DISABLE_P25; - else if (m_args.at(1U) == "nxdn") - m_command = REMOTE_COMMAND::DISABLE_NXDN; - else if (m_args.at(1U) == "fm") - m_command = REMOTE_COMMAND::DISABLE_FM; - else - replyStr = "KO"; - } else if (m_args.at(0U) == "page" && m_args.size() >= PAGE_ARGS) { - // Page command is in the form of "page " - m_command = REMOTE_COMMAND::PAGE; - } else if (m_args.at(0U) == "page_bcd" && m_args.size() >= PAGE_ARGS) { - // BCD page command is in the form of "page_bcd " - m_command = REMOTE_COMMAND::PAGE_BCD; - } else if (m_args.at(0U) == "page_a1" && m_args.size() == 2) { - // Alert1 page command is in the form of "page_a1 " - m_command = REMOTE_COMMAND::PAGE_A1; - } else if (m_args.at(0U) == "page_a2" && m_args.size() >= PAGE_ARGS) { - // Alert2 page command is in the form of "page_a2 " - m_command = REMOTE_COMMAND::PAGE_A2; - } else if (m_args.at(0U) == "cw" && m_args.size() >= CW_ARGS) { - // CW command is in the form of "cw " - m_command = REMOTE_COMMAND::CW; - } else if (m_args.at(0U) == "reload") { - // Reload command is in the form of "reload" - m_command = REMOTE_COMMAND::RELOAD; - } else if (m_args.at(0U) == "status") { - if (m_host != nullptr) { - m_host->buildNetworkStatusString(replyStr); - } else { - replyStr = "KO"; - } - - m_command = REMOTE_COMMAND::CONNECTION_STATUS; - } else if (m_args.at(0U) == "hosts") { - if (m_host != nullptr) { - m_host->buildNetworkHostsString(replyStr); - } else { - replyStr = "KO"; - } - - m_command = REMOTE_COMMAND::CONFIG_HOSTS; - } else { - replyStr = "KO"; - } - - ::snprintf(buffer, BUFFER_LENGTH * 2, "%s remote command of \"%s\" received", ((m_command == REMOTE_COMMAND::NONE) ? "Invalid" : "Valid"), command); - if (m_command == REMOTE_COMMAND::NONE) { - m_args.clear(); - LogWarning(buffer); - } else { -#if !defined(REMOTE_COMMAND_NO_LOG) - LogMessage(buffer); -#endif - } - - m_socket.write((unsigned char*)replyStr.c_str(), (unsigned int)replyStr.length(), address, addrlen); + start = command.find_first_not_of(' ', end); } + + if (m_args.at(0U) == "mode" && m_args.size() >= SET_MODE_ARGS) { + // Mode command is in the form of "mode [|fixed]" + if (m_args.at(1U) == "idle") + m_command = REMOTE_COMMAND::MODE_IDLE; + else if (m_args.at(1U) == "lockout") + m_command = REMOTE_COMMAND::MODE_LOCKOUT; +#if defined(USE_DSTAR) + else if (m_args.at(1U) == "d-star") + m_command = REMOTE_COMMAND::MODE_DSTAR; +#endif +#if defined(USE_DMR) + else if (m_args.at(1U) == "dmr") + m_command = REMOTE_COMMAND::MODE_DMR; +#endif +#if defined(USE_YSF) + else if (m_args.at(1U) == "ysf") + m_command = REMOTE_COMMAND::MODE_YSF; +#endif +#if defined(USE_P25) + else if (m_args.at(1U) == "p25") + m_command = REMOTE_COMMAND::MODE_P25; +#endif +#if defined(USE_NXDN) + else if (m_args.at(1U) == "nxdn") + m_command = REMOTE_COMMAND::MODE_NXDN; +#endif + else + reply = "KO"; + } else if (m_args.at(0U) == "enable" && m_args.size() >= ENABLE_ARGS) { +#if defined(USE_DSTAR) + if (m_args.at(1U) == "dstar") + m_command = REMOTE_COMMAND::ENABLE_DSTAR; + else +#endif +#if defined(USE_DMR) + if (m_args.at(1U) == "dmr") + m_command = REMOTE_COMMAND::ENABLE_DMR; + else +#endif +#if defined(USE_YSF) + if (m_args.at(1U) == "ysf") + m_command = REMOTE_COMMAND::ENABLE_YSF; + else +#endif +#if defined(USE_P25) + if (m_args.at(1U) == "p25") + m_command = REMOTE_COMMAND::ENABLE_P25; + else +#endif +#if defined(USE_NXDN) + if (m_args.at(1U) == "nxdn") + m_command = REMOTE_COMMAND::ENABLE_NXDN; + else +#endif +#if defined(USE_FM) + if (m_args.at(1U) == "fm") + m_command = REMOTE_COMMAND::ENABLE_FM; + else +#endif + reply = "KO"; + } else if (m_args.at(0U) == "disable" && m_args.size() >= DISABLE_ARGS) { +#if defined(USE_DSTAR) + if (m_args.at(1U) == "dstar") + m_command = REMOTE_COMMAND::DISABLE_DSTAR; + else +#endif +#if defined(USE_DMR) + if (m_args.at(1U) == "dmr") + m_command = REMOTE_COMMAND::DISABLE_DMR; + else +#endif +#if defined(USE_YSF) + if (m_args.at(1U) == "ysf") + m_command = REMOTE_COMMAND::DISABLE_YSF; + else +#endif +#if defined(USE_P25) + if (m_args.at(1U) == "p25") + m_command = REMOTE_COMMAND::DISABLE_P25; + else +#endif +#if defined(USE_NXDN) + if (m_args.at(1U) == "nxdn") + m_command = REMOTE_COMMAND::DISABLE_NXDN; + else +#endif +#if defined(USE_FM) + if (m_args.at(1U) == "fm") + m_command = REMOTE_COMMAND::DISABLE_FM; + else +#endif + reply = "KO"; +#if defined(USE_POCSAG) + } else if (m_args.at(0U) == "page" && m_args.size() >= PAGE_ARGS) { + // Page command is in the form of "page " + m_command = REMOTE_COMMAND::PAGE; + } else if (m_args.at(0U) == "page_bcd" && m_args.size() >= PAGE_ARGS) { + // BCD page command is in the form of "page_bcd " + m_command = REMOTE_COMMAND::PAGE_BCD; + } else if (m_args.at(0U) == "page_a1" && m_args.size() == 2) { + // Alert1 page command is in the form of "page_a1 " + m_command = REMOTE_COMMAND::PAGE_A1; + } else if (m_args.at(0U) == "page_a2" && m_args.size() >= PAGE_ARGS) { + // Alert2 page command is in the form of "page_a2 " + m_command = REMOTE_COMMAND::PAGE_A2; +#endif + } else if (m_args.at(0U) == "cw" && m_args.size() >= CW_ARGS) { + // CW command is in the form of "cw " + m_command = REMOTE_COMMAND::CW; + } else if (m_args.at(0U) == "reload") { + // Reload command is in the form of "reload" + m_command = REMOTE_COMMAND::RELOAD; + } else if (m_args.at(0U) == "status") { + if (m_host != nullptr) { + m_host->buildNetworkStatusString(reply); + } else { + reply = "KO"; + } + + m_command = REMOTE_COMMAND::CONNECTION_STATUS; + } else if (m_args.at(0U) == "hosts") { + if (m_host != nullptr) { + m_host->buildNetworkHostsString(reply); + } else { + reply = "KO"; + } + + m_command = REMOTE_COMMAND::CONFIG_HOSTS; + } else { + reply = "KO"; + } + + char buffer[200U]; + ::snprintf(buffer, 200, "%s remote command of \"%s\" received", ((m_command == REMOTE_COMMAND::NONE) ? "Invalid" : "Valid"), command.c_str()); + + if (m_command == REMOTE_COMMAND::NONE) { + m_args.clear(); + LogWarning(buffer); + } else { + LogMessage(buffer); + } + + m_mqtt->publish("response", reply.c_str()); return m_command; } @@ -193,17 +214,29 @@ unsigned int CRemoteControl::getArgCount() const switch (m_command) { case REMOTE_COMMAND::MODE_IDLE: case REMOTE_COMMAND::MODE_LOCKOUT: +#if defined(USE_DSTAR) case REMOTE_COMMAND::MODE_DSTAR: +#endif +#if defined(USE_DMR) case REMOTE_COMMAND::MODE_DMR: +#endif +#if defined(USE_YSF) case REMOTE_COMMAND::MODE_YSF: +#endif +#if defined(USE_P25) case REMOTE_COMMAND::MODE_P25: +#endif +#if defined(USE_NXDN) case REMOTE_COMMAND::MODE_NXDN: +#endif return (unsigned int)m_args.size() - SET_MODE_ARGS; +#if defined(USE_POCSAG) case REMOTE_COMMAND::PAGE: case REMOTE_COMMAND::PAGE_BCD: case REMOTE_COMMAND::PAGE_A1: case REMOTE_COMMAND::PAGE_A2: return (unsigned int)m_args.size() - 1U; +#endif case REMOTE_COMMAND::CW: return (unsigned int)m_args.size() - 1U; default: @@ -216,19 +249,31 @@ std::string CRemoteControl::getArgString(unsigned int n) const switch (m_command) { case REMOTE_COMMAND::MODE_IDLE: case REMOTE_COMMAND::MODE_LOCKOUT: +#if defined(USE_DSTAR) case REMOTE_COMMAND::MODE_DSTAR: +#endif +#if defined(USE_DMR) case REMOTE_COMMAND::MODE_DMR: +#endif +#if defined(USE_YSF) case REMOTE_COMMAND::MODE_YSF: +#endif +#if defined(USE_P25) case REMOTE_COMMAND::MODE_P25: +#endif +#if defined(USE_NXDN) case REMOTE_COMMAND::MODE_NXDN: +#endif n += SET_MODE_ARGS; break; +#if defined(USE_POCSAG) case REMOTE_COMMAND::PAGE: case REMOTE_COMMAND::PAGE_BCD: case REMOTE_COMMAND::PAGE_A1: case REMOTE_COMMAND::PAGE_A2: n += 1U; break; +#endif case REMOTE_COMMAND::CW: n += 1U; break; @@ -251,8 +296,3 @@ int CRemoteControl::getArgInt(unsigned int n) const { return ::atoi(getArgString(n).c_str()); } - -void CRemoteControl::close() -{ - m_socket.close(); -} diff --git a/RemoteControl.h b/RemoteControl.h index edf6e01..e02f1a3 100644 --- a/RemoteControl.h +++ b/RemoteControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019,2020,2021,2024,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2019,2020,2021,2023,2024,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,39 +19,79 @@ #ifndef RemoteControl_H #define RemoteControl_H -#include "UDPSocket.h" +#include "Defines.h" #include #include +#include "MQTTConnection.h" + class CMMDVMHost; enum class REMOTE_COMMAND { NONE, MODE_IDLE, MODE_LOCKOUT, +#if defined(USE_DSTAR) MODE_DSTAR, +#endif +#if defined(USE_DMR) MODE_DMR, +#endif +#if defined(USE_YSF) MODE_YSF, +#endif +#if defined(USE_P25) MODE_P25, +#endif +#if defined(USE_NXDN) MODE_NXDN, +#endif +#if defined(USE_FM) MODE_FM, +#endif +#if defined(USE_DSTAR) ENABLE_DSTAR, +#endif +#if defined(USE_DMR) ENABLE_DMR, +#endif +#if defined(USE_YSF) ENABLE_YSF, +#endif +#if defined(USE_P25) ENABLE_P25, +#endif +#if defined(USE_NXDN) ENABLE_NXDN, +#endif +#if defined(USE_FM) ENABLE_FM, +#endif +#if defined(USE_DSTAR) DISABLE_DSTAR, +#endif +#if defined(USE_DMR) DISABLE_DMR, +#endif +#if defined(USE_YSF) DISABLE_YSF, +#endif +#if defined(USE_P25) DISABLE_P25, +#endif +#if defined(USE_NXDN) DISABLE_NXDN, +#endif +#if defined(USE_FM) DISABLE_FM, +#endif +#if defined(USE_POCSAG) PAGE, PAGE_BCD, PAGE_A1, PAGE_A2, +#endif CW, RELOAD, CONNECTION_STATUS, @@ -60,12 +100,10 @@ enum class REMOTE_COMMAND { class CRemoteControl { public: - CRemoteControl(class CMMDVMHost *host, const std::string address, unsigned int port); + CRemoteControl(class CMMDVMHost *host, CMQTTConnection* mqtt); ~CRemoteControl(); - bool open(); - - REMOTE_COMMAND getCommand(); + REMOTE_COMMAND getCommand(const std::string& command); unsigned int getArgCount() const; @@ -73,13 +111,9 @@ public: unsigned int getArgUInt(unsigned int n) const; signed int getArgInt(unsigned int n) const; - void close(); - private: CMMDVMHost* m_host; - CUDPSocket m_socket; - sockaddr_storage m_addr; - unsigned int m_addrLen; + CMQTTConnection* m_mqtt; REMOTE_COMMAND m_command; std::vector m_args; }; diff --git a/SHA256.cpp b/SHA256.cpp deleted file mode 100644 index 5f81805..0000000 --- a/SHA256.cpp +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Copyright (C) 2005,2006,2008 Free Software Foundation, Inc. - * Copyright (C) 2011,2015,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "SHA256.h" - -#include -#include -#include - -#ifdef WORDS_BIGENDIAN -# define SWAP(n) (n) -#else -# define SWAP(n) \ - (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) -#endif - -#define BLOCKSIZE 4096 -#if BLOCKSIZE % 64 != 0 -# error "invalid BLOCKSIZE" -#endif - -/* This array contains the bytes used to pad the buffer to the next - 64-byte boundary. */ -static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; - - -/* - Takes a pointer to a 256 bit block of data (eight 32 bit ints) and - intializes it to the start constants of the SHA256 algorithm. This - must be called before using hash in the call to sha256_hash -*/ -CSHA256::CSHA256() : -m_state(nullptr), -m_total(nullptr), -m_buflen(0U), -m_buffer(nullptr) -{ - m_state = new uint32_t[8U]; - m_total = new uint32_t[2U]; - m_buffer = new uint32_t[32U]; - - init(); -} - -CSHA256::~CSHA256() -{ - delete[] m_state; - delete[] m_total; - delete[] m_buffer; -} - -void CSHA256::init() -{ - m_state[0] = 0x6a09e667UL; - m_state[1] = 0xbb67ae85UL; - m_state[2] = 0x3c6ef372UL; - m_state[3] = 0xa54ff53aUL; - m_state[4] = 0x510e527fUL; - m_state[5] = 0x9b05688cUL; - m_state[6] = 0x1f83d9abUL; - m_state[7] = 0x5be0cd19UL; - - m_total[0] = m_total[1] = 0; - m_buflen = 0; -} - -/* Copy the value from v into the memory location pointed to by *cp, - If your architecture allows unaligned access this is equivalent to - * (uint32_t *) cp = v */ -static inline void set_uint32(unsigned char* cp, uint32_t v) -{ - assert(cp != nullptr); - - ::memcpy(cp, &v, sizeof v); -} - -/* Put result from CTX in first 32 bytes following RESBUF. The result - must be in little endian byte order. */ -unsigned char* CSHA256::read(unsigned char* resbuf) -{ - assert(resbuf != nullptr); - - for (unsigned int i = 0U; i < 8U; i++) - set_uint32(resbuf + i * sizeof(m_state[0]), SWAP(m_state[i])); - - return resbuf; -} - -/* Process the remaining bytes in the internal buffer and the usual - prolog according to the standard and write the result to RESBUF. */ -void CSHA256::conclude() -{ - /* Take yet unprocessed bytes into account. */ - unsigned int bytes = m_buflen; - unsigned int size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4; - - /* Now count remaining bytes. */ - m_total[0] += bytes; - if (m_total[0] < bytes) - ++m_total[1]; - - /* Put the 64-bit file length in *bits* at the end of the buffer. - Use set_uint32 rather than a simple assignment, to avoid risk of - unaligned access. */ - set_uint32((unsigned char*)&m_buffer[size - 2], SWAP((m_total[1] << 3) | (m_total[0] >> 29))); - set_uint32((unsigned char*)&m_buffer[size - 1], SWAP(m_total[0] << 3)); - - ::memcpy(&((char*)m_buffer)[bytes], fillbuf, (size - 2) * 4 - bytes); - - /* Process last bytes. */ - processBlock((unsigned char*)m_buffer, size * 4); -} - -unsigned char* CSHA256::finish(unsigned char* resbuf) -{ - assert(resbuf != nullptr); - - conclude(); - - return read(resbuf); -} - -/* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -unsigned char* CSHA256::buffer(const unsigned char* buffer, unsigned int len, unsigned char* resblock) -{ - assert(buffer != nullptr); - assert(resblock != nullptr); - - /* Initialize the computation context. */ - init(); - - /* Process whole buffer but last len % 64 bytes. */ - processBytes(buffer, len); - - /* Put result in desired memory area. */ - return finish(resblock); -} - -void CSHA256::processBytes(const unsigned char* buffer, unsigned int len) -{ - assert(buffer != nullptr); - - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if (m_buflen != 0U) { - unsigned int left_over = m_buflen; - unsigned int add = 128U - left_over > len ? len : 128U - left_over; - - ::memcpy(&((char*)m_buffer)[left_over], buffer, add); - m_buflen += add; - - if (m_buflen > 64U) { - processBlock((unsigned char*)m_buffer, m_buflen & ~63U); - - m_buflen &= 63U; - - /* The regions in the following copy operation cannot overlap. */ - ::memcpy(m_buffer, &((char*)m_buffer)[(left_over + add) & ~63U], m_buflen); - } - - buffer += add; - len -= add; - } - - /* Process available complete blocks. */ - if (len >= 64U) { -//#if !_STRING_ARCH_unaligned -//# define alignof(type) offsetof (struct { char c; type x; }, x) -//# define UNALIGNED_P(p) (((unsigned int) p) % alignof (uint32_t) != 0) -// if (UNALIGNED_P (buffer)) { -// while (len > 64U) { -// ::memcpy(m_buffer, buffer, 64U); -// processBlock((unsigned char*)m_buffer, 64U); -// buffer += 64U; -// len -= 64U; -// } -// } else -//#endif - { - processBlock(buffer, len & ~63U); - buffer += (len & ~63U); - len &= 63U; - } - } - - /* Move remaining bytes in internal buffer. */ - if (len > 0U) { - unsigned int left_over = m_buflen; - - ::memcpy(&((char*)m_buffer)[left_over], buffer, len); - left_over += len; - - if (left_over >= 64U) { - processBlock((unsigned char*)m_buffer, 64U); - left_over -= 64U; - ::memcpy(m_buffer, &m_buffer[16], left_over); - } - - m_buflen = left_over; - } -} - -/* --- Code below is the primary difference between sha1.c and sha256.c --- */ - -/* SHA256 round constants */ -#define K(I) roundConstants[I] -static const uint32_t roundConstants[64] = { - 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, - 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, - 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, - 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, - 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, - 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, - 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, - 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, - 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, - 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, - 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, - 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, - 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, - 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, - 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, - 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL, -}; - -/* Round functions. */ -#define F2(A,B,C) ( ( A & B ) | ( C & ( A | B ) ) ) -#define F1(E,F,G) ( G ^ ( E & ( F ^ G ) ) ) - -/* Process LEN bytes of BUFFER, accumulating context into CTX. - It is assumed that LEN % 64 == 0. - Most of this code comes from GnuPG's cipher/sha1.c. */ - -void CSHA256::processBlock(const unsigned char* buffer, unsigned int len) -{ - assert(buffer != nullptr); - - const uint32_t* words = (uint32_t*)buffer; - unsigned int nwords = len / sizeof(uint32_t); - const uint32_t* endp = words + nwords; - uint32_t x[16]; - uint32_t a = m_state[0]; - uint32_t b = m_state[1]; - uint32_t c = m_state[2]; - uint32_t d = m_state[3]; - uint32_t e = m_state[4]; - uint32_t f = m_state[5]; - uint32_t g = m_state[6]; - uint32_t h = m_state[7]; - - /* First increment the byte count. FIPS PUB 180-2 specifies the possible - length of the file up to 2^64 bits. Here we only compute the - number of bytes. Do a double word increment. */ - m_total[0] += len; - if (m_total[0] < len) - ++m_total[1]; - - #define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) - #define S0(x) (rol(x,25)^rol(x,14)^(x>>3)) - #define S1(x) (rol(x,15)^rol(x,13)^(x>>10)) - #define SS0(x) (rol(x,30)^rol(x,19)^rol(x,10)) - #define SS1(x) (rol(x,26)^rol(x,21)^rol(x,7)) - - #define M(I) (tm = S1(x[(I-2)&0x0f]) + x[(I-7)&0x0f] + S0(x[(I-15)&0x0f]) + x[I&0x0f], x[I&0x0f] = tm) - - #define R(A,B,C,D,E,F,G,H,K,M) do { t0 = SS0(A) + F2(A,B,C); \ - t1 = H + SS1(E) + F1(E,F,G) + K + M; \ - D += t1; H = t0 + t1; \ - } while(0) - - while (words < endp) { - uint32_t tm; - uint32_t t0, t1; - /* FIXME: see sha1.c for a better implementation. */ - for (unsigned int t = 0U; t < 16U; t++) { - x[t] = SWAP(*words); - words++; - } - - R( a, b, c, d, e, f, g, h, K( 0), x[ 0] ); - R( h, a, b, c, d, e, f, g, K( 1), x[ 1] ); - R( g, h, a, b, c, d, e, f, K( 2), x[ 2] ); - R( f, g, h, a, b, c, d, e, K( 3), x[ 3] ); - R( e, f, g, h, a, b, c, d, K( 4), x[ 4] ); - R( d, e, f, g, h, a, b, c, K( 5), x[ 5] ); - R( c, d, e, f, g, h, a, b, K( 6), x[ 6] ); - R( b, c, d, e, f, g, h, a, K( 7), x[ 7] ); - R( a, b, c, d, e, f, g, h, K( 8), x[ 8] ); - R( h, a, b, c, d, e, f, g, K( 9), x[ 9] ); - R( g, h, a, b, c, d, e, f, K(10), x[10] ); - R( f, g, h, a, b, c, d, e, K(11), x[11] ); - R( e, f, g, h, a, b, c, d, K(12), x[12] ); - R( d, e, f, g, h, a, b, c, K(13), x[13] ); - R( c, d, e, f, g, h, a, b, K(14), x[14] ); - R( b, c, d, e, f, g, h, a, K(15), x[15] ); - R( a, b, c, d, e, f, g, h, K(16), M(16) ); - R( h, a, b, c, d, e, f, g, K(17), M(17) ); - R( g, h, a, b, c, d, e, f, K(18), M(18) ); - R( f, g, h, a, b, c, d, e, K(19), M(19) ); - R( e, f, g, h, a, b, c, d, K(20), M(20) ); - R( d, e, f, g, h, a, b, c, K(21), M(21) ); - R( c, d, e, f, g, h, a, b, K(22), M(22) ); - R( b, c, d, e, f, g, h, a, K(23), M(23) ); - R( a, b, c, d, e, f, g, h, K(24), M(24) ); - R( h, a, b, c, d, e, f, g, K(25), M(25) ); - R( g, h, a, b, c, d, e, f, K(26), M(26) ); - R( f, g, h, a, b, c, d, e, K(27), M(27) ); - R( e, f, g, h, a, b, c, d, K(28), M(28) ); - R( d, e, f, g, h, a, b, c, K(29), M(29) ); - R( c, d, e, f, g, h, a, b, K(30), M(30) ); - R( b, c, d, e, f, g, h, a, K(31), M(31) ); - R( a, b, c, d, e, f, g, h, K(32), M(32) ); - R( h, a, b, c, d, e, f, g, K(33), M(33) ); - R( g, h, a, b, c, d, e, f, K(34), M(34) ); - R( f, g, h, a, b, c, d, e, K(35), M(35) ); - R( e, f, g, h, a, b, c, d, K(36), M(36) ); - R( d, e, f, g, h, a, b, c, K(37), M(37) ); - R( c, d, e, f, g, h, a, b, K(38), M(38) ); - R( b, c, d, e, f, g, h, a, K(39), M(39) ); - R( a, b, c, d, e, f, g, h, K(40), M(40) ); - R( h, a, b, c, d, e, f, g, K(41), M(41) ); - R( g, h, a, b, c, d, e, f, K(42), M(42) ); - R( f, g, h, a, b, c, d, e, K(43), M(43) ); - R( e, f, g, h, a, b, c, d, K(44), M(44) ); - R( d, e, f, g, h, a, b, c, K(45), M(45) ); - R( c, d, e, f, g, h, a, b, K(46), M(46) ); - R( b, c, d, e, f, g, h, a, K(47), M(47) ); - R( a, b, c, d, e, f, g, h, K(48), M(48) ); - R( h, a, b, c, d, e, f, g, K(49), M(49) ); - R( g, h, a, b, c, d, e, f, K(50), M(50) ); - R( f, g, h, a, b, c, d, e, K(51), M(51) ); - R( e, f, g, h, a, b, c, d, K(52), M(52) ); - R( d, e, f, g, h, a, b, c, K(53), M(53) ); - R( c, d, e, f, g, h, a, b, K(54), M(54) ); - R( b, c, d, e, f, g, h, a, K(55), M(55) ); - R( a, b, c, d, e, f, g, h, K(56), M(56) ); - R( h, a, b, c, d, e, f, g, K(57), M(57) ); - R( g, h, a, b, c, d, e, f, K(58), M(58) ); - R( f, g, h, a, b, c, d, e, K(59), M(59) ); - R( e, f, g, h, a, b, c, d, K(60), M(60) ); - R( d, e, f, g, h, a, b, c, K(61), M(61) ); - R( c, d, e, f, g, h, a, b, K(62), M(62) ); - R( b, c, d, e, f, g, h, a, K(63), M(63) ); - - a = m_state[0] += a; - b = m_state[1] += b; - c = m_state[2] += c; - d = m_state[3] += d; - e = m_state[4] += e; - f = m_state[5] += f; - g = m_state[6] += g; - h = m_state[7] += h; - } -} diff --git a/SHA256.h b/SHA256.h deleted file mode 100644 index 9174834..0000000 --- a/SHA256.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2005,2006,2008,2009 Free Software Foundation, Inc. - * Copyright (C) 2011,2015,2016,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef SHA256_H -#define SHA256_H - -#include - -const unsigned int SHA256_DIGEST_SIZE = 256U / 8U; - -class CSHA256 { -public: - CSHA256(); - ~CSHA256(); - - /* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is necessary that LEN is a multiple of 64!!! */ - void processBlock(const unsigned char* buffer, unsigned int len); - - /* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is NOT required that LEN is a multiple of 64. */ - void processBytes(const unsigned char* buffer, unsigned int len); - - /* Process the remaining bytes in the buffer and put result from CTX - in first 32 bytes following RESBUF. The result is always in little - endian byte order, so that a byte-wise output yields to the wanted - ASCII representation of the message digest. */ - unsigned char* finish(unsigned char* resbuf); - - /* Put result from CTX in first 32 bytes following RESBUF. The result is - always in little endian byte order, so that a byte-wise output yields - to the wanted ASCII representation of the message digest. */ - unsigned char* read(unsigned char* resbuf); - - /* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ - unsigned char* buffer(const unsigned char* buffer, unsigned int len, unsigned char* resblock); - -private: - uint32_t* m_state; - uint32_t* m_total; - unsigned int m_buflen; - uint32_t* m_buffer; - - void init(); - void conclude(); -}; - -#endif diff --git a/SMeter.cpp b/SMeter.cpp deleted file mode 100644 index 75b36f8..0000000 --- a/SMeter.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2015-2021 by Jonathan Naylor G4KLX - * Copyright (C) 2021 by Geoffrey Merck F4FXL / KC3FRA - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "SMeter.h" - -const unsigned int RSSI_S1 = 141U; -const unsigned int RSSI_S9 = 93U; - -void CSMeter::getSignal(unsigned int rssi, unsigned int& signal, unsigned int& plus) -{ - if (rssi > RSSI_S1) { - signal = 0U; - plus = rssi - RSSI_S1; - } else if (rssi > RSSI_S9 && rssi <= RSSI_S1) { - signal = ((RSSI_S1 - rssi) / 6U) + 1U; - plus = (RSSI_S1 - rssi) % 6U; - } else { - signal = 9U; - plus = RSSI_S9 - rssi; - } -} diff --git a/SMeter.h b/SMeter.h deleted file mode 100644 index 76c4d60..0000000 --- a/SMeter.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2015-2021 by Jonathan Naylor G4KLX - * Copyright (C) 2021 by Geoffrey Merck F4FXL / KC3FRA - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#if !defined(SMETER_H) -#define SMETER_H - -class CSMeter -{ -public: - static void getSignal(unsigned int rssi, unsigned int& signal, unsigned int& plus); -}; - -#endif \ No newline at end of file diff --git a/Sync.cpp b/Sync.cpp index 70abf36..7984423 100644 --- a/Sync.cpp +++ b/Sync.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2018,2020,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2018,2020,2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,14 +28,16 @@ #include #include - +#if defined(USE_DSTAR) void CSync::addDStarSync(unsigned char* data) { assert(data != nullptr); ::memcpy(data + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES); } +#endif +#if defined(USE_DMR) void CSync::addDMRDataSync(unsigned char* data, bool duplex) { assert(data != nullptr); @@ -61,21 +63,27 @@ void CSync::addDMRAudioSync(unsigned char* data, bool duplex) data[i + 13U] = (data[i + 13U] & ~SYNC_MASK[i]) | MS_SOURCED_AUDIO_SYNC[i]; } } +#endif +#if defined(USE_YSF) void CSync::addYSFSync(unsigned char* data) { assert(data != nullptr); ::memcpy(data, YSF_SYNC_BYTES, YSF_SYNC_LENGTH_BYTES); } +#endif +#if defined(USE_P25) void CSync::addP25Sync(unsigned char* data) { assert(data != nullptr); ::memcpy(data, P25_SYNC_BYTES, P25_SYNC_LENGTH_BYTES); } +#endif +#if defined(USE_NXDN) void CSync::addNXDNSync(unsigned char* data) { assert(data != nullptr); @@ -83,3 +91,4 @@ void CSync::addNXDNSync(unsigned char* data) for (unsigned int i = 0U; i < NXDN_FSW_BYTES_LENGTH; i++) data[i] = (data[i] & ~NXDN_FSW_BYTES_MASK[i]) | NXDN_FSW_BYTES[i]; } +#endif diff --git a/Sync.h b/Sync.h index 131f88a..d32d463 100644 --- a/Sync.h +++ b/Sync.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016,2018,2020,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016,2018,2020,2021,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,19 +19,31 @@ #if !defined(SYNC_H) #define SYNC_H +#include "Defines.h" + class CSync { public: +#if defined(USE_DSTAR) static void addDStarSync(unsigned char* data); +#endif +#if defined(USE_DMR) static void addDMRDataSync(unsigned char* data, bool duplex); static void addDMRAudioSync(unsigned char* data, bool duplex); +#endif +#if defined(USE_YSF) static void addYSFSync(unsigned char* data); +#endif +#if defined(USE_P25) static void addP25Sync(unsigned char* data); +#endif +#if defined(USE_NXDN) static void addNXDNSync(unsigned char* data); +#endif private: }; diff --git a/TFTSurenoo.cpp b/TFTSurenoo.cpp deleted file mode 100644 index 15353da..0000000 --- a/TFTSurenoo.cpp +++ /dev/null @@ -1,525 +0,0 @@ -/* - * Copyright (C) 2019 by SASANO Takayoshi JG1UAA - * Copyright (C) 2015,2016,2018,2019,2020,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#include "TFTSurenoo.h" -#include "Thread.h" -#include "Log.h" - -#include -#include -#include - -/* - * UART-TFT LCD Driver for Surenoo JC22-V05 (128x160) - * other Surenoo UART-LCD will be work, but display area is still 160x128 - * (tested with JC028-V03 240x320 module) - */ -struct layoutdef { - int x_width; - int y_width; - int rotation; - int mode_font_size; - int status_font_size; - int status_margin; -}; - -#define ROTATION_PORTRAIT 0 -#define ROTATION_LANDSCAPE 1 - -#define FONT_SMALL 16 // 8x16 -#define FONT_MEDIUM 24 // 12x24 -#define FONT_LARGE 32 // 16x32 - -static const struct layoutdef Layout[] = { - {160, 128, ROTATION_LANDSCAPE, FONT_MEDIUM, FONT_SMALL, 32}, - {128, 160, ROTATION_PORTRAIT, FONT_MEDIUM, FONT_SMALL, 32}, - {320, 240, ROTATION_LANDSCAPE, FONT_LARGE, FONT_MEDIUM, 48}, - {240, 320, ROTATION_PORTRAIT, FONT_LARGE, FONT_MEDIUM, 48}, -}; - -#define X_WIDTH Layout[m_screenLayout].x_width -#define Y_WIDTH Layout[m_screenLayout].y_width -#define ROTATION Layout[m_screenLayout].rotation - -#define COLOUR_BLACK 0 -#define COLOUR_RED 1 -#define COLOUR_GREEN 2 -#define COLOUR_BLUE 3 -#define COLOUR_YELLOW 4 -#define COLOUR_CYAN 5 -#define COLOUR_MAGENTA 6 -#define COLOUR_GREY 7 -#define COLOUR_DARK_GREY 8 -#define COLOUR_DARK_RED 9 -#define COLOUR_DARK_GREEN 10 -#define COLOUR_DARK_BLUE 11 -#define COLOUR_DARK_YELLOW 12 -#define COLOUR_DARK_CYAN 13 -#define COLOUR_DARK_MAGENTA 14 -#define COLOUR_WHITE 15 - -#define INFO_COLOUR COLOUR_CYAN -#define EXT_COLOUR COLOUR_DARK_GREEN -#define BG_COLOUR COLOUR_BLACK -#define ERROR_COLOUR COLOUR_DARK_RED -#define MODE_COLOUR COLOUR_YELLOW - -// MODE_FONT_SIZE should be equal or larger than STATUS_FONT_SIZE -#define MODE_FONT_SIZE Layout[m_screenLayout].mode_font_size -#define STATUS_FONT_SIZE Layout[m_screenLayout].status_font_size - -#define STATUS_MARGIN Layout[m_screenLayout].status_margin - -#define MODE_CHARS (X_WIDTH / (MODE_FONT_SIZE / 2)) -#define STATUS_CHARS (X_WIDTH / (STATUS_FONT_SIZE / 2)) -#define STATUS_LINES ((Y_WIDTH - STATUS_MARGIN) / STATUS_FONT_SIZE) -#define statusLineOffset(x) ((STATUS_CHARS + 1) * ((x) + 1)) -#define statusLineNo(x) (x) -#define INFO_LINES statusLineNo(2) - -// This module sometimes ignores display command (too busy?), -// so supress display refresh -#define REFRESH_PERIOD 600 // msec - -#define STR_CRLF "\x0D\x0A" -#define STR_DMR "DMR" -#define STR_DSTAR "D-STAR" -#define STR_MMDVM "MMDVM" -#define STR_NXDN "NXDN" -#define STR_P25 "P25" -#define STR_YSF "SystemFusion" - -CTFTSurenoo::CTFTSurenoo(const std::string& callsign, unsigned int dmrid, ISerialPort* serial, unsigned int brightness, bool duplex, unsigned int screenLayout) : -CDisplay(), -m_callsign(callsign), -m_dmrid(dmrid), -m_serial(serial), -m_brightness(brightness), -m_mode(MODE_IDLE), -m_duplex(duplex), -//m_duplex(true), // uncomment to force duplex display for testing! -m_refresh(false), -m_refreshTimer(1000U, 0U, REFRESH_PERIOD), -m_lineBuf(nullptr), -m_temp(), -m_screenLayout(screenLayout) -{ - assert(serial != nullptr); - assert(brightness >= 0U && brightness <= 255U); -} - -CTFTSurenoo::~CTFTSurenoo() -{ -} - -bool CTFTSurenoo::open() -{ - bool ret = m_serial->open(); - if (!ret) { - LogError("Cannot open the port for the TFT Serial"); - delete m_serial; - return false; - } - - m_lineBuf = new char[statusLineOffset(STATUS_LINES)]; - - lcdReset(); - clearScreen(BG_COLOUR); - setIdle(); - - m_refreshTimer.start(); - - return true; -} - -void CTFTSurenoo::setIdleInt() -{ - setModeLine(STR_MMDVM); - - ::snprintf(m_temp, sizeof(m_temp), "%s / %u", m_callsign.c_str(), m_dmrid); - setStatusLine(statusLineNo(0), m_temp); - setStatusLine(statusLineNo(1), "IDLE"); - - m_mode = MODE_IDLE; -} - -void CTFTSurenoo::setErrorInt(const char* text) -{ - assert(text != nullptr); - - setModeLine(STR_MMDVM); - setStatusLine(statusLineNo(0), text); - setStatusLine(statusLineNo(1), "ERROR"); - - m_mode = MODE_ERROR; -} - -void CTFTSurenoo::setLockoutInt() -{ - setModeLine(STR_MMDVM); - setStatusLine(statusLineNo(1), "LOCKOUT"); - - m_mode = MODE_LOCKOUT; -} - -void CTFTSurenoo::setQuitInt() -{ - // m_refreshTimer has stopped, need delay here - CThread::sleep(REFRESH_PERIOD); - - setModeLine(STR_MMDVM); - setStatusLine(statusLineNo(1), "STOPPED"); - - refreshDisplay(); - - m_mode = MODE_QUIT; -} - -void CTFTSurenoo::setFMInt() -{ - setModeLine(STR_MMDVM); - setStatusLine(statusLineNo(1), "FM"); - - m_mode = MODE_FM; -} - -void CTFTSurenoo::writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector) -{ - assert(my1 != nullptr); - assert(my2 != nullptr); - assert(your != nullptr); - assert(type != nullptr); - assert(reflector != nullptr); - - setModeLine(STR_MMDVM); - - ::snprintf(m_temp, sizeof(m_temp), "%s %s/%s", type, my1, my2); - setStatusLine(statusLineNo(0), m_temp); - - ::snprintf(m_temp, sizeof(m_temp), "%s", your); - setStatusLine(statusLineNo(1), m_temp); - - if (::strcmp(reflector, " ") != 0) - ::snprintf(m_temp, sizeof(m_temp), "via %s", reflector); - else - ::strcpy(m_temp, ""); - setStatusLine(statusLineNo(2), m_temp); - - m_mode = MODE_DSTAR; -} - -void CTFTSurenoo::clearDStarInt() -{ - setStatusLine(statusLineNo(0), "Listening"); - for (int i = 1; i < STATUS_LINES; i++) - setStatusLine(statusLineNo(i), ""); -} - -void CTFTSurenoo::writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type) -{ - assert(type != nullptr); - - if (m_mode != MODE_DMR) { - setModeLine(STR_DMR); - if (m_duplex) { - setStatusLine(statusLineNo(0), "Listening"); - setStatusLine(statusLineNo(1), "TS1"); - setStatusLine(statusLineNo(2), "Listening"); - setStatusLine(statusLineNo(3), "TS2"); - } - } - - int pos = m_duplex ? (slotNo - 1) : 0; - ::snprintf(m_temp, sizeof(m_temp), "%s %s", type, src.c_str()); - setStatusLine(statusLineNo(pos * 2), m_temp); - - ::snprintf(m_temp, sizeof(m_temp), "TS%d %s%s", slotNo, group ? "TG" : "", dst.c_str()); - setStatusLine(statusLineNo(pos * 2 + 1), m_temp); - - m_mode = MODE_DMR; -} - -int CTFTSurenoo::writeDMRIntEx(unsigned int slotNo, const CUserDBentry& src, bool group, const std::string& dst, const char* type) -{ - assert(type != nullptr); - - // duplex mode is not supported - if (m_duplex) - return -1; - - setModeLine(STR_DMR); - setStatusLine(statusLineNo(2), (src.get(keyFIRST_NAME) + " " + src.get(keyLAST_NAME)).c_str()); - setStatusLine(statusLineNo(3), src.get(keyCITY).c_str()); - setStatusLine(statusLineNo(4), src.get(keySTATE).c_str()); - setStatusLine(statusLineNo(5), src.get(keyCOUNTRY).c_str()); - - m_mode = MODE_DMR; - - return 1; -} - -void CTFTSurenoo::clearDMRInt(unsigned int slotNo) -{ - int pos = m_duplex ? (slotNo - 1) : 0; - setStatusLine(statusLineNo(pos * 2), "Listening"); - - if (m_duplex) { - ::snprintf(m_temp, sizeof(m_temp), "TS%d", slotNo); - setStatusLine(statusLineNo(pos * 2 + 1), m_temp); - } else { - for (int i = 1; i < STATUS_LINES; i++) - setStatusLine(statusLineNo(i), ""); - } -} - -void CTFTSurenoo::writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin) -{ - assert(source != nullptr); - assert(dest != nullptr); - assert(type != nullptr); - assert(origin != nullptr); - - setModeLine(STR_YSF); - - ::snprintf(m_temp, sizeof(m_temp), "%s %s", type, source); - setStatusLine(statusLineNo(0), m_temp); - - ::snprintf(m_temp, sizeof(m_temp), "DG-ID %u", dgid); - setStatusLine(statusLineNo(1), m_temp); - - if (::strcmp(origin, " ") != 0) - ::snprintf(m_temp, sizeof(m_temp), "at %s", origin); - else - ::strcpy(m_temp, ""); - setStatusLine(statusLineNo(2), m_temp); - - m_mode = MODE_YSF; -} - -void CTFTSurenoo::clearFusionInt() -{ - clearDStarInt(); -} - -void CTFTSurenoo::writeP25Int(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(source != nullptr); - assert(type != nullptr); - - setModeLine(STR_P25); - - ::snprintf(m_temp, sizeof(m_temp), "%s %s", type, source); - setStatusLine(statusLineNo(0), m_temp); - - ::snprintf(m_temp, sizeof(m_temp), "%s%u", group ? "TG" : "", dest); - setStatusLine(statusLineNo(1), m_temp); - - m_mode = MODE_P25; -} - -void CTFTSurenoo::clearP25Int() -{ - clearDStarInt(); -} - -void CTFTSurenoo::writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type) -{ - assert(source != nullptr); - assert(type != nullptr); - - if (m_mode != MODE_NXDN) - setModeLine(STR_NXDN); - - ::snprintf(m_temp, sizeof(m_temp), "%s %s", type, source); - setStatusLine(statusLineNo(0), m_temp); - - ::snprintf(m_temp, sizeof(m_temp), "%s%u", group ? "TG" : "", dest); - setStatusLine(statusLineNo(1), m_temp); - - m_mode = MODE_NXDN; -} - -int CTFTSurenoo::writeNXDNIntEx(const CUserDBentry& source, bool group, unsigned int dest, const char* type) -{ - assert(type != nullptr); - - setModeLine(STR_NXDN); - setStatusLine(statusLineNo(2), (source.get(keyFIRST_NAME) + " " + source.get(keyLAST_NAME)).c_str()); - setStatusLine(statusLineNo(3), source.get(keyCITY).c_str()); - setStatusLine(statusLineNo(4), source.get(keySTATE).c_str()); - setStatusLine(statusLineNo(5), source.get(keyCOUNTRY).c_str()); - - m_mode = MODE_NXDN; - - return 1; -} - -void CTFTSurenoo::clearNXDNInt() -{ - clearDStarInt(); -} - -void CTFTSurenoo::writePOCSAGInt(uint32_t ric, const std::string& message) -{ - setStatusLine(statusLineNo(1), "POCSAG TX"); - - m_mode = MODE_POCSAG; -} - -void CTFTSurenoo::clearPOCSAGInt() -{ - setStatusLine(statusLineNo(1), "IDLE"); -} - -void CTFTSurenoo::writeCWInt() -{ - setStatusLine(statusLineNo(1), "CW TX"); - - m_mode = MODE_CW; -} - -void CTFTSurenoo::clearCWInt() -{ - setStatusLine(statusLineNo(1), "IDLE"); -} - -void CTFTSurenoo::close() -{ - delete[] m_lineBuf; - - m_serial->close(); - delete m_serial; -} - -void CTFTSurenoo::clockInt(unsigned int ms) -{ - m_refreshTimer.clock(ms); // renew timer status - - if (m_refreshTimer.isRunning() && m_refreshTimer.hasExpired()) { - refreshDisplay(); - m_refreshTimer.start(); // reset timer, wait for next period - } -} - -void CTFTSurenoo::setLineBuffer(char *buf, const char *text, int maxchar) -{ - int i; - - for (i = 0; i < maxchar && text[i] != '\0'; i++) - buf[i] = text[i]; - buf[i] = '\0'; - - m_refresh = true; -} - -void CTFTSurenoo::setModeLine(const char *text) -{ - setLineBuffer(m_lineBuf, text, MODE_CHARS); - - // clear all status line - for (int i = 0; i < STATUS_LINES; i++) setStatusLine(i, ""); -} - -void CTFTSurenoo::setStatusLine(unsigned int line, const char *text) -{ - setLineBuffer(m_lineBuf + statusLineOffset(line), text, STATUS_CHARS); -} - -void CTFTSurenoo::refreshDisplay(void) -{ - if (!m_refresh) return; - - // send CR+LF to avoid first command is not processed - ::snprintf(m_temp, sizeof(m_temp), STR_CRLF); - m_serial->write((unsigned char*)m_temp, (unsigned int)::strlen(m_temp)); - - // config display - setRotation(ROTATION); - setBrightness(m_brightness); - setBackground(BG_COLOUR); - m_serial->write((unsigned char*)m_temp, (unsigned int)::strlen(m_temp)); - CThread::sleep(5); - - // clear display - ::snprintf(m_temp, sizeof(m_temp), "BOXF(%d,%d,%d,%d,%d);", - 0, 0, X_WIDTH - 1, Y_WIDTH - 1, BG_COLOUR); - m_serial->write((unsigned char*)m_temp, (unsigned int)::strlen(m_temp)); - - // mode line - ::snprintf(m_temp, sizeof(m_temp), "DCV%d(%d,%d,'%s',%d);", - MODE_FONT_SIZE, 0, 0, m_lineBuf, MODE_COLOUR); - m_serial->write((unsigned char*)m_temp, (unsigned int)::strlen(m_temp)); - - // status line - for (int i = 0; i < STATUS_LINES; i++) { - char *p = m_lineBuf + statusLineOffset(i); - if (!::strlen(p)) continue; - - ::snprintf(m_temp, sizeof(m_temp), "DCV%d(%d,%d,'%s',%d);", - STATUS_FONT_SIZE, 0, - STATUS_MARGIN + STATUS_FONT_SIZE * i, p, - (!m_duplex && i >= INFO_LINES) ? EXT_COLOUR : INFO_COLOUR); - m_serial->write((unsigned char*)m_temp, (unsigned int)::strlen(m_temp)); - } - - // sending CR+LF finishes commands - ::snprintf(m_temp, sizeof(m_temp), STR_CRLF); - m_serial->write((unsigned char*)m_temp, (unsigned int)::strlen(m_temp)); - - m_refresh = false; -} - -void CTFTSurenoo::lcdReset(void) -{ - ::snprintf(m_temp, sizeof(m_temp), "RESET;" STR_CRLF); - m_serial->write((unsigned char*)m_temp, (unsigned int)::strlen(m_temp)); - CThread::sleep(250); // document says 230ms -} - -void CTFTSurenoo::clearScreen(unsigned char colour) -{ - assert(colour >= 0U && colour <= 63U); - - ::snprintf(m_temp, sizeof(m_temp), "CLR(%d);" STR_CRLF, colour); - m_serial->write((unsigned char*)m_temp, (unsigned int)::strlen(m_temp)); - CThread::sleep(100); // at least 60ms (@240x320 panel) -} - -void CTFTSurenoo::setBackground(unsigned char colour) -{ - assert(colour >= 0U && colour <= 63U); - - ::snprintf(m_temp, sizeof(m_temp), "SBC(%d);", colour); - m_serial->write((unsigned char*)m_temp, (unsigned int)::strlen(m_temp)); -} - -void CTFTSurenoo::setRotation(unsigned char rotation) -{ - assert(rotation >= 0U && rotation <= 1U); - - ::snprintf(m_temp, sizeof(m_temp), "DIR(%d);", rotation); - m_serial->write((unsigned char*)m_temp, (unsigned int)::strlen(m_temp)); -} - -void CTFTSurenoo::setBrightness(unsigned char brightness) -{ - assert(brightness >= 0U && brightness <= 255U); - - ::snprintf(m_temp, sizeof(m_temp), "BL(%d);", brightness); - m_serial->write((unsigned char*)m_temp, (unsigned int)::strlen(m_temp)); -} diff --git a/TFTSurenoo.h b/TFTSurenoo.h deleted file mode 100644 index 41941d2..0000000 --- a/TFTSurenoo.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2019 by SASANO Takayoshi JG1UAA - * Copyright (C) 2015,2016,2018,2020,2025 by Jonathan Naylor G4KLX - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#if !defined(TFTSURENOO_H) -#define TFTSURENOO_H - -#include "Display.h" -#include "Defines.h" -#include "SerialPort.h" -#include "UserDBentry.h" - -#include - -class CTFTSurenoo : public CDisplay -{ -public: - CTFTSurenoo(const std::string& callsign, unsigned int dmrid, ISerialPort* serial, unsigned int brightness, bool duplex, unsigned int screenLayout); - virtual ~CTFTSurenoo(); - - virtual bool open(); - - virtual void close(); - -protected: - virtual void setIdleInt(); - virtual void setErrorInt(const char* text); - virtual void setLockoutInt(); - virtual void setQuitInt(); - virtual void setFMInt(); - - virtual void writeDStarInt(const char* my1, const char* my2, const char* your, const char* type, const char* reflector); - virtual void clearDStarInt(); - - virtual void writeDMRInt(unsigned int slotNo, const std::string& src, bool group, const std::string& dst, const char* type); - virtual int writeDMRIntEx(unsigned int slotNo, const CUserDBentry& src, bool group, const std::string& dst, const char* type); - virtual void clearDMRInt(unsigned int slotNo); - - virtual void writeFusionInt(const char* source, const char* dest, unsigned char dgid, const char* type, const char* origin); - virtual void clearFusionInt(); - - virtual void writeP25Int(const char* source, bool group, unsigned int dest, const char* type); - virtual void clearP25Int(); - - virtual void writeNXDNInt(const char* source, bool group, unsigned int dest, const char* type); - virtual int writeNXDNIntEx(const CUserDBentry& 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(); - - virtual void clockInt(unsigned int ms); - -private: - std::string m_callsign; - unsigned int m_dmrid; - ISerialPort* m_serial; - unsigned int m_brightness; - unsigned char m_mode; - bool m_duplex; - bool m_refresh; - CTimer m_refreshTimer; - char* m_lineBuf; - char m_temp[128]; - unsigned int m_screenLayout; - - void setLineBuffer(char *buf, const char *text, int maxchar); - void setModeLine(const char *text); - void setStatusLine(unsigned int line, const char *text); - void refreshDisplay(); - - void lcdReset(); - void clearScreen(unsigned char colour); - void setBackground(unsigned char colour); - void setRotation(unsigned char rotation); - void setBrightness(unsigned char brightness); -}; - -#endif diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 47a0774..ff6f504 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -25,13 +25,7 @@ #include #endif -#if defined(HAVE_LOG_H) #include "Log.h" -#else -#define LogMessage(fmt, ...) ::fprintf(stderr, fmt "\n", ## __VA_ARGS__) -#define LogError(fmt, ...) ::fprintf(stderr, fmt "\n", ## __VA_ARGS__) -#define LogInfo(fmt, ...) ::fprintf(stderr, fmt "\n", ## __VA_ARGS__) -#endif CUDPSocket::CUDPSocket(const std::string& address, unsigned short port) : m_localAddress(address), diff --git a/UserDB.cpp b/UserDB.cpp index 455e307..7b209e5 100644 --- a/UserDB.cpp +++ b/UserDB.cpp @@ -1,5 +1,6 @@ /* - * Copyright (C) 2020,2025 by SASANO Takayoshi JG1UAA + * Copyright (C) 2020 by SASANO Takayoshi JG1UAA + * Copyright (C) 2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +19,8 @@ #include "UserDB.h" #include "Log.h" +#if defined(USE_DMR) || defined(USE_P25) || defined(USE_NXDN) + #include #include #include @@ -187,3 +190,6 @@ char* CUserDB::tokenize(char* str, char** next) return str; } + +#endif + diff --git a/UserDB.h b/UserDB.h index acae103..1bc75c6 100644 --- a/UserDB.h +++ b/UserDB.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2020 by SASANO Takayoshi JG1UAA + * Copyright (C) 2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +22,9 @@ #include "UserDBentry.h" #include "Mutex.h" +#include "Defines.h" + +#if defined(USE_DMR) || defined(USE_P25) || defined(USE_NXDN) class CUserDB { public: @@ -41,3 +45,6 @@ private: }; #endif + +#endif + diff --git a/UserDBentry.cpp b/UserDBentry.cpp index 0121544..79e193d 100644 --- a/UserDBentry.cpp +++ b/UserDBentry.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2020 by SASANO Takayoshi JG1UAA + * Copyright (C) 2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,6 +17,9 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "UserDBentry.h" + +#if defined(USE_DMR) || defined(USE_P25) || defined(USE_NXDN) + #include CUserDBentry::CUserDBentry() : @@ -57,3 +61,6 @@ void CUserDBentry::clear(void) { m_db.clear(); } + +#endif + diff --git a/UserDBentry.h b/UserDBentry.h index cbca92f..8f80a45 100644 --- a/UserDBentry.h +++ b/UserDBentry.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2020 by SASANO Takayoshi JG1UAA + * Copyright (C) 2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +20,10 @@ #if !defined(USERDBENTRY_H) #define USERDBENTRY_H +#include "Defines.h" + +#if defined(USE_DMR) || defined(USE_P25) || defined(USE_NXDN) + #include #include #include @@ -48,3 +53,6 @@ private: }; #endif + +#endif + diff --git a/Utils.cpp b/Utils.cpp index cf24ac6..02b5f87 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009,2014,2015,2016,2021,2025 Jonathan Naylor, G4KLX + * Copyright (C) 2009,2014,2015,2016,2021,2022,2023,2025 Jonathan Naylor, G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,6 +17,13 @@ #include #include +#if defined(_WIN32) || defined(_WIN64) +#include +#else +#include +#include +#endif + void CUtils::dump(const std::string& title, const unsigned char* data, unsigned int length) { assert(data != nullptr); @@ -170,3 +177,25 @@ void CUtils::removeChar(unsigned char * haystack, char needdle) haystack[j] = '\0'; } + +std::string CUtils::createTimestamp() +{ + char buffer[100U]; + +#if defined(_WIN32) || defined(_WIN64) + SYSTEMTIME st; + ::GetSystemTime(&st); + + ::sprintf(buffer, "%04u-%02u-%02u %02u:%02u:%02u.%03u", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); +#else + struct timeval now; + ::gettimeofday(&now, nullptr); + + struct tm* tm = ::gmtime(&now.tv_sec); + + ::sprintf(buffer, "%04d-%02d-%02d %02d:%02d:%02d.%03lld", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec / 1000LL); +#endif + + return buffer; +} + diff --git a/Utils.h b/Utils.h index 1e2ee9e..6b5a876 100644 --- a/Utils.h +++ b/Utils.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009,2014,2015,2021 by Jonathan Naylor, G4KLX + * Copyright (C) 2009,2014,2015,2021,2022,2023 by Jonathan Naylor, G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,6 +34,8 @@ public: static void removeChar(unsigned char * haystack, char needdle); + static std::string createTimestamp(); + private: }; diff --git a/Version.h b/Version.h index 0e0fbe8..2b24a1f 100644 --- a/Version.h +++ b/Version.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2025 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2026 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 = "20250828"; +const char* VERSION = "20260226"; #endif diff --git a/YSFControl.cpp b/YSFControl.cpp index 8d95031..52020c4 100644 --- a/YSFControl.cpp +++ b/YSFControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2021,2025 Jonathan Naylor, G4KLX + * Copyright (C) 2015-2021,2023,2025 Jonathan Naylor, G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,19 +16,21 @@ #include "Sync.h" #include "Log.h" +#if defined(USE_YSF) + #include #include #include #include -// #define DUMP_YSF +const unsigned int RSSI_COUNT = 10U; // 10 * 100ms = 1000ms +const unsigned int BER_COUNT = 10U; // 10 * 100ms = 1000ms -CYSFControl::CYSFControl(const std::string& callsign, bool selfOnly, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool lowDeviation, bool remoteGateway, CRSSIInterpolator* rssiMapper) : +CYSFControl::CYSFControl(const std::string& callsign, bool selfOnly, CYSFNetwork* network, unsigned int timeout, bool duplex, bool lowDeviation, bool remoteGateway, CRSSIInterpolator* rssiMapper) : m_callsign(nullptr), m_selfCallsign(nullptr), m_selfOnly(selfOnly), m_network(network), -m_display(display), m_duplex(duplex), m_lowDeviation(lowDeviation), m_remoteGateway(remoteGateway), @@ -45,7 +47,6 @@ m_netFrames(0U), m_netLost(0U), m_rfErrs(0U), m_rfBits(1U), -m_netErrs(0U), m_netBits(1U), m_rfSource(nullptr), m_rfDest(nullptr), @@ -56,15 +57,17 @@ m_netN(0U), m_rfPayload(), m_netPayload(), m_rssiMapper(rssiMapper), -m_rssi(0U), -m_maxRSSI(0U), -m_minRSSI(0U), -m_aveRSSI(0U), +m_rssi(0), +m_maxRSSI(0), +m_minRSSI(0), +m_aveRSSI(0), +m_rssiCountTotal(0U), +m_rssiAccum(0), m_rssiCount(0U), -m_enabled(true), -m_fp(nullptr) +m_bitsCount(0U), +m_bitErrsAccum(0U), +m_enabled(true) { - assert(display != nullptr); assert(rssiMapper != nullptr); m_rfPayload.setUplink(callsign); @@ -108,10 +111,13 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len) unsigned char type = data[0U]; if ((type == TAG_LOST) && (m_rfState == RPT_RF_STATE::AUDIO)) { - if (m_rssi != 0U) - LogMessage("YSF, transmission lost from %10.10s to %10.10s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_rfSource, m_rfDest, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else + if (m_rssi != 0) { + LogMessage("YSF, transmission lost from %10.10s to %10.10s, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", m_rfSource, m_rfDest, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + writeJSONRF("lost", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + } else { LogMessage("YSF, transmission lost from %10.10s to %10.10s, %.1f seconds, BER: %.1f%%", m_rfSource, m_rfDest, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeJSONRF("lost", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + } writeEndRF(); return false; } @@ -137,19 +143,19 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len) raw |= (data[123U] << 0) & 0x00FFU; // Convert the raw RSSI to dBm - int rssi = m_rssiMapper->interpolate(raw); - if (rssi != 0) - LogDebug("YSF, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi); + m_rssi = m_rssiMapper->interpolate(raw); + if (m_rssi != 0) + LogDebug("YSF, raw RSSI: %u, reported RSSI: %d dBm", raw, m_rssi); - // RSSI is always reported as positive - m_rssi = (rssi >= 0) ? rssi : -rssi; - - if (m_rssi > m_minRSSI) + if (m_rssi < m_minRSSI) m_minRSSI = m_rssi; - if (m_rssi < m_maxRSSI) + if (m_rssi > m_maxRSSI) m_maxRSSI = m_rssi; m_aveRSSI += m_rssi; + m_rssiCountTotal++; + + m_rssiAccum += m_rssi; m_rssiCount++; } @@ -233,6 +239,7 @@ bool CYSFControl::processVWData(bool valid, unsigned char *data) if (!ret) { LogMessage("YSF, invalid access attempt from %10.10s to DG-ID %u", m_rfSource, dgid); m_rfState = RPT_RF_STATE::REJECTED; + writeJSONRF("rejected", "voice_vw", m_rfSource, dgid); return true; } } @@ -252,12 +259,16 @@ bool CYSFControl::processVWData(bool valid, unsigned char *data) m_minRSSI = m_rssi; m_maxRSSI = m_rssi; m_aveRSSI = m_rssi; + m_rssiCountTotal = 1U; + + m_rssiAccum = m_rssi; m_rssiCount = 1U; -#if defined(DUMP_YSF) - openFile(); -#endif - m_display->writeFusion((char*)m_rfSource, (char*)m_rfDest, dgid, "R", " "); + + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + LogMessage("YSF, received RF header from %10.10s to DG-ID %u", m_rfSource, dgid); + writeJSONRF("start", "voice_vw", m_rfSource, dgid); CSync::addYSFSync(data + 2U); @@ -270,9 +281,6 @@ bool CYSFControl::processVWData(bool valid, unsigned char *data) writeNetwork(data, m_rfFrames % 128U); -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif if (m_duplex) { fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); fich.setDev(m_lowDeviation); @@ -282,7 +290,7 @@ bool CYSFControl::processVWData(bool valid, unsigned char *data) m_rfFrames++; - m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -305,9 +313,7 @@ bool CYSFControl::processVWData(bool valid, unsigned char *data) data[1U] = 0x00U; writeNetwork(data, m_rfFrames % 128U); -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif + if (m_duplex) { fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); fich.setDev(m_lowDeviation); @@ -317,10 +323,13 @@ bool CYSFControl::processVWData(bool valid, unsigned char *data) m_rfFrames++; - if (m_rssi != 0U) - LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else + if (m_rssi != 0) { + LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + writeJSONRF("end", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + } else { LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeJSONRF("end", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + } writeEndRF(); } @@ -346,14 +355,13 @@ bool CYSFControl::processVWData(bool valid, unsigned char *data) unsigned int errors = m_rfPayload.processVoiceFRModeAudio2(data + 2U); m_rfErrs += errors; m_rfBits += 288U; - m_display->writeFusionBER(float(errors) / 2.88F); LogDebug("YSF, V Mode 3, seq %u, AMBE FEC %u/288 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 2.88F); } else { unsigned int errors = m_rfPayload.processVoiceFRModeAudio5(data + 2U); m_rfErrs += errors; m_rfBits += 720U; - m_display->writeFusionBER(float(errors) / 7.2F); LogDebug("YSF, V Mode 3, seq %u, AMBE FEC %u/720 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 7.2F); + writeJSONBER(720U, errors); } fich.encode(data + 2U); @@ -370,12 +378,9 @@ bool CYSFControl::processVWData(bool valid, unsigned char *data) writeQueueRF(data); } -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif m_rfFrames++; - m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -402,6 +407,7 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) if (!ret) { LogMessage("YSF, invalid access attempt from %10.10s to DG-ID %u", m_rfSource, dgid); m_rfState = RPT_RF_STATE::REJECTED; + writeJSONRF("rejected", "voice_dn", m_rfSource, dgid); return true; } } @@ -421,12 +427,16 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) m_minRSSI = m_rssi; m_maxRSSI = m_rssi; m_aveRSSI = m_rssi; + m_rssiCountTotal = 1U; + + m_rssiAccum = m_rssi; m_rssiCount = 1U; -#if defined(DUMP_YSF) - openFile(); -#endif - m_display->writeFusion((char*)m_rfSource, (char*)m_rfDest, dgid, "R", " "); + + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + LogMessage("YSF, received RF header from %10.10s to DG-ID %u", m_rfSource, dgid); + writeJSONRF("start", "voice_dn", m_rfSource, dgid); CSync::addYSFSync(data + 2U); @@ -439,9 +449,6 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) writeNetwork(data, m_rfFrames % 128U); -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif if (m_duplex) { fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); fich.setDev(m_lowDeviation); @@ -451,7 +458,7 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) m_rfFrames++; - m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -474,9 +481,7 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) data[1U] = 0x00U; writeNetwork(data, m_rfFrames % 128U); -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif + if (m_duplex) { fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); fich.setDev(m_lowDeviation); @@ -486,10 +491,13 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) m_rfFrames++; - if (m_rssi != 0U) - LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else + if (m_rssi != 0) { + LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + writeJSONRF("end", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + } else { LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, BER: %.1f%%", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeJSONRF("end", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + } writeEndRF(); } @@ -517,8 +525,8 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) unsigned int errors = m_rfPayload.processVDMode1Audio(data + 2U); m_rfErrs += errors; m_rfBits += 235U; - m_display->writeFusionBER(float(errors) / 2.35F); LogDebug("YSF, V/D Mode 1, seq %u, AMBE FEC %u/235 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 2.35F); + writeJSONBER(235U, errors); } break; @@ -527,8 +535,8 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) unsigned int errors = m_rfPayload.processVDMode2Audio(data + 2U); m_rfErrs += errors; m_rfBits += 405U; - m_display->writeFusionBER(float(errors) / 4.05F); LogDebug("YSF, V/D Mode 2, seq %u, Repetition FEC %u/405 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 4.05F); + writeJSONBER(405U, errors); } break; @@ -552,12 +560,9 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) writeQueueRF(data); } -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif m_rfFrames++; - m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } else if (valid && (m_rfState == RPT_RF_STATE::LISTENING)) { @@ -598,6 +603,7 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) if (!ret) { LogMessage("YSF, invalid access attempt from %10.10s to DG-ID %u", m_rfSource, dgid); m_rfState = RPT_RF_STATE::REJECTED; + writeJSONRF("rejected", "voice_dn", m_rfSource, dgid); return true; } } @@ -611,10 +617,14 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) m_minRSSI = m_rssi; m_maxRSSI = m_rssi; m_aveRSSI = m_rssi; + m_rssiCountTotal = 1U; + + m_rssiAccum = m_rssi; m_rssiCount = 1U; -#if defined(DUMP_YSF) - openFile(); -#endif + + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + // Build a new header and transmit it unsigned char buffer[YSF_FRAME_LENGTH_BYTES + 2U]; @@ -648,11 +658,8 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) writeQueueRF(buffer); } -#if defined(DUMP_YSF) - writeFile(buffer + 2U); -#endif - m_display->writeFusion((char*)m_rfSource, (char*)m_rfDest, dgid, "R", " "); LogMessage("YSF, received RF late entry from %10.10s to DG-ID %u", m_rfSource, dgid); + writeJSONRF("late_entry", "voice_dn", m_rfSource, dgid); CSync::addYSFSync(data + 2U); @@ -672,12 +679,9 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) writeQueueRF(data); } -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif m_rfFrames++; - m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -704,6 +708,7 @@ bool CYSFControl::processFRData(bool valid, unsigned char *data) if (!ret) { LogMessage("YSF, invalid access attempt from %10.10s to DG-ID %u", m_rfSource, dgid); m_rfState = RPT_RF_STATE::REJECTED; + writeJSONRF("rejected", "data_fr", m_rfSource, dgid); return true; } } @@ -720,12 +725,16 @@ bool CYSFControl::processFRData(bool valid, unsigned char *data) m_minRSSI = m_rssi; m_maxRSSI = m_rssi; m_aveRSSI = m_rssi; + m_rssiCountTotal = 1U; + + m_rssiAccum = m_rssi; m_rssiCount = 1U; -#if defined(DUMP_YSF) - openFile(); -#endif - m_display->writeFusion((char*)m_rfSource, (char*)m_rfDest, dgid, "R", " "); + + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + LogMessage("YSF, received RF header from %10.10s to DG-ID %u", m_rfSource, dgid); + writeJSONRF("start", "data_fr", m_rfSource, dgid); CSync::addYSFSync(data + 2U); @@ -737,9 +746,7 @@ bool CYSFControl::processFRData(bool valid, unsigned char *data) data[1U] = 0x00U; writeNetwork(data, m_rfFrames % 128U); -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif + if (m_duplex) { fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); fich.setDev(m_lowDeviation); @@ -749,7 +756,7 @@ bool CYSFControl::processFRData(bool valid, unsigned char *data) m_rfFrames++; - m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -772,9 +779,7 @@ bool CYSFControl::processFRData(bool valid, unsigned char *data) data[1U] = 0x00U; writeNetwork(data, m_rfFrames % 128U); -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif + if (m_duplex) { fich.setMR(m_remoteGateway ? YSF_MR_NOT_BUSY : YSF_MR_BUSY); fich.setDev(m_lowDeviation); @@ -784,10 +789,13 @@ bool CYSFControl::processFRData(bool valid, unsigned char *data) m_rfFrames++; - if (m_rssi != 0U) - LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, RSSI: -%u/-%u/-%u dBm", m_rfSource, dgid, float(m_rfFrames) / 10.0F, m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - else + if (m_rssi != 0) { + LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds, RSSI: %d/%d/%d dBm", m_rfSource, dgid, float(m_rfFrames) / 10.0F, m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + writeJSONRF("end", float(m_rfFrames) / 10.0F, 0.0F, m_minRSSI, m_maxRSSI, m_aveRSSI / int(m_rssiCountTotal)); + } else { LogMessage("YSF, received RF end of transmission from %10.10s to DG-ID %u, %.1f seconds", m_rfSource, dgid, float(m_rfFrames) / 10.0F); + writeJSONRF("end", float(m_rfFrames) / 10.0F, 0.0F); + } writeEndRF(); } @@ -826,12 +834,9 @@ bool CYSFControl::processFRData(bool valid, unsigned char *data) writeQueueRF(data); } -#if defined(DUMP_YSF) - writeFile(data + 2U); -#endif m_rfFrames++; - m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -867,15 +872,9 @@ void CYSFControl::writeEndRF() m_rfDest = nullptr; if (m_netState == RPT_NET_STATE::IDLE) { - m_display->clearFusion(); - if (m_network != nullptr) m_network->reset(); } - -#if defined(DUMP_YSF) - closeFile(); -#endif } void CYSFControl::writeEndNet() @@ -888,8 +887,6 @@ void CYSFControl::writeEndNet() m_netPayload.reset(); - m_display->clearFusion(); - if (m_network != nullptr) m_network->reset(); } @@ -929,8 +926,8 @@ void CYSFControl::writeNetwork() ::memcpy(m_netDest, data + 24U, YSF_CALLSIGN_LENGTH); if (::memcmp(m_netSource, " ", 10U) != 0 && ::memcmp(m_netDest, " ", 10U) != 0) { - m_display->writeFusion((char*)m_netSource, (char*)m_netDest, dgid, "N", (char*)(data + 4U)); LogMessage("YSF, received network data from %10.10s to DG-ID %u at %10.10s", m_netSource, dgid, data + 4U); + writeJSONNet("start", m_netSource, dgid, data + 4U); } m_netTimeoutTimer.start(); @@ -940,7 +937,6 @@ void CYSFControl::writeNetwork() m_netState = RPT_NET_STATE::AUDIO; m_netFrames = 0U; m_netLost = 0U; - m_netErrs = 0U; m_netBits = 1U; m_netN = 0U; } else { @@ -995,8 +991,7 @@ void CYSFControl::writeNetwork() if (ok) processNetCallsigns(data, dgid); - unsigned int errors = m_netPayload.processVDMode1Audio(data + 35U); - m_netErrs += errors; + m_netPayload.processVDMode1Audio(data + 35U); m_netBits += 235U; } break; @@ -1006,8 +1001,7 @@ void CYSFControl::writeNetwork() if (ok) processNetCallsigns(data, dgid); - unsigned int errors = m_netPayload.processVDMode2Audio(data + 35U); - m_netErrs += errors; + m_netPayload.processVDMode2Audio(data + 35U); m_netBits += 135U; } break; @@ -1020,12 +1014,10 @@ void CYSFControl::writeNetwork() if (fn == 0U && ft == 1U) { // The first packet after the header is odd m_netPayload.processVoiceFRModeData(data + 35U); - unsigned int errors = m_netPayload.processVoiceFRModeAudio2(data + 35U); - m_netErrs += errors; + m_netPayload.processVoiceFRModeAudio2(data + 35U); m_netBits += 288U; } else { - unsigned int errors = m_netPayload.processVoiceFRModeAudio5(data + 35U); - m_netErrs += errors; + m_netPayload.processVoiceFRModeAudio5(data + 35U); m_netBits += 720U; } break; @@ -1047,7 +1039,8 @@ void CYSFControl::writeNetwork() m_netN = n; if (end) { - LogMessage("YSF, received network end of transmission from %10.10s to DG-ID %u, %.1f seconds, %u%% packet loss, BER: %.1f%%", m_netSource, dgid, float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); + LogMessage("YSF, received network end of transmission from %10.10s to DG-ID %u at %10.10s, %.1f seconds, %u%% packet loss", m_netSource, dgid, data + 4U, float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames); + writeJSONNet("end", float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames); writeEndNet(); } } @@ -1067,7 +1060,8 @@ void CYSFControl::clock(unsigned int ms) m_networkWatchdog.clock(ms); if (m_networkWatchdog.hasExpired()) { - LogMessage("YSF, network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%%", float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); + LogMessage("YSF, network watchdog has expired, %.1f seconds, %u%% packet loss", float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames); + writeJSONNet("lost", float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames); writeEndNet(); } } @@ -1129,46 +1123,6 @@ void CYSFControl::writeNetwork(const unsigned char *data, unsigned int count) m_network->write(m_rfSource, m_rfDest, data + 2U, count, data[0U] == TAG_EOT); } -bool CYSFControl::openFile() -{ - if (m_fp != nullptr) - return true; - - time_t t; - ::time(&t); - - struct tm* tm = ::localtime(&t); - - char name[100U]; - ::sprintf(name, "YSF_%04d%02d%02d_%02d%02d%02d.ambe", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); - - m_fp = ::fopen(name, "wb"); - if (m_fp == nullptr) - return false; - - ::fwrite("YSF", 1U, 3U, m_fp); - - return true; -} - -bool CYSFControl::writeFile(const unsigned char* data) -{ - if (m_fp == nullptr) - return false; - - ::fwrite(data, 1U, YSF_FRAME_LENGTH_BYTES, m_fp); - - return true; -} - -void CYSFControl::closeFile() -{ - if (m_fp != nullptr) { - ::fclose(m_fp); - m_fp = nullptr; - } -} - bool CYSFControl::checkCallsign(const unsigned char* callsign) const { return ::memcmp(callsign, m_selfCallsign, ::strlen((char*)m_selfCallsign)) == 0; @@ -1192,8 +1146,8 @@ void CYSFControl::processNetCallsigns(const unsigned char* data, unsigned char d } if (::memcmp(m_netSource, " ", 10U) != 0 && ::memcmp(m_netDest, " ", 10U) != 0) { - m_display->writeFusion((char*)m_netSource, (char*)m_netDest, dgid, "N", (char*)(data + 4U)); LogMessage("YSF, received network data from %10.10s to DG-ID %u at %10.10s", m_netSource, dgid, data + 4U); + writeJSONNet("start", m_netSource, dgid, data + 4U); } } } @@ -1252,3 +1206,182 @@ void CYSFControl::enable(bool enabled) m_enabled = enabled; } + +void CYSFControl::writeJSONRSSI() +{ + if (m_rssi == 0) + return; + + if (m_rssiCount >= RSSI_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "YSF"; + + json["value"] = m_rssiAccum / int(m_rssiCount); + + WriteJSON("RSSI", json); + + m_rssiAccum = 0; + m_rssiCount = 0U; + } +} + +void CYSFControl::writeJSONBER(unsigned int bits, unsigned int errs) +{ + m_bitsCount += bits; + m_bitErrsAccum += errs; + + if (m_bitsCount >= (BER_COUNT * bits)) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "YSF"; + + json["value"] = float(m_bitErrsAccum * 100U) / float(m_bitsCount); + + WriteJSON("BER", json); + + m_bitErrsAccum = 0U; + m_bitsCount = 1U; + } +} + +void CYSFControl::writeJSONRF(const char* action, const char* mode, const unsigned char* source, unsigned char dgid) +{ + assert(action != nullptr); + assert(mode != nullptr); + assert(source != nullptr); + + nlohmann::json json; + + writeJSONRF(json, action, source, dgid); + + json["mode"] = mode; + + WriteJSON("YSF", json); +} + +void CYSFControl::writeJSONRF(const char* action, float duration, float ber) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSONRF(json, action); + + json["duration"] = duration; + json["ber"] = ber; + + WriteJSON("YSF", json); +} + +void CYSFControl::writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSONRF(json, action); + + json["duration"] = duration; + json["ber"] = ber; + + nlohmann::json rssi; + rssi["min"] = minRSSI; + rssi["max"] = maxRSSI; + rssi["ave"] = aveRSSI; + + json["rssi"] = rssi; + + WriteJSON("YSF", json); +} + +void CYSFControl::writeJSONNet(const char* action, const unsigned char* source, unsigned char dgid, const unsigned char* reflector) +{ + assert(action != nullptr); + assert(source != nullptr); + assert(reflector != nullptr); + + nlohmann::json json; + + writeJSONNet(json, action, source, dgid); + + json["reflector"] = convertBuffer(reflector); + + WriteJSON("YSF", json); +} + +void CYSFControl::writeJSONNet(const char* action, float duration, unsigned int loss) +{ + assert(action != nullptr); + + nlohmann::json json; + + writeJSONNet(json, action); + + json["duration"] = duration; + json["loss"] = loss; + + WriteJSON("YSF", json); +} + +void CYSFControl::writeJSONRF(nlohmann::json& json, const char* action) +{ + assert(action != nullptr); + + json["timestamp"] = CUtils::createTimestamp(); + json["action"] = action; +} + +void CYSFControl::writeJSONRF(nlohmann::json& json, const char* action, const unsigned char* source, unsigned char dgid) +{ + assert(action != nullptr); + assert(source != nullptr); + + json["timestamp"] = CUtils::createTimestamp(); + + json["source_cs"] = convertBuffer(source); + + json["source"] = "rf"; + json["action"] = action; + json["dg-id"] = int(dgid); +} + +void CYSFControl::writeJSONNet(nlohmann::json& json, const char* action) +{ + assert(action != nullptr); + + json["timestamp"] = CUtils::createTimestamp(); + json["action"] = action; +} + +void CYSFControl::writeJSONNet(nlohmann::json& json, const char* action, const unsigned char* source, unsigned char dgid) +{ + assert(action != nullptr); + assert(source != nullptr); + + json["timestamp"] = CUtils::createTimestamp(); + + json["source_cs"] = convertBuffer(source); + + json["source"] = "network"; + json["action"] = action; + json["dg-id"] = int(dgid); +} + +std::string CYSFControl::convertBuffer(const unsigned char* buffer) const +{ + assert(buffer != nullptr); + + std::string callsign((char*)buffer, 10U); + + size_t pos = callsign.find_first_of(' '); + if (pos != std::string::npos) + callsign = callsign.substr(0U, pos); + + return callsign; +} + +#endif + diff --git a/YSFControl.h b/YSFControl.h index e1076f3..d8ea890 100644 --- a/YSFControl.h +++ b/YSFControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2020 by Jonathan Naylor G4KLX + * Copyright (C) 2015-2020,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,16 +26,19 @@ #include "RingBuffer.h" #include "StopWatch.h" #include "YSFFICH.h" -#include "Display.h" #include "Defines.h" #include "Timer.h" #include "Modem.h" +#if defined(USE_YSF) + #include +#include + class CYSFControl { public: - CYSFControl(const std::string& callsign, bool selfOnly, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool lowDeviation, bool remoteGateway, CRSSIInterpolator* rssiMapper); + CYSFControl(const std::string& callsign, bool selfOnly, CYSFNetwork* network, unsigned int timeout, bool duplex, bool lowDeviation, bool remoteGateway, CRSSIInterpolator* rssiMapper); ~CYSFControl(); bool writeModem(unsigned char* data, unsigned int len); @@ -53,7 +56,6 @@ private: unsigned char* m_selfCallsign; bool m_selfOnly; CYSFNetwork* m_network; - CDisplay* m_display; bool m_duplex; bool m_lowDeviation; bool m_remoteGateway; @@ -70,7 +72,6 @@ private: unsigned int m_netLost; unsigned int m_rfErrs; unsigned int m_rfBits; - unsigned int m_netErrs; unsigned int m_netBits; unsigned char* m_rfSource; unsigned char* m_rfDest; @@ -81,13 +82,16 @@ private: CYSFPayload m_rfPayload; CYSFPayload m_netPayload; CRSSIInterpolator* m_rssiMapper; - unsigned char m_rssi; - unsigned char m_maxRSSI; - unsigned char m_minRSSI; - unsigned int m_aveRSSI; + int m_rssi; + int m_maxRSSI; + int m_minRSSI; + int m_aveRSSI; + unsigned int m_rssiCountTotal; + int m_rssiAccum; unsigned int m_rssiCount; + unsigned int m_bitsCount; + unsigned int m_bitErrsAccum; bool m_enabled; - FILE* m_fp; bool processVWData(bool valid, unsigned char *data); bool processDNData(bool valid, unsigned char *data); @@ -101,12 +105,29 @@ private: void writeEndRF(); void writeEndNet(); - bool openFile(); - bool writeFile(const unsigned char* data); - void closeFile(); + void writeJSONRSSI(); + void writeJSONBER(unsigned int bits, unsigned int errs); + + void writeJSONRF(const char* action, const char* mode, const unsigned char* source, unsigned char dgid); + void writeJSONRF(const char* action, float duration, float ber); + void writeJSONRF(const char* action, float duration, float ber, int minRSSI, int maxRSSI, int aveRSSI); + + void writeJSONNet(const char* action, const unsigned char* source, unsigned char dgid, const unsigned char* reflector); + void writeJSONNet(const char* action, float duration, unsigned int loss); + + void writeJSONRF(nlohmann::json& json, const char* action); + void writeJSONRF(nlohmann::json& json, const char* action, const unsigned char* source, unsigned char dgid); + + void writeJSONNet(nlohmann::json& json, const char* action); + void writeJSONNet(nlohmann::json& json, const char* action, const unsigned char* source, unsigned char dgid); + + std::string convertBuffer(const unsigned char* buffer) const; bool checkCallsign(const unsigned char* callsign) const; void processNetCallsigns(const unsigned char* data, unsigned char dgid); }; #endif + +#endif + diff --git a/YSFConvolution.cpp b/YSFConvolution.cpp index 9b2afd0..8210bf1 100644 --- a/YSFConvolution.cpp +++ b/YSFConvolution.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2016,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2016,2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,6 +18,8 @@ #include "YSFConvolution.h" +#if defined(USE_YSF) + #include #include #include @@ -148,3 +150,6 @@ void CYSFConvolution::encode(const unsigned char* in, unsigned char* out, unsign k++; } } + +#endif + diff --git a/YSFConvolution.h b/YSFConvolution.h index b49ddc5..65bf719 100644 --- a/YSFConvolution.h +++ b/YSFConvolution.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015,2016.2021 by Jonathan Naylor G4KLX + * Copyright (C) 2015,2016.2021,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(YSFConvolution_H) #define YSFConvolution_H +#include "Defines.h" + +#if defined(USE_YSF) + #include class CYSFConvolution { @@ -44,3 +48,5 @@ private: #endif +#endif + diff --git a/YSFFICH.cpp b/YSFFICH.cpp index 13d127d..333aad0 100644 --- a/YSFFICH.cpp +++ b/YSFFICH.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016,2017,2019,2020,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2016,2017,2019,2020,2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,8 @@ #include "CRC.h" #include "Log.h" +#if defined(USE_YSF) + #include #include #include @@ -288,3 +290,6 @@ CYSFFICH& CYSFFICH::operator=(const CYSFFICH& fich) return *this; } + +#endif + diff --git a/YSFFICH.h b/YSFFICH.h index a6e8851..5028080 100644 --- a/YSFFICH.h +++ b/YSFFICH.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016,2017,2019,2020 by Jonathan Naylor G4KLX + * Copyright (C) 2016,2017,2019,2020,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,10 @@ #if !defined(YSFFICH_H) #define YSFFICH_H +#include "Defines.h" + +#if defined(USE_YSF) + class CYSFFICH { public: CYSFFICH(const CYSFFICH& fich); @@ -57,3 +61,6 @@ private: }; #endif + +#endif + diff --git a/YSFNetwork.cpp b/YSFNetwork.cpp index d300415..28db30a 100644 --- a/YSFNetwork.cpp +++ b/YSFNetwork.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014,2016,2019,2020,2021,2025 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2014,2016,2019,2020,2021,2023,2025 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,10 +18,11 @@ #include "YSFDefines.h" #include "YSFNetwork.h" -#include "Defines.h" #include "Utils.h" #include "Log.h" +#if defined(USE_YSF) + #include #include #include @@ -204,3 +205,6 @@ void CYSFNetwork::enable(bool enabled) m_enabled = enabled; } + +#endif + diff --git a/YSFNetwork.h b/YSFNetwork.h index 918134b..ad1ae18 100644 --- a/YSFNetwork.h +++ b/YSFNetwork.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2014,2016,2020,2021 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2014,2016,2020,2021,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,8 +22,11 @@ #include "YSFDefines.h" #include "RingBuffer.h" #include "UDPSocket.h" +#include "Defines.h" #include "Timer.h" +#if defined(USE_YSF) + #include #include @@ -63,3 +66,6 @@ private: }; #endif + +#endif + diff --git a/YSFPayload.cpp b/YSFPayload.cpp index cc9dc9a..14645b6 100644 --- a/YSFPayload.cpp +++ b/YSFPayload.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2017,2020,2025 Jonathan Naylor, G4KLX +* Copyright (C) 2016,2017,2020,2023,2025 Jonathan Naylor, G4KLX * Copyright (C) 2016 Mathias Weyland, HB9FRV * * This program is free software; you can redistribute it and/or modify @@ -19,6 +19,8 @@ #include "CRC.h" #include "Log.h" +#if defined(USE_YSF) + #include #include #include @@ -1025,3 +1027,6 @@ void CYSFPayload::reset() m_source = nullptr; m_dest = nullptr; } + +#endif + diff --git a/YSFPayload.h b/YSFPayload.h index 06b858f..913b87c 100644 --- a/YSFPayload.h +++ b/YSFPayload.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016,2017,2020 by Jonathan Naylor G4KLX +* Copyright (C) 2016,2017,2020,2023 by Jonathan Naylor G4KLX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,6 +20,9 @@ #define YSFPayload_H #include "AMBEFEC.h" +#include "Defines.h" + +#if defined(USE_YSF) #include @@ -65,3 +68,6 @@ private: }; #endif + +#endif + diff --git a/schema.json b/schema.json new file mode 100644 index 0000000..ba647ad --- /dev/null +++ b/schema.json @@ -0,0 +1,182 @@ +{ + "$defs": { + "mmdvm_mode": {"type": "string", "enum": ["lockout", "idle", "error", "CW", "D-Star", "DMR", "YSF", "P25", "NXDN", "POCSAG", "FM"]}, + "dstar_callsign": {"type": "string", "minLength": 8, "maxLength": 8}, + "dstar_extension": {"type": "string", "minLength": 4, "maxLength": 4}, + "ysf_callsign": {"type": "string", "minLength": 10, "maxLength": 10}, + "dmr_id": {"type": "integer", "minimum": 1, "maximum": 16777215}, + "p25_id": {"type": "integer", "minimum": 1, "maximum": 65535}, + "nxdn_id": {"type": "integer", "minimum": 1, "maximum": 65535}, + "pocsag_ric": {"type": "integer"}, + "destination_type": {"type": "string", "enum": ["group", "individual"]}, + "mode": {"type": "string", "enum": ["voice", "data"]}, + "ysf_mode": {"type": "string", "enum": ["voice_vw", "voice_dn", "data_fr"]}, + "dg-id": {"type": "integer", "minimum": 0, "maximum": 99}, + "dmr_slot": {"type": "integer", "enum": [1, 2]}, + "source": {"type": "string", "enum": ["rf", "network"]}, + "fm_state": {"type": "string", "enum": ["listening", "kerchunk_rf", "relaying_rf", "relaying_wait_rf", "timeout_rf", "timeout_wait_rf", "kerchunk_ext", "relaying_ext", "relaying_wait_ext", "timeout_ext", "timeout_wait_ext", "hang", "unknown"]}, + "pocsag_source": {"type": "string", "enum": ["local", "network"]}, + "pocsag_functional": {"type": "string", "enum": ["numeric", "alphanumeric", "alert_1", "alert_2", "end"]}, + "action": {"type": "string", "enum": ["invalid", "rejected", "start", "late_entry", "end", "lost"]}, + "dmr_action": {"type": "string", "enum": ["csbk", "timeout", "invalid", "rejected", "start", "late_entry", "end", "lost"]}, + "duration": {"type": "number", "minimum": 0.0}, + "loss": {"type": "number", "minimum": 0.0}, + "ber": {"type": "number", "minimum": 0.0}, + "rssi": {"type": "integer", "minimum": -200, "maximum": -10}, + "timestamp": {"type": "string"} + }, + + "MMDVM": { + "type": "object", + "timestamp": {"$ref": "#/$defs/timestamp"}, + "mode": {"$ref": "#/$defs/mmdvm_mode"}, + "message": {"type": "string"}, + "required": ["timestamp"] + }, + + "RSSI" : { + "type": "object", + "timestamp": {"$ref": "#/$defs/timestamp"}, + "mode": {"$ref": "#/$defs/mmdvm_mode"}, + "slot": {"$ref": "#/$defs/dmr_slot"}, + "value": {"$ref": "#/$defs/rssi"}, + "required": ["timestamp", "mode", "value"] + }, + + "BER" : { + "type": "object", + "timestamp": {"$ref": "#/$defs/timestamp"}, + "mode": {"$ref": "#/$defs/mmdvm_mode"}, + "slot": {"$ref": "#/$defs/dmr_slot"}, + "value": {"$ref": "#/$defs/ber"}, + "required": ["timestamp", "mode", "value"] + }, + + "Text" : { + "type": "object", + "timestamp": {"$ref": "#/$defs/timestamp"}, + "mode": {"$ref": "#/$defs/mmdvm_mode"}, + "slot": {"$ref": "#/$defs/dmr_slot"}, + "value": {"type": "string"}, + "required": ["timestamp", "mode", "value"] + }, + + "D-Star": { + "type": "object", + "timestamp": {"$ref": "#/$defs/timestamp"}, + "source_cs": {"$ref": "#/$defs/dstar_callsign"}, + "source_ext": {"$ref": "#/$defs/dstar_extension"}, + "destination_cs": {"$ref": "#/$defs/dstar_callsign"}, + "reflector": {"$ref": "#/$defs/dstar_callsign"}, + "source": {"$ref": "#/$defs/source"}, + "action": {"$ref": "#/$defs/action"}, + "duration": {"$ref": "#/$defs/duration"}, + "loss": {"$ref": "#/$defs/loss"}, + "ber": {"$ref": "#/$defs/ber"}, + "rssi": { + "min": {"$ref": "#/$defs/rssi"}, + "max": {"$ref": "#/$defs/rssi"}, + "ave": {"$ref": "#/$defs/rssi"} + }, + "required": ["timestamp", "action"] + }, + + "DMR": { + "type": "object", + "timestamp": {"$ref": "#/$defs/timestamp"}, + "source_id": {"$ref": "#/$defs/dmr_id"}, + "source_info": {"type": "string"}, + "destination_id": {"$ref": "#/$defs/dmr_id"}, + "destination_type": {"$ref": "#/$defs/destination_type"}, + "slot": {"$ref": "#/$defs/dmr_slot"}, + "source": {"$ref": "#/$defs/source"}, + "action": {"$ref": "#/$defs/dmr_action"}, + "csbk_desc": {"type": "string"}, + "frames": {"type": "integer"}, + "duration": {"$ref": "#/$defs/duration"}, + "loss": {"$ref": "#/$defs/loss"}, + "ber": {"$ref": "#/$defs/ber"}, + "rssi": { + "min": {"$ref": "#/$defs/rssi"}, + "max": {"$ref": "#/$defs/rssi"}, + "ave": {"$ref": "#/$defs/rssi"} + }, + "required": ["timestamp", "slot", "action"] + }, + + "YSF": { + "type": "object", + "timestamp": {"$ref": "#/$defs/timestamp"}, + "source_cs": {"$ref": "#/$defs/ysf_callsign"}, + "source": {"$ref": "#/$defs/source"}, + "action": {"$ref": "#/$defs/action"}, + "mode": {"$ref": "#/$defs/ysf_mode"}, + "dg-id": {"$ref": "#/$defs/dg-id"}, + "reflector": {"$ref": "#/$defs/ysf_callsign"}, + "duration": {"$ref": "#/$defs/duration"}, + "loss": {"$ref": "#/$defs/loss"}, + "ber": {"$ref": "#/$defs/ber"}, + "rssi": { + "min": {"$ref": "#/$defs/rssi"}, + "max": {"$ref": "#/$defs/rssi"}, + "ave": {"$ref": "#/$defs/rssi"} + }, + "required": ["timestamp", "action"] + }, + + "P25": { + "type": "object", + "timestamp": {"$ref": "#/$defs/timestamp"}, + "source_id": {"$ref": "#/$defs/dmr_id"}, + "source_info": {"type": "string"}, + "destination_id": {"$ref": "#/$defs/p25_id"}, + "destination_type": {"$ref": "#/$defs/destination_type"}, + "source": {"$ref": "#/$defs/source"}, + "action": {"$ref": "#/$defs/action"}, + "duration": {"$ref": "#/$defs/duration"}, + "loss": {"$ref": "#/$defs/loss"}, + "ber": {"$ref": "#/$defs/ber"}, + "rssi": { + "min": {"$ref": "#/$defs/rssi"}, + "max": {"$ref": "#/$defs/rssi"}, + "ave": {"$ref": "#/$defs/rssi"} + }, + "required": ["timestamp", "action"] + }, + + "NXDN": { + "type": "object", + "timestamp": {"$ref": "#/$defs/timestamp"}, + "source_id": {"$ref": "#/$defs/nxdn_id"}, + "source_info": {"type": "string"}, + "destination_id": {"$ref": "#/$defs/nxdn_id"}, + "destination_type": {"$ref": "#/$defs/destination_type"}, + "source": {"$ref": "#/$defs/source"}, + "action": {"$ref": "#/$defs/action"}, + "duration": {"$ref": "#/$defs/duration"}, + "ber": {"$ref": "#/$defs/ber"}, + "rssi": { + "min": {"$ref": "#/$defs/rssi"}, + "max": {"$ref": "#/$defs/rssi"}, + "ave": {"$ref": "#/$defs/rssi"} + }, + "required": ["timestamp", "action"] + }, + + "POCSAG": { + "type": "object", + "timestamp": {"$ref": "#/$defs/timestamp"}, + "ric": {"$ref": "#/$defs/pocsag_ric"}, + "functional": {"$ref": "#/$defs/pocsag_functional"}, + "source": {"$ref": "#/$defs/pocsag_source"}, + "message": {"type": "string"}, + "required": ["timestamp", "ric", "source"] + }, + + "FM": { + "type": "object", + "timestamp": {"$ref": "#/$defs/timestamp"}, + "state": {"$ref": "#/$defs/fm_state"}, + "required": ["timestamp", "state"] + } +}