Adds a complete simulation harness for the MeshCore radio platform, including:
### ADAPTIVE routing strategy (sim/src/RoutingStrategies.h)
Hash-based relay gate selects a deterministic subset of nodes to forward each
flood, with relay percentage adapted to local neighbour density:
- SPARSE (<=4 neighbours): 100% relay -- no redundancy to spare
- MEDIUM (5-14): 25% relay
- DENSE (>=15): 15% relay
Selection uses hash(packet_seed XOR node_seed) with no coordination required.
Different packets select different subsets for stochastic load balancing.
Relay suppression: nodes cancel queued outbound TX if they overhear another
node already relaying the same flood (matched by packet hash).
### DensityEstimator (sim/src/DensityEstimator.h)
Passive sliding-window neighbour count from normal traffic -- no extra messages.
Only counts direct senders (hop_count==1) over a configurable window (default 60s).
Uses std::unordered_set for unique counting; no fixed-array cap.
### Adaptive TX power saving (sim/src/SimNode.h)
power_save_enabled reduces TX power in DENSE/MEDIUM tiers:
DENSE: configurable (default 10 dBm, saves 75% TX current vs 20 dBm)
MEDIUM: configurable (default 14 dBm, saves 60% TX current)
SPARSE: always full power -- every hop counts on marginal links
Validated: 100% delivery maintained at -10dBm DENSE reduction. Energy savings
~35% radio total. TX power reduction also lowers area RF noise floor.
### Scenario suite (sim/scenarios/)
scenario_adaptive: ADAPTIVE vs DEFAULT vs PATH_SNR_HYBRID comparison
scenario_concurrent: 2-8 simultaneous flood sources
scenario_longchain: 20-hop chains at marginal SNR
scenario_mixed: ADAPTIVE + legacy DEFAULT interoperability
scenario_dutycycle: EU 1% duty cycle enforcement validation
scenario_relay_pct_sweep: grid sweep to find optimal DENSE%/MEDIUM% operating point
scenario_txpower: TX power saving vs delivery rate vs energy trade-off
### Key empirical results
FM50 ADAPTIVE vs DEFAULT: equal delivery rate, -65% airtime, -70% collisions
CH20 (chain): ADAPTIVE = SPARSE throughout, identical to DEFAULT
TX power MODERATE (-10dB): 100% delivery, -35% energy across all topologies
Legacy interop: zero delivery regression with mixed firmware
### Bug fixes
- DensityEstimator: replace seen[64] array with unordered_set (was capping at 64 neighbours)
- SimBus::onTransmit: add len > 255 guard before memcpy into 255-byte buffer
- SimNode p_forward gate: >= -> > (was silently dropping relays at p_forward=1.0)
- SimNode TX power reset: remove redundant !power_save_enabled clause
- scenario_concurrent: fix stale ConcurrentResult* pointer after vector reallocation
- scenario_relay_pct_sweep: update TxCallback from 4-arg to 5-arg (tx_power_dbm)
- scenario_adaptive: remove unused variable last_suppressed
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
* room server: added RegionMap, and new CommonCLI wiring, default_scope handling
* sensor: only minimal RegionMap wiring. Still needs work to handle default-scope
The Waveshare RP2040-LoRa board has an RF switch with two complementary
control lines:
- DIO2 (CTRL) -- driven automatically by the SX1262 chip: HIGH on TX,
LOW on RX
- GPIO17 (!CTRL) -- was not configured in MeshCore, so it floated and
the switch never properly selected the LNA/RX path
Without this pin driven, the RF switch never switches to RX mode,
resulting in completely degraded reception. TX was unaffected because
DIO2 alone is sufficient to activate the TX path.
Adding SX126X_RXEN=17 lets RadioLib drive GPIO17 as the complement of
DIO2, so the switch correctly routes the signal to the LNA on receive.
Reference: https://files.waveshare.com/wiki/RP2040-LoRa/Rp2040-lora-sch.pdf