Public Access
The Sniffer lists packets newest first (time, RSSI, SNR, and for Meshtastic the sender, receiver and hops), with the clear header and a hex dump on Enter (Q96); `p` picks an EU868 preset, kept in Settings (Q95). `c` starts a Capture: pcap with LoRaTap in /captures/lora, its own Clean-up category, recorded by a small Service so it carries on with the App closed (Q97, Q100). The Status Bar shows L while listening, bright on each packet, and CAP while capturing (Q101). StorageService gains raw appends for binary files. Debug Builds get `lora inject` to test all of this with no transmitter in range: a Capture made on the device reads back in TShark field for field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
99 lines
3.4 KiB
C++
99 lines
3.4 KiB
C++
#include <unity.h>
|
|
|
|
#include <vector>
|
|
|
|
#include "loratap.h"
|
|
|
|
using namespace roro::lora;
|
|
|
|
void setUp() {}
|
|
void tearDown() {}
|
|
|
|
static uint32_t le32(const std::vector<uint8_t>& b, size_t at) {
|
|
return b[at] | b[at + 1] << 8 | b[at + 2] << 16 | (uint32_t)b[at + 3] << 24;
|
|
}
|
|
|
|
void test_pcap_global_header() {
|
|
std::vector<uint8_t> out;
|
|
appendPcapHeader(out);
|
|
TEST_ASSERT_EQUAL_size_t(24, out.size());
|
|
TEST_ASSERT_EQUAL_HEX32(0xA1B2C3D4, le32(out, 0)); // microsecond timestamps, little-endian file
|
|
TEST_ASSERT_EQUAL_UINT8(2, out[4]); // version 2.4
|
|
TEST_ASSERT_EQUAL_UINT8(4, out[6]);
|
|
TEST_ASSERT_EQUAL_UINT32(65535, le32(out, 16)); // snap length
|
|
TEST_ASSERT_EQUAL_UINT32(270, le32(out, 20)); // LINKTYPE_LORATAP
|
|
}
|
|
|
|
void test_record_with_loratap_v0() {
|
|
RxInfo rx;
|
|
rx.frequencyHz = 869525000;
|
|
rx.bandwidthKHz = 250;
|
|
rx.spreadingFactor = 11;
|
|
rx.rssi = -97.0f;
|
|
rx.snr = 6.25f;
|
|
rx.noiseFloor = -110.0f;
|
|
rx.syncWord = 0x2B;
|
|
const uint8_t payload[] = {1, 2, 3};
|
|
std::vector<uint8_t> out;
|
|
appendRecord(out, 1791230400u, 250000u, rx, payload, sizeof payload);
|
|
|
|
TEST_ASSERT_EQUAL_size_t(16 + 15 + 3, out.size());
|
|
TEST_ASSERT_EQUAL_UINT32(1791230400u, le32(out, 0));
|
|
TEST_ASSERT_EQUAL_UINT32(250000u, le32(out, 4));
|
|
TEST_ASSERT_EQUAL_UINT32(18, le32(out, 8)); // captured: LoRaTap header + payload
|
|
TEST_ASSERT_EQUAL_UINT32(18, le32(out, 12));
|
|
|
|
const uint8_t* t = out.data() + 16; // LoRaTap v0, big-endian
|
|
TEST_ASSERT_EQUAL_UINT8(0, t[0]); // version
|
|
TEST_ASSERT_EQUAL_UINT8(0, t[1]); // padding
|
|
TEST_ASSERT_EQUAL_UINT16(15, t[2] << 8 | t[3]);
|
|
TEST_ASSERT_EQUAL_UINT32(869525000u, (uint32_t)t[4] << 24 | t[5] << 16 | t[6] << 8 | t[7]);
|
|
TEST_ASSERT_EQUAL_UINT8(2, t[8]); // bandwidth in 125 kHz steps
|
|
TEST_ASSERT_EQUAL_UINT8(11, t[9]);
|
|
TEST_ASSERT_EQUAL_UINT8(42, t[10]); // packet RSSI: -139 + 42 = -97 dBm (SNR >= 0)
|
|
TEST_ASSERT_EQUAL_UINT8(42, t[11]); // max RSSI: the packet's
|
|
TEST_ASSERT_EQUAL_UINT8(29, t[12]); // current RSSI: the noise floor, -139 + 29 = -110 dBm
|
|
TEST_ASSERT_EQUAL_UINT8(25, t[13]); // SNR in quarter dB
|
|
TEST_ASSERT_EQUAL_HEX8(0x2B, t[14]);
|
|
TEST_ASSERT_EQUAL_UINT8(3, t[17]);
|
|
}
|
|
|
|
// Below 0 dB SNR the spec says quarter dB, but Wireshark (checked with tshark 4.2) reads plain dBm.
|
|
void test_negative_snr() {
|
|
RxInfo rx;
|
|
rx.bandwidthKHz = 125;
|
|
rx.rssi = -120.5f;
|
|
rx.snr = -7.5f;
|
|
std::vector<uint8_t> out;
|
|
appendRecord(out, 0, 0, rx, nullptr, 0);
|
|
const uint8_t* t = out.data() + 16;
|
|
TEST_ASSERT_EQUAL_UINT8(1, t[8]);
|
|
TEST_ASSERT_EQUAL_UINT8(19, t[10]); // -139 + 19 = -120 dBm (rounded half away)
|
|
TEST_ASSERT_EQUAL_INT8(-30, static_cast<int8_t>(t[13])); // -7.5 dB
|
|
}
|
|
|
|
void test_rssi_clamped() {
|
|
RxInfo rx;
|
|
rx.rssi = -150.0f; // below what LoRaTap can say
|
|
rx.snr = 1.0f;
|
|
rx.noiseFloor = 20.0f;
|
|
std::vector<uint8_t> out;
|
|
appendRecord(out, 0, 0, rx, nullptr, 0);
|
|
TEST_ASSERT_EQUAL_UINT8(0, out[16 + 10]);
|
|
TEST_ASSERT_EQUAL_UINT8(159, out[16 + 12]);
|
|
}
|
|
|
|
void test_capture_path() {
|
|
TEST_ASSERT_EQUAL_STRING("/captures/lora/20261005-200000.pcap", capturePath(1791230400).c_str());
|
|
}
|
|
|
|
int main() {
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_pcap_global_header);
|
|
RUN_TEST(test_record_with_loratap_v0);
|
|
RUN_TEST(test_negative_snr);
|
|
RUN_TEST(test_rssi_clamped);
|
|
RUN_TEST(test_capture_path);
|
|
return UNITY_END();
|
|
}
|