Merge branch 'mqtt' into mqtt_plus

This commit is contained in:
Jonathan Naylor 2023-06-16 15:45:17 +01:00
commit f5e82b6019
26 changed files with 1304 additions and 506 deletions

View file

@ -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);

View file

@ -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);
};

128
DMRTA.cpp
View file

@ -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 <cstring>
#include <cassert>
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;
}

21
DMRTA.h
View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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);

View file

@ -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,

View file

@ -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)
{
}

View file

@ -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;

View file

@ -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);

View file

@ -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();

View file

@ -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);
}

View file

@ -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

View file

@ -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);

View file

@ -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);
};

View file

@ -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;

View file

@ -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);

View file

@ -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();

2
OLED.h
View file

@ -32,6 +32,8 @@
#include "UserDBentry.h"
#include <string>
#include <fstream>
#include <sstream>
#include "ArduiPi_OLED_lib.h"
#include "Adafruit_GFX.h"

View file

@ -31,6 +31,9 @@
#include <cstring>
#include <ctime>
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);

View file

@ -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);
};

View file

@ -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);

View file

@ -21,6 +21,9 @@
#include <cstring>
#include <ctime>
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);

View file

@ -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;

View file

@ -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"]
}
}