LoRa Scanner: MeshCore searches for nodes and their answers, read

The first two MeshCore packets heard were these: a search for repeaters
and a repeater answering. Both are in clear; they are the test's bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
This commit is contained in:
2026-10-09 20:51:15 +02:00
co-authored by Claude Opus 5.5
parent 8ba41f72b6
commit 3339d9ee59
6 changed files with 97 additions and 4 deletions
+42
View File
@@ -146,6 +146,47 @@ void test_detail_lines() {
TEST_ASSERT_EQUAL_size_t(0, lora::headerLines(text, sizeof text, lora::Protocol::None).size());
}
// As received near Brussels on 2026-10-09: a search for repeaters, and one answering.
void test_discover_as_heard() {
const uint8_t ask[] = {0x2e, 0x00, 0x80, 0x04, 0xbb, 0xdd, 0x5f, 0x5a, 0x00, 0x00, 0x00, 0x00};
const uint8_t answer[] = {0x2e, 0x00, 0x92, 0x2d, 0xbb, 0xdd, 0x5f, 0x5a, 0x18, 0xa6, 0x3f, 0xae, 0xf4, 0x01, 0x9b, 0xee,
0xd0, 0x4f, 0xfd, 0xd3, 0xd1, 0x72, 0x65, 0xba, 0x4b, 0x9e, 0xeb, 0xd0, 0xdf, 0xb0, 0xb2, 0xc4,
0x94, 0xec, 0x50, 0x5b, 0x03, 0x20, 0x59, 0x9d};
Packet p;
Discover d;
TEST_ASSERT_TRUE(parsePacket(ask, sizeof ask, p));
TEST_ASSERT_TRUE(p.route == Route::Direct);
TEST_ASSERT_TRUE(p.type == Payload::Control);
TEST_ASSERT_TRUE(parseDiscover(p, d));
TEST_ASSERT_FALSE(d.response);
TEST_ASSERT_EQUAL_HEX8(0x04, d.kinds);
TEST_ASSERT_EQUAL_HEX32(0x5a5fddbb, d.tag);
TEST_ASSERT_TRUE(parsePacket(answer, sizeof answer, p));
TEST_ASSERT_TRUE(parseDiscover(p, d));
TEST_ASSERT_TRUE(d.response);
TEST_ASSERT_EQUAL(2, d.kind);
TEST_ASSERT_EQUAL_FLOAT(11.25f, d.snr);
TEST_ASSERT_EQUAL_HEX32(0x5a5fddbb, d.tag);
TEST_ASSERT_EQUAL_size_t(32, d.idLen);
lora::PacketSummary a{ask, sizeof ask, -107.0f, -7.0f, true, lora::Protocol::MeshCore};
TEST_ASSERT_EQUAL_STRING("20:44:01 -107 -7.0 who's there?", lora::row(a, "20:44:01").c_str());
lora::PacketSummary r{answer, sizeof answer, -96.0f, 3.0f, true, lora::Protocol::MeshCore};
TEST_ASSERT_EQUAL_STRING("20:44:02 -96 3.0 here 18a6", lora::row(r, "20:44:02").c_str());
auto lines = lora::headerLines(ask, sizeof ask, lora::Protocol::MeshCore);
TEST_ASSERT_EQUAL_size_t(4, lines.size());
TEST_ASSERT_EQUAL_STRING("MeshCore control, direct", lines[0].c_str());
TEST_ASSERT_EQUAL_STRING("No route in it: to a neighbour", lines[1].c_str());
TEST_ASSERT_EQUAL_STRING("A search for nodes nearby", lines[2].c_str());
TEST_ASSERT_EQUAL_STRING("Wanted: Repeater", lines[3].c_str());
lines = lora::headerLines(answer, sizeof answer, lora::Protocol::MeshCore);
TEST_ASSERT_EQUAL_size_t(5, lines.size());
TEST_ASSERT_EQUAL_STRING("Repeater answering a search", lines[2].c_str());
TEST_ASSERT_EQUAL_STRING("Key 18a63faef4019bee...", lines[3].c_str());
TEST_ASSERT_EQUAL_STRING("It heard the search at 11.2 dB SNR", lines[4].c_str());
}
void test_scan_presets() {
TEST_ASSERT_EQUAL_size_t(8, lora::scanPresetCount());
lora::ScanPreset p = lora::scanPreset(0); // the default stays first: it is stored by number
@@ -176,6 +217,7 @@ int main() {
RUN_TEST(test_advert);
RUN_TEST(test_rows);
RUN_TEST(test_detail_lines);
RUN_TEST(test_discover_as_heard);
RUN_TEST(test_scan_presets);
return UNITY_END();
}