#include #include #include #include "meshtastic_header.h" #include "meshtastic_presets.h" using namespace roro::meshtastic; void setUp() {} void tearDown() {} // The 16 bytes every Meshtastic packet starts with, little-endian, never encrypted. static const uint8_t kHeader[] = { 0xFF, 0xFF, 0xFF, 0xFF, // to: broadcast 0x78, 0x56, 0x34, 0x12, // from: !12345678 0xEF, 0xBE, 0xAD, 0xDE, // id 0x6B, // flags: hop start 3, want ack, hop limit 3 0x08, // channel hash: LongFast with the default key 0x00, // next hop: none 0x42, // relay node: last byte of the Node that relayed it }; void test_header_fields() { PacketHeader h; TEST_ASSERT_TRUE(parseHeader(kHeader, sizeof kHeader, h)); TEST_ASSERT_EQUAL_HEX32(0xFFFFFFFF, h.to); TEST_ASSERT_EQUAL_HEX32(0x12345678, h.from); TEST_ASSERT_EQUAL_HEX32(0xDEADBEEF, h.id); TEST_ASSERT_EQUAL_UINT8(0x08, h.channelHash); TEST_ASSERT_EQUAL_UINT8(0x00, h.nextHop); TEST_ASSERT_EQUAL_UINT8(0x42, h.relayNode); TEST_ASSERT_TRUE(h.broadcast()); } void test_header_flags() { PacketHeader h; parseHeader(kHeader, sizeof kHeader, h); TEST_ASSERT_EQUAL_UINT8(3, h.hopLimit()); TEST_ASSERT_EQUAL_UINT8(3, h.hopStart()); TEST_ASSERT_TRUE(h.wantAck()); TEST_ASSERT_FALSE(h.viaMqtt()); TEST_ASSERT_EQUAL_INT(0, h.hopsAway()); uint8_t relayed[16]; std::memcpy(relayed, kHeader, 16); relayed[12] = 0xF1 | 0x10; // hop start 7, via MQTT, hop limit 1 parseHeader(relayed, 16, h); TEST_ASSERT_EQUAL_UINT8(1, h.hopLimit()); TEST_ASSERT_EQUAL_UINT8(7, h.hopStart()); TEST_ASSERT_TRUE(h.viaMqtt()); TEST_ASSERT_FALSE(h.wantAck()); TEST_ASSERT_EQUAL_INT(6, h.hopsAway()); } void test_header_from_old_firmware_has_no_hop_start() { uint8_t old[16]; std::memcpy(old, kHeader, 16); old[12] = 0x02; // hop limit 2, hop start 0: firmware before 2.3 didn't set it PacketHeader h; parseHeader(old, 16, h); TEST_ASSERT_EQUAL_INT(-1, h.hopsAway()); // unknown, not "2 hops below zero" } void test_header_too_short() { PacketHeader h; TEST_ASSERT_FALSE(parseHeader(kHeader, 15, h)); TEST_ASSERT_FALSE(parseHeader(nullptr, 0, h)); } void test_node_ids() { TEST_ASSERT_EQUAL_STRING("!12345678", nodeId(0x12345678).c_str()); TEST_ASSERT_EQUAL_STRING("!0000abcd", nodeId(0xABCD).c_str()); TEST_ASSERT_EQUAL_STRING("all", nodeId(kBroadcast).c_str()); } // Meshtastic shows these hashes for the default key ("AQ=="). void test_channel_hash_with_default_key() { TEST_ASSERT_EQUAL_UINT8(8, channelHash("LongFast", kDefaultKey, sizeof kDefaultKey)); TEST_ASSERT_EQUAL_UINT8(31, channelHash("MediumFast", kDefaultKey, sizeof kDefaultKey)); } void test_djb2() { TEST_ASSERT_EQUAL_UINT32(5381, djb2("")); TEST_ASSERT_EQUAL_UINT32(130429955u, djb2("LongFast")); } void test_eu868_presets() { TEST_ASSERT_EQUAL_size_t(7, kEu868PresetCount); const Preset& lf = kEu868Presets[0]; // the default comes first TEST_ASSERT_EQUAL_STRING("LongFast", lf.name); TEST_ASSERT_EQUAL_FLOAT(250.0f, lf.bwKHz); TEST_ASSERT_EQUAL_UINT8(11, lf.sf); TEST_ASSERT_EQUAL_UINT8(5, lf.cr); const Preset* ls = findPreset("LongSlow"); TEST_ASSERT_NOT_NULL(ls); TEST_ASSERT_EQUAL_FLOAT(125.0f, ls->bwKHz); TEST_ASSERT_EQUAL_UINT8(12, ls->sf); TEST_ASSERT_EQUAL_UINT8(8, ls->cr); TEST_ASSERT_NULL(findPreset("ShortTurbo")); // 500 kHz doesn't fit the 250 kHz sub-band } // The frequency slot comes from the channel name's hash; an unnamed channel uses the preset's name. void test_eu868_frequencies() { TEST_ASSERT_EQUAL_UINT32(869525000u, eu868FrequencyHz(*findPreset("LongFast"))); TEST_ASSERT_EQUAL_UINT32(869525000u, eu868FrequencyHz(*findPreset("MediumFast"))); TEST_ASSERT_EQUAL_UINT32(869462500u, eu868FrequencyHz(*findPreset("LongSlow"))); TEST_ASSERT_EQUAL_UINT32(869587500u, eu868FrequencyHz(*findPreset("LongMod"))); // A 250 kHz preset has one slot, whatever the channel's name. TEST_ASSERT_EQUAL_UINT32(869525000u, eu868FrequencyHz(*findPreset("LongFast"), "Belgium")); } void test_radio_constants() { TEST_ASSERT_EQUAL_HEX8(0x2B, kSyncWord); TEST_ASSERT_EQUAL_UINT16(16, kPreambleLength); } int main() { UNITY_BEGIN(); RUN_TEST(test_header_fields); RUN_TEST(test_header_flags); RUN_TEST(test_header_from_old_firmware_has_no_hop_start); RUN_TEST(test_header_too_short); RUN_TEST(test_node_ids); RUN_TEST(test_channel_hash_with_default_key); RUN_TEST(test_djb2); RUN_TEST(test_eu868_presets); RUN_TEST(test_eu868_frequencies); RUN_TEST(test_radio_constants); return UNITY_END(); }