mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-10-23 09:52:23 +00:00
fix(mqtt): omit SNR/RSSI instead of publishing placeholder values
The reconstruction-less fallback path in buildPacketJSON (queued packet with no captured raw frame that cannot be re-serialized) published fabricated SNR 12.5 / RSSI -65 that consumers could not distinguish from real radio readings. It now passes NAN / MQTT_RSSI_UNKNOWN, and buildPacketMessage omits the SNR/RSSI keys entirely when no measurement exists, mirroring the existing score omission. Ports the fix from ACETyr's PR #13 (0a037040) on mqtt-bridge-implementation. Co-Authored-By: ACETyr <35745290+ACETyr@users.noreply.github.com>
This commit is contained in:
1 parent
91d9db9c7f
commit
a91009e3dd
4 files changed
+93
-6
No files matched your search
@@ -209,8 +209,8 @@ int MQTTMessageBuilder::buildPacketJSON(
|
||||
packet_type, route_str,
|
||||
packet->payload_len,
|
||||
raw_hex,
|
||||
12.5f, // SNR - using reasonable default
|
||||
-65, // RSSI - using reasonable default
|
||||
NAN, // SNR - unknown on this reconstruction-less fallback path
|
||||
MQTT_RSSI_UNKNOWN, // RSSI - unknown on this reconstruction-less fallback path
|
||||
NAN, // score - unknown on this reconstruction-less fallback path
|
||||
hash_str,
|
||||
has_path ? packet->path : nullptr,
|
||||
|
||||
@@ -120,8 +120,6 @@ int MQTTPayloadBuilder::buildPacketMessage(
|
||||
snprintf(len_str, sizeof(len_str), "%d", len);
|
||||
snprintf(packet_type_str, sizeof(packet_type_str), "%d", packet_type);
|
||||
snprintf(payload_len_str, sizeof(payload_len_str), "%d", payload_len);
|
||||
snprintf(snr_str, sizeof(snr_str), "%.1f", snr);
|
||||
snprintf(rssi_str, sizeof(rssi_str), "%d", rssi);
|
||||
|
||||
root["timestamp"] = timestamp;
|
||||
root["hash"] = hash;
|
||||
@@ -138,8 +136,16 @@ int MQTTPayloadBuilder::buildPacketMessage(
|
||||
root["origin_id"] = origin_id;
|
||||
|
||||
if (direction && strcmp(direction, "rx") == 0) {
|
||||
root["SNR"] = snr_str;
|
||||
root["RSSI"] = rssi_str;
|
||||
// Omit SNR/RSSI entirely when no measurement exists rather than publish
|
||||
// a placeholder a consumer could mistake for a real reading.
|
||||
if (!isnan(snr)) {
|
||||
snprintf(snr_str, sizeof(snr_str), "%.1f", snr);
|
||||
root["SNR"] = snr_str;
|
||||
}
|
||||
if (rssi != MQTT_RSSI_UNKNOWN) {
|
||||
snprintf(rssi_str, sizeof(rssi_str), "%d", rssi);
|
||||
root["RSSI"] = rssi_str;
|
||||
}
|
||||
if (!isnan(score)) {
|
||||
snprintf(score_str, sizeof(score_str), "%d", static_cast<int>(score * 1000));
|
||||
root["score"] = score_str;
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
// Mesh-independent JSON serialization core for MQTT publication payloads.
|
||||
// MQTTMessageBuilder keeps the firmware-facing API and delegates these three
|
||||
// deterministic contracts here so they can be exercised by native tests.
|
||||
|
||||
// RSSI sentinel for "no radio measurement": far outside the small negative ints
|
||||
// a radio actually reports, so a real reading can never collide with it.
|
||||
static const int MQTT_RSSI_UNKNOWN = INT16_MIN;
|
||||
|
||||
class MQTTPayloadBuilder {
|
||||
public:
|
||||
static int buildStatusMessage(
|
||||
|
||||
@@ -36,6 +36,16 @@ static int buildRepresentativePacket(JsonDocument& scratch, const char* directio
|
||||
path_hash_size, 64, buffer, buffer_size);
|
||||
}
|
||||
|
||||
static int buildPacketWithMetrics(JsonDocument& scratch, const char* direction,
|
||||
float snr, int rssi, float score,
|
||||
char* buffer, size_t buffer_size) {
|
||||
return MQTTPayloadBuilder::buildPacketMessage(
|
||||
scratch, "DEN Repeater", "0123456789ABCDEF", kTimestamp, direction,
|
||||
"12:34:56", "18/07/2026", 42, 4, "D", 20, "A0B1C2D3",
|
||||
snr, rssi, score, "89ABCDEF01234567", nullptr, 0, 0, 64,
|
||||
buffer, buffer_size);
|
||||
}
|
||||
|
||||
TEST(MQTTPayloadBuilder, MinimalStatusHasExactRequiredContract) {
|
||||
JsonDocument scratch;
|
||||
char buffer[768];
|
||||
@@ -169,6 +179,72 @@ TEST(MQTTPayloadBuilder, RxPacketOmitsUnknownNanScore) {
|
||||
EXPECT_FALSE(parsed["score"].is<JsonVariant>());
|
||||
}
|
||||
|
||||
TEST(MQTTPayloadBuilder, RxPacketOmitsUnknownSnrAndRssi) {
|
||||
JsonDocument scratch;
|
||||
char buffer[2048];
|
||||
int len = buildPacketWithMetrics(
|
||||
scratch, "rx", NAN, MQTT_RSSI_UNKNOWN, NAN, buffer, sizeof(buffer));
|
||||
|
||||
ASSERT_GT(len, 0);
|
||||
JsonDocument parsed;
|
||||
ASSERT_FALSE(deserializeJson(parsed, buffer));
|
||||
EXPECT_FALSE(parsed["SNR"].is<JsonVariant>());
|
||||
EXPECT_FALSE(parsed["RSSI"].is<JsonVariant>());
|
||||
EXPECT_FALSE(parsed["score"].is<JsonVariant>());
|
||||
}
|
||||
|
||||
TEST(MQTTPayloadBuilder, RxPacketFormatsRealSnrAndRssi) {
|
||||
JsonDocument scratch;
|
||||
char buffer[2048];
|
||||
int len = buildPacketWithMetrics(
|
||||
scratch, "rx", 7.25f, -93, NAN, buffer, sizeof(buffer));
|
||||
|
||||
ASSERT_GT(len, 0);
|
||||
JsonDocument parsed;
|
||||
ASSERT_FALSE(deserializeJson(parsed, buffer));
|
||||
EXPECT_STREQ("7.2", parsed["SNR"].as<const char*>());
|
||||
EXPECT_STREQ("-93", parsed["RSSI"].as<const char*>());
|
||||
EXPECT_FALSE(parsed["score"].is<JsonVariant>());
|
||||
}
|
||||
|
||||
TEST(MQTTPayloadBuilder, RxPacketOmitsOnlyUnknownMetric) {
|
||||
// SNR unknown, RSSI real: only the SNR key is omitted.
|
||||
JsonDocument scratch;
|
||||
char buffer[2048];
|
||||
int len = buildPacketWithMetrics(
|
||||
scratch, "rx", NAN, -93, NAN, buffer, sizeof(buffer));
|
||||
|
||||
ASSERT_GT(len, 0);
|
||||
JsonDocument parsed;
|
||||
ASSERT_FALSE(deserializeJson(parsed, buffer));
|
||||
EXPECT_FALSE(parsed["SNR"].is<JsonVariant>());
|
||||
EXPECT_STREQ("-93", parsed["RSSI"].as<const char*>());
|
||||
|
||||
// RSSI unknown, SNR real: only the RSSI key is omitted.
|
||||
len = buildPacketWithMetrics(
|
||||
scratch, "rx", 7.25f, MQTT_RSSI_UNKNOWN, NAN, buffer, sizeof(buffer));
|
||||
|
||||
ASSERT_GT(len, 0);
|
||||
parsed.clear();
|
||||
ASSERT_FALSE(deserializeJson(parsed, buffer));
|
||||
EXPECT_STREQ("7.2", parsed["SNR"].as<const char*>());
|
||||
EXPECT_FALSE(parsed["RSSI"].is<JsonVariant>());
|
||||
}
|
||||
|
||||
TEST(MQTTPayloadBuilder, TxPacketOmitsSnrRssiAndScoreWithRealValues) {
|
||||
JsonDocument scratch;
|
||||
char buffer[2048];
|
||||
int len = buildPacketWithMetrics(
|
||||
scratch, "tx", 7.25f, -93, 0.5f, buffer, sizeof(buffer));
|
||||
|
||||
ASSERT_GT(len, 0);
|
||||
JsonDocument parsed;
|
||||
ASSERT_FALSE(deserializeJson(parsed, buffer));
|
||||
EXPECT_FALSE(parsed["SNR"].is<JsonVariant>());
|
||||
EXPECT_FALSE(parsed["RSSI"].is<JsonVariant>());
|
||||
EXPECT_FALSE(parsed["score"].is<JsonVariant>());
|
||||
}
|
||||
|
||||
TEST(MQTTPayloadBuilder, RawMessageHasExactContractAndEscapesData) {
|
||||
char buffer[512];
|
||||
JsonDocument doc;
|
||||
|
||||
Reference in new issue
Block a user