Add RSSI reporting to FM and AX.25.

This commit is contained in:
Jonathan Naylor
2023-08-04 16:12:38 +01:00
parent 0b69928256
commit fdc917518b
9 changed files with 224 additions and 133 deletions

View File

@@ -30,11 +30,13 @@ const unsigned char BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04
#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
CAX25Control::CAX25Control(CAX25Network* network, bool trace) :
CAX25Control::CAX25Control(CAX25Network* network, bool trace, CRSSIInterpolator* rssiMapper) :
m_network(network),
m_trace(trace),
m_rssiMapper(rssiMapper),
m_enabled(true)
{
assert(rssiMapper != NULL);
}
CAX25Control::~CAX25Control()
@@ -48,17 +50,33 @@ bool CAX25Control::writeModem(unsigned char *data, unsigned int len)
if (!m_enabled)
return false;
if (m_trace)
decode(data, len);
bool hasRSSI = data[0U] == TAG_RSSI;
decodeJSON("rf", data, len);
unsigned int offset = hasRSSI ? 3U : 1U;
int rssi = 0;
if (hasRSSI) {
uint16_t raw = 0U;
raw |= (data[1U] << 8) & 0xFF00U;
raw |= (data[2U] << 0) & 0x00FFU;
// Convert the raw RSSI to dBm
rssi = m_rssiMapper->interpolate(raw);
if (rssi != 0)
LogDebug("AX.25, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
}
if (m_trace)
decode(data + offset, len - offset);
decodeJSON("rf", data + offset, len - offset, rssi);
CUtils::dump(1U, "AX.25 received packet", data, len);
if (m_network == NULL)
return true;
return m_network->write(data, len);
return m_network->write(data + offset, len - offset);
}
unsigned int CAX25Control::readModem(unsigned char* data)
@@ -185,7 +203,7 @@ void CAX25Control::decode(const unsigned char* data, unsigned int length)
LogMessage("AX.25, %s %.*s", text.c_str(), length - n, data + n);
}
void CAX25Control::decodeJSON(const char* source, const unsigned char* data, unsigned int length)
void CAX25Control::decodeJSON(const char* source, const unsigned char* data, unsigned int length, int rssi)
{
assert(source != NULL);
assert(data != NULL);
@@ -205,6 +223,9 @@ void CAX25Control::decodeJSON(const char* source, const unsigned char* data, uns
decodeAddressJSON(data + 0U, text, isDigi);
json["destination_cs"] = text;
if (rssi != 0)
json["rssi"] = rssi;
unsigned int n = 14U;
if (more) {

View File

@@ -19,6 +19,7 @@
#if !defined(AX25Control_H)
#define AX25Control_H
#include "RSSIInterpolator.h"
#include "AX25Network.h"
#include "Defines.h"
@@ -28,7 +29,7 @@
class CAX25Control {
public:
CAX25Control(CAX25Network* network, bool trace);
CAX25Control(CAX25Network* network, bool trace, CRSSIInterpolator* rssiMapper);
~CAX25Control();
bool writeModem(unsigned char* data, unsigned int len);
@@ -40,10 +41,11 @@ public:
private:
CAX25Network* m_network;
bool m_trace;
CRSSIInterpolator* m_rssiMapper;
bool m_enabled;
void decode(const unsigned char* data, unsigned int length);
void decodeJSON(const char* source, const unsigned char* data, unsigned int length);
void decodeJSON(const char* source, const unsigned char* data, unsigned int length, int rssi = 0);
bool decodeAddress(const unsigned char* data, std::string& text, bool isDigi = false) const;
bool decodeAddressJSON(const unsigned char* data, std::string& text, bool& isDigi) const;
};

View File

@@ -50,6 +50,7 @@ const unsigned char TAG_HEADER = 0x00U;
const unsigned char TAG_DATA = 0x01U;
const unsigned char TAG_LOST = 0x02U;
const unsigned char TAG_EOT = 0x03U;
const unsigned char TAG_RSSI = 0x04U;
const unsigned int DSTAR_MODEM_DATA_LEN = 220U;

View File

@@ -43,12 +43,13 @@ const unsigned char FS_TIMEOUT_EXT = 9U;
const unsigned char FS_TIMEOUT_WAIT_EXT = 10U;
const unsigned char FS_HANG = 11U;
CFMControl::CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn) :
CFMControl::CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn, CRSSIInterpolator* rssiMapper) :
m_network(network),
m_txAudioGain(txAudioGain),
m_rxAudioGain(rxAudioGain),
m_preEmphasisOn(preEmphasisOn),
m_deEmphasisOn(deEmphasisOn),
m_rssiMapper(rssiMapper),
m_enabled(false),
m_incomingRFAudio(1600U, "Incoming RF FM Audio"),
m_preEmphasis(NULL),
@@ -59,6 +60,7 @@ m_filterStage3(NULL)
{
assert(txAudioGain > 0.0F);
assert(rxAudioGain > 0.0F);
assert(rssiMapper != NULL);
m_preEmphasis = new CIIRDirectForm1Filter(8.315375384336983F, -7.03334621603483F,0.0F,1.0F, 0.282029168302153F,0.0F, PREEMPHASIS_GAIN_DB);
m_deEmphasis = new CIIRDirectForm1Filter(0.07708787090460224F, 0.07708787090460224F,0.0F, 1.0F, -0.8458242581907955F,0.0F, DEEMPHASIS_GAIN_DB);
@@ -84,9 +86,6 @@ bool CFMControl::writeModem(const unsigned char* data, unsigned int length)
assert(data != NULL);
assert(length > 0U);
if (m_network == NULL)
return true;
if (data[0U] == TAG_HEADER) {
switch (data[1U]) {
case FS_LISTENING: writeJSON("listening"); break;
@@ -107,6 +106,24 @@ bool CFMControl::writeModem(const unsigned char* data, unsigned int length)
return true;
}
if (data[0U] == TAG_RSSI) {
uint16_t raw = 0U;
raw |= (data[0U] << 8) & 0xFF00U;
raw |= (data[1U] << 0) & 0x00FFU;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper->interpolate(raw);
if (rssi != 0) {
LogDebug("FM, raw RSSI: %u, reported RSSI: %d dBm", raw, rssi);
writeJSONRSSI(rssi);
}
return true;
}
if (m_network == NULL)
return true;
if (data[0U] == TAG_EOT)
return m_network->writeEnd();
@@ -115,19 +132,21 @@ bool CFMControl::writeModem(const unsigned char* data, unsigned int length)
m_incomingRFAudio.addData(data + 1U, length - 1U);
unsigned int bufferLength = m_incomingRFAudio.dataSize();
if (bufferLength > 240U) // 160 samples 12-bit
bufferLength = 240U; // 160 samples 12-bit
if (bufferLength >= 3U) {
bufferLength = bufferLength - bufferLength % 3U; // Round down to nearest multiple of 3
unsigned char bufferData[240U]; // 160 samples 12-bit
m_incomingRFAudio.getData(bufferData, bufferLength);
unsigned int pack = 0U;
unsigned char* packPointer = (unsigned char*)&pack;
float out[160U]; // 160 samples 12-bit
unsigned int nOut = 0U;
short unpackedSamples[2U];
for (unsigned int i = 0U; i < bufferLength; i += 3U) {
// Extract unsigned 12 bit unsigned sample pairs packed into 3 bytes to 16 bit signed
@@ -135,8 +154,9 @@ bool CFMControl::writeModem(const unsigned char* data, unsigned int length)
packPointer[1U] = bufferData[i + 1U];
packPointer[2U] = bufferData[i + 2U];
unpackedSamples[1U] = short(int(pack & FM_MASK) - 2048);
unpackedSamples[0U] = short(int(pack >> 12 & FM_MASK) - 2048); //
short unpackedSamples[2U];
unpackedSamples[1U] = short(int((pack >> 0) & FM_MASK) - 2048);
unpackedSamples[0U] = short(int((pack >> 12) & FM_MASK) - 2048); //
// Process unpacked sample pair
for (unsigned char j = 0U; j < 2U; j++) {
@@ -188,7 +208,7 @@ unsigned int CFMControl::readModem(unsigned char* data, unsigned int space)
unsigned int sample12bit = (unsigned int)((sampleFloat + 1.0F) * 2048.0F + 0.5F);
// Pack 2 samples into 3 bytes
if ((i & 1U) == 0) {
if ((i & 1U) == 0U) {
pack = 0U;
pack = sample12bit << 12;
} else {
@@ -225,5 +245,15 @@ void CFMControl::writeJSON(const char* state)
WriteJSON("FM", json);
}
void CFMControl::writeJSONRSSI(int rssi)
{
nlohmann::json json;
json["timestamp"] = CUtils::createTimestamp();
json["mode"] = "FM";
json["value"] = rssi;
WriteJSON("RSSI", json);
}
#endif

View File

@@ -22,6 +22,7 @@
#include "FMNetwork.h"
#include "Defines.h"
#include "IIRDirectForm1Filter.h"
#include "RSSIInterpolator.h"
#if defined(USE_FM)
@@ -34,7 +35,7 @@
class CFMControl {
public:
CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn);
CFMControl(CFMNetwork* network, float txAudioGain, float rxAudioGain, bool preEmphasisOn, bool deEmphasisOn, CRSSIInterpolator* rssiMapper);
~CFMControl();
bool writeModem(const unsigned char* data, unsigned int length);
@@ -51,6 +52,7 @@ private:
float m_rxAudioGain;
bool m_preEmphasisOn;
bool m_deEmphasisOn;
CRSSIInterpolator* m_rssiMapper;
bool m_enabled;
CRingBuffer<unsigned char> m_incomingRFAudio;
CIIRDirectForm1Filter* m_preEmphasis;
@@ -60,6 +62,7 @@ private:
CIIRDirectForm1Filter* m_filterStage3;
void writeJSON(const char* state);
void writeJSONRSSI(int rssi);
};
#endif

View File

@@ -352,7 +352,6 @@ int CMMDVMHost::run()
}
#endif
#endif
::LogInitialise(m_conf.getLogDisplayLevel(), m_conf.getLogMQTTLevel());
std::vector<std::pair<std::string, void (*)(const unsigned char*, unsigned int)>> subscriptions;
@@ -878,7 +877,7 @@ int CMMDVMHost::run()
LogInfo(" P-Persist: %u", pPersist);
LogInfo(" Trace: %s", trace ? "yes" : "no");
m_ax25 = new CAX25Control(m_ax25Network, trace);
m_ax25 = new CAX25Control(m_ax25Network, trace, rssi);
}
#endif
@@ -890,7 +889,7 @@ int CMMDVMHost::run()
float rxAudioGain = m_conf.getFMRXAudioGain();
m_fmRFModeHang = m_conf.getFMModeHang();
m_fm = new CFMControl(m_fmNetwork, txAudioGain, rxAudioGain, preEmphasis, deEmphasis);
m_fm = new CFMControl(m_fmNetwork, txAudioGain, rxAudioGain, preEmphasis, deEmphasis, rssi);
}
#endif
@@ -1119,7 +1118,7 @@ int CMMDVMHost::run()
if (m_mode == MODE_IDLE || m_mode == MODE_FM) {
m_ax25->writeModem(data, len);
} else if (m_mode != MODE_LOCKOUT) {
LogWarning("NXDN modem data received when in mode %u", m_mode);
LogWarning("AX.25 modem data received when in mode %u", m_mode);
}
}
#endif

View File

@@ -98,6 +98,7 @@ const unsigned char MMDVM_POCSAG_DATA = 0x50U;
#if defined(USE_AX25)
const unsigned char MMDVM_AX25_DATA = 0x55U;
const unsigned char MMDVM_AX25_DATA_EX = 0x56U;
#endif
#if defined(USE_FM)
@@ -108,6 +109,7 @@ const unsigned char MMDVM_FM_PARAMS4 = 0x63U;
const unsigned char MMDVM_FM_DATA = 0x65U;
const unsigned char MMDVM_FM_STATUS = 0x66U;
const unsigned char MMDVM_FM_EOT = 0x67U;
const unsigned char MMDVM_FM_RSSI = 0x68U;
#endif
const unsigned char MMDVM_ACK = 0x70U;
@@ -929,6 +931,20 @@ void CModem::clock(unsigned int ms)
m_rxFMData.addData(m_buffer + m_offset, m_length - m_offset);
}
break;
case MMDVM_FM_RSSI: {
if(m_trace)
CUtils::dump(1U, "RX FM RSSI", m_buffer, m_length);
unsigned int data1 = m_length - m_offset + 1U;
m_rxFMData.addData((unsigned char*)&data1, sizeof(unsigned int));
unsigned char data2 = TAG_RSSI;
m_rxFMData.addData(&data2, 1U);
m_rxFMData.addData(m_buffer + m_offset, m_length - m_offset);
}
break;
#endif
#if defined(USE_AX25)
@@ -936,8 +952,25 @@ void CModem::clock(unsigned int ms)
if (m_trace)
CUtils::dump(1U, "RX AX.25 Data", m_buffer, m_length);
unsigned int data = m_length - m_offset;
m_rxAX25Data.addData((unsigned char*)&data, sizeof(unsigned int));
unsigned int data1 = m_length - m_offset + 1U;
m_rxAX25Data.addData((unsigned char*)&data1, sizeof(unsigned int));
unsigned char data2 = TAG_DATA;
m_rxFMData.addData(&data2, 1U);
m_rxAX25Data.addData(m_buffer + m_offset, m_length - m_offset);
}
break;
case MMDVM_AX25_DATA_EX: {
if (m_trace)
CUtils::dump(1U, "RX AX.25 Data Extended", m_buffer, m_length);
unsigned int data1 = m_length - m_offset + 1U;
m_rxAX25Data.addData((unsigned char*)&data1, sizeof(unsigned int));
unsigned char data2 = TAG_RSSI;
m_rxFMData.addData(&data2, 1U);
m_rxAX25Data.addData(m_buffer + m_offset, m_length - m_offset);
}

View File

@@ -19,6 +19,6 @@
#if !defined(VERSION_H)
#define VERSION_H
const char* VERSION = "20230730";
const char* VERSION = "20230804";
#endif

View File

@@ -198,6 +198,8 @@
},
"type": {"$ref": "#/$defs/ax25_type"},
"pid": {"$ref": "#/$defs/ax25_pid"},
"rssi": {"$ref": "#/$defs/rssi"},
"ber": {"$ref": "#/$defs/ber"},
"data": {"type": "string"},
"required": ["timestamp", "source", "destination", "source", "type"]
},