#include "services/noise_test.h" #include #include #include #include "platform/console.h" #include "sweep_view.h" namespace roro { void NoiseTest::start(std::vector conditions, int passes) { if (running()) return; conditions_ = std::move(conditions); results_.clear(); report_.clear(); passes_ = passes; index_ = 0; console.printf("noise test: %u conditions, %d passes each\n", (unsigned)conditions_.size(), passes_); begin(millis()); } void NoiseTest::begin(uint32_t nowMs) { const Condition& c = conditions_[index_]; if (c.apply) c.apply(); radio_.sweep(true); // sets the radio up again, with whatever the condition changed std::fill(std::begin(lowest_), std::end(lowest_), 0); std::fill(std::begin(counts_), std::end(counts_), 0); seen_ = 0; phase_ = Phase::Settle; phaseMs_ = nowMs; } void NoiseTest::step(uint32_t nowMs) { if (phase_ == Phase::Idle) return; const Condition& c = conditions_[index_]; if (phase_ == Phase::Settle) { if (nowMs - phaseMs_ < c.settleMs) return; lastSeq_ = radio_.sweeps(); phase_ = Phase::Sweep; phaseMs_ = nowMs; return; } SweepFrame f; if (radio_.sweeps() != lastSeq_ && radio_.sweepFrame(f)) { lastSeq_ = f.seq; fromHz_ = f.fromHz, stepHz_ = f.stepHz, steps_ = f.steps; for (uint16_t i = 0; i < f.steps; i++) { lowest_[i] = seen_ ? std::min(lowest_[i], f.dbm[i]) : f.dbm[i]; counts_[std::clamp(-f.dbm[i], 0, 127)]++; } seen_++; } bool stuck = nowMs - phaseMs_ > 20000; // the radio never swept: don't hang the test if (seen_ < passes_ && !stuck) return; Result r; r.name = c.name; if (seen_) { // The floor: the median of every reading. The top and the peaks: from the lowest reading // at each step, so a burst in one pass doesn't count and a steady carrier does. uint32_t total = 0, half = static_cast(seen_) * steps_ / 2; for (int level = 127; level >= 0; level--) { total += counts_[level]; if (total > half) { r.floor = -level; break; } } lora::SweepStats st = lora::summarize(lowest_, steps_, fromHz_, stepHz_); r.top = st.top; char p[32]; for (auto& peak : st.peaks) { std::snprintf(p, sizeof p, "%s%.1f %d", r.peaks.empty() ? "" : ", ", peak.hz / 1e6, peak.dbm); r.peaks += p; } } else { r.name += " (no sweep)"; } results_.push_back(r); radio_.sweep(false); if (c.restore) c.restore(); if (++index_ < conditions_.size()) begin(nowMs); else finish(); } void NoiseTest::finish() { phase_ = Phase::Idle; char line[160]; report_.push_back("noise test: floor = median of every reading; top and peaks = steady (lowest of the passes), dBm at 125 kHz"); for (auto& r : results_) { std::snprintf(line, sizeof line, "noise test: %-26s floor %4d top %4d %s", r.name.c_str(), r.floor, r.top, r.peaks.empty() ? "no steady peak" : r.peaks.c_str()); report_.push_back(line); } report_.push_back("noise test: done"); printReport(console); } void NoiseTest::printReport(Print& out) const { if (report_.empty()) return (void)out.println(running() ? "noise test: still running" : "noise test: none run yet"); for (auto& l : report_) out.println(l.c_str()); } } // namespace roro