diff --git a/DMRSlot.cpp b/DMRSlot.cpp index df47c0a..304c30b 100644 --- a/DMRSlot.cpp +++ b/DMRSlot.cpp @@ -69,6 +69,9 @@ const unsigned int NO_HEADERS_SIMPLEX = 8U; const unsigned int NO_HEADERS_DUPLEX = 3U; const unsigned int NO_PREAMBLE_CSBK = 15U; +const unsigned int RSSI_COUNT = 4U; // 4 * 360ms = 1440ms +const unsigned int BER_COUNT = 24U * 141U; // 24 * 60ms = 1440ms + // #define DUMP_DMR CDMRSlot::CDMRSlot(unsigned int slotNo, unsigned int timeout) : @@ -81,12 +84,13 @@ m_rfEmbeddedData(NULL), m_rfEmbeddedReadN(0U), m_rfEmbeddedWriteN(1U), m_rfTalkerId(TALKER_ID_NONE), -m_rfTalkerAlias(), +m_rfTalkerAlias(slotNo), m_netEmbeddedLC(), m_netEmbeddedData(NULL), m_netEmbeddedReadN(0U), m_netEmbeddedWriteN(1U), m_netTalkerId(TALKER_ID_NONE), +m_netTalkerAlias(slotNo), m_rfLC(NULL), m_netLC(NULL), m_rfSeqNo(0U), @@ -115,7 +119,11 @@ m_rssi(0U), m_maxRSSI(0U), m_minRSSI(0U), m_aveRSSI(0U), +m_rssiCountTotal(0U), +m_rssiAccum(0U), m_rssiCount(0U), +m_bitErrsAccum(0U), +m_bitsCount(0U), m_enabled(true), m_fp(NULL) { @@ -149,11 +157,11 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) 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); - writeJSONRF("lost", 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); + 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_rssiCountTotal); + writeJSONRF("lost", float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / 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", srcId, src, flco == FLCO_GROUP, dstId, 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) { @@ -173,7 +181,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) 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", srcId, src, flco == FLCO_GROUP, dstId); + writeJSONRF("lost"); writeEndRF(); return false; } @@ -203,6 +211,9 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) m_maxRSSI = m_rssi; m_aveRSSI += m_rssi; + m_rssiCountTotal++; + + m_rssiAccum += m_rssi; m_rssiCount++; } @@ -284,11 +295,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); @@ -305,6 +322,8 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) setShortLC(m_slotNo, dstId, flco, ACTIVITY_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()); @@ -369,11 +388,11 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) 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); - writeJSONRF("end", 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); + 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_rssiCountTotal); + writeJSONRF("end", float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / 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)); - writeJSONRF("end", srcId, src, flco == FLCO_GROUP, dstId, float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeJSONRF("end", float(m_rfFrames) / 16.667F, float(m_rfErrs * 100U) / float(m_rfBits)); } m_display->writeDMRTA(m_slotNo, NULL, " "); @@ -441,6 +460,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) setShortLC(m_slotNo, dstId, gi ? FLCO_GROUP : FLCO_USER_USER, ACTIVITY_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); @@ -448,7 +468,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) 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", srcId, src, gi, dstId); + writeJSONRF("end"); writeEndRF(); } @@ -551,6 +571,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) setShortLC(m_slotNo, dstId, gi ? FLCO_GROUP : FLCO_USER_USER, ACTIVITY_DATA); m_display->writeDMR(m_slotNo, src, gi, dst, "R"); m_display->writeDMRRSSI(m_slotNo, m_rssi); + writeJSONRSSI(); } return true; @@ -558,11 +579,6 @@ 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; @@ -599,7 +615,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", srcId, src, flco == FLCO_GROUP, dstId); + writeJSONRF("end"); writeEndRF(); } @@ -616,11 +632,15 @@ 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_rfErrs += errors; + m_bitErrsAccum += errors; + m_display->writeDMRBER(m_slotNo, float(errors) / 1.41F); - m_rfErrs += errors; + writeJSONBER(); } - m_rfBits += 141U; + m_bitsCount += 141U; + m_rfBits += 141U; m_rfFrames++; m_rfEmbeddedReadN = (m_rfEmbeddedReadN + 1U) % 2U; @@ -629,6 +649,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; @@ -664,11 +685,15 @@ 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_display->writeDMRBER(m_slotNo, float(errors) / 1.41F); - m_rfErrs += errors; + writeJSONBER(); } - m_rfBits += 141U; + m_bitsCount += 141U; + m_rfBits += 141U; m_rfFrames++; // Get the LCSS from the EMB @@ -695,7 +720,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) case FLCO_GPS_INFO: if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded GPS Info", m_slotNo); - CUtils::dump(2U, text, data, 9U); + CUtils::dump(1U, text, data, 9U); logGPSPosition(data); } if (m_network != NULL) @@ -709,12 +734,16 @@ 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(0, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, (unsigned char*)m_rfTalkerAlias.get(), "R"); + + bool complete = m_rfTalkerAlias.add(0U, data + 2U, 7U); + if (complete) { + writeJSONText(m_rfTalkerAlias.get()); + m_display->writeDMRTA(m_slotNo, m_rfTalkerAlias.get(), "R"); + } if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Header", m_slotNo); - CUtils::dump(2U, text, data, 9U); + CUtils::dump(1U, text, data, 9U); } m_rfTalkerId |= TALKER_ID_HEADER; @@ -728,12 +757,16 @@ 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(1, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, (unsigned char*)m_rfTalkerAlias.get(), "R"); + + bool complete = m_rfTalkerAlias.add(1U, data + 2U, 7U); + if (complete) { + writeJSONText(m_rfTalkerAlias.get()); + m_display->writeDMRTA(m_slotNo, m_rfTalkerAlias.get(), "R"); + } if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 1", m_slotNo); - CUtils::dump(2U, text, data, 9U); + CUtils::dump(1U, text, data, 9U); } m_rfTalkerId |= TALKER_ID_BLOCK1; @@ -747,12 +780,16 @@ 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(2, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, (unsigned char*)m_rfTalkerAlias.get(), "R"); + + bool complete = m_rfTalkerAlias.add(2U, data + 2U, 7U); + if (complete) { + writeJSONText(m_rfTalkerAlias.get()); + m_display->writeDMRTA(m_slotNo, m_rfTalkerAlias.get(), "R"); + } if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 2", m_slotNo); - CUtils::dump(2U, text, data, 9U); + CUtils::dump(1U, text, data, 9U); } m_rfTalkerId |= TALKER_ID_BLOCK2; @@ -766,12 +803,16 @@ 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(3, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, (unsigned char*)m_rfTalkerAlias.get(), "R"); + + bool complete = m_rfTalkerAlias.add(3U, data + 2U, 7U); + if (complete) { + writeJSONText(m_rfTalkerAlias.get()); + m_display->writeDMRTA(m_slotNo, m_rfTalkerAlias.get(), "R"); + } if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 3", m_slotNo); - CUtils::dump(2U, text, data, 9U); + CUtils::dump(1U, text, data, 9U); } m_rfTalkerId |= TALKER_ID_BLOCK3; @@ -893,11 +934,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); @@ -924,10 +971,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; @@ -945,6 +994,8 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len) 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()); @@ -1012,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; @@ -1323,7 +1377,7 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) // 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", srcId, src, flco == FLCO_GROUP, dstId, float(m_netFrames) / 16.667F, (m_netLost * 100U) / m_netFrames, float(m_netErrs * 100U) / float(m_netBits)); + writeJSONNet("end", 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) { @@ -1378,7 +1432,7 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) 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", srcId, src, gi, dstId); + writeJSONNet("end"); writeEndNet(); } } else if (dataType == DT_VOICE_SYNC) { @@ -1538,20 +1592,24 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) case FLCO_GPS_INFO: if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded GPS Info", m_slotNo); - CUtils::dump(2U, text, data, 9U); + CUtils::dump(1U, text, data, 9U); logGPSPosition(data); } break; case FLCO_TALKER_ALIAS_HEADER: if (!(m_netTalkerId & TALKER_ID_HEADER)) { if (!m_netTalkerId) - m_rfTalkerAlias.reset(); - m_rfTalkerAlias.add(0, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, (unsigned char*)m_rfTalkerAlias.get(), "N"); + m_netTalkerAlias.reset(); + + bool complete = m_netTalkerAlias.add(0U, data + 2U, 7U); + if (complete) { + writeJSONText(m_netTalkerAlias.get()); + m_display->writeDMRTA(m_slotNo, m_netTalkerAlias.get(), "N"); + } if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Header", m_slotNo); - CUtils::dump(2U, text, data, 9U); + CUtils::dump(1U, text, data, 9U); } m_netTalkerId |= TALKER_ID_HEADER; @@ -1560,13 +1618,17 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) case FLCO_TALKER_ALIAS_BLOCK1: if (!(m_netTalkerId & TALKER_ID_BLOCK1)) { if (!m_netTalkerId) - m_rfTalkerAlias.reset(); - m_rfTalkerAlias.add(1, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, (unsigned char*)m_rfTalkerAlias.get(), "N"); + m_netTalkerAlias.reset(); + + bool complete = m_netTalkerAlias.add(1U, data + 2U, 7U); + if (complete) { + writeJSONText(m_netTalkerAlias.get()); + m_display->writeDMRTA(m_slotNo, m_netTalkerAlias.get(), "N"); + } if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 1", m_slotNo); - CUtils::dump(2U, text, data, 9U); + CUtils::dump(1U, text, data, 9U); } m_netTalkerId |= TALKER_ID_BLOCK1; @@ -1575,13 +1637,17 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) case FLCO_TALKER_ALIAS_BLOCK2: if (!(m_netTalkerId & TALKER_ID_BLOCK2)) { if (!m_netTalkerId) - m_rfTalkerAlias.reset(); - m_rfTalkerAlias.add(2, data + 2U, 7U); - m_display->writeDMRTA(m_slotNo, (unsigned char*)m_rfTalkerAlias.get(), "N"); + m_netTalkerAlias.reset(); + + bool complete = m_netTalkerAlias.add(2U, data + 2U, 7U); + if (complete) { + writeJSONText(m_netTalkerAlias.get()); + m_display->writeDMRTA(m_slotNo, m_netTalkerAlias.get(), "N"); + } if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 2", m_slotNo); - CUtils::dump(2U, text, data, 9U); + CUtils::dump(1U, text, data, 9U); } m_netTalkerId |= TALKER_ID_BLOCK2; @@ -1590,13 +1656,17 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData) case FLCO_TALKER_ALIAS_BLOCK3: if (!(m_netTalkerId & TALKER_ID_BLOCK3)) { if (!m_netTalkerId) - m_rfTalkerAlias.reset(); - m_rfTalkerAlias.add(3, data+2U, 7U); - m_display->writeDMRTA(m_slotNo, (unsigned char*)m_rfTalkerAlias.get(), "N"); + m_netTalkerAlias.reset(); + + bool complete = m_netTalkerAlias.add(3U, data + 2U, 7U); + if (complete) { + writeJSONText(m_netTalkerAlias.get()); + m_display->writeDMRTA(m_slotNo, m_netTalkerAlias.get(), "N"); + } if (m_dumpTAData) { ::sprintf(text, "DMR Slot %u, Embedded Talker Alias Block 3", m_slotNo); - CUtils::dump(2U, text, data, 9U); + CUtils::dump(1U, text, data, 9U); } m_netTalkerId |= TALKER_ID_BLOCK3; @@ -1804,12 +1874,8 @@ 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", srcId, src, m_netLC->getFLCO() == FLCO_GROUP, dstId); + writeJSONNet("end"); writeEndNet(); } } else { @@ -1873,12 +1939,8 @@ 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", srcId, src, m_rfLC->getFLCO() == FLCO_GROUP, dstId); + writeJSONRF("timeout"); m_rfTimeout = true; } } @@ -1886,12 +1948,8 @@ 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", srcId, src, m_netLC->getFLCO() == FLCO_GROUP, dstId); + writeJSONNet("timeout"); m_netTimeout = true; } } @@ -1901,25 +1959,17 @@ 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", 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)); + writeJSONNet("lost", 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", srcId, src, m_netLC->getFLCO() == FLCO_GROUP, dstId); + writeJSONNet("lost"); writeEndNet(); #if defined(DUMP_DMR) closeFile(); @@ -2282,6 +2332,9 @@ void CDMRSlot::enable(bool enabled) m_rfErrs = 0U; m_rfBits = 1U; + m_bitErrsAccum = 0U; + m_bitsCount = 1U; + m_rfSeqNo = 0U; m_rfN = 0U; @@ -2313,6 +2366,68 @@ void CDMRSlot::enable(bool enabled) m_enabled = enabled; } +void CDMRSlot::writeJSONRSSI() +{ + if (m_rssiCount >= RSSI_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "DMR"; + json["slot"] = int(m_slotNo); + + json["value"] = -int(m_rssiAccum / m_rssiCount); + + WriteJSON("RSSI", json); + + m_rssiAccum = 0U; + 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 != NULL); + + 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 != NULL); + + 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 != NULL); @@ -2351,13 +2466,13 @@ void CDMRSlot::writeJSONRF(const char* action, unsigned int srcId, const std::st WriteJSON("DMR", json); } -void CDMRSlot::writeJSONRF(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, float duration, float ber) +void CDMRSlot::writeJSONRF(const char* action, float duration, float ber) { assert(action != NULL); nlohmann::json json; - writeJSON(json, "rf", action, srcId, srcInfo, grp, dstId); + writeJSON(json, action); json["duration"] = duration; json["ber"] = ber; @@ -2365,13 +2480,13 @@ void CDMRSlot::writeJSONRF(const char* action, unsigned int srcId, const std::st WriteJSON("DMR", json); } -void CDMRSlot::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 CDMRSlot::writeJSONRF(const char* action, 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); + writeJSON(json, action); json["duration"] = duration; json["ber"] = ber; @@ -2386,6 +2501,17 @@ void CDMRSlot::writeJSONRF(const char* action, unsigned int srcId, const std::st WriteJSON("DMR", json); } +void CDMRSlot::writeJSONNet(const char* action) +{ + assert(action != NULL); + + 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 != NULL); @@ -2424,13 +2550,13 @@ void CDMRSlot::writeJSONNet(const char* action, unsigned int srcId, const std::s WriteJSON("DMR", json); } -void CDMRSlot::writeJSONNet(const char* action, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, float duration, float loss, float ber) +void CDMRSlot::writeJSONNet(const char* action, float duration, float loss, float ber) { assert(action != NULL); nlohmann::json json; - writeJSON(json, "network", action, srcId, srcInfo, grp, dstId); + writeJSON(json, action); json["duration"] = duration; json["loss"] = loss; @@ -2439,6 +2565,15 @@ void CDMRSlot::writeJSONNet(const char* action, unsigned int srcId, const std::s WriteJSON("DMR", json); } +void CDMRSlot::writeJSON(nlohmann::json& json, const char* action) +{ + assert(action != NULL); + + 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 != NULL); diff --git a/DMRSlot.h b/DMRSlot.h index dfc16c0..5d13f24 100644 --- a/DMRSlot.h +++ b/DMRSlot.h @@ -82,6 +82,7 @@ private: unsigned int m_netEmbeddedReadN; unsigned int m_netEmbeddedWriteN; unsigned char m_netTalkerId; + CDMRTA m_netTalkerAlias; CDMRLC* m_rfLC; CDMRLC* m_netLC; unsigned char m_rfSeqNo; @@ -110,7 +111,11 @@ private: unsigned char m_maxRSSI; unsigned char m_minRSSI; unsigned int m_aveRSSI; + unsigned int m_rssiCountTotal; + unsigned int m_rssiAccum; unsigned int m_rssiCount; + unsigned int m_bitErrsAccum; + unsigned int m_bitsCount; bool m_enabled; FILE* m_fp; @@ -134,7 +139,7 @@ private: static unsigned char* m_idle; - static FLCO m_flco1; + static FLCO m_flco1; static unsigned char m_id1; static ACTIVITY_TYPE m_activity1; static FLCO m_flco2; @@ -160,17 +165,24 @@ private: static void setShortLC(unsigned int slotNo, unsigned int id, FLCO flco = FLCO_GROUP, ACTIVITY_TYPE type = ACTIVITY_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, 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 writeJSONRF(const char* action, float duration, float ber); + void writeJSONRF(const char* action, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned 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, unsigned int srcId, const std::string& srcInfo, bool grp, unsigned int dstId, float duration, float loss, float ber); + 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); }; diff --git a/DMRTA.cpp b/DMRTA.cpp index 9e60673..055116e 100644 --- a/DMRTA.cpp +++ b/DMRTA.cpp @@ -1,5 +1,5 @@ /* -* Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX +* Copyright (C) 2015,2016,2017,2018,2023 Jonathan Naylor, G4KLX * Copyright (C) 2018 by Shawn Chain, BG5HHP * * This program is free software; you can redistribute it and/or modify @@ -18,8 +18,9 @@ #include #include -CDMRTA::CDMRTA() : -m_TA(), +CDMRTA::CDMRTA(unsigned int slotNo) : +m_slotNo(slotNo), +m_ta(), m_buf() { } @@ -31,13 +32,14 @@ CDMRTA::~CDMRTA() bool CDMRTA::add(unsigned int blockId, const unsigned char* data, unsigned int len) { assert(data != NULL); - if (blockId > 3) { + + if (blockId > 3U) { // invalid block id reset(); return false; } - unsigned int offset = blockId * 7; + unsigned int offset = blockId * 7U; if (offset + len >= sizeof(m_buf)) { // buffer overflow @@ -52,75 +54,83 @@ bool CDMRTA::add(unsigned int blockId, const unsigned char* data, unsigned int l const unsigned char* CDMRTA::get() { - return (unsigned char*)m_TA; + return (unsigned char*)m_ta; } void CDMRTA::reset() { - ::memset(m_TA, 0, sizeof(m_TA)); - ::memset(m_buf, 0, sizeof(m_buf)); + ::memset(m_ta, 0x00U, sizeof(m_ta)); + ::memset(m_buf, 0x00U, sizeof(m_buf)); } bool CDMRTA::decodeTA() { - unsigned char *b; - unsigned char c; - int j; - unsigned int i, t1, t2; + unsigned int taFormat = (m_buf[0] >> 6U) & 0x03U; + unsigned int taSize = (m_buf[0] >> 1U) & 0x1FU; + ::strcpy(m_ta, "(could not decode)"); - unsigned char* talkerAlias = m_buf; + switch (taFormat) { + case 0U: { // 7 bit + ::memset(m_ta, 0x00U, sizeof(m_ta)); - unsigned int TAformat = (talkerAlias[0] >> 6U) & 0x03U; - unsigned int TAsize = (talkerAlias[0] >> 1U) & 0x1FU; - ::strcpy(m_TA, "(could not decode)"); + unsigned char* b = m_buf; + unsigned int t1 = 0U; + unsigned int t2 = 0U; + unsigned char c = 0U; - switch (TAformat) { - case 0U: // 7 bit - ::memset(m_TA, 0, sizeof(m_TA)); - b = &talkerAlias[0]; - t1 = 0U; t2 = 0U; c = 0U; - for (i = 0U; (i < 32U) && (t2 < TAsize); i++) { - for (j = 7; j >= 0; j--) { - c = (c << 1U) | (b[i] >> j); - if (++t1 == 7U) { - if (i > 0U) - m_TA[t2++] = c & 0x7FU; + for (unsigned int i = 0U; (i < 32U) && (t2 < taSize); i++) { + for (int j = 7; j >= 0; j--) { + c = (c << 1U) | (b[i] >> j); - t1 = 0U; - c = 0U; - } - } - } - m_TA[TAsize] = 0; - break; + if (++t1 == 7U) { + if (i > 0U) + m_ta[t2++] = c & 0x7FU; - case 1U: // ISO 8 bit - case 2U: // UTF8 - ::memcpy(m_TA, talkerAlias + 1U, sizeof(m_TA)); - break; + t1 = 0U; + c = 0U; + } + } + } - case 3U: // UTF16 poor man's conversion - t2=0; - ::memset(&m_TA, 0, sizeof(m_TA)); - for (i = 0U; (i < 15U) && (t2 < TAsize); i++) { - if (talkerAlias[2U * i + 1U] == 0) - m_TA[t2++] = talkerAlias[2U * i + 2U]; - else - m_TA[t2++] = '?'; - } - m_TA[TAsize] = 0; - break; - } + m_ta[taSize] = 0; + } + break; - size_t TAlen = ::strlen(m_TA); - LogMessage("DMR Talker Alias (Data Format %u, Received %u/%u char): '%s'", TAformat, TAlen, TAsize, m_TA); + case 1U: // ISO 8 bit + case 2U: // UTF8 + ::memcpy(m_ta, m_buf + 1U, sizeof(m_ta)); + break; - if (TAlen > TAsize) { - if (TAlen < 29U) - strcat(m_TA, " ?"); - else - strcpy(m_TA + 28U, " ?"); - } + case 3U: { // UTF16 poor man's conversion + unsigned int t2 = 0U; + ::memset(&m_ta, 0x00U, sizeof(m_ta)); - return TAlen >= TAsize; + for (unsigned int i = 0U; (i < 15U) && (t2 < taSize); i++) { + if (m_buf[2U * i + 1U] == 0) + m_ta[t2++] = m_buf[2U * i + 2U]; + else + m_ta[t2++] = '?'; + } + + m_ta[taSize] = 0; + } + break; + } + + size_t taLen = ::strlen(m_ta); + + if (taLen == taSize) + LogMessage("DMR Slot %u, Talker Alias \"%s\"", m_slotNo, m_ta); + + LogDebug("DMR Slot %u, Talker Alias (Data Format %u, Received %u/%u char): '%s'", m_slotNo, taFormat, taLen, taSize, m_ta); + + if (taLen > taSize) { + if (taLen < 29U) + ::strcat(m_ta, " ?"); + else + ::strcpy(m_ta + 28U, " ?"); + } + + return taLen >= taSize; } + diff --git a/DMRTA.h b/DMRTA.h index d53f93f..ccf48d9 100644 --- a/DMRTA.h +++ b/DMRTA.h @@ -1,5 +1,5 @@ /* -* Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX +* Copyright (C) 2015,2016,2017,2018,2023 Jonathan Naylor, G4KLX * Copyright (C) 2018 by Shawn Chain, BG5HHP * * This program is free software; you can redistribute it and/or modify @@ -17,19 +17,22 @@ class CDMRTA { public: - CDMRTA(); - ~CDMRTA(); + CDMRTA(unsigned int slotNo); + ~CDMRTA(); - bool add(unsigned int blockId, const unsigned char* data, unsigned int len); - const unsigned char* get(); - void reset(); + bool add(unsigned int blockId, const unsigned char* data, unsigned int len); + + const unsigned char* get(); + + void reset(); protected: - bool decodeTA(); + bool decodeTA(); private: - char m_TA[32]; - unsigned char m_buf[32]; + unsigned int m_slotNo; + char m_ta[32]; + unsigned char m_buf[32]; }; #endif diff --git a/DStarControl.cpp b/DStarControl.cpp index 04b7a6f..acfc8f5 100644 --- a/DStarControl.cpp +++ b/DStarControl.cpp @@ -26,6 +26,9 @@ const unsigned int MAX_SYNC_BIT_ERRORS = 2U; const unsigned int FAST_DATA_BEEP_GRACE_FRAMES = 6U; +const unsigned int RSSI_COUNT = 3U * 21U; // 3 * 420ms = 1260ms +const unsigned int BER_COUNT = 63U * 48U; // 63 * 20ms = 1260ms + bool CallsignCompare(const std::string& arg, const unsigned char* my) { for (unsigned int i = 0U; i < (DSTAR_LONG_CALLSIGN_LENGTH - 1U); i++) { @@ -57,7 +60,8 @@ m_netHeader(), m_rfState(RS_RF_LISTENING), m_netState(RS_NET_IDLE), m_net(false), -m_slowData(), +m_rfSlowData(), +m_netSlowData(), m_rfN(0U), m_netN(0U), m_networkWatchdog(1000U, 0U, 1500U), @@ -82,7 +86,11 @@ m_rssi(0U), m_maxRSSI(0U), m_minRSSI(0U), m_aveRSSI(0U), +m_rssiCountTotal(0U), +m_rssiAccum(0U), m_rssiCount(0U), +m_bitErrsAccum(0U), +m_bitsCount(0U), m_enabled(true), m_fp(NULL), m_rfVoiceSyncData(NULL), @@ -226,11 +234,11 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) 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); - writeJSONRF("lost", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + 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_rssiCountTotal); + writeJSONRF("lost", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / 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", 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; @@ -275,7 +283,10 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) 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? @@ -298,7 +309,10 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) m_maxRSSI = m_rssi; m_aveRSSI += m_rssi; - m_rssiCount++; + m_rssiCountTotal++; + + m_rssiAccum += m_rssi; + m_rssiCountTotal++; } if (type == TAG_HEADER) { @@ -368,8 +382,13 @@ 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(); + if (m_duplex) { // Modify the header header.setRepeater(false); @@ -395,6 +414,7 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) if (m_netState == RS_NET_IDLE) { m_display->writeDStar((char*)my1, (char*)my2, (char*)your, "R", " "); m_display->writeDStarRSSI(m_rssi); + writeJSONRSSI(); } LogMessage("D-Star, received RF header from %8.8s/%4.4s to %8.8s", my1, my2, your); @@ -432,11 +452,11 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) 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); - writeJSONRF("end", my1, my2, your, float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + 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_rssiCountTotal); + writeJSONRF("end", float(m_rfFrames) / 50.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / 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", 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(); @@ -451,32 +471,44 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) } else if (m_rfState == RS_RF_LISTENING) { // The sync is regenerated by the modem so can do exact match if (::memcmp(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES) == 0) { - m_slowData.start(); + m_rfSlowData.start(); m_rfState = RS_RF_LATE_ENTRY; } return false; } else if (m_rfState == RS_RF_AUDIO) { // The sync is regenerated by the modem so can do exact match - if (::memcmp(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES) == 0) + if (::memcmp(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES) == 0) { + m_rfSlowData.start(); m_rfN = 0U; + } // Regenerate the sync and send the RSSI data to the display if (m_rfN == 0U) { CSync::addDStarSync(data + 1U); m_display->writeDStarRSSI(m_rssi); + writeJSONRSSI(); } unsigned int errors = 0U; if (!m_rfHeader.isDataPacket()) { errors = maybeFixupVoiceFrame(data, len, 1U, "RF", m_rfN, m_duplex, m_rfVoiceSyncData, m_rfVoiceSyncDataLen, m_rfNextFrameIsFastData, m_rfSkipDTMFBlankingFrames); + m_bitErrsAccum += errors; + m_rfErrs += errors; m_display->writeDStarBER(float(errors) / 0.48F); - m_rfErrs += errors; + writeJSONBER(); } - m_rfBits += 48U; + m_bitsCount += 48U; + m_rfBits += 48U; m_rfFrames++; + const unsigned char* text = m_rfSlowData.addText(data + 1U); + if (text != NULL) { + LogMessage("D-Star, slow data text = \"%s\"", text); + writeJSONText(text); + } + if (m_net) { if (m_rfN == 1U) writeNetworkDataRF(m_rfVoiceSyncData, 0U, false); @@ -495,11 +527,11 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) } else if (m_rfState == RS_RF_LATE_ENTRY) { // The sync is regenerated by the modem so can do exact match if (::memcmp(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES, DSTAR_SYNC_BYTES, DSTAR_DATA_FRAME_LENGTH_BYTES) == 0) { - m_slowData.reset(); + m_rfSlowData.reset(); return false; } - CDStarHeader* header = m_slowData.add(data + 1U); + CDStarHeader* header = m_rfSlowData.addHeader(data + 1U); if (header == NULL) return false; @@ -573,7 +605,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]; @@ -606,10 +638,12 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) unsigned int errors = 0U; if (!m_rfHeader.isDataPacket()) { errors = maybeFixupVoiceFrame(data, len, 1U, "RF", m_rfN, m_duplex, m_rfVoiceSyncData, m_rfVoiceSyncDataLen, m_rfNextFrameIsFastData, m_rfSkipDTMFBlankingFrames); - m_rfErrs += errors; + m_bitErrsAccum += errors; + m_rfErrs += errors; } - m_rfBits += 48U; + m_bitsCount += 48U; + m_rfBits += 48U; if (m_net) writeNetworkDataRF(data, errors, false); @@ -625,6 +659,8 @@ bool CDStarControl::writeModem(unsigned char *data, unsigned int len) 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); @@ -656,6 +692,8 @@ void CDStarControl::writeEndRF() { m_rfState = RS_RF_LISTENING; + m_rfTimeoutTimer.stop(); + if (m_netState == RS_NET_IDLE) { m_display->clearDStar(); @@ -663,8 +701,6 @@ void CDStarControl::writeEndRF() if (m_network != NULL) m_network->reset(); - } else { - m_rfTimeoutTimer.stop(); } } @@ -795,7 +831,7 @@ 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", my1, my2, your, float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames); - writeJSONNet("end", my1, my2, your, float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames); + writeJSONNet("end", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames); writeEndNet(); } else if (type == TAG_DATA) { @@ -819,8 +855,16 @@ void CDStarControl::writeNetwork() m_netN = n; // Regenerate the sync - if (n == 0U) + if (n == 0U) { CSync::addDStarSync(data + 2U); + m_netSlowData.start(); + } + + const unsigned char* text = m_netSlowData.addText(data + 2U); + if (text != NULL) { + LogMessage("D-Star, slow data text = \"%s\"", text); + writeJSONText(text); + } m_packetTimer.start(); m_netFrames++; @@ -877,7 +921,7 @@ void CDStarControl::clock() m_netFrames += 1U; LogMessage("D-Star, network watchdog has expired, %.1f seconds, %u%% packet loss", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames); - writeJSONNet("lost", my1, my2, your, float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames); + writeJSONNet("lost", float(m_netFrames) / 50.0F, (m_netLost * 100U) / m_netFrames); writeEndNet(); #if defined(DUMP_DSTAR) closeFile(); @@ -1209,14 +1253,14 @@ void CDStarControl::sendAck() if (m_ackMessage == DSTAR_ACK_RSSI && m_rssi != 0) { if (status == LS_LINKED_DEXTRA || status == LS_LINKED_DPLUS || status == LS_LINKED_DCS || status == LS_LINKED_CCS || status == LS_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%% -%udBm ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCountTotal); } else { - ::sprintf(text, "BER:%.1f%% -%udBm ", float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCount); + ::sprintf(text, "BER:%.1f%% -%udBm ", float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCountTotal); } } else if (m_ackMessage == DSTAR_ACK_SMETER && m_rssi != 0) { unsigned int signal, plus; char signalText[15U]; - CSMeter::getSignal(m_aveRSSI / m_rssiCount, signal, plus); + CSMeter::getSignal(m_aveRSSI / m_rssiCountTotal, signal, plus); if (plus != 0U) ::sprintf(signalText, "S%u+%02u", signal, plus); else @@ -1233,12 +1277,12 @@ void CDStarControl::sendAck() ::sprintf(text, "BER: %.1f%% ", float(m_rfErrs * 100U) / float(m_rfBits)); } - m_slowData.setText(text); + m_rfSlowData.setText(text); ::memcpy(data, DSTAR_NULL_FRAME_DATA_BYTES, DSTAR_FRAME_LENGTH_BYTES + 1U); for (unsigned int i = 0U; i < 19U; i++) { - m_slowData.get(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES); + m_rfSlowData.getSlowData(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES); writeQueueDataRF(data); } @@ -1274,14 +1318,14 @@ void CDStarControl::sendError() if (m_ackMessage == DSTAR_ACK_RSSI && m_rssi != 0) { if (status == LS_LINKED_DEXTRA || status == LS_LINKED_DPLUS || status == LS_LINKED_DCS || status == LS_LINKED_CCS || status == LS_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%% -%udBm ", reflector, float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCountTotal); } else { - ::sprintf(text, "BER:%.1f%% -%udBm ", float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCount); + ::sprintf(text, "BER:%.1f%% -%udBm ", float(m_rfErrs * 100U) / float(m_rfBits), m_aveRSSI / m_rssiCountTotal); } } else if (m_ackMessage == DSTAR_ACK_SMETER && m_rssi != 0) { unsigned int signal, plus; char signalText[15U]; - CSMeter::getSignal(m_aveRSSI / m_rssiCount, signal, plus); + CSMeter::getSignal(m_aveRSSI / m_rssiCountTotal, signal, plus); if (plus != 0U) ::sprintf(signalText, "S%u+%02u", signal, plus); else @@ -1298,12 +1342,12 @@ void CDStarControl::sendError() ::sprintf(text, "BER: %.1f%% ", float(m_rfErrs * 100U) / float(m_rfBits)); } - m_slowData.setText(text); + m_rfSlowData.setText(text); ::memcpy(data, DSTAR_NULL_FRAME_DATA_BYTES, DSTAR_FRAME_LENGTH_BYTES + 1U); for (unsigned int i = 0U; i < 19U; i++) { - m_slowData.get(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES); + m_rfSlowData.getSlowData(data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES); writeQueueDataRF(data); } @@ -1338,6 +1382,54 @@ void CDStarControl::enable(bool enabled) m_enabled = enabled; } +void CDStarControl::writeJSONRSSI() +{ + if (m_rssiCountTotal >= RSSI_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "D-Star"; + + json["value"] = -int(m_rssiAccum / m_rssiCountTotal); + + WriteJSON("RSSI", json); + + m_rssiAccum = 0U; + 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 != NULL); + + 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 != NULL); @@ -1347,35 +1439,36 @@ void CDStarControl::writeJSONRF(const char* action, const unsigned char* my1, co nlohmann::json json; - writeJSONRF(json, action, my1, my2, your); + 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, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, float duration, float ber) +void CDStarControl::writeJSONRF(const char* action, float duration, float ber) { assert(action != NULL); - assert(my1 != NULL); - assert(my2 != NULL); - assert(your != NULL); nlohmann::json json; - writeJSONRF(json, action, my1, my2, your, duration, ber); + writeJSONRF(json, action, duration, ber); WriteJSON("D-Star", json); } -void CDStarControl::writeJSONRF(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI) +void CDStarControl::writeJSONRF(const char* action, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI) { assert(action != NULL); - assert(my1 != NULL); - assert(my2 != NULL); - assert(your != NULL); nlohmann::json json; - writeJSONRF(json, action, my1, my2, your, duration, ber); + writeJSONRF(json, action, duration, ber); nlohmann::json rssi; rssi["min"] = -int(minRSSI); @@ -1396,7 +1489,14 @@ void CDStarControl::writeJSONNet(const char* action, const unsigned char* my1, c nlohmann::json json; - writeJSONNet(json, action, my1, my2, your); + 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 != NULL) json["reflector"] = convertBuffer(reflector, DSTAR_LONG_CALLSIGN_LENGTH); @@ -1404,67 +1504,31 @@ void CDStarControl::writeJSONNet(const char* action, const unsigned char* my1, c WriteJSON("D-Star", json); } -void CDStarControl::writeJSONNet(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, float duration, float loss) +void CDStarControl::writeJSONNet(const char* action, float duration, float loss) { assert(action != NULL); - assert(my1 != NULL); - assert(my2 != NULL); - assert(your != NULL); nlohmann::json json; - writeJSONNet(json, action, my1, my2, your); + 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, const unsigned char* my1, const unsigned char* my2, const unsigned char* your) +void CDStarControl::writeJSONRF(nlohmann::json& json, const char* action, float duration, float ber) { assert(action != NULL); - assert(my1 != NULL); - assert(my2 != NULL); - assert(your != NULL); 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; -} - -void CDStarControl::writeJSONRF(nlohmann::json& json, const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, float duration, float ber) -{ - assert(action != NULL); - assert(my1 != NULL); - assert(my2 != NULL); - assert(your != NULL); - - writeJSONRF(json, action, my1, my2, your); - json["duration"] = duration; json["ber"] = ber; -} -void CDStarControl::writeJSONNet(nlohmann::json& json, const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your) -{ - assert(action != NULL); - assert(my1 != NULL); - assert(my2 != NULL); - assert(your != NULL); - - 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; } diff --git a/DStarControl.h b/DStarControl.h index f7e6bec..1eef4f8 100644 --- a/DStarControl.h +++ b/DStarControl.h @@ -71,7 +71,8 @@ private: RPT_RF_STATE m_rfState; RPT_NET_STATE m_netState; bool m_net; - CDStarSlowData m_slowData; + CDStarSlowData m_rfSlowData; + CDStarSlowData m_netSlowData; unsigned char m_rfN; unsigned char m_netN; CTimer m_networkWatchdog; @@ -96,7 +97,11 @@ private: unsigned char m_maxRSSI; unsigned char m_minRSSI; unsigned int m_aveRSSI; + unsigned int m_rssiCountTotal; + unsigned int m_rssiAccum; unsigned int m_rssiCount; + unsigned int m_bitErrsAccum; + unsigned int m_bitsCount; bool m_enabled; FILE* m_fp; unsigned char* m_rfVoiceSyncData; @@ -135,15 +140,17 @@ private: void writeEndRF(); void writeEndNet(); - void writeJSONRF(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your); - void writeJSONRF(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, float duration, float ber); - void writeJSONRF(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI); - void writeJSONNet(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, const unsigned char* reflector = NULL); - void writeJSONNet(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, float duration, float loss); + void writeJSONRSSI(); + void writeJSONBER(); + void writeJSONText(const unsigned char* text); - void writeJSONRF(nlohmann::json& json, const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your); - void writeJSONRF(nlohmann::json& json, const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, float duration, float ber); - void writeJSONNet(nlohmann::json& json, const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your); + 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, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI); + void writeJSONNet(const char* action, const unsigned char* my1, const unsigned char* my2, const unsigned char* your, const unsigned char* reflector = NULL); + 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; diff --git a/DStarSlowData.cpp b/DStarSlowData.cpp index d33ca7e..5f11b70 100644 --- a/DStarSlowData.cpp +++ b/DStarSlowData.cpp @@ -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 @@ -18,6 +18,7 @@ #include "DStarSlowData.h" #include "DStarDefines.h" +#include "Utils.h" #include "CRC.h" #include "Log.h" @@ -31,6 +32,7 @@ m_ptr(0U), m_buffer(NULL), m_text(NULL), m_textPtr(0U), +m_textBits(0x00U), m_state(SDD_FIRST) { m_header = new unsigned char[50U]; // DSTAR_HEADER_LENGTH_BYTES @@ -45,7 +47,7 @@ CDStarSlowData::~CDStarSlowData() delete[] m_text; } -CDStarHeader* CDStarSlowData::add(const unsigned char* data) +CDStarHeader* CDStarSlowData::addHeader(const unsigned char* data) { assert(data != NULL); @@ -93,18 +95,92 @@ CDStarHeader* CDStarSlowData::add(const unsigned char* data) return new CDStarHeader(m_header); } +const unsigned char* CDStarSlowData::addText(const unsigned char* data) +{ + assert(data != NULL); + + switch (m_state) { + case SDD_FIRST: + m_buffer[0U] = data[9U] ^ DSTAR_SCRAMBLER_BYTES[0U]; + m_buffer[1U] = data[10U] ^ DSTAR_SCRAMBLER_BYTES[1U]; + m_buffer[2U] = data[11U] ^ DSTAR_SCRAMBLER_BYTES[2U]; + m_state = SDD_SECOND; + return NULL; + + case SDD_SECOND: + m_buffer[3U] = data[9U] ^ DSTAR_SCRAMBLER_BYTES[0U]; + m_buffer[4U] = data[10U] ^ DSTAR_SCRAMBLER_BYTES[1U]; + m_buffer[5U] = data[11U] ^ DSTAR_SCRAMBLER_BYTES[2U]; + m_state = SDD_FIRST; + break; + } + + switch (m_buffer[0U]) { + case DSTAR_SLOW_DATA_TYPE_TEXT | 0U: + CUtils::dump(1U, "D-Star slow data text fragment", m_buffer, 6U); + m_text[0U] = m_buffer[1U] & 0x7FU; + m_text[1U] = m_buffer[2U] & 0x7FU; + m_text[2U] = m_buffer[3U] & 0x7FU; + m_text[3U] = m_buffer[4U] & 0x7FU; + m_text[4U] = m_buffer[5U] & 0x7FU; + m_textBits |= 0x01U; + break; + case DSTAR_SLOW_DATA_TYPE_TEXT | 1U: + CUtils::dump(1U, "D-Star slow data text fragment", m_buffer, 6U); + m_text[5U] = m_buffer[1U] & 0x7FU; + m_text[6U] = m_buffer[2U] & 0x7FU; + m_text[7U] = m_buffer[3U] & 0x7FU; + m_text[8U] = m_buffer[4U] & 0x7FU; + m_text[9U] = m_buffer[5U] & 0x7FU; + m_textBits |= 0x02U; + break; + case DSTAR_SLOW_DATA_TYPE_TEXT | 2U: + CUtils::dump(1U, "D-Star slow data text fragment", m_buffer, 6U); + m_text[10U] = m_buffer[1U] & 0x7FU; + m_text[11U] = m_buffer[2U] & 0x7FU; + m_text[12U] = m_buffer[3U] & 0x7FU; + m_text[13U] = m_buffer[4U] & 0x7FU; + m_text[14U] = m_buffer[5U] & 0x7FU; + m_textBits |= 0x04U; + break; + case DSTAR_SLOW_DATA_TYPE_TEXT | 3U: + CUtils::dump(1U, "D-Star slow data text fragment", m_buffer, 6U); + m_text[15U] = m_buffer[1U] & 0x7FU; + m_text[16U] = m_buffer[2U] & 0x7FU; + m_text[17U] = m_buffer[3U] & 0x7FU; + m_text[18U] = m_buffer[4U] & 0x7FU; + m_text[19U] = m_buffer[5U] & 0x7FU; + m_text[20U] = 0x00U; + m_textBits |= 0x08U; + break; + default: + return NULL; + } + + if (m_textBits != 0x0FU) + return NULL; + + CUtils::dump(1U, "D-STar slow data text", m_text, 20U); + + m_textBits = 0x00U; + + return m_text; +} + void CDStarSlowData::start() { ::memset(m_header, 0x00U, DSTAR_HEADER_LENGTH_BYTES); - m_ptr = 0U; - m_state = SDD_FIRST; + m_ptr = 0U; + m_state = SDD_FIRST; + m_textBits = 0x00U; } void CDStarSlowData::reset() { - m_ptr = 0U; - m_state = SDD_FIRST; + m_ptr = 0U; + m_state = SDD_FIRST; + m_textBits = 0x00U; } void CDStarSlowData::setText(const char* text) @@ -139,10 +215,11 @@ void CDStarSlowData::setText(const char* text) m_text[22U] = text[18U]; m_text[23U] = text[19U]; - m_textPtr = 0U; + m_textPtr = 0U; + m_textBits = 0x00U; } -void CDStarSlowData::get(unsigned char* data) +void CDStarSlowData::getSlowData(unsigned char* data) { assert(data != NULL); diff --git a/DStarSlowData.h b/DStarSlowData.h index 52e0f5c..eaedf45 100644 --- a/DStarSlowData.h +++ b/DStarSlowData.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 @@ -26,13 +26,15 @@ public: CDStarSlowData(); ~CDStarSlowData(); - CDStarHeader* add(const unsigned char* data); + CDStarHeader* addHeader(const unsigned char* data); + + const unsigned char* addText(const unsigned char* data); void start(); void reset(); void setText(const char* text); - void get(unsigned char* data); + void getSlowData(unsigned char* data); private: unsigned char* m_header; @@ -40,6 +42,7 @@ private: unsigned char* m_buffer; unsigned char* m_text; unsigned int m_textPtr; + unsigned char m_textBits; enum SDD_STATE { SDD_FIRST, diff --git a/Display.cpp b/Display.cpp index ebb1864..718ed84 100644 --- a/Display.cpp +++ b/Display.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016,2017,2018,2020,2021 by Jonathan Naylor G4KLX + * Copyright (C) 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 @@ -189,10 +189,15 @@ void CDisplay::writeDMRRSSI(unsigned int slotNo, unsigned char rssi) writeDMRRSSIInt(slotNo, rssi); } -void CDisplay::writeDMRTA(unsigned int slotNo, unsigned char* talkerAlias, const char* type) +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); + 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) @@ -487,7 +492,7 @@ void CDisplay::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi) { } -void CDisplay::writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, const char* type) +void CDisplay::writeDMRTAInt(unsigned int slotNo, const unsigned char* talkerAlias, const char* type) { } diff --git a/Display.h b/Display.h index fae929b..109b0ca 100644 --- a/Display.h +++ b/Display.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016,2017,2018,2020,2021 by Jonathan Naylor G4KLX + * Copyright (C) 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 @@ -53,7 +53,7 @@ public: void writeDMR(unsigned int slotNo, const class 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, unsigned char* talkerAlias, const char* type); + 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); @@ -103,7 +103,7 @@ protected: 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 class 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, unsigned char* talkerAlias, const char* type); + 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; diff --git a/M17Control.cpp b/M17Control.cpp index 72d97c5..c1abf2e 100644 --- a/M17Control.cpp +++ b/M17Control.cpp @@ -50,6 +50,9 @@ const unsigned char SCRAMBLER[] = { 0x5DU, 0x0CU, 0xC8U, 0x52U, 0x43U, 0x91U, 0x1DU, 0xF8U, 0x6EU, 0x68U, 0x2FU, 0x35U, 0xDAU, 0x14U, 0xEAU, 0xCDU, 0x76U, 0x19U, 0x8DU, 0xD5U, 0x80U, 0xD1U, 0x33U, 0x87U, 0x13U, 0x57U, 0x18U, 0x2DU, 0x29U, 0x78U, 0xC3U}; +const unsigned int RSSI_COUNT = 28U; // 28 * 40ms = 1120ms +const unsigned int BER_COUNT = 28U * 272U; // 28 * 40ms = 1120ms + // #define DUMP_M17 const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; @@ -86,21 +89,34 @@ m_rfCollectedLSF(), m_rfLSFn(0U), m_netLSF(), m_netLSFn(0U), +m_rfTextBits(0x00U), +m_netTextBits(0x00U), +m_rfText(NULL), +m_netText(NULL), m_rssiMapper(rssiMapper), m_rssi(0U), m_maxRSSI(0U), m_minRSSI(0U), m_aveRSSI(0U), +m_rssiCountTotal(0U), +m_rssiAccum(0U), m_rssiCount(0U), +m_bitsCount(0U), +m_bitErrsAccum(0U), m_enabled(true), m_fp(NULL) { assert(display != NULL); assert(rssiMapper != NULL); + + m_rfText = new char[4U * M17_META_LENGTH_BYTES]; + m_netText = new char[4U * M17_META_LENGTH_BYTES]; } CM17Control::~CM17Control() { + delete[] m_netText; + delete[] m_rfText; } bool CM17Control::writeModem(unsigned char* data, unsigned int len) @@ -114,11 +130,11 @@ bool CM17Control::writeModem(unsigned char* data, unsigned int len) if (type == TAG_LOST && (m_rfState == RS_RF_AUDIO || m_rfState == RS_RF_DATA_AUDIO)) { if (m_rssi != 0U) { - LogMessage("M17, transmission lost from %s to %s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_source.c_str(), m_dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - writeJSONRF("lost", m_rfState, m_source, m_dest, float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + LogMessage("M17, transmission lost from %s to %s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_source.c_str(), m_dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCountTotal); + writeJSONRF("lost", float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCountTotal); } else { LogMessage("M17, transmission lost from %s to %s, %.1f seconds, BER: %.1f%%", m_source.c_str(), m_dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); - writeJSONRF("lost", m_rfState, m_source, m_dest, float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + writeJSONRF("lost", float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); } writeEndRF(); return false; @@ -159,7 +175,10 @@ bool CM17Control::writeModem(unsigned char* data, unsigned int len) m_maxRSSI = m_rssi; m_aveRSSI += m_rssi; - m_rssiCount++; + m_rssiCountTotal++; + + m_rssiAccum += m_rssi; + m_rssiCountTotal++; } unsigned char temp[M17_FRAME_LENGTH_BYTES]; @@ -190,16 +209,26 @@ bool CM17Control::writeModem(unsigned char* data, unsigned int len) m_rfFrames = 0U; m_rfErrs = ber; m_rfBits = 368U; + m_rfCollectingLSF.reset(); m_rfCollectedLSF.reset(); m_rfTimeoutTimer.start(); + m_minRSSI = m_rssi; m_maxRSSI = m_rssi; m_aveRSSI = m_rssi; - m_rssiCount = 1U; + m_rssiCountTotal = 1U; + + m_rssiAccum = m_rssi; + m_rssiCount = 1U; + + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + m_rfLSFn = 0U; m_rfLSFCount = 0U; - + m_rfTextBits = 0x00U; + ::memset(m_rfText, 0x00U, 4U * M17_META_LENGTH_BYTES); #if defined(DUMP_M17) openFile(); #endif @@ -244,15 +273,25 @@ bool CM17Control::writeModem(unsigned char* data, unsigned int len) m_rfFrames = 0U; m_rfErrs = 0U; m_rfBits = 1U; + m_rfCollectingLSF.reset(); m_rfCollectedLSF.reset(); m_rfTimeoutTimer.start(); + m_minRSSI = m_rssi; m_maxRSSI = m_rssi; m_aveRSSI = m_rssi; - m_rssiCount = 1U; - m_rfLSFCount = 0U; + m_rssiCountTotal = 1U; + m_rssiAccum = m_rssi; + m_rssiCount = 1U; + + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + + m_rfLSFCount = 0U; + m_rfTextBits = 0x00U; + ::memset(m_rfText, 0x00U, 4U * M17_META_LENGTH_BYTES); #if defined(DUMP_M17) openFile(); #endif @@ -285,6 +324,39 @@ bool CM17Control::writeModem(unsigned char* data, unsigned int len) if (valid) { m_rfCollectedLSF = m_rfCollectingLSF; m_rfCollectingLSF.reset(); + + unsigned char encryptionType = m_rfCollectedLSF.getEncryptionType(); + unsigned char encryptionSubType = m_rfCollectedLSF.getEncryptionSubType(); + if (encryptionType == M17_ENCRYPTION_TYPE_NONE && encryptionSubType == M17_ENCRYPTION_SUB_TYPE_TEXT) { + unsigned char meta[20U]; + m_rfCollectedLSF.getMeta(meta); + CUtils::dump(1U, "M17, LSF text data fragment", meta, M17_META_LENGTH_BYTES); + + m_rfTextBits |= meta[0U]; + + switch (meta[0U] & 0x0FU) { + case 0x01U: + ::memcpy(m_rfText + 0U, meta + 1U, M17_META_LENGTH_BYTES - 1U); + break; + case 0x02U: + ::memcpy(m_rfText + 13U, meta + 1U, M17_META_LENGTH_BYTES - 1U); + break; + case 0x04U: + ::memcpy(m_rfText + 26U, meta + 1U, M17_META_LENGTH_BYTES - 1U); + break; + case 0x08U: + ::memcpy(m_rfText + 39U, meta + 1U, M17_META_LENGTH_BYTES - 1U); + break; + default: + break; + } + + if (m_rfTextBits == 0x11U || m_rfTextBits == 0x33U || m_rfTextBits == 0x77U || m_rfTextBits == 0xFFU) { + LogMessage("M17, text Data: \"%s\"", m_rfText); + writeJSONText(m_rfText); + m_rfTextBits = 0x00U; + } + } } } @@ -316,6 +388,13 @@ bool CM17Control::writeModem(unsigned char* data, unsigned int len) m_rfBits += 272U; m_rfErrs += errors; + m_bitErrsAccum += errors; + m_bitsCount += 272U; + writeJSONBER(); + + m_display->writeM17RSSI(m_rssi); + writeJSONRSSI(); + float ber = float(m_rfErrs) / float(m_rfBits); m_display->writeM17BER(ber); @@ -365,7 +444,6 @@ bool CM17Control::writeModem(unsigned char* data, unsigned int len) // Remove any erronous EOF from the FN netData[M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + 0U] &= 0x7FU; - // The CRC is added in the networking code m_network->write(netData); @@ -423,11 +501,11 @@ bool CM17Control::writeModem(unsigned char* data, unsigned int len) } if (m_rssi != 0U) { - LogMessage("M17, received RF end of transmission from %s to %s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_source.c_str(), m_dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); - writeJSONRF("end", m_rfState, m_source, m_dest, float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); + LogMessage("M17, received RF end of transmission from %s to %s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm", m_source.c_str(), m_dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCountTotal); + writeJSONRF("end", float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); } else { LogMessage("M17, received RF end of transmission from %s to %s, %.1f seconds, BER: %.1f%%", m_source.c_str(), m_dest.c_str(), float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits)); - writeJSONRF("end", m_rfState, m_source, m_dest, float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + writeJSONRF("end", float(m_rfFrames) / 25.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCountTotal); } writeEndRF(); @@ -557,8 +635,10 @@ void CM17Control::writeNetwork() m_netTimeoutTimer.start(); m_elapsed.start(); - m_netFrames = 0U; - m_netLSFn = 0U; + m_netFrames = 0U; + m_netLSFn = 0U; + m_netTextBits = 0x00U; + ::memset(m_netText, 0x00U, 4U * M17_META_LENGTH_BYTES); // Create a dummy start message unsigned char start[M17_FRAME_LENGTH_BYTES + 2U]; @@ -590,6 +670,39 @@ void CM17Control::writeNetwork() m_netLSF.setSource(m_callsign); m_netLSF.setCAN(m_can); + + unsigned char encryptionType = m_netLSF.getEncryptionType(); + unsigned char encryptionSubType = m_netLSF.getEncryptionSubType(); + if (encryptionType == M17_ENCRYPTION_TYPE_NONE && encryptionSubType == M17_ENCRYPTION_SUB_TYPE_TEXT) { + unsigned char meta[20U]; + m_netLSF.getMeta(meta); + CUtils::dump(1U, "M17, LSF text data fragment", meta, M17_META_LENGTH_BYTES); + + m_netTextBits |= meta[0U]; + + switch (meta[0U] & 0x0FU) { + case 0x01U: + ::memcpy(m_netText + 0U, meta + 1U, M17_META_LENGTH_BYTES - 1U); + break; + case 0x02U: + ::memcpy(m_netText + 13U, meta + 1U, M17_META_LENGTH_BYTES - 1U); + break; + case 0x04U: + ::memcpy(m_netText + 26U, meta + 1U, M17_META_LENGTH_BYTES - 1U); + break; + case 0x08U: + ::memcpy(m_netText + 39U, meta + 1U, M17_META_LENGTH_BYTES - 1U); + break; + default: + break; + } + + if (m_netTextBits == 0x11U || m_netTextBits == 0x33U || m_netTextBits == 0x77U || m_netTextBits == 0xFFU) { + LogMessage("M17, text Data: \"%s\"", m_netText); + writeJSONText(m_netText); + m_netTextBits = 0x00U; + } + } } unsigned char data[M17_FRAME_LENGTH_BYTES + 2U]; @@ -647,7 +760,7 @@ void CM17Control::writeNetwork() uint16_t fn = (netData[28U] << 8) + (netData[29U] << 0); if ((fn & 0x8000U) == 0x8000U) { LogMessage("M17, received network end of transmission from %s to %s, %.1f seconds", m_source.c_str(), m_dest.c_str(), float(m_netFrames) / 25.0F); - writeJSONNet("end", m_netState, m_source, m_dest, float(m_netFrames) / 25.0F); + writeJSONNet("end", float(m_netFrames) / 25.0F); unsigned char data[M17_FRAME_LENGTH_BYTES + 2U]; @@ -782,7 +895,7 @@ void CM17Control::clock(unsigned int ms) if (m_networkWatchdog.hasExpired()) { LogMessage("M17, network watchdog has expired, %.1f seconds", float(m_netFrames) / 25.0F); - writeJSONNet("lost", m_netState, m_source, m_dest, float(m_netFrames) / 25.0F); + writeJSONNet("lost", float(m_netFrames) / 25.0F); writeEndNet(); } } @@ -926,6 +1039,54 @@ void CM17Control::enable(bool enabled) m_enabled = enabled; } +void CM17Control::writeJSONRSSI() +{ + if (m_rssiCountTotal >= RSSI_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "M17"; + + json["value"] = -int(m_rssiAccum / m_rssiCountTotal); + + WriteJSON("RSSI", json); + + m_rssiAccum = 0U; + m_rssiCountTotal = 0U; + } +} + +void CM17Control::writeJSONBER() +{ + if (m_bitsCount >= BER_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "M17"; + + json["value"] = float(m_bitErrsAccum * 100U) / float(m_bitsCount); + + WriteJSON("BER", json); + + m_bitErrsAccum = 0U; + m_bitsCount = 1U; + } +} + +void CM17Control::writeJSONText(const char* text) +{ + assert(text != NULL); + + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "M17"; + + json["value"] = std::string(text); + + WriteJSON("Text", json); +} + void CM17Control::writeJSONRF(const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest) { assert(action != NULL); @@ -937,24 +1098,24 @@ void CM17Control::writeJSONRF(const char* action, RPT_RF_STATE state, const std: WriteJSON("M17", json); } -void CM17Control::writeJSONRF(const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest, float duration, float ber) +void CM17Control::writeJSONRF(const char* action, float duration, float ber) { assert(action != NULL); nlohmann::json json; - writeJSONRF(json, action, state, source, dest, duration, ber); + writeJSONRF(json, action, duration, ber); WriteJSON("M17", json); } -void CM17Control::writeJSONRF(const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI) +void CM17Control::writeJSONRF(const char* action, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI) { assert(action != NULL); nlohmann::json json; - writeJSONRF(json, action, state, source, dest, duration, ber); + writeJSONRF(json, action, duration, ber); nlohmann::json rssi; rssi["min"] = -int(minRSSI); @@ -977,19 +1138,28 @@ void CM17Control::writeJSONNet(const char* action, RPT_NET_STATE state, const st WriteJSON("M17", json); } -void CM17Control::writeJSONNet(const char* action, RPT_NET_STATE state, const std::string& source, const std::string& dest, float duration) +void CM17Control::writeJSONNet(const char* action, float duration) { assert(action != NULL); nlohmann::json json; - writeJSONNet(json, action, state, source, dest); + writeJSONNet(json, action); json["duration"] = duration; WriteJSON("M17", json); } +void CM17Control::writeJSONRF(nlohmann::json& json, const char* action) +{ + assert(action != NULL); + + json["timestamp"] = CUtils::createTimestamp(); + + json["action"] = action; +} + void CM17Control::writeJSONRF(nlohmann::json& json, const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest) { assert(action != NULL); @@ -1018,16 +1188,25 @@ void CM17Control::writeJSONRF(nlohmann::json& json, const char* action, RPT_RF_S } } -void CM17Control::writeJSONRF(nlohmann::json& json, const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest, float duration, float ber) +void CM17Control::writeJSONRF(nlohmann::json& json, const char* action, float duration, float ber) { assert(action != NULL); - writeJSONRF(json, action, state, source, dest); + writeJSONRF(json, action); json["duration"] = duration; json["ber"] = ber; } +void CM17Control::writeJSONNet(nlohmann::json& json, const char* action) +{ + assert(action != NULL); + + json["timestamp"] = CUtils::createTimestamp(); + + json["action"] = action; +} + void CM17Control::writeJSONNet(nlohmann::json& json, const char* action, RPT_NET_STATE state, const std::string& source, const std::string& dest) { assert(action != NULL); diff --git a/M17Control.h b/M17Control.h index ae505c2..3472232 100644 --- a/M17Control.h +++ b/M17Control.h @@ -78,12 +78,20 @@ private: unsigned int m_rfLSFn; CM17LSF m_netLSF; unsigned int m_netLSFn; + unsigned char m_rfTextBits; + unsigned char m_netTextBits; + char* m_rfText; + char* m_netText; CRSSIInterpolator* m_rssiMapper; unsigned char m_rssi; unsigned char m_maxRSSI; unsigned char m_minRSSI; unsigned int m_aveRSSI; + unsigned int m_rssiCountTotal; + unsigned int m_rssiAccum; unsigned int m_rssiCount; + unsigned int m_bitsCount; + unsigned int m_bitErrsAccum; bool m_enabled; FILE* m_fp; @@ -105,15 +113,21 @@ private: void writeEndRF(); void writeEndNet(); + void writeJSONRSSI(); + void writeJSONBER(); + void writeJSONText(const char* text); + void writeJSONRF(const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest); - void writeJSONRF(const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest, float duration, float ber); - void writeJSONRF(const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI); + void writeJSONRF(const char* action, float duration, float ber); + void writeJSONRF(const char* action, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI); void writeJSONNet(const char* action, RPT_NET_STATE state, const std::string& source, const std::string& dest); - void writeJSONNet(const char* action, RPT_NET_STATE state, const std::string& source, const std::string& dest, float duration); + void writeJSONNet(const char* action, float duration); void writeJSONRF(nlohmann::json& json, const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest); - void writeJSONRF(nlohmann::json& json, const char* action, RPT_RF_STATE state, const std::string& source, const std::string& dest, float duration, float ber); + void writeJSONRF(nlohmann::json& json, const char* action); + void writeJSONRF(nlohmann::json& json, const char* action, float duration, float ber); + void writeJSONNet(nlohmann::json& json, const char* action); void writeJSONNet(nlohmann::json& json, const char* action, RPT_NET_STATE state, const std::string& source, const std::string& dest); bool openFile(); diff --git a/MMDVMHost.cpp b/MMDVMHost.cpp index 160e28a..35d2a17 100644 --- a/MMDVMHost.cpp +++ b/MMDVMHost.cpp @@ -117,15 +117,24 @@ int main(int argc, char** argv) delete host; - if (m_signal == 2) - ::LogInfo("MMDVMHost-%s exited on receipt of SIGINT", VERSION); - - if (m_signal == 15) - ::LogInfo("MMDVMHost-%s exited on receipt of SIGTERM", VERSION); - - if (m_signal == 1) - ::LogInfo("MMDVMHost-%s is restarting on receipt of SIGHUP", VERSION); - } while (m_signal == 1); + switch (m_signal) { + case 2: + ::LogInfo("MMDVMHost-%s exited on receipt of SIGINT", VERSION); + break; + case 15: + ::LogInfo("MMDVMHost-%s exited on receipt of SIGTERM", VERSION); + break; + case 1: + ::LogInfo("MMDVMHost-%s exited on receipt of SIGHUP", VERSION); + break; + case 10: + ::LogInfo("MMDVMHost-%s is restarting on receipt of SIGUSR1", VERSION); + break; + default: + ::LogInfo("MMDVMHost-%s exited on receipt of an unknown signal", VERSION); + break; + } + } while (m_signal == 10); ::LogFinalise(); @@ -301,8 +310,8 @@ int CMMDVMHost::run() LogInfo(HEADER3); LogInfo(HEADER4); - LogMessage("MMDVMHost-%s is starting", VERSION); - LogMessage("Built %s %s (GitID #%.7s)", __TIME__, __DATE__, gitversion); + LogInfo("MMDVMHost-%s is starting", VERSION); + LogInfo("Built %s %s (GitID #%.7s)", __TIME__, __DATE__, gitversion); readParams(); @@ -357,6 +366,9 @@ int CMMDVMHost::run() m_display = CDisplay::createDisplay(m_conf, m_modem); + LogInfo("Opening network connections"); + writeJSONMessage("Opening network connections"); + if (m_dstarEnabled && m_conf.getDStarNetworkEnabled()) { ret = createDStarNetwork(); if (!ret) @@ -492,6 +504,9 @@ int CMMDVMHost::run() m_dmrLookup->read(); } + LogInfo("Starting protocol handlers"); + writeJSONMessage("Starting protocol handlers"); + CStopWatch stopWatch; stopWatch.start(); @@ -769,7 +784,8 @@ int CMMDVMHost::run() setMode(MODE_IDLE); - LogMessage("MMDVMHost-%s is running", VERSION); + LogInfo("MMDVMHost-%s is running", VERSION); + writeJSONMessage("MMDVMHost is running"); while (!m_killed) { bool lockout = m_modem->hasLockout(); @@ -1311,18 +1327,15 @@ int CMMDVMHost::run() setMode(MODE_QUIT); - m_modem->close(); - delete m_modem; - - m_display->close(); - delete m_display; - if (m_dmrLookup != NULL) m_dmrLookup->stop(); if (m_nxdnLookup != NULL) m_nxdnLookup->stop(); + LogInfo("Closing network connections"); + writeJSONMessage("Closing network connections"); + if (m_dstarNetwork != NULL) { m_dstarNetwork->close(); delete m_dstarNetwork; @@ -1378,10 +1391,8 @@ int CMMDVMHost::run() delete m_remoteControl; } - if (m_mqtt != NULL) { - m_mqtt->close(); - delete m_mqtt; - } + LogInfo("Stopping protocol handlers"); + writeJSONMessage("Stopping protocol handlers"); delete m_dstar; delete m_dmr; @@ -1393,6 +1404,20 @@ int CMMDVMHost::run() delete m_fm; delete m_ax25; + 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 != NULL) { + m_mqtt->close(); + delete m_mqtt; + } + return 0; } @@ -1994,7 +2019,7 @@ void CMMDVMHost::setMode(unsigned char mode) m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("D-Star"); - writeJSON("D-Star"); + writeJSONMode("D-Star"); break; case MODE_DMR: @@ -2043,7 +2068,7 @@ void CMMDVMHost::setMode(unsigned char mode) m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("DMR"); - writeJSON("DMR"); + writeJSONMode("DMR"); break; case MODE_YSF: @@ -2088,7 +2113,7 @@ void CMMDVMHost::setMode(unsigned char mode) m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("System Fusion"); - writeJSON("YSF"); + writeJSONMode("YSF"); break; case MODE_P25: @@ -2133,7 +2158,7 @@ void CMMDVMHost::setMode(unsigned char mode) m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("P25"); - writeJSON("P25"); + writeJSONMode("P25"); break; case MODE_NXDN: @@ -2178,7 +2203,7 @@ void CMMDVMHost::setMode(unsigned char mode) m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("NXDN"); - writeJSON("NXDN"); + writeJSONMode("NXDN"); break; case MODE_M17: @@ -2223,7 +2248,7 @@ void CMMDVMHost::setMode(unsigned char mode) m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("M17"); - writeJSON("M17"); + writeJSONMode("M17"); break; case MODE_POCSAG: @@ -2268,7 +2293,7 @@ void CMMDVMHost::setMode(unsigned char mode) m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("POCSAG"); - writeJSON("POCSAG"); + writeJSONMode("POCSAG"); break; case MODE_FM: @@ -2318,7 +2343,7 @@ void CMMDVMHost::setMode(unsigned char mode) m_modeTimer.start(); m_cwIdTimer.stop(); createLockFile("FM"); - writeJSON("FM"); + writeJSONMode("FM"); break; case MODE_LOCKOUT: @@ -2368,7 +2393,7 @@ void CMMDVMHost::setMode(unsigned char mode) m_modeTimer.stop(); m_cwIdTimer.stop(); removeLockFile(); - writeJSON("lockout"); + writeJSONMode("lockout"); break; case MODE_ERROR: @@ -2418,7 +2443,7 @@ void CMMDVMHost::setMode(unsigned char mode) m_modeTimer.stop(); m_cwIdTimer.stop(); removeLockFile(); - writeJSON("error"); + writeJSONMode("error"); break; default: @@ -2477,7 +2502,7 @@ void CMMDVMHost::setMode(unsigned char mode) m_mode = MODE_IDLE; m_modeTimer.stop(); removeLockFile(); - writeJSON("idle"); + writeJSONMode("idle"); break; } } @@ -2768,7 +2793,7 @@ void CMMDVMHost::buildNetworkHostsString(std::string &str) str += std::string(" fm:\"") + ((m_fmEnabled && (m_fmNetwork != NULL)) ? m_conf.getFMGatewayAddress() : "NONE") + "\""; } -void CMMDVMHost::writeJSON(const std::string& mode) +void CMMDVMHost::writeJSONMode(const std::string& mode) { nlohmann::json json; @@ -2778,3 +2803,13 @@ void CMMDVMHost::writeJSON(const std::string& mode) WriteJSON("MMDVM", json); } +void CMMDVMHost::writeJSONMessage(const std::string& message) +{ + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["message"] = message; + + WriteJSON("MMDVM", json); +} + diff --git a/MMDVMHost.h b/MMDVMHost.h index 98eb75c..9bd1d3d 100644 --- a/MMDVMHost.h +++ b/MMDVMHost.h @@ -142,7 +142,8 @@ private: void createLockFile(const char* mode) const; void removeLockFile() const; - void writeJSON(const std::string& mode); + void writeJSONMode(const std::string& mode); + void writeJSONMessage(const std::string& message); }; #endif diff --git a/NXDNControl.cpp b/NXDNControl.cpp index b904b24..ca25f0c 100644 --- a/NXDNControl.cpp +++ b/NXDNControl.cpp @@ -34,6 +34,9 @@ const unsigned char SCRAMBLER[] = { // #define DUMP_NXDN +const unsigned int RSSI_COUNT = 28U; // 28 * 40ms = 1120ms +const unsigned int BER_COUNT = 28U; // 28 * 40ms = 1120ms + 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]) @@ -70,7 +73,11 @@ m_rssi(0U), m_maxRSSI(0U), m_minRSSI(0U), m_aveRSSI(0U), +m_rssiCountTotal(0U), +m_rssiAccum(0U), m_rssiCount(0U), +m_bitsCount(0U), +m_bitErrsAccum(0U), m_enabled(true), m_fp(NULL) { @@ -99,11 +106,11 @@ bool CNXDNControl::writeModem(unsigned char *data, unsigned int len) 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); - writeJSONRF("lost", srcId, source, grp, dstId, float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + 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_rssiCountTotal); + writeJSONRF("lost", float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / 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", srcId, source, grp, 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; @@ -141,6 +148,9 @@ bool CNXDNControl::writeModem(unsigned char *data, unsigned int len) m_maxRSSI = m_rssi; m_aveRSSI += m_rssi; + m_rssiCountTotal++; + + m_rssiAccum += m_rssi; m_rssiCount++; } @@ -274,23 +284,33 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne 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); - writeJSONRF("end", srcId, source, grp, dstId, float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + 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_rssiCountTotal); + writeJSONRF("end", float(m_rfFrames) / 12.5F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / 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", srcId, source, grp, 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 = RS_RF_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; + #if defined(DUMP_NXDN) openFile(); #endif @@ -391,13 +411,22 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne m_rfFrames = 0U; m_rfErrs = 0U; m_rfBits = 1U; + m_rfTimeoutTimer.start(); + m_rfState = RS_RF_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; + #if defined(DUMP_NXDN) openFile(); #endif @@ -493,6 +522,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; + writeJSONBER(188U, errors); m_display->writeNXDNBER(float(errors) / 1.88F); LogDebug("NXDN, AMBE FEC %u/188 (%.1f%%)", errors, float(errors) / 1.88F); @@ -512,6 +542,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; + writeJSONBER(94U, errors); m_display->writeNXDNBER(float(errors) / 0.94F); LogDebug("NXDN, AMBE FEC %u/94 (%.1f%%)", errors, float(errors) / 0.94F); @@ -524,6 +555,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; + writeJSONBER(94U, errors); m_display->writeNXDNBER(float(errors) / 0.94F); LogDebug("NXDN, AMBE FEC %u/94 (%.1f%%)", errors, float(errors) / 0.94F); @@ -566,6 +598,7 @@ bool CNXDNControl::processVoice(unsigned char usc, unsigned char option, unsigne m_rfFrames++; m_display->writeNXDNRSSI(m_rssi); + writeJSONRSSI(); } return true; @@ -614,6 +647,7 @@ bool CNXDNControl::processData(unsigned char option, unsigned char *data) 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); @@ -688,7 +722,7 @@ bool CNXDNControl::processData(unsigned char option, unsigned char *data) 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", srcId, source, grp, dstId); + writeJSONNet("end"); writeEndRF(); } @@ -858,7 +892,7 @@ void CNXDNControl::writeNetwork() 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", srcId, source.get(keyCALLSIGN), grp, 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); @@ -1016,15 +1050,8 @@ void CNXDNControl::clock(unsigned int ms) m_networkWatchdog.clock(ms); if (m_networkWatchdog.hasExpired()) { - unsigned short srcId = m_netLayer3.getSourceUnitId(); - unsigned short dstId = m_netLayer3.getDestinationGroupId(); - bool grp = m_netLayer3.getIsGroup(); - - class CUserDBentry source; - m_lookup->findWithName(srcId, &source); - LogMessage("NXDN, network watchdog has expired, %.1f seconds", float(m_netFrames) / 12.5F); - writeJSONNet("lost", srcId, source.get(keyCALLSIGN), grp, dstId, float(m_netFrames) / 12.5F); + writeJSONNet("lost", float(m_netFrames) / 12.5F); writeEndNet(); } } @@ -1166,6 +1193,43 @@ void CNXDNControl::enable(bool enabled) m_enabled = enabled; } +void CNXDNControl::writeJSONRSSI() +{ + if (m_rssiCount >= RSSI_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "NXDN"; + + json["value"] = -int(m_rssiAccum / m_rssiCount); + + WriteJSON("RSSI", json); + + m_rssiAccum = 0U; + 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 != NULL); @@ -1177,13 +1241,13 @@ void CNXDNControl::writeJSONRF(const char* action, unsigned short srcId, const s WriteJSON("NXDN", json); } -void CNXDNControl::writeJSONRF(const char* action, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId, float duration, float ber) +void CNXDNControl::writeJSONRF(const char* action, float duration, float ber) { assert(action != NULL); nlohmann::json json; - writeJSON(json, "rf", action, srcId, srcInfo, grp, dstId); + writeJSON(json, action); json["duration"] = duration; json["ber"] = ber; @@ -1191,13 +1255,13 @@ void CNXDNControl::writeJSONRF(const char* action, unsigned short srcId, const s WriteJSON("NXDN", json); } -void CNXDNControl::writeJSONRF(const char* action, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI) +void CNXDNControl::writeJSONRF(const char* action, 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); + writeJSON(json, action); json["duration"] = duration; json["ber"] = ber; @@ -1212,6 +1276,17 @@ void CNXDNControl::writeJSONRF(const char* action, unsigned short srcId, const s WriteJSON("NXDN", json); } +void CNXDNControl::writeJSONNet(const char* action) +{ + assert(action != NULL); + + 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 != NULL); @@ -1236,19 +1311,27 @@ void CNXDNControl::writeJSONNet(const char* action, unsigned short srcId, const WriteJSON("NXDN", json); } -void CNXDNControl::writeJSONNet(const char* action, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId, float duration) +void CNXDNControl::writeJSONNet(const char* action, float duration) { assert(action != NULL); nlohmann::json json; - writeJSON(json, "network", action, srcId, srcInfo, grp, dstId); + writeJSON(json, action); json["duration"] = duration; WriteJSON("NXDN", json); } +void CNXDNControl::writeJSON(nlohmann::json& json, const char* action) +{ + assert(action != NULL); + + 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 != NULL); diff --git a/NXDNControl.h b/NXDNControl.h index 7fdbb66..5b538d1 100644 --- a/NXDNControl.h +++ b/NXDNControl.h @@ -82,7 +82,11 @@ private: unsigned char m_maxRSSI; unsigned char m_minRSSI; unsigned int m_aveRSSI; + unsigned int m_rssiCountTotal; + unsigned int m_rssiAccum; unsigned int m_rssiCount; + unsigned int m_bitsCount; + unsigned int m_bitErrsAccum; bool m_enabled; FILE* m_fp; @@ -103,14 +107,19 @@ private: 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, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId, float duration, float ber); - void writeJSONRF(const char* action, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI); + void writeJSONRF(const char* action, float duration, float ber); + void writeJSONRF(const char* action, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned 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, unsigned short srcId, const std::string& srcInfo, bool grp, unsigned short dstId, float duration); + 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); }; diff --git a/Nextion.cpp b/Nextion.cpp index 4875fe5..6df9a6d 100644 --- a/Nextion.cpp +++ b/Nextion.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016,2017,2018,2020 by Jonathan Naylor G4KLX + * Copyright (C) 2016,2017,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 @@ -477,7 +477,7 @@ void CNextion::writeDMRRSSIInt(unsigned int slotNo, unsigned char rssi) } } -void CNextion::writeDMRTAInt(unsigned int slotNo, unsigned char* talkerAlias, const char* type) +void CNextion::writeDMRTAInt(unsigned int slotNo, const unsigned char* talkerAlias, const char* type) { if (!(m_screenLayout & LAYOUT_TA_ENABLE)) return; diff --git a/Nextion.h b/Nextion.h index 713fc90..d440526 100644 --- a/Nextion.h +++ b/Nextion.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016,2017,2018,2020 by Jonathan Naylor G4KLX + * Copyright (C) 2016,2017,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 @@ -50,8 +50,7 @@ protected: 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, unsigned char* talkerAlias, const char* type); - + 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); diff --git a/OLED.cpp b/OLED.cpp index f012d8d..19ca09f 100644 --- a/OLED.cpp +++ b/OLED.cpp @@ -243,8 +243,9 @@ bool COLED::open() m_display.display(); // display it (clear display) OLED_statusbar(); - m_display.setCursor(0,OLED_LINE3); - m_display.print("Startup"); + m_display.setCursor(0,OLED_LINE4); + m_display.setTextSize(1); + m_display.print(" -Initializing-"); m_display.display(); return true; @@ -367,11 +368,12 @@ void COLED::setQuitInt() OLED_statusbar(); m_display.setCursor(0,30); - m_display.setTextSize(3); - m_display.print("Stopped"); + m_display.setTextSize(2); + m_display.print(" Stopping"); m_display.setTextSize(1); m_display.display(); + sleep(2); } void COLED::setFMInt() @@ -417,7 +419,7 @@ void COLED::clearDStarInt() m_display.fillRect(0,OLED_LINE3, m_display.width(),m_display.height(),BLACK); //clear everything beneath the logo m_display.setCursor(40,OLED_LINE3); - m_display.print("Listening"); + m_display.print("Standby"); m_display.setCursor(0,OLED_LINE5); m_display.printf("%s",m_ipaddress.c_str()); @@ -497,18 +499,18 @@ void COLED::clearDMRInt(unsigned int slotNo) 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 Listening"); + 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 Listening"); + 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 Listening",slotNo); + m_display.printf("Slot: %i Standby",slotNo); } m_display.fillRect(0, OLED_LINE6, m_display.width(), 20, BLACK); @@ -540,7 +542,7 @@ void COLED::clearFusionInt() m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); m_display.setCursor(40,OLED_LINE4); - m_display.print("Listening"); + m_display.print("Standby"); m_display.setCursor(0,OLED_LINE6); m_display.printf("%s",m_ipaddress.c_str()); @@ -570,7 +572,7 @@ void COLED::clearP25Int() m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); m_display.setCursor(40,OLED_LINE4); - m_display.print("Listening"); + m_display.print("Standby"); m_display.setCursor(0,OLED_LINE6); m_display.printf("%s",m_ipaddress.c_str()); @@ -620,7 +622,7 @@ void COLED::clearNXDNInt() m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); m_display.setCursor(40,OLED_LINE3); - m_display.print("Listening"); + m_display.print("Standby"); m_display.setCursor(0,OLED_LINE6); m_display.printf("%s",m_ipaddress.c_str()); @@ -636,10 +638,14 @@ void COLED::writeM17Int(const char* source, const char* dest, const char* type) m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); m_display.setCursor(0,OLED_LINE3); - m_display.printf("%s %s", type, source); + m_display.printf("from: %s %s", type, source); m_display.setCursor(0,OLED_LINE4); - m_display.printf(" %s", dest); + m_display.printf("to: %s", dest); + + m_display.setCursor(0,OLED_LINE6); + m_display.printf("%s",m_ipaddress.c_str()); + OLED_statusbar(); m_display.display(); @@ -650,7 +656,7 @@ void COLED::clearM17Int() m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); m_display.setCursor(40,OLED_LINE4); - m_display.print("Listening"); + m_display.print("Standby"); m_display.setCursor(0,OLED_LINE6); m_display.printf("%s",m_ipaddress.c_str()); @@ -725,7 +731,7 @@ void COLED::clearPOCSAGInt() m_display.fillRect(0, OLED_LINE2, m_display.width(), m_display.height(), BLACK); m_display.setCursor(40,OLED_LINE3); - m_display.print("Listening"); + m_display.print("Standby"); m_display.setCursor(0,OLED_LINE6); m_display.printf("%s",m_ipaddress.c_str()); @@ -739,7 +745,7 @@ void COLED::writeCWInt() m_display.setCursor(0,30); m_display.setTextSize(3); - m_display.print("CW TX"); + m_display.print("CW ID TX"); m_display.setTextSize(1); m_display.display(); @@ -751,14 +757,18 @@ void COLED::clearCWInt() { m_display.clearDisplay(); - m_display.setCursor(0,30); - m_display.setTextSize(3); - m_display.print("Idle"); - + m_display.setCursor(0,OLED_LINE1); + m_display.setTextSize(2); m_display.setTextSize(1); - m_display.display(); + m_display.print(" -IDLE-"); + m_display.setCursor(0,OLED_LINE3); + m_display.printf("%s",m_ipaddress.c_str()); + if (m_displayScroll) - m_display.startscrollleft(0x02,0x0f); + m_display.startscrolldiagleft(0x00,0x0f); + m_display.display(); + + } void COLED::close() @@ -767,9 +777,9 @@ void COLED::close() m_display.fillRect(0, 0, m_display.width(), 16, BLACK); if (m_displayScroll) m_display.startscrollleft(0x00,0x01); - m_display.setCursor(0,00); + m_display.setCursor(0,OLED_LINE3); m_display.setTextSize(2); - m_display.print("-CLOSE-"); + m_display.print(" -OFFLINE-"); m_display.display(); m_display.close(); diff --git a/OLED.h b/OLED.h index 9b6be22..14723cb 100644 --- a/OLED.h +++ b/OLED.h @@ -32,6 +32,8 @@ #include "UserDBentry.h" #include +#include +#include #include "ArduiPi_OLED_lib.h" #include "Adafruit_GFX.h" diff --git a/P25Control.cpp b/P25Control.cpp index 0c45e60..c75d891 100644 --- a/P25Control.cpp +++ b/P25Control.cpp @@ -31,6 +31,9 @@ #include #include +const unsigned int RSSI_COUNT = 7U; // 7 * 180ms = 1260ms +const unsigned int BER_COUNT = 7U * 1233U; // 7 * 180ms = 1260ms + // #define DUMP_P25 const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; @@ -79,7 +82,11 @@ m_rssi(0U), m_maxRSSI(0U), m_minRSSI(0U), m_aveRSSI(0U), +m_rssiCountTotal(0U), +m_rssiAccum(0U), m_rssiCount(0U), +m_bitsCount(0U), +m_bitErrsAccum(0U), m_enabled(true), m_fp(NULL) { @@ -128,11 +135,11 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) 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); - 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); + 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_rssiCountTotal); + writeJSONRF("lost", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / 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", srcId, source, grp, 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)); } if (m_netState == RS_NET_IDLE) @@ -217,6 +224,9 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) m_maxRSSI = m_rssi; m_aveRSSI += m_rssi; + m_rssiCountTotal++; + + m_rssiAccum += m_rssi; m_rssiCount++; } @@ -266,8 +276,14 @@ bool CP25Control::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_bitErrsAccum = 0U; + m_bitsCount = 0U; + createRFHeader(); writeNetwork(data + 2U, P25_DUID_HEADER, false); } else if (m_rfState == RS_RF_AUDIO) { @@ -298,6 +314,10 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) m_rfFrames++; m_lastDUID = duid; + m_bitsCount += 1233U; + m_bitErrsAccum += errors; + writeJSONBER(); + // Add busy bits addBusyBits(data + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true); @@ -314,6 +334,7 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) } m_display->writeP25RSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -344,6 +365,10 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) m_rfFrames++; m_lastDUID = duid; + m_bitsCount += 1233U; + m_bitErrsAccum += errors; + writeJSONBER(); + // Add busy bits addBusyBits(data + 2U, P25_LDU_FRAME_LENGTH_BITS, false, true); @@ -360,6 +385,7 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) } m_display->writeP25RSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -469,11 +495,11 @@ bool CP25Control::writeModem(unsigned char* data, unsigned int len) 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); - 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); + 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_rssiCountTotal); + writeJSONRF("end", float(m_rfFrames) / 5.56F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / 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", srcId, source, grp, 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)); } m_display->clearP25(); @@ -734,12 +760,8 @@ 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)); + writeJSONNet("lost", float(m_netFrames) / 50.0F, float(m_netLost * 100U) / float(m_netFrames)); m_display->clearP25(); m_networkWatchdog.stop(); @@ -1141,7 +1163,7 @@ void CP25Control::createNetTerminator() 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 " : "", 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)); + writeJSONNet("end", float(m_netFrames) / 50.0F, float(m_netLost * 100U) / float(m_netFrames)); m_display->clearP25(); m_netTimeout.stop(); @@ -1217,6 +1239,40 @@ void CP25Control::enable(bool enabled) m_enabled = enabled; } +void CP25Control::writeJSONRSSI() +{ + if (m_rssiCount >= RSSI_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "P25"; + + json["value"] = -int(m_rssiAccum / m_rssiCount); + + WriteJSON("RSSI", json); + + m_rssiAccum = 0U; + 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 != NULL); @@ -1228,13 +1284,13 @@ void CP25Control::writeJSONRF(const char* action, unsigned int srcId, const std: 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) +void CP25Control::writeJSONRF(const char* action, float duration, float ber) { assert(action != NULL); nlohmann::json json; - writeJSON(json, "rf", action, srcId, srcInfo, grp, dstId); + writeJSON(json, action); json["duration"] = duration; json["ber"] = ber; @@ -1242,13 +1298,13 @@ void CP25Control::writeJSONRF(const char* action, unsigned int srcId, const std: 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) +void CP25Control::writeJSONRF(const char* action, 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); + writeJSON(json, action); json["duration"] = duration; json["ber"] = ber; @@ -1274,13 +1330,13 @@ void CP25Control::writeJSONNet(const char* action, unsigned int srcId, const std 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) +void CP25Control::writeJSONNet(const char* action, float duration, float loss) { assert(action != NULL); nlohmann::json json; - writeJSON(json, "network", action, srcId, srcInfo, grp, dstId); + writeJSON(json, action); json["duration"] = duration; json["loss"] = loss; @@ -1288,6 +1344,14 @@ void CP25Control::writeJSONNet(const char* action, unsigned int srcId, const std WriteJSON("P25", json); } +void CP25Control::writeJSON(nlohmann::json& json, const char* action) +{ + assert(action != NULL); + + 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 != NULL); diff --git a/P25Control.h b/P25Control.h index 4e7dfb8..7d4814e 100644 --- a/P25Control.h +++ b/P25Control.h @@ -93,7 +93,11 @@ private: unsigned char m_maxRSSI; unsigned char m_minRSSI; unsigned int m_aveRSSI; + unsigned int m_rssiCountTotal; + unsigned int m_rssiAccum; unsigned int m_rssiCount; + unsigned int m_bitsCount; + unsigned int m_bitErrsAccum; bool m_enabled; FILE* m_fp; @@ -121,13 +125,17 @@ private: 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, 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 writeJSONRF(const char* action, float duration, float ber); + void writeJSONRF(const char* action, 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 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); }; diff --git a/UARTController.cpp b/UARTController.cpp index 27aeed2..0d636d8 100644 --- a/UARTController.cpp +++ b/UARTController.cpp @@ -357,6 +357,12 @@ bool CUARTController::setRaw() ::cfsetispeed(&termios, B460800); break; #endif /*B460800*/ +#if defined(B500000) + case 500000U: + ::cfsetospeed(&termios, B500000); + ::cfsetispeed(&termios, B500000); + break; +#endif /*B500000*/ default: LogError("Unsupported serial port speed - %u", m_speed); ::close(m_fd); diff --git a/YSFControl.cpp b/YSFControl.cpp index 874a602..ee65f4c 100644 --- a/YSFControl.cpp +++ b/YSFControl.cpp @@ -21,6 +21,9 @@ #include #include +const unsigned int RSSI_COUNT = 13U; // 13 * 100ms = 1300ms +const unsigned int BER_COUNT = 13U; // 13 * 100ms = 1300ms + // #define DUMP_YSF CYSFControl::CYSFControl(const std::string& callsign, bool selfOnly, CYSFNetwork* network, CDisplay* display, unsigned int timeout, bool duplex, bool lowDeviation, bool remoteGateway, CRSSIInterpolator* rssiMapper) : @@ -59,7 +62,11 @@ m_rssi(0U), m_maxRSSI(0U), m_minRSSI(0U), m_aveRSSI(0U), +m_rssiCountTotal(0U), +m_rssiAccum(0U), m_rssiCount(0U), +m_bitsCount(0U), +m_bitErrsAccum(0U), m_enabled(true), m_fp(NULL) { @@ -105,15 +112,14 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len) return false; unsigned char type = data[0U]; - unsigned char dgid = m_lastFICH.getDGId(); if (type == TAG_LOST && m_rfState == RS_RF_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); - writeJSONRF("lost", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + 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_rssiCountTotal); + writeJSONRF("lost", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / 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", m_rfSource, dgid, 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; @@ -153,6 +159,9 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len) m_maxRSSI = m_rssi; m_aveRSSI += m_rssi; + m_rssiCountTotal++; + + m_rssiAccum += m_rssi; m_rssiCount++; } @@ -256,7 +265,14 @@ 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; + + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + #if defined(DUMP_YSF) openFile(); #endif @@ -288,6 +304,7 @@ bool CYSFControl::processVWData(bool valid, unsigned char *data) m_rfFrames++; m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -323,11 +340,11 @@ 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); - writeJSONRF("end", "voice_vw", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + 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_rssiCountTotal); + writeJSONRF("end", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / 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", "voice_vw", 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(); @@ -362,6 +379,7 @@ bool CYSFControl::processVWData(bool valid, unsigned char *data) 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); @@ -384,6 +402,7 @@ bool CYSFControl::processVWData(bool valid, unsigned char *data) m_rfFrames++; m_display->writeFusionRSSI(m_rssi); + wrteJSONRSSI(); return true; } @@ -430,7 +449,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; + + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + #if defined(DUMP_YSF) openFile(); #endif @@ -462,6 +488,7 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) m_rfFrames++; m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -497,11 +524,11 @@ 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); - writeJSONRF("end", "voice_dn", m_rfSource, dgid, float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + 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_rssiCountTotal); + writeJSONRF("end", float(m_rfFrames) / 10.0F, float(m_rfErrs * 100U) / float(m_rfBits), m_minRSSI, m_maxRSSI, m_aveRSSI / 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", "voice_dn", 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(); @@ -532,6 +559,7 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) 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; @@ -542,6 +570,7 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) 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; @@ -571,6 +600,7 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) m_rfFrames++; m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } else if (valid && m_rfState == RS_RF_LISTENING) { @@ -625,7 +655,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; + + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + #if defined(DUMP_YSF) openFile(); #endif @@ -693,6 +730,7 @@ bool CYSFControl::processDNData(bool valid, unsigned char *data) m_rfFrames++; m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -736,7 +774,14 @@ 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; + + m_bitErrsAccum = 0U; + m_bitsCount = 0U; + #if defined(DUMP_YSF) openFile(); #endif @@ -767,6 +812,7 @@ bool CYSFControl::processFRData(bool valid, unsigned char *data) m_rfFrames++; m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -802,11 +848,11 @@ 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); - writeJSONRF("end", "data_fr", m_rfSource, dgid, float(m_rfFrames) / 10.0F, 0.0F, m_minRSSI, m_maxRSSI, m_aveRSSI / m_rssiCount); + 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_rssiCountTotal); + writeJSONRF("end", float(m_rfFrames) / 10.0F, 0.0F, m_minRSSI, m_maxRSSI, m_aveRSSI / 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", "data_fr", m_rfSource, dgid, float(m_rfFrames) / 10.0F, 0.0F); + writeJSONRF("end", float(m_rfFrames) / 10.0F, 0.0F); } writeEndRF(); @@ -852,6 +898,7 @@ bool CYSFControl::processFRData(bool valid, unsigned char *data) m_rfFrames++; m_display->writeFusionRSSI(m_rssi); + writeJSONRSSI(); return true; } @@ -1064,7 +1111,7 @@ void CYSFControl::writeNetwork() if (end) { 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", 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(); } } @@ -1081,9 +1128,8 @@ void CYSFControl::clock(unsigned int ms) m_networkWatchdog.clock(ms); if (m_networkWatchdog.hasExpired()) { - unsigned char dgid = m_lastFICH.getDGId(); LogMessage("YSF, network watchdog has expired, %.1f seconds, %u%% packet loss", float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames); - writeJSONNet("lost", m_netSource, dgid, float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames); + writeJSONNet("lost", float(m_netFrames) / 10.0F, (m_netLost * 100U) / m_netFrames); writeEndNet(); } } @@ -1248,6 +1294,43 @@ void CYSFControl::enable(bool enabled) m_enabled = enabled; } +void CYSFControl::writeJSONRSSI() +{ + if (m_rssiCount >= RSSI_COUNT) { + nlohmann::json json; + + json["timestamp"] = CUtils::createTimestamp(); + json["mode"] = "YSF"; + + json["value"] = -int(m_rssiAccum / m_rssiCount); + + WriteJSON("RSSI", json); + + m_rssiAccum = 0U; + 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 != NULL); @@ -1263,14 +1346,13 @@ void CYSFControl::writeJSONRF(const char* action, const char* mode, const unsign WriteJSON("YSF", json); } -void CYSFControl::writeJSONRF(const char* action, const unsigned char* source, unsigned char dgid, float duration, float ber) +void CYSFControl::writeJSONRF(const char* action, float duration, float ber) { assert(action != NULL); - assert(source != NULL); nlohmann::json json; - writeJSONRF(json, action, source, dgid); + writeJSONRF(json, action); json["duration"] = duration; json["ber"] = ber; @@ -1278,56 +1360,14 @@ void CYSFControl::writeJSONRF(const char* action, const unsigned char* source, u WriteJSON("YSF", json); } -void CYSFControl::writeJSONRF(const char* action, const char* mode, const unsigned char* source, unsigned char dgid, float duration, float ber) +void CYSFControl::writeJSONRF(const char* action, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI) { assert(action != NULL); - assert(mode != NULL); - assert(source != NULL); nlohmann::json json; - writeJSONRF(json, action, source, dgid); + writeJSONRF(json, action); - json["mode"] = mode; - json["duration"] = duration; - json["ber"] = ber; - - WriteJSON("YSF", json); -} - -void CYSFControl::writeJSONRF(const char* action, const unsigned char* source, unsigned char dgid, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI) -{ - assert(action != NULL); - assert(source != NULL); - - nlohmann::json json; - - writeJSONRF(json, action, source, dgid); - - 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("YSF", json); -} - -void CYSFControl::writeJSONRF(const char* action, const char* mode, const unsigned char* source, unsigned char dgid, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI) -{ - assert(action != NULL); - assert(mode != NULL); - assert(source != NULL); - - nlohmann::json json; - - writeJSONRF(json, action, source, dgid); - - json["mode"] = mode; json["duration"] = duration; json["ber"] = ber; @@ -1356,14 +1396,13 @@ void CYSFControl::writeJSONNet(const char* action, const unsigned char* source, WriteJSON("YSF", json); } -void CYSFControl::writeJSONNet(const char* action, const unsigned char* source, unsigned char dgid, float duration, unsigned int loss) +void CYSFControl::writeJSONNet(const char* action, float duration, unsigned int loss) { assert(action != NULL); - assert(source != NULL); nlohmann::json json; - writeJSONNet(json, action, source, dgid); + writeJSONNet(json, action); json["duration"] = duration; json["loss"] = loss; @@ -1371,22 +1410,12 @@ void CYSFControl::writeJSONNet(const char* action, const unsigned char* source, WriteJSON("YSF", json); } -void CYSFControl::writeJSONNet(const char* action, const unsigned char* source, unsigned char dgid, const unsigned char* reflector, float duration, unsigned int loss) +void CYSFControl::writeJSONRF(nlohmann::json& json, const char* action) { assert(action != NULL); - assert(source != NULL); - assert(reflector != NULL); - nlohmann::json json; - - writeJSONNet(json, action, source, dgid); - - json["reflector"] = convertBuffer(reflector); - - json["duration"] = duration; - json["loss"] = loss; - - WriteJSON("YSF", json); + json["timestamp"] = CUtils::createTimestamp(); + json["action"] = action; } void CYSFControl::writeJSONRF(nlohmann::json& json, const char* action, const unsigned char* source, unsigned char dgid) @@ -1403,6 +1432,14 @@ void CYSFControl::writeJSONRF(nlohmann::json& json, const char* action, const un json["dg-id"] = int(dgid); } +void CYSFControl::writeJSONNet(nlohmann::json& json, const char* action) +{ + assert(action != NULL); + + 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 != NULL); diff --git a/YSFControl.h b/YSFControl.h index 1fe92ad..2ff85c4 100644 --- a/YSFControl.h +++ b/YSFControl.h @@ -86,7 +86,11 @@ private: unsigned char m_maxRSSI; unsigned char m_minRSSI; unsigned int m_aveRSSI; + unsigned int m_rssiCountTotal; + unsigned int m_rssiAccum; unsigned int m_rssiCount; + unsigned int m_bitsCount; + unsigned int m_bitErrsAccum; bool m_enabled; FILE* m_fp; @@ -102,17 +106,20 @@ private: void writeEndRF(); void writeEndNet(); + 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, const unsigned char* source, unsigned char dgid, float duration, float ber); - void writeJSONRF(const char* action, const char* mode, const unsigned char* source, unsigned char dgid, float duration, float ber); - void writeJSONRF(const char* action, const unsigned char* source, unsigned char dgid, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI); - void writeJSONRF(const char* action, const char* mode, const unsigned char* source, unsigned char dgid, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI); + void writeJSONRF(const char* action, float duration, float ber); + void writeJSONRF(const char* action, float duration, float ber, unsigned char minRSSI, unsigned char maxRSSI, unsigned int aveRSSI); void writeJSONNet(const char* action, const unsigned char* source, unsigned char dgid, const unsigned char* reflector); - void writeJSONNet(const char* action, const unsigned char* source, unsigned char dgid, float duration, unsigned int loss); - void writeJSONNet(const char* action, const unsigned char* source, unsigned char dgid, const unsigned char* reflector, float duration, unsigned int loss); + 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; diff --git a/schema.json b/schema.json index 5ffd1f7..4ab3e94 100644 --- a/schema.json +++ b/schema.json @@ -35,7 +35,35 @@ "type": "object", "timestamp": {"$ref": "#/$defs/timestamp"}, "mode": {"$ref": "#/$defs/mmdvm_mode"}, - "required": ["timestamp", "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": { @@ -55,7 +83,7 @@ "max": {"$ref": "#/$defs/rssi"}, "ave": {"$ref": "#/$defs/rssi"} }, - "required": ["timestamp", "source_cs", "source_ext", "destination_cs", "source", "action"] + "required": ["timestamp", "action"] }, "DMR": { @@ -78,7 +106,7 @@ "max": {"$ref": "#/$defs/rssi"}, "ave": {"$ref": "#/$defs/rssi"} }, - "required": ["timestamp", "source_id", "destination_id", "destination_type", "slot", "source", "action"] + "required": ["timestamp", "slot", "action"] }, "YSF": { @@ -98,7 +126,7 @@ "max": {"$ref": "#/$defs/rssi"}, "ave": {"$ref": "#/$defs/rssi"} }, - "required": ["timestamp", "source_cs", "dg-id", "source", "action"] + "required": ["timestamp", "action"] }, "P25": { @@ -118,7 +146,7 @@ "max": {"$ref": "#/$defs/rssi"}, "ave": {"$ref": "#/$defs/rssi"} }, - "required": ["timestamp", "source_id", "destination_id", "destination_type", "source", "action"] + "required": ["timestamp", "action"] }, "NXDN": { @@ -137,7 +165,7 @@ "max": {"$ref": "#/$defs/rssi"}, "ave": {"$ref": "#/$defs/rssi"} }, - "required": ["timestamp", "source_id", "destination_id", "destination_type", "source", "action"] + "required": ["timestamp", "action"] }, "POCSAG": { @@ -189,6 +217,6 @@ "max": {"$ref": "#/$defs/rssi"}, "ave": {"$ref": "#/$defs/rssi"} }, - "required": ["timestamp", "source_cs", "destination_cs", "source", "action", "traffic_type"] + "required": ["timestamp", "action"] } }