From 17c194c4661c18ad1c06ed49ea84a03f969dc763 Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Tue, 17 Jan 2023 15:11:40 +0000 Subject: [PATCH] Add P25 to JSON/MQTT. --- MMDVMHost.cpp | 2 + MMDVMHost.h | 1 - NXDNControl.cpp | 4 +- NXDNControl.h | 2 + P25Control.cpp | 126 ++++++++++++++++++++++++++++++++++++++++++++---- P25Control.h | 13 ++++- POCSAGControl.h | 2 + 7 files changed, 137 insertions(+), 13 deletions(-) diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index 8a38f7b..b00fc5d 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -43,6 +43,8 @@ #include +#include + #if !defined(_WIN32) && !defined(_WIN64) #include #include diff --git a/MMDVMHost.h b/MMDVMHost.h index 77a057b..a6cb437 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -47,7 +47,6 @@ #include - class CMMDVMHost { public: diff --git a/NXDNControl.cpp b/NXDNControl.cpp index f0d6667..b904b24 100644 --- a/NXDNControl.cpp +++ b/NXDNControl.cpp @@ -1258,8 +1258,10 @@ void CNXDNControl::writeJSON(nlohmann::json& json, const char* source, const cha json["source"] = source; json["action"] = action; json["source_id"] = int(srcId); - json["source_info"] = srcInfo; json["destination_id"] = int(dstId); json["destination_type"] = grp ? "group" : "individual"; + + if (!srcInfo.empty()) + json["source_info"] = srcInfo; } diff --git a/NXDNControl.h b/NXDNControl.h index 0c4b5be..7fdbb66 100644 --- a/NXDNControl.h +++ b/NXDNControl.h @@ -34,6 +34,8 @@ #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); diff --git a/P25Control.cpp b/P25Control.cpp index 43383d2..0c45e60 100644 --- a/P25Control.cpp +++ b/P25Control.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2016-2019,2021 by Jonathan Naylor G4KLX +* Copyright (C) 2016-2019,2021,2023 by Jonathan Naylor G4KLX * Copyright (C) 2018 by Bryan Biedenkapp * * This program is free software; you can redistribute it and/or modify @@ -124,12 +124,16 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) if (data[0U] == TAG_LOST && m_rfState == RS_RF_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) + 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 + writeJSONRF("lost", srcId, source, grp, dstId, float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + } 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", srcId, source, grp, dstId, float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits)); + } if (m_netState == RS_NET_IDLE) m_display->clearP25(); @@ -254,6 +258,7 @@ 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); + writeJSONRF("start", srcId, source, grp, dstId); m_display->writeP25(source.c_str(), grp, dstId, "R"); m_rfState = RS_RF_AUDIO; @@ -455,17 +460,21 @@ 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 = RS_RF_LISTENING; m_rfTimeout.stop(); m_rfData.reset(); m_lastDUID = duid; - if (m_rssi != 0U) + 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 + writeJSONRF("end", srcId, source, grp, dstId, float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + } 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", srcId, source, grp, dstId, float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits)); + } m_display->clearP25(); @@ -725,8 +734,14 @@ void CP25Control::clock(unsigned int ms) m_networkWatchdog.clock(ms); if (m_networkWatchdog.hasExpired()) { + unsigned int dstId = m_netData.getDstId(); + unsigned int srcId = m_netData.getSrcId(); + std::string source = m_lookup->find(srcId); + LogMessage("P25, network watchdog has expired, %.1f seconds, %u%% packet loss", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames); + writeJSONNet("lost", srcId, source, m_netData.getLCF() == P25_LCF_GROUP, dstId, float(m_netFrames) / 50.0F, float(m_netLost * 100U) / float(m_netFrames)); m_display->clearP25(); + m_networkWatchdog.stop(); m_netState = RS_NET_IDLE; m_netData.reset(); @@ -971,7 +986,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); - + writeJSONNet("start", srcId, source, lcf == P25_LCF_GROUP, dstId); m_display->writeP25(source.c_str(), lcf == P25_LCF_GROUP, dstId, "N"); m_netState = RS_NET_AUDIO; @@ -1121,9 +1136,12 @@ 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", srcId, source, m_netData.getLCF() == P25_LCF_GROUP, dstId, float(m_netFrames) / 50.0F, float(m_netLost * 100U) / float(m_netFrames)); m_display->clearP25(); m_netTimeout.stop(); @@ -1198,3 +1216,91 @@ void CP25Control::enable(bool enabled) m_enabled = enabled; } + +void CP25Control::writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(action != NULL); + + nlohmann::json json; + + writeJSON(json, "rf", action, srcId, srcInfo, grp, dstId); + + WriteJSON("P25", json); +} + +void CP25Control::writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, float duration, float ber) +{ + assert(action != NULL); + + nlohmann::json json; + + writeJSON(json, "rf", action, srcId, srcInfo, grp, dstId); + + json["duration"] = duration; + json["ber"] = ber; + + WriteJSON("P25", json); +} + +void CP25Control::writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI) +{ + assert(action != NULL); + + nlohmann::json json; + + writeJSON(json, "rf", action, srcId, srcInfo, grp, dstId); + + json["duration"] = duration; + json["ber"] = ber; + + nlohmann::json rssi; + rssi["min"] = -int(minRSSI); + rssi["max"] = -int(maxRSSI); + rssi["ave"] = -int(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 != NULL); + + nlohmann::json json; + + writeJSON(json, "network", action, srcId, srcInfo, grp, dstId); + + WriteJSON("P25", json); +} + +void CP25Control::writeJSONNet(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, float duration, float loss) +{ + assert(action != NULL); + + nlohmann::json json; + + writeJSON(json, "network", action, srcId, srcInfo, grp, dstId); + + json["duration"] = duration; + json["loss"] = loss; + + WriteJSON("P25", json); +} + +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 != NULL); + assert(action != NULL); + + 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; +} + diff --git a/P25Control.h b/P25Control.h index 35e12bc..4e7dfb8 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 * * This program is free software; you can redistribute it and/or modify @@ -35,6 +35,8 @@ #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); @@ -118,6 +120,15 @@ private: bool openFile(); bool writeFile(const unsigned char* data, unsigned char length); void closeFile(); + + 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, float duration, float ber); + void writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI); + + 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, float duration, float loss); + + void writeJSON(nlohmann::json& json, const char* source, const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); }; #endif diff --git a/POCSAGControl.h b/POCSAGControl.h index aa9077b..58ff928 100644 --- a/POCSAGControl.h +++ b/POCSAGControl.h @@ -30,6 +30,8 @@ #include #include +#include + struct POCSAGData { unsigned int m_ric; std::string m_text;