From 72f249b12ef5459d6608cb4dc0bd3f3c26cb298d Mon Sep 17 00:00:00 2001 From: Jonathan Naylor Date: Wed, 18 Jan 2023 17:58:53 +0000 Subject: [PATCH] Add DMR to JSON/MQTT. --- DMRSlot.cpp | 274 ++++++++++++++++++++++++++++++++++++++++++++++------ DMRSlot.h | 15 ++- schema.json | 5 +- 3 files changed, 265 insertions(+), 29 deletions(-) diff --git a/DMRSlot.cpp b/DMRSlot.cpp index 825e80b..7f472ea 100644 --- a/DMRSlot.cpp +++ b/DMRSlot.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2021 Jonathan Naylor, G4KLX + * Copyright (C) 2015-2021,2023 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 @@ -142,14 +142,20 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) return false; if (data[0U] == TAG_LOST && m_rfState == RS_RF_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) + 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 + writeJSONRF("lost", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + } 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", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits)); + } + if (m_rfTimeout) { writeEndRF(); return false; @@ -160,11 +166,14 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) } if (data[0U] == TAG_LOST && m_rfState == RS_RF_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", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId); writeEndRF(); return false; } @@ -219,10 +228,13 @@ 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 (!CDMRAccessControl::validateSrcId(srcId)) { LogMessage("DMR Slot %u, RF user %u rejected", m_slotNo, srcId); + writeJSONRF("rejected", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId); delete lc; m_rfState = RS_RF_LISTENING; return false; @@ -230,6 +242,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", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId); delete lc; m_rfState = RS_RF_LISTENING; return false; @@ -288,9 +301,6 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) m_rfState = RS_RF_AUDIO; - std::string src = m_lookup->find(srcId); - std::string dst = m_lookup->find(dstId); - if (m_netState == RS_NET_IDLE) { setShortLC(m_slotNo, dstId, flco, ACTIVITY_VOICE); m_display->writeDMR(m_slotNo, src, flco == FLCO_GROUP, dst, "R"); @@ -298,6 +308,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) } 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", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId); return true; } else if (dataType == DT_VOICE_PI_HEADER) { @@ -351,14 +362,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) + 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 + writeJSONRF("end", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + } 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)); + writeJSONRF("end", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits)); + } m_display->writeDMRTA(m_slotNo, NULL, " "); @@ -381,15 +397,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", m_slotNo, srcId, src, gi, dstId); m_rfState = RS_RF_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", m_slotNo, srcId, src, gi, dstId); m_rfState = RS_RF_LISTENING; return false; } @@ -417,9 +437,6 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) m_rfState = RS_RF_DATA; - std::string src = m_lookup->find(srcId); - std::string dst = m_lookup->find(dstId); - if (m_netState == RS_NET_IDLE) { setShortLC(m_slotNo, dstId, gi ? FLCO_GROUP : FLCO_USER_USER, ACTIVITY_DATA); m_display->writeDMR(m_slotNo, src, gi, dst, "R"); @@ -427,9 +444,11 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) } 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", m_slotNo, 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", m_slotNo, srcId, src, gi, dstId); writeEndRF(); } @@ -453,16 +472,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", m_slotNo, srcId, src, gi, dstId); m_rfState = RS_RF_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", m_slotNo, srcId, src, gi, dstId); m_rfState = RS_RF_LISTENING; return false; } @@ -485,33 +508,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", m_slotNo, 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", m_slotNo, 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", m_slotNo, 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", m_slotNo, 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", m_slotNo, 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", m_slotNo, 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", m_slotNo, 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", m_slotNo, srcId, src, gi, dstId); break; default: LogWarning("DMR Slot %u, unhandled RF CSBK type - 0x%02X", m_slotNo, csbko); @@ -530,6 +558,11 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (m_rfState != RS_RF_DATA || m_rfFrames == 0U) return false; + unsigned int srcId = m_rfLC->getSrcId(); + unsigned int dstId = m_rfLC->getDstId(); + std::string src = m_lookup->find(srcId); + FLCO flco = m_rfLC->getFLCO(); + // Regenerate the rate 1/2 payload if (dataType == DT_RATE_12_DATA) { CBPTC19696 bptc; @@ -566,6 +599,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", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId); writeEndRF(); } @@ -799,10 +833,13 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) if (lc != NULL) { 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 (!CDMRAccessControl::validateSrcId(srcId)) { LogMessage("DMR Slot %u, RF user %u rejected", m_slotNo, srcId); + writeJSONRF("rejected", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId); delete lc; m_rfState = RS_RF_LISTENING; return false; @@ -810,6 +847,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", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId); delete lc; m_rfState = RS_RF_LISTENING; return false; @@ -902,9 +940,6 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) m_rfState = RS_RF_AUDIO; - std::string src = m_lookup->find(srcId); - std::string dst = m_lookup->find(dstId); - if (m_netState == RS_NET_IDLE) { setShortLC(m_slotNo, dstId, flco, ACTIVITY_VOICE); m_display->writeDMR(m_slotNo, src, flco == FLCO_GROUP, dst, "R"); @@ -913,6 +948,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) } 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", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId); return true; } @@ -1147,6 +1183,7 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) #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", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId); } else if (dataType == DT_VOICE_PI_HEADER) { if (m_netState != RS_NET_AUDIO) { CDMRLC* lc = new CDMRLC(dmrData.getFLCO(), dmrData.getSrcId(), dmrData.getDstId()); @@ -1217,6 +1254,7 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) 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", m_slotNo, srcId, src, m_netLC->getFLCO() == FLCO_GROUP, dstId); } // Regenerate the Slot Type @@ -1276,13 +1314,16 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) 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)); + writeJSONNet("end", m_slotNo, srcId, src, flco == FLCO_GROUP, dstId, float(m_netFrames) / 16.667F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); m_display->writeDMRTA(m_slotNo, NULL, " "); writeEndNet(); } else if (dataType == DT_DATA_HEADER) { @@ -1333,9 +1374,11 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) 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", m_slotNo, 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", m_slotNo, srcId, src, gi, dstId); writeEndNet(); } } else if (dataType == DT_VOICE_SYNC) { @@ -1418,6 +1461,7 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) 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", m_slotNo, srcId, src, m_netLC->getFLCO() == FLCO_GROUP, dstId); } if (m_netState == RS_NET_AUDIO) { @@ -1680,24 +1724,31 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) 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", m_slotNo, 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", m_slotNo, 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", m_slotNo, 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", m_slotNo, 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", m_slotNo, 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", m_slotNo, 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", m_slotNo, srcId, src, gi, dstId); break; default: LogWarning("DMR Slot %u, unhandled network CSBK type - 0x%02X", m_slotNo, csbko); @@ -1753,7 +1804,12 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) writeQueueNet(data); if (m_netFrames == 0U) { + unsigned int srcId = m_netLC->getSrcId(); + unsigned int dstId = m_netLC->getDstId(); + std::string src = m_lookup->find(srcId); + LogMessage("DMR Slot %u, ended network data transmission", m_slotNo); + writeJSONNet("end", m_slotNo, srcId, src, m_netLC->getFLCO() == FLCO_GROUP, dstId); writeEndNet(); } } else { @@ -1817,7 +1873,12 @@ void CDMRSlot::clock() m_rfTimeoutTimer.clock(ms); if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) { if (!m_rfTimeout) { + unsigned int srcId = m_rfLC->getSrcId(); + unsigned int dstId = m_rfLC->getDstId(); + std::string src = m_lookup->find(srcId); + LogMessage("DMR Slot %u, RF user has timed out", m_slotNo); + writeJSONRF("timeout", m_slotNo, srcId, src, m_rfLC->getFLCO() == FLCO_GROUP, dstId); m_rfTimeout = true; } } @@ -1825,7 +1886,12 @@ void CDMRSlot::clock() m_netTimeoutTimer.clock(ms); if (m_netTimeoutTimer.isRunning() && m_netTimeoutTimer.hasExpired()) { if (!m_netTimeout) { + unsigned int srcId = m_netLC->getSrcId(); + unsigned int dstId = m_netLC->getDstId(); + std::string src = m_lookup->find(srcId); + LogMessage("DMR Slot %u, network user has timed out", m_slotNo); + writeJSONNet("timeout", m_slotNo, srcId, src, m_netLC->getFLCO() == FLCO_GROUP, dstId); m_netTimeout = true; } } @@ -1835,15 +1901,25 @@ void CDMRSlot::clock() if (m_networkWatchdog.hasExpired()) { if (m_netState == RS_NET_AUDIO) { + unsigned int srcId = m_netLC->getSrcId(); + unsigned int dstId = m_netLC->getDstId(); + std::string src = m_lookup->find(srcId); + // 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", m_slotNo, srcId, src, m_netLC->getFLCO() == FLCO_GROUP, dstId, float(m_netFrames) / 16.667F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); writeEndNet(true); #if defined(DUMP_DMR) closeFile(); #endif } else { + unsigned int srcId = m_netLC->getSrcId(); + unsigned int dstId = m_netLC->getDstId(); + std::string src = m_lookup->find(srcId); + LogMessage("DMR Slot %u, network watchdog has expired", m_slotNo); + writeJSONNet("lost", m_slotNo, srcId, src, m_netLC->getFLCO() == FLCO_GROUP, dstId); writeEndNet(); #if defined(DUMP_DMR) closeFile(); @@ -2236,3 +2312,147 @@ void CDMRSlot::enable(bool enabled) m_enabled = enabled; } + +void CDMRSlot::writeJSONRF(const char* action, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(action != NULL); + + nlohmann::json json; + + writeJSON(json, "rf", action, slotNo, srcId, srcInfo, grp, dstId); + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONRF(const char* action, const char* desc, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(action != NULL); + assert(desc != NULL); + + nlohmann::json json; + + writeJSON(json, "rf", action, slotNo, srcId, srcInfo, grp, dstId); + + json["csbk_desc"] = desc; + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONRF(const char* action, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, unsigned int frames) +{ + assert(action != NULL); + + nlohmann::json json; + + writeJSON(json, "rf", action, slotNo, srcId, srcInfo, grp, dstId); + + json["frames"] = int(frames); + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONRF(const char* action, unsigned int slotNo, 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, slotNo, srcId, srcInfo, grp, dstId); + + json["duration"] = duration; + json["ber"] = ber; + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONRF(const char* action, unsigned int slotNo, 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, slotNo, 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("DMR", json); +} + +void CDMRSlot::writeJSONNet(const char* action, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(action != NULL); + + nlohmann::json json; + + writeJSON(json, "network", action, slotNo, srcId, srcInfo, grp, dstId); + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONNet(const char* action, const char* desc, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId) +{ + assert(action != NULL); + assert(desc != NULL); + + nlohmann::json json; + + writeJSON(json, "network", action, slotNo, srcId, srcInfo, grp, dstId); + + json["csbk_desc"] = desc; + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONNet(const char* action, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, unsigned int frames) +{ + assert(action != NULL); + + nlohmann::json json; + + writeJSON(json, "network", action, slotNo, srcId, srcInfo, grp, dstId); + + json["frames"] = int(frames); + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSONNet(const char* action, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, float duration, float loss, float ber) +{ + assert(action != NULL); + + nlohmann::json json; + + writeJSON(json, "network", action, slotNo, srcId, srcInfo, grp, dstId); + + json["duration"] = duration; + json["loss"] = loss; + json["ber"] = ber; + + WriteJSON("DMR", json); +} + +void CDMRSlot::writeJSON(nlohmann::json& json, const char* source, const char* action, unsigned int slotNo, 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["slot"] = int(slotNo); + 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/DMRSlot.h b/DMRSlot.h index 62bcc91..6be9476 100644 --- a/DMRSlot.h +++ b/DMRSlot.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2021 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 @@ -157,6 +157,19 @@ private: void insertSilence(unsigned int count); static void setShortLC(unsigned int slotNo, unsigned int id, FLCO flco = FLCO_GROUP, ACTIVITY_TYPE type = ACTIVITY_NONE); + + void writeJSONRF(const char* action, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); + void writeJSONRF(const char* action, unsigned int slotNo, 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 slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); + void writeJSONRF(const char* action, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, float duration, float ber); + void writeJSONRF(const char* action, unsigned int slotNo, 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 slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); + void writeJSONNet(const char* action, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, unsigned int frames); + void writeJSONNet(const char* action, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, float duration, float loss, float ber); + void writeJSONNet(const char* action, const char* desc, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); + + void writeJSON(nlohmann::json& json, const char* source, const char* action, unsigned int slotNo, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId); }; #endif diff --git a/schema.json b/schema.json index a68636f..0d5bbcd 100644 --- a/schema.json +++ b/schema.json @@ -22,6 +22,7 @@ "pocsag_source": {"type": "string", "enum": ["local", "network"]}, "pocsag_functional": {"type": "string", "enum": ["numeric", "alphanumeric", "alert_1", "alert_2"]}, "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}, @@ -65,7 +66,9 @@ "destination_type": {"$ref": "#/$defs/destination_type"}, "slot": {"$ref": "#/$defs/dmr_slot"}, "source": {"$ref": "#/$defs/source"}, - "action": {"$ref": "#/$defs/action"}, + "action": {"$ref": "#/$defs/dmr_action"}, + "csbk_desc": {"type": "string"}, + "frames": {"type": "integer"}, "duration": {"$ref": "#/$defs/duration"}, "loss": {"$ref": "#/$defs/loss"}, "ber": {"$ref": "#/$defs/ber"},