Skip to content

feat: complete Phase 3 roadmap + COST 231 + ITM variability modes - #21

Draft
d3mocide wants to merge 2 commits into
devfrom
claude/roadmap-features-prod-nyba2c
Draft

feat: complete Phase 3 roadmap + COST 231 + ITM variability modes#21
d3mocide wants to merge 2 commits into
devfrom
claude/roadmap-features-prod-nyba2c

Conversation

@d3mocide

@d3mocide d3mocide commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Knocks out the remaining roadmap items ahead of the next production build. Closes P3-1, P3-3, P3-4, P4-2 and P4-6.

Important

This PR rebuilds meshrf.wasm and regenerates libmeshrf/js/meshrf.js — the module loader every WASM hook imports. See Verification of the WASM rebuild below for what I checked and what still needs a human pass before shipping.

P3-1 — Client-side Hata / FSPL

Okumura-Hata is now implemented in JavaScript (src/utils/math/hata.js), mirroring rf_physics.calculate_hata_loss term-for-term including the input clamping. Both engines agree to 4 decimal places on the reference cases (900 MHz / 5 km / 50 m / 1.5 m → 146.9428 dB on each side).

A new dispatcher (src/utils/math/pathLoss.js) lets LinkLayer resolve fspl and hata locally, so neither model needs /api/calculate-link and both keep working offline / in the PWA.

bullington and server-side itm deliberately stay on the backend — the Python implementation applies clutter_height and k_factor to the terrain profile, and the current JS calculateBullingtonDiffraction does not, so moving them would silently change existing results.

P4-2 — COST 231-Hata extension

Extends Hata coverage from 150–1500 MHz to 150–2000 MHz in both engines. Selecting "Hata" auto-dispatches to COST 231 at or above 1500 MHz; cost231 is also accepted as an explicit backend model.

Per the standard, COST 231 defines only the 3 dB metropolitan correction and has no suburban or rural term. Rather than silently reusing the Okumura-Hata corrections at frequencies where they aren't defined, selecting those environments above the crossover now raises an explicit warning. Link Analysis validity warnings are driven from a single getHataValidity helper instead of inline thresholds.

P3-3 — WASM ITM for batch reports

Batch Processing gained a propagation-model selector:

  • Bullington (Fast) — FSPL + knife-edge diffraction over 20-point profiles (unchanged default).
  • Longley-Rice ITM — the same WASM engine Link Analysis uses, over a 100-point profile, honouring Ground Type, Climate Zone and Reliability, so batch numbers now match single-link analysis.

The WASM module loads lazily on selection — useWasmITM gained an enabled flag so this always-mounted panel doesn't pull a ~MB instantiation onto the startup path. A link that fails ITM falls back to Bullington rather than failing the whole report, and the Model column records which model actually produced each row.

P3-4 — Per-node configs in batch CSV

CSV import now reads optional Antenna_Height, Antenna_Gain, TX_Power, Device and Antenna columns, with aliases (Height/AGL/Gain/Power) and loose preset matching — HELTEC_V3, heltec-v3 and Heltec V3 all resolve to the same device. An Antenna preset supplies its nominal gain; an explicit Antenna_Gain wins.

nodeConfig.js merges overrides over the global A/B config field-by-field. Blank cells and existing three-column files behave exactly as before, so this is backward compatible.

P4-6 — ITM statistical variability modes

Previously time_pct was hardcoded to 50.0 in meshrf_itm.cpp, so every prediction was the median forecast with no way to plan for worst-case conditions.

C++ / WASM: added time_pct, loc_pct, sit_pct and mdvar to LinkParameters, exposed them through Embind, and threaded variability into the coverage path — calculate_rf_coverage gained three trailing arguments, so useRFCoverageTool now passes 20 (Embind matches on arity).

Frontend: a Reliability selector in the Environment sidebar offers Best Case (10/10/10), Typical (50/50/50, default) and Reliable (90/90/90), wired through Link Analysis, RF Coverage (click, recalc and drag paths) and batch ITM reports. Batch rows record the mode that produced them (e.g. ITM 90/90/90).

Measured on a synthetic ridge (915 MHz, 10 m TX / 2 m RX) — roughly a 20 dB spread:

Mode Predicted loss
Best Case 10/10/10 193.49 dB
Typical 50/50/50 203.76 dB
Reliable 90/90/90 213.66 dB

Verification of the WASM rebuild

public/meshrf.wasm, libmeshrf/js/meshrf.wasm and libmeshrf/js/meshrf.js are regenerated artifacts. Built with emcc 6.0.4 via the repo's own Dockerfile.wasm recipe; ROADMAP P4-6 documents the exact command.

Checked against the installed artifacts, not assumed:

  • All four bindings still present (calculate_itm, calculate_rf_coverage, calculate_viewshed, LinkParameters).
  • LinkParameters defaults intact for every pre-existing field, plus the four new ones.
  • The new 20-argument calculate_rf_coverage call succeeds and returns a full grid.
  • calculate_viewshed still works — it shares the rebuilt glue even though this change doesn't touch it.
  • Backward compatibility: a call that never sets the new fields returns a bit-identical result to an explicit 50/50/50.

What this does not cover: emcc 6.0.4 is almost certainly not the version that produced the previously committed artifact (the JS glue moved 48,650 → 48,130 bytes). Unit tests can't catch a loader regression, so link analysis, RF coverage and viewshed each want a manual pass in the running app before this ships to prod.

Also in this PR

  • Bug fix: Bullington diffraction was never displayed for the Hata model in LinkLayer — the check compared against 'Hata' while the model value is lowercase 'hata', so the condition could never be true.
  • Mesh report export writes via a Blob instead of a data: URI and escapes CSV fields, so node names containing commas or quotes no longer corrupt the report.
  • Mesh report gained Model, PathLoss_dB and per-node TX/RX height/gain/power columns, plus a progress bar.

Still open

P3-2 (model selection for RF coverage) — needs a JS raster path over the stitched elevation grid, now easier since calculateClientPathLoss exists.

P4-6 statistical contours — the variability controls are done; rendering probability-of-reception contours from multiple percentage runs is tracked as the remaining piece.

Testing

  • Frontend: 52 passed (36 new — Hata/COST 231 math against hand-computed references, the client-side dispatcher, CSV override parsing, config merging, and the variability mapping).
  • Backend: 23 passed (14 new in rf-engine/tests/test_rf_physics.py, pinning the same reference values so the two engines can't drift apart).
  • npm run lint and npm run build clean.

Docs updated: ROADMAP.md, CHANGELOG.md, README.md, Documentation/batch-processing.md, Documentation/link-analyzer.md.

claude added 2 commits July 27, 2026 20:22
Closes P3-1, P3-3, P3-4 and P4-2 ahead of the next production build.

P3-1 -- Client-side Hata/FSPL:
Port Okumura-Hata to JavaScript (src/utils/math/hata.js), mirroring
rf_physics.calculate_hata_loss term-for-term including input clamping;
both engines agree to 4 decimal places on the reference cases. Add a
dispatcher (src/utils/math/pathLoss.js) and wire LinkLayer to resolve
`fspl` and `hata` locally, so neither needs /api/calculate-link and both
keep working offline/PWA. `bullington` and server `itm` stay on the
backend, which applies clutter_height and k_factor to the profile in ways
the current JS Bullington does not.

P4-2 -- COST 231-Hata:
Extend Hata coverage from 150-1500 MHz to 150-2000 MHz in both engines.
Selecting "Hata" auto-dispatches to COST 231 at/above 1500 MHz; `cost231`
is also accepted as an explicit backend model. Per the standard, COST 231
defines only the 3 dB metropolitan correction and has no suburban/rural
term, so those environments now warn above the crossover rather than
silently reusing the Okumura-Hata corrections. Link Analysis validity
warnings now come from a single getHataValidity helper.

P3-3 -- WASM ITM for batch reports:
Add a propagation-model selector to Batch Processing. ITM runs the same
WASM engine as Link Analysis over a 100-point profile (vs 20 for
Bullington), honouring Ground Type and Climate Zone, so batch numbers
match single-link analysis. The module loads lazily on selection
(useWasmITM gained an `enabled` flag, keeping it off the startup path),
and a link that fails ITM falls back to Bullington instead of failing the
whole report.

P3-4 -- Per-node configs in batch CSV:
Parse optional Antenna_Height, Antenna_Gain, TX_Power, Device and Antenna
columns, with aliases and loose preset-name matching. nodeConfig.js merges
them over the global A/B config field-by-field; blank cells and existing
three-column files behave exactly as before.

Also fixed: Bullington diffraction was never displayed for Hata in
LinkLayer -- the check compared against 'Hata' while the model value is
lowercase 'hata'. Mesh report export now writes via a Blob and escapes CSV
fields, so node names containing commas no longer corrupt the report, and
it gained Model/PathLoss_dB/per-node parameter columns plus a progress bar.

P4-6 (variability modes) is documented as blocked: it needs new
LinkParameters fields and an emscripten rebuild of meshrf.wasm.

Tests: 44 frontend (28 new), 23 backend (14 new). Lint and build clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU7LVUXdVx5YfXt4eJJSPs
ITM's time/location/situation variability was hardcoded to 50/50/50 in
meshrf_itm.cpp, so every prediction was the median forecast with no way to
plan for worst-case conditions. It is now user-selectable.

C++ / WASM:
- Add time_pct, loc_pct, sit_pct and mdvar to LinkParameters, defaulted to
  the values calculate_radial_loss previously hardcoded (50/50/50, mdvar=12).
- Expose them through Embind.
- Thread variability into the coverage path; calculate_rf_coverage gained
  three trailing arguments, so useRFCoverageTool now passes 20 (Embind
  matches on arity).
- Regenerate public/meshrf.wasm, libmeshrf/js/meshrf.wasm and
  libmeshrf/js/meshrf.js. ROADMAP P4-6 documents the rebuild command.

Frontend:
- RELIABILITY_MODES presets in EnvironmentContext: Best Case (10/10/10),
  Typical (50/50/50, default), Reliable (90/90/90).
- Reliability selector in the Environment sidebar.
- Wired through Link Analysis, RF Coverage (click, recalc and drag paths)
  and batch ITM reports; batch rows record the mode that produced them.

Backward compatibility is the important property here, and it is verified
against the built module rather than assumed: a call that never sets the new
fields returns a bit-identical result to an explicit 50/50/50. Struct
defaults, all four Embind bindings, the 20-arg coverage call and the
untouched viewshed binding were each exercised against the installed
artifacts.

Measured on a synthetic ridge (915 MHz, 10 m TX / 2 m RX):
10/10/10 = 193.49 dB, 50/50/50 = 203.76 dB, 90/90/90 = 213.66 dB.

Note the WASM was rebuilt with emcc 6.0.4, which also regenerates the JS
glue every WASM hook imports. Unit tests cannot cover a glue regression, so
link analysis, coverage and viewshed want a manual pass before this ships.

Tests: 52 frontend (8 new), 23 backend. Lint and build clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FU7LVUXdVx5YfXt4eJJSPs
@d3mocide d3mocide changed the title feat(propagation): complete Phase 3 roadmap items + COST 231 extension feat: complete Phase 3 roadmap + COST 231 + ITM variability modes Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants