Public Access
Tab in the LoRa Scanner sweeps 863-870 MHz in 100 kHz steps (the strongest of three RSSI readings at each, at 125 kHz), shown as bars with peak hold over a waterfall, the Sniffer's frequency marked (Q98). The Sweep pauses the Sniffer and keeps its packets; Tab resumes it (Q99). Status Bar: SW. The floor, top and peaks are host-tested; `lora sweep on|off|dump` prints them on the console. At the desk: about 607 ms a pass, a flat floor at -100 to -102 dBm (15 dB above the chip's own) and a steady carrier at 863.2 MHz. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
75 lines
2.5 KiB
C++
75 lines
2.5 KiB
C++
#include <unity.h>
|
|
|
|
#include <vector>
|
|
|
|
#include "sweep_view.h"
|
|
|
|
using namespace roro::lora;
|
|
|
|
void setUp() {}
|
|
void tearDown() {}
|
|
|
|
// 863.0 to 870.0 MHz in 100 kHz steps: 71 readings.
|
|
static std::vector<int8_t> band(int8_t floor) { return std::vector<int8_t>(71, floor); }
|
|
|
|
void test_floor_is_the_median() {
|
|
auto b = band(-95);
|
|
b[10] = -60; // a few strong steps don't move the median
|
|
b[11] = -62;
|
|
b[50] = -70;
|
|
SweepStats s = summarize(b.data(), b.size(), 863000000, 100000);
|
|
TEST_ASSERT_EQUAL_INT(-95, s.floor);
|
|
TEST_ASSERT_EQUAL_INT(-60, s.top);
|
|
}
|
|
|
|
void test_peaks_strongest_first_and_merged() {
|
|
auto b = band(-95);
|
|
b[11] = -70; // 864.1 MHz, with shoulders: one peak, not three
|
|
b[10] = -75;
|
|
b[12] = -78;
|
|
b[65] = -55; // 869.5 MHz
|
|
b[30] = -90; // only 5 dB above the floor: not a peak
|
|
SweepStats s = summarize(b.data(), b.size(), 863000000, 100000);
|
|
TEST_ASSERT_EQUAL_size_t(2, s.peaks.size());
|
|
TEST_ASSERT_EQUAL_UINT32(869500000u, s.peaks[0].hz);
|
|
TEST_ASSERT_EQUAL_INT(-55, s.peaks[0].dbm);
|
|
TEST_ASSERT_EQUAL_UINT32(864100000u, s.peaks[1].hz);
|
|
}
|
|
|
|
void test_at_most_three_peaks() {
|
|
auto b = band(-100);
|
|
for (int i = 5; i < 70; i += 10) b[i] = static_cast<int8_t>(-80 + i / 10);
|
|
SweepStats s = summarize(b.data(), b.size(), 863000000, 100000);
|
|
TEST_ASSERT_EQUAL_size_t(3, s.peaks.size());
|
|
TEST_ASSERT_EQUAL_INT(-74, s.peaks[0].dbm); // the strongest three
|
|
}
|
|
|
|
void test_flat_band_has_no_peaks() {
|
|
auto b = band(-88);
|
|
SweepStats s = summarize(b.data(), b.size(), 863000000, 100000);
|
|
TEST_ASSERT_EQUAL_size_t(0, s.peaks.size());
|
|
TEST_ASSERT_EQUAL_INT(-88, s.floor);
|
|
}
|
|
|
|
// The waterfall's scale: dark at the bottom, through blue and green to red at the top.
|
|
void test_heat_scale() {
|
|
TEST_ASSERT_EQUAL_UINT8(0, heatLevel(-130, -120, -60));
|
|
TEST_ASSERT_EQUAL_UINT8(0, heatLevel(-120, -120, -60));
|
|
TEST_ASSERT_EQUAL_UINT8(255, heatLevel(-60, -120, -60));
|
|
TEST_ASSERT_EQUAL_UINT8(255, heatLevel(-20, -120, -60));
|
|
TEST_ASSERT_EQUAL_UINT8(128, heatLevel(-90, -120, -60));
|
|
TEST_ASSERT_TRUE(heatColor(0) != heatColor(255));
|
|
TEST_ASSERT_EQUAL_HEX16(0x0000, heatColor(0)); // black
|
|
TEST_ASSERT_EQUAL_HEX16(0xF800, heatColor(255)); // red
|
|
}
|
|
|
|
int main() {
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_floor_is_the_median);
|
|
RUN_TEST(test_peaks_strongest_first_and_merged);
|
|
RUN_TEST(test_at_most_three_peaks);
|
|
RUN_TEST(test_flat_band_has_no_peaks);
|
|
RUN_TEST(test_heat_scale);
|
|
return UNITY_END();
|
|
}
|