1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2026-05-07 14:44:07 -04:00

expose demod fields for plaintext packets, gate on header CRC

This commit is contained in:
jvn314 2026-04-25 01:30:45 +00:00
parent 3da5bff59b
commit c368600b52
3 changed files with 70 additions and 21 deletions

View File

@ -1,8 +1,3 @@
## meshtasticdemodwebapiadapter.cpp:35
Initializes SWGChirpChatDemodSettings instead of SWGMeshtasticDemodSettings.
Copy-paste artifact from ChirpChat plugin. Fix separately in its own commit.
Branch: feature-meshtastic-more-udp or open a dedicated bug-fix branch.
## meshtasticdemodgui.cpp - checkbox signal pattern
All checkboxes currently use stateChanged(int) pattern.
Consider refactoring all to clicked(bool) for simplicity in a future cleanup commit.
@ -12,3 +7,12 @@ Affects: sendViaUDP, sendJsonViaUDP, and likely other checkboxes in the same fil
m_lastMsgBytes[0] is read without an explicit non-empty size check before the APRS detection block.
Pre-existing issue, not introduced by JSON UDP feature. Fix separately in its own commit.
Safe fix: add `&& !m_lastMsgBytes.isEmpty()` to the APRS condition.
## meshtasticdemod.cpp:658 - hash_matching_index fabricated
hash_matching_index is hardcoded to 0 for decrypted packets and "none"
for all others. The decoder sorts key candidates by hash match but does
not expose the actual index used in DecodeResult. The field is therefore
inaccurate for multi-key configurations. Fix requires extending
DecodeResult to expose the matched key index, then wiring it through
buildMeshtasticJsonPacket. For now the field remains but should not be
relied upon.

View File

@ -25,7 +25,6 @@
rf.snr_db Signal minus noise in dB
lora.packet_type meshtastic, lorawan, helium, unset, or custom
lora.sync_word Hex string e.g. 0x2b
lora.frame_id Hex string e.g. 0x1
lora.header_fec ok, fix, err, or n/a
lora.header_crc ok or err
lora.payload_fec ok, fix, err, or n/a (n/a when early_eom is true)
@ -40,6 +39,7 @@
## Fields Present Only When lora.packet_type Is meshtastic
meshtastic.channel_hash Hex string e.g. 0x08
meshtastic.packet_id Meshtastic 32-bit unique packet ID hex string e.g. 0x5a42f351
meshtastic.hash_matching_index Integer index of matched key, or none
meshtastic.decryption decrypted, plaintext, or not_decrypted
meshtastic.key_label Key name, no_key, or unknown_key
@ -78,6 +78,14 @@
n/a Not applicable, packet ended early before payload was received
## Note on packet_id
The Meshtastic packet ID is a large random 32-bit value generated by the originating node.
Its primary purpose is to serve as a cryptographic nonce component for AES-CTR decryption,
combined with the from node ID. It also serves as a deduplication key in the mesh so that
relay nodes can discard copies of already-forwarded packets. It is not a sequential counter.
## Example: Fully Decoded Meshtastic Packet
{
@ -89,12 +97,11 @@
"spreading_factor": 11,
"signal_db": -46.0,
"noise_db": -84.2,
"snr_db": 38.2
"snr_db": 13.6
},
"lora": {
"packet_type": "meshtastic",
"sync_word": "0x2b",
"frame_id": "0x1",
"header_fec": "ok",
"header_crc": "ok",
"payload_fec": "ok",
@ -107,6 +114,7 @@
},
"meshtastic": {
"channel_hash": "0x08",
"packet_id": "0x5a42f351",
"hash_matching_index": 0,
"decryption": "decrypted",
"key_label": "LongFast:default",
@ -144,6 +152,7 @@
},
"meshtastic": {
"channel_hash": "0x08",
"packet_id": "unknown",
"hash_matching_index": "none",
"decryption": "not_decrypted",
"key_label": "unknown_key",

View File

@ -632,12 +632,59 @@ QString MeshtasticDemod::buildMeshtasticJsonPacket(
if (syncWord == 0x2B)
{
QJsonObject mesh;
const bool headerCrcOk = msg.getHeaderCRCStatus();
const QString channelHash = getMeshField(meshResult, "header.channel_hash");
mesh["channel_hash"] = channelHash.isEmpty() ? QStringLiteral("unknown") : channelHash;
if (headerCrcOk && !channelHash.isEmpty()) {
mesh["channel_hash"] = channelHash;
} else {
mesh["channel_hash"] = QStringLiteral("unknown");
}
const QString packetId = getMeshField(meshResult, "header.id");
mesh["packet_id"] = packetId.isEmpty() ? QStringLiteral("unknown") : packetId;
if (headerCrcOk && !packetId.isEmpty()) {
mesh["packet_id"] = packetId;
} else {
mesh["packet_id"] = QStringLiteral("unknown");
}
const QString hopStart = getMeshField(meshResult, "header.hop_start");
const QString hopLimit = getMeshField(meshResult, "header.hop_limit");
const QString relayNode = getMeshField(meshResult, "header.relay_node");
if (headerCrcOk)
{
if (!hopStart.isEmpty()) {
mesh["hop_start"] = hopStart.toInt();
} else {
mesh["hop_start"] = QStringLiteral("unknown");
}
if (!hopLimit.isEmpty()) {
mesh["hop_limit"] = hopLimit.toInt();
} else {
mesh["hop_limit"] = QStringLiteral("unknown");
}
if (!hopStart.isEmpty() && !hopLimit.isEmpty()) {
mesh["hops_consumed"] = hopStart.toInt() - hopLimit.toInt();
} else {
mesh["hops_consumed"] = QStringLiteral("unknown");
}
if (!relayNode.isEmpty()) {
mesh["relay_node"] = relayNode.toInt();
} else {
mesh["relay_node"] = QStringLiteral("unknown");
}
}
else
{
mesh["hop_start"] = QStringLiteral("unknown");
mesh["hop_limit"] = QStringLiteral("unknown");
mesh["hops_consumed"] = QStringLiteral("unknown");
mesh["relay_node"] = QStringLiteral("unknown");
}
const QString decodePath = getMeshField(meshResult, "decode.path");
const QString keyLabel = getMeshField(meshResult, "decode.key_label");
@ -672,17 +719,6 @@ QString MeshtasticDemod::buildMeshtasticJsonPacket(
{
mesh["channel_type"] = m_settings.m_meshtasticPresetName;
const QString hopStart = getMeshField(meshResult, "header.hop_start");
const QString hopLimit = getMeshField(meshResult, "header.hop_limit");
const QString relayNode = getMeshField(meshResult, "header.relay_node");
if (!hopStart.isEmpty()) { mesh["hop_start"] = hopStart.toInt(); }
if (!hopLimit.isEmpty()) { mesh["hop_limit"] = hopLimit.toInt(); }
if (!hopStart.isEmpty() && !hopLimit.isEmpty()) {
mesh["hops_consumed"] = hopStart.toInt() - hopLimit.toInt();
}
if (!relayNode.isEmpty()) { mesh["relay_node"] = relayNode.toInt(); }
QJsonObject fields;
for (const auto& field : meshResult.fields)
{