Files
pyxis/tests/native/test_radio_activity.cpp
T

147 lines
5.8 KiB
C++

#include <cstdint>
#include <iostream>
#include "RadioActivityHistory.h"
using RadioActivity::Event;
using RadioActivity::History;
using RadioActivity::has_event;
int main() {
int passed = 0;
int failed = 0;
auto check = [&](const char* name, bool condition) {
if (condition) {
++passed;
} else {
++failed;
std::cerr << "FAIL: " << name << "\n";
}
};
History history;
for (std::size_t i = 0; i < History::CAPACITY + 5; ++i) {
history.record(-130 + static_cast<int16_t>(i % 3));
}
auto bounded = history.snapshot();
check("history has fixed capacity", bounded.count == History::CAPACITY);
check("snapshot is chronological after wrap",
bounded.samples[0].sequence == 5 &&
bounded.samples[bounded.count - 1].sequence == History::CAPACITY + 4);
History baseline;
baseline.record(-121);
baseline.record(-120);
baseline.record(-119);
baseline.record(-120);
auto learned = baseline.snapshot();
check("noise floor becomes ready from accepted samples", learned.noise_floor_ready);
check("noise floor learns a stable baseline", learned.noise_floor == -120);
baseline.record(-95);
auto interference = baseline.snapshot();
check("sample above noise threshold is interference",
has_event(interference.samples[interference.count - 1], Event::Interference));
check("interference does not raise learned noise floor", interference.noise_floor == -120);
baseline.record(-112);
auto accepted = baseline.snapshot();
check("sample within eleven dB is accepted as noise",
accepted.samples[accepted.count - 1].events == 0);
check("accepted sample updates bounded noise estimator", accepted.noise_floor > -120);
History events;
events.mark_event(Event::Tx);
events.mark_event(Event::Rx);
check("events do not append off-cadence samples", events.snapshot().count == 0);
events.record(-118);
auto marked = events.snapshot();
check("events share one fixed-cadence bucket", marked.count == 1);
check("transmit metadata survives until the next bucket", has_event(marked.samples[0], Event::Tx));
check("receive metadata shares the next bucket", has_event(marked.samples[0], Event::Rx));
check("channel load counts activity", marked.channel_load_percent == 100);
History skipped;
skipped.mark_event(Event::Tx, 3);
skipped.record_gap();
skipped.record_gap();
skipped.record_gap();
auto tx_gap = skipped.snapshot();
check("skipped cadence buckets are represented", tx_gap.count == 3);
check("TX duration spans every queued bucket",
has_event(tx_gap.samples[0], Event::Tx) &&
has_event(tx_gap.samples[1], Event::Tx) &&
has_event(tx_gap.samples[2], Event::Tx));
check("gap buckets do not invent RSSI", !tx_gap.samples[0].rssi_valid);
check("TX gap buckets contribute to load", tx_gap.channel_load_percent == 100);
History aligned;
aligned.mark_event(Event::Tx, 2);
aligned.record_gap(false);
aligned.record_gap();
aligned.record(-100);
auto right_aligned = aligned.snapshot();
check("pending duration does not mark older catch-up gaps",
!has_event(right_aligned.samples[0], Event::Tx));
check("pending duration is right-aligned to current time",
has_event(right_aligned.samples[1], Event::Tx) &&
has_event(right_aligned.samples[2], Event::Tx));
History rx_burst;
rx_burst.mark_event(Event::Rx);
rx_burst.mark_event(Event::Rx);
rx_burst.record(-110);
rx_burst.record(-120);
auto rx_bucket = rx_burst.snapshot();
check("multiple RX events coalesce within one cadence bucket",
has_event(rx_bucket.samples[0], Event::Rx));
check("coalesced RX does not spill into a future bucket",
!has_event(rx_bucket.samples[1], Event::Rx));
History unknown_gap;
unknown_gap.record_gap();
auto unknown = unknown_gap.snapshot();
check("unknown contention gap advances time without activity",
unknown.count == 1 && unknown.channel_load_percent == 0);
check("newest gap makes current RSSI unavailable", !unknown.current_rssi_valid);
unknown_gap.record(-101);
auto current = unknown_gap.snapshot();
check("newest measured bucket makes current RSSI valid", current.current_rssi_valid);
check("current RSSI comes from the newest measured bucket", current.current_rssi == -101);
History clamped;
clamped.record(-200);
clamped.record(20);
auto limits = clamped.snapshot();
check("RSSI is clamped to graph bounds",
limits.samples[0].rssi_dbm == History::MIN_RSSI_DBM &&
limits.samples[1].rssi_dbm == History::MAX_RSSI_DBM);
check("channel load is bounded", limits.channel_load_percent <= 100);
History resettable;
resettable.record(-121);
resettable.record(-120);
resettable.record(-119);
resettable.record(-120);
resettable.mark_event(Event::Tx);
const uint32_t prior_generation = resettable.generation();
resettable.reset();
auto cleared = resettable.snapshot();
check("reset clears samples", cleared.count == 0);
check("reset invalidates current RSSI", !cleared.current_rssi_valid);
check("reset clears learned noise floor", !cleared.noise_floor_ready);
check("reset clears channel load", cleared.channel_load_percent == 0);
check("reset advances the current-channel generation",
resettable.generation() != prior_generation);
resettable.record(-100);
auto after_reset = resettable.snapshot();
check("reset clears pending events and restarts sequence",
after_reset.samples[0].events == 0 &&
after_reset.samples[0].sequence == 0);
std::cout << passed << " passed, " << failed << " failed\n";
return failed == 0 ? 0 : 1;
}