Feature/ibp beacon integration#906
Conversation
Adds a live NCDXF/IARU International Beacon Project panel that shows which of the 18 beacons is transmitting on each IBP band right now, with a per-slot countdown and bearing/distance from the operator's QTH. No server changes — the schedule is fully deterministic. - src/utils/ibp.js: beacon list, getSchedule(), slot/countdown helpers - src/hooks/useIBP.js: 1 s ticker, recomputes only on 10 s slot boundary - src/components/IBPPanel.jsx: 5-row band table, progress bar, geo column - Wired into App.jsx (useIBP hook) and DockableApp.jsx (factory + panel selector under Propagation group) - i18n keys added to en.json Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a Leaflet map layer plugin for the 18 NCDXF/IARU IBP beacons. No network requests — schedule is fully deterministic. - Static dim markers for all 18 inactive beacon sites - Pulsing highlighted marker (band-coloured + label) for each active beacon; updates every 10 s on slot boundary - Dashed great-circle arcs from operator QTH to each active beacon, coloured by band, replicated across world copies - Draggable/minimisable control panel: band filter, path toggle, inactive-station toggle, live slot countdown - Registered in layerRegistry under Propagation category - i18n name/description keys added to en.json Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
activeBandsByBeacon stores raw band objects {mhz, label, offset} but
buildPopup was accessing b.band.label (undefined), causing a TypeError
that aborted the entire marker-drawing forEach so no beacons appeared.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Clicking a band row in IBPPanel or an active beacon marker on the map now calls tuneTo(band.mhz) when rig control is enabled. The cursor and tooltip update to signal interactivity only when the rig is connected. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Polls /api/rbn/spots for all 18 IBP beacon callsigns every 60 s. When a beacon has been heard by RBN skimmers in the last 5 minutes, a compact "RBN N× +XdB" indicator appears beneath its location in the IBP panel, showing skimmer count and best SNR. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds ibp.title, ibp.cycleCountdown, ibp.cycleCountdown.tooltip, ibp.slotProgress.tooltip, ibp.footer, ibp.tune, ibp.rbn.heard, ibp.rbn.tooltip, plugins.layers.ibp.name and .description to: ca, de, es, fr, it, ja, ka, ko, ms, nl, pt, ru, sl, th, zh Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
IBP beacons transmit in CW but getModeFromFreq() returns DATA/USB for 14.100, 21.150, 24.930 and 28.200 MHz. Pass 'CW' explicitly as the second argument to tuneTo() in both IBPPanel and the map layer so all 5 beacon frequencies set the correct mode regardless of the band plan. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MichaelWheeley
left a comment
There was a problem hiding this comment.
had Mrs.W review src/lang/th.json
two minor changes made as comments
- th.json: improve ibp.rbn.tooltip and plugins.layers.ibp.description wording for more natural Thai - useIBPRBN.js: apply Prettier formatting (fetch options object style) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolved conflict in th.json: kept IBP keys from feature branch and Thai translations for band.conditions, contest, dxCluster, keybindings and plugin layer strings from upstream/Staging. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
accius
left a comment
There was a problem hiding this comment.
Review — IBP Beacon integration
Excellent, thorough PR — deterministic schedule, clean separation of panel/layer/RBN hook, and complete i18n across all 15 languages. Couple of real issues plus some polish.
Likely bug
useIBPRBN.jscounts the wrong field.countis computed asnew Set(json.spots.map((s) => s.callsign)).size, but every spot returned from/api/rbn/spots?callsign=<X>has the same callsign (the spotted station) — socountis always 0 or 1. I believe the intent is "number of distinct skimmers that heard the beacon" — that should benew Set(json.spots.map((s) => s.spotter)).size(or whatever the skimmer field is named in the RBN spot object).
Robustness
-
window.deLocationis read directly insideuseIBPLayer.js. That's a brittle global; the rest of the layer system receivesconfig/observerLocationviauseLayer({ map, enabled, config, … }). Please thread the operator QTH throughconfigrather than offwindow. -
setTimeout(…, 100)/setTimeout(…, 150)to wire up event handlers and draggable behaviour on the Leaflet control. Brittle on slow first paint. ArequestAnimationFrameinsideonAdd(orcontrolRef.current.getContainer()immediately afteraddControl) is deterministic. -
useIBPinterval keeps ticking once per second for the lifetime of the app regardless of whether the panel/layer is mounted. Cheap but unnecessary — consider gating byenabled, or accepting the cost as documented. -
18 parallel HTTP requests / 60s from each user for the RBN cross-reference. For the current user-base it's fine, but consider a single multi-callsign endpoint (e.g.
/api/rbn/spots?callsigns=a,b,c) to cut load by ~18× once this gets traction.
Consistency / checklist
-
Hardcoded colors in inactive markers:
fillColor: '#666',color: '#999'inuseIBPLayer.js. Please use CSS variables per checklist. -
"Wired into all three layouts" — the PR description claims Modern, Classic, and Dockable wiring, but only
DockableApp.jsxactually rendersIBPPanel. App.jsx passesibpintolayoutProps, but neitherModernLayout.jsxnorClassicLayout.jsxconsume it. If IBP is intended as dockable-only, update the description; if it's meant for all three, add the panel. -
getUpcomingScheduleis exported and unused in this PR — happy for it to land for future timeline work, but consider aTODOor drop until needed.
Small nits
-
IBP band offsets
{0, 17, 16, 15, 14}— a quick sentence naming the math ("band N starts N slots earlier than 14.1 in the cycle, so offset is(-N) mod 18") in the comment would help future readers. -
elapsedcomputed but unused in the "Update countdown" effect. -
ibp.rbn.hearduses{{count}}×while some locales render that as{{count}}xvisually — acceptable, just noting.
Checklist:
- Dark / Light / Retro — please verify (panel + layer + control panel).
- Mobile — floating control at
topright+ draggable tends to land off-screen on small viewports.
Looks good overall — address 1, 2, 6, and the layout-wiring claim and this is ready.
MichaelWheeley
left a comment
There was a problem hiding this comment.
Claude did a pretty good job!
@MichaelWheeley exactly. :-) |
Clarify & correctness: - useIBPRBN: add comment clarifying spot.callsign = skimmer, spot.dx = beacon - ibp.js: rewrite band offset comment with (-N mod 18) math explanation - ibp.js: add TODO note on getUpcomingSchedule (Phase 4 timeline) - useIBP: document intentional unconditional interval Robustness: - useIBPLayer: thread deLat/deLon via PluginLayer props, drop window.deLocation - PluginLayer: forward deLat/deLon to useLayer hook - WorldMap: pass deLocation.lat/lon as deLat/deLon to PluginLayer - useIBPLayer: replace setTimeout(100) with direct div.querySelector wiring - useIBPLayer: replace setTimeout(150) with double-rAF for makeDraggable Consistency: - useIBPLayer: replace hardcoded hex (accius#666/#999/#aaa/accius#888) with CSS variables; popup HTML uses var(--text-muted) inline; circleMarker options resolved via getComputedStyle at render time - useIBPLayer: remove unused elapsed variable Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Thanks for the thorough review — all points addressed in commits Likely bug —
|
Add a multi-callsign branch to GET /api/rbn/spots: when ?callsigns=a,b,c
is present the handler resolves all callsigns in one request (capped at 30),
reusing the existing per-callsign rbnApiCaches entries, and returns a single
{ results: { [callsign]: { count, spots[] } } } object.
useIBPRBN now issues one fetch per 60 s instead of 18, cutting per-user
HTTP load by ~18× as adoption grows. The original ?callsign= (singular)
route is unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
What does this PR do?
IBP Beacon Integration — Phases 1–3 (#878)
Adds full support for the NCDXF/IARU International Beacon Project — a network of 18 HF beacons transmitting sequentially on 14.100, 18.110, 21.150, 24.930 and 28.200 MHz in a fixed 3-minute/18-beacon cycle.
The schedule is fully deterministic — no server or network call is needed to compute it.
Phase 1 — IBP Panel (
IBPPanel.jsx)ibp) under the Propagation groupPhase 2 — Map Layer Plugin (
useIBPLayer.js)layerRegistry.jsbuildPopup()shows callsign, location, grid, active bands and seconds remainingPhase 3 — RBN Cross-Reference (
useIBPRBN.js)/api/rbn/spotsfor all 18 IBP beacon callsigns every 60 sRBN N× +XdBindicator appears beneath its location in the panelRig Control Integration
band.mhzwhen rig control is enabledtuneTo()on clickrefto avoid re-creating markers on rig state changesi18n
ca,de,es,fr,it,ja,ka,ko,ms,nl,pt,ru,sl,th,zhFiles changed
src/utils/ibp.jssrc/hooks/useIBP.jssrc/hooks/useIBPRBN.jssrc/components/IBPPanel.jsxsrc/plugins/layers/useIBPLayer.jssrc/plugins/layerRegistry.jssrc/App.jsxuseIBPwired intolayoutPropssrc/DockableApp.jsxibppanel factory addedsrc/lang/en.json+ 15 othersTesting
Checklist
server.js: caches have TTLs and size caps (we serve 2,000+ concurrent users)var(--accent-cyan), etc.).bak,.old,console.logdebug lines, or test scripts includedScreenshots (if visual change)
IPB Panel:

IBP map layer:
