From 54d3b144ea332da1a6e03dab3585dd36d6efdef2 Mon Sep 17 00:00:00 2001 From: R0ck Date: Thu, 3 Sep 2026 22:30:09 +0100 Subject: [PATCH] seed the radio's noise per node, and say what the chip binned Three things the chip model needed a host for. A seed for its receiver noise, from MESHBENCH_NOISE_SEED. Firmware takes its entropy from the radio and derives its identity from it, so a chip that answers the same numbers to every node hands every node the same keypair - two nRF52 boards really did report one public key. Seeded rather than sampled so a run stays reproducible. A count of frames handed to a chip that was not listening and binned. It accuses this side rather than the firmware, and a node that hears everything and forwards nothing looks identical either way, so it is worth a line at exit. And a timestamp on each traced SPI transaction. The question a trace gets asked is almost always "what did the firmware do after DIO1 went high", and an unstamped list of commands cannot be lined up against the pin, the chip's own state, or the engine's events. Also picks up the model's noise fixes, which is what the seed is for. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Q9HbD44EKWWTRYgxbFGxf6 --- bridge/radioserver.cpp | 30 +++++++++++++++++++++++++++++- vendor/virtual-sx1262 | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/bridge/radioserver.cpp b/bridge/radioserver.cpp index 749fc90..a2756e2 100644 --- a/bridge/radioserver.cpp +++ b/bridge/radioserver.cpp @@ -65,6 +65,7 @@ using socklen_compat_t = socklen_t; #endif +#include #include #include #include @@ -109,6 +110,19 @@ VirtualSX1262 gChip; // MESHCORE_RADIO_TRACE=1 logs every SPI transaction. Off by default: this is // on the hot path of every byte. const bool gTracing = getenv("MESHCORE_RADIO_TRACE") != nullptr; + +// Seed for this chip's receiver noise, set per node by whoever starts us. +// +// Firmware takes its entropy from the radio - RadioLib reads the instantaneous +// RSSI eight times for a random byte, and MeshCore seeds its PRNG from that and +// derives its identity from the PRNG. One stream per node, or every node comes +// up with the same keypair. Seeded rather than sampled so a run stays +// reproducible. +uint64_t noiseSeedFromEnv() { + const char* e = getenv("MESHBENCH_NOISE_SEED"); + if (e == nullptr) return 0; + return strtoull(e, nullptr, 10); +} std::vector gTrace; std::mutex gChipMu; // QEMU and the engine both reach the chip uint32_t gSimMillis = 0; @@ -328,7 +342,13 @@ bool serviceQemu(sock_t fd, uint64_t* transactions, uint64_t* bytes) { // nRF52, so when one of them fails to bring its radio up, a diff of the // three traces says which command got an answer it did not like. if (gTracing && !gTrace.empty()) { - fprintf(stderr, "spi:"); + // Timestamped, because the question a trace gets asked is almost always + // "what did the firmware do after DIO1 went high", and an unstamped + // list of commands cannot be lined up against anything - not the pin, + // not the chip's own state, not the engine's events. + fprintf(stderr, "[%llu] spi:", + (unsigned long long)std::chrono::duration_cast( + std::chrono::steady_clock::now().time_since_epoch()).count()); for (size_t i = 0; i < gTrace.size() && i < 24; i++) { fprintf(stderr, " %02x", gTrace[i]); } @@ -383,6 +403,7 @@ int main(int argc, char** argv) { return 2; } const char* path = argv[1]; + gChip.setNoiseSeed(noiseSeedFromEnv()); std::string bridgeAddr; for (int i = 2; i < argc - 1; i++) { if (strcmp(argv[i], "--bridge") == 0) bridgeAddr = argv[i + 1]; @@ -517,6 +538,13 @@ int main(int argc, char** argv) { } } + // Frames the chip was handed while it was not listening and did not come + // back for in time. Reported because it is invisible otherwise and it accuses + // this side rather than the firmware: a node that hears everything and + // forwards nothing looks identical whether the packets reached the driver or + // were binned here. + printf("radioserver: %u frames dropped into a deaf receiver\n", + gChip.framesDropped()); printf("radioserver: %llu transactions, %llu bytes\n", (unsigned long long)transactions, (unsigned long long)bytes); if (bridgeFd != BAD_SOCK) CLOSE_SOCK(bridgeFd); diff --git a/vendor/virtual-sx1262 b/vendor/virtual-sx1262 index 821139f..2b9145e 160000 --- a/vendor/virtual-sx1262 +++ b/vendor/virtual-sx1262 @@ -1 +1 @@ -Subproject commit 821139fc42f1d15bd6133053912e4f4e2470cd79 +Subproject commit 2b9145e7036fa66c5a9daa10d9b375017456e2a3