[feat](trx-rs): add NOAA/Meteor predictions; rename wxsat → sat

- Add NOAA-15/18/19 and Meteor-M N2-3/N2-4 to predictions list
- Rename PREDICTION_SATS (was HAM_SATS) to include weather + ham sats
- Rename all wxsat identifiers to sat throughout JS/HTML/CSS/Rust:
  wxsat.js → sat.js, WXSAT_JS → SAT_JS, /wxsat.js route → /sat.js,
  all #wxsat-* element IDs, .wxsat-* CSS classes, window.addWxsat* →
  window.addSat*, window.onServerWxsatImage → window.onServerSatImage,
  etc. (backend protocol strings unchanged)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
2026-03-28 13:53:26 +01:00
parent adec33708f
commit aab344b729
8 changed files with 233 additions and 229 deletions
@@ -215,7 +215,7 @@ function applyAuthRestrictions() {
"ft4-decode-toggle-btn", "ft4-decode-toggle-btn",
"ft2-decode-toggle-btn", "ft2-decode-toggle-btn",
"wspr-decode-toggle-btn", "wspr-decode-toggle-btn",
"wxsat-decode-toggle-btn", "sat-decode-toggle-btn",
"lrpt-decode-toggle-btn", "lrpt-decode-toggle-btn",
"hf-aprs-decode-toggle-btn", "hf-aprs-decode-toggle-btn",
"cw-auto", "cw-auto",
@@ -228,7 +228,7 @@ function applyAuthRestrictions() {
"settings-clear-ft4-history", "settings-clear-ft4-history",
"settings-clear-ft2-history", "settings-clear-ft2-history",
"settings-clear-wspr-history", "settings-clear-wspr-history",
"settings-clear-wxsat-history" "settings-clear-sat-history"
]; ];
pluginToggleBtns.forEach(id => { pluginToggleBtns.forEach(id => {
const btn = document.getElementById(id); const btn = document.getElementById(id);
@@ -3231,13 +3231,13 @@ function render(update) {
hfAprsToggleBtn.style.borderColor = hfAprsOn ? "#00d17f" : ""; hfAprsToggleBtn.style.borderColor = hfAprsOn ? "#00d17f" : "";
hfAprsToggleBtn.style.color = hfAprsOn ? "#00d17f" : ""; hfAprsToggleBtn.style.color = hfAprsOn ? "#00d17f" : "";
} }
const wxsatToggleBtn = document.getElementById("wxsat-decode-toggle-btn"); const satToggleBtn = document.getElementById("sat-decode-toggle-btn");
if (wxsatToggleBtn) { if (satToggleBtn) {
const wxsatOn = !!update.wxsat_decode_enabled; const satOn = !!update.wxsat_decode_enabled;
wxsatToggleBtn.dataset.enabled = wxsatOn ? "true" : "false"; satToggleBtn.dataset.enabled = satOn ? "true" : "false";
wxsatToggleBtn.textContent = wxsatOn ? "Disable NOAA APT" : "Enable NOAA APT"; satToggleBtn.textContent = satOn ? "Disable NOAA APT" : "Enable NOAA APT";
wxsatToggleBtn.style.borderColor = wxsatOn ? "#00d17f" : ""; satToggleBtn.style.borderColor = satOn ? "#00d17f" : "";
wxsatToggleBtn.style.color = wxsatOn ? "#00d17f" : ""; satToggleBtn.style.color = satOn ? "#00d17f" : "";
} }
const lrptToggleBtn = document.getElementById("lrpt-decode-toggle-btn"); const lrptToggleBtn = document.getElementById("lrpt-decode-toggle-btn");
if (lrptToggleBtn) { if (lrptToggleBtn) {
@@ -3247,7 +3247,7 @@ function render(update) {
lrptToggleBtn.style.borderColor = lrptOn ? "#00d17f" : ""; lrptToggleBtn.style.borderColor = lrptOn ? "#00d17f" : "";
lrptToggleBtn.style.color = lrptOn ? "#00d17f" : ""; lrptToggleBtn.style.color = lrptOn ? "#00d17f" : "";
} }
if (window.updateWxsatLiveState) window.updateWxsatLiveState(update); if (window.updateSatLiveState) window.updateSatLiveState(update);
const cwAutoEl = document.getElementById("cw-auto"); const cwAutoEl = document.getElementById("cw-auto");
const cwWpmEl = document.getElementById("cw-wpm"); const cwWpmEl = document.getElementById("cw-wpm");
const cwToneEl = document.getElementById("cw-tone"); const cwToneEl = document.getElementById("cw-tone");
@@ -3423,7 +3423,7 @@ function render(update) {
["about-dec-wspr", update.wspr_decode_enabled], ["about-dec-wspr", update.wspr_decode_enabled],
["about-dec-cw", update.cw_decode_enabled], ["about-dec-cw", update.cw_decode_enabled],
["about-dec-aprs", update.aprs_decode_enabled || update.hf_aprs_decode_enabled], ["about-dec-aprs", update.aprs_decode_enabled || update.hf_aprs_decode_enabled],
["about-dec-wxsat", update.wxsat_decode_enabled], ["about-dec-sat", update.wxsat_decode_enabled],
["about-dec-lrpt", update.lrpt_decode_enabled], ["about-dec-lrpt", update.lrpt_decode_enabled],
]; ];
for (const [id, enabled] of decMap) { for (const [id, enabled] of decMap) {
@@ -4489,7 +4489,7 @@ const locatorMarkers = new Map();
const decodeContactPaths = new Map(); const decodeContactPaths = new Map();
let selectedMapQsoKey = null; let selectedMapQsoKey = null;
const mapMarkers = new Set(); const mapMarkers = new Set();
const DEFAULT_MAP_SOURCE_FILTER = { ais: true, vdes: true, aprs: true, bookmark: false, ft8: true, ft4: true, ft2: true, wspr: true, wxsat: false }; const DEFAULT_MAP_SOURCE_FILTER = { ais: true, vdes: true, aprs: true, bookmark: false, ft8: true, ft4: true, ft2: true, wspr: true, sat: false };
const mapFilter = { ...DEFAULT_MAP_SOURCE_FILTER }; const mapFilter = { ...DEFAULT_MAP_SOURCE_FILTER };
const mapLocatorFilter = { phase: "band", bands: new Set() }; const mapLocatorFilter = { phase: "band", bands: new Set() };
let mapSearchFilter = ""; let mapSearchFilter = "";
@@ -4861,7 +4861,7 @@ function locatorFilterColor(type) {
function mapSourceColor(type) { function mapSourceColor(type) {
if (type === "ais") return "#38bdf8"; if (type === "ais") return "#38bdf8";
if (type === "vdes") return "#a78bfa"; if (type === "vdes") return "#a78bfa";
if (type === "wxsat") return "#f59e0b"; if (type === "sat") return "#f59e0b";
if (type === "aprs") return "#00d17f"; if (type === "aprs") return "#00d17f";
return locatorFilterColor(type); return locatorFilterColor(type);
} }
@@ -5676,10 +5676,10 @@ function syncAprsReceiverMarker() {
// Weather satellite image overlays on the map // Weather satellite image overlays on the map
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const wxsatOverlays = new Map(); // key -> { overlay, track, msg } const satOverlays = new Map(); // key -> { overlay, track, msg }
let wxsatOverlaySeq = 0; let satOverlaySeq = 0;
window.addWxsatMapOverlay = function(msg) { window.addSatMapOverlay = function(msg) {
if (!msg || !msg.geo_bounds || !msg.path) return; if (!msg || !msg.geo_bounds || !msg.path) return;
const bounds = msg.geo_bounds; const bounds = msg.geo_bounds;
// bounds = [south, west, north, east] // bounds = [south, west, north, east]
@@ -5688,14 +5688,14 @@ window.addWxsatMapOverlay = function(msg) {
[bounds[0], bounds[1]], // SW [bounds[0], bounds[1]], // SW
[bounds[2], bounds[3]] // NE [bounds[2], bounds[3]] // NE
); );
const key = "wxsat-" + (++wxsatOverlaySeq); const key = "sat-" + (++satOverlaySeq);
const overlay = L.imageOverlay(msg.path, latLngBounds, { const overlay = L.imageOverlay(msg.path, latLngBounds, {
opacity: 0.55, opacity: 0.55,
interactive: true, interactive: true,
zIndex: 300, zIndex: 300,
}); });
overlay.__trxType = "wxsat"; overlay.__trxType = "sat";
overlay.__trxWxsatKey = key; overlay.__trxSatKey = key;
overlay.__trxRigIds = msg.rig_id ? new Set([msg.rig_id]) : new Set(); overlay.__trxRigIds = msg.rig_id ? new Set([msg.rig_id]) : new Set();
overlay.__trxHistoryVisible = true; overlay.__trxHistoryVisible = true;
mapMarkers.add(overlay); mapMarkers.add(overlay);
@@ -5718,13 +5718,13 @@ window.addWxsatMapOverlay = function(msg) {
if (msg.ground_track && Array.isArray(msg.ground_track) && msg.ground_track.length >= 2) { if (msg.ground_track && Array.isArray(msg.ground_track) && msg.ground_track.length >= 2) {
const latlngs = msg.ground_track.map(function(pt) { return [pt[0], pt[1]]; }); const latlngs = msg.ground_track.map(function(pt) { return [pt[0], pt[1]]; });
track = L.polyline(latlngs, { track = L.polyline(latlngs, {
color: mapSourceColor("wxsat"), color: mapSourceColor("sat"),
weight: 2, weight: 2,
opacity: 0.7, opacity: 0.7,
dashArray: "6, 4", dashArray: "6, 4",
}); });
track.__trxType = "wxsat"; track.__trxType = "sat";
track.__trxWxsatKey = key; track.__trxSatKey = key;
track.__trxRigIds = overlay.__trxRigIds; track.__trxRigIds = overlay.__trxRigIds;
track.__trxHistoryVisible = true; track.__trxHistoryVisible = true;
mapMarkers.add(track); mapMarkers.add(track);
@@ -5733,7 +5733,7 @@ window.addWxsatMapOverlay = function(msg) {
} }
} }
wxsatOverlays.set(key, { overlay: overlay, track: track, msg: msg }); satOverlays.set(key, { overlay: overlay, track: track, msg: msg });
if (aprsMap) { if (aprsMap) {
overlay.addTo(aprsMap); overlay.addTo(aprsMap);
@@ -5741,8 +5741,8 @@ window.addWxsatMapOverlay = function(msg) {
applyMapFilter(); applyMapFilter();
}; };
window.removeWxsatMapOverlay = function(key) { window.removeSatMapOverlay = function(key) {
const entry = wxsatOverlays.get(key); const entry = satOverlays.get(key);
if (!entry) return; if (!entry) return;
if (entry.overlay) { if (entry.overlay) {
mapMarkers.delete(entry.overlay); mapMarkers.delete(entry.overlay);
@@ -5752,12 +5752,12 @@ window.removeWxsatMapOverlay = function(key) {
mapMarkers.delete(entry.track); mapMarkers.delete(entry.track);
if (aprsMap && aprsMap.hasLayer(entry.track)) entry.track.removeFrom(aprsMap); if (aprsMap && aprsMap.hasLayer(entry.track)) entry.track.removeFrom(aprsMap);
} }
wxsatOverlays.delete(key); satOverlays.delete(key);
}; };
window.clearWxsatMapOverlays = function() { window.clearSatMapOverlays = function() {
for (const [key] of wxsatOverlays) { for (const [key] of satOverlays) {
window.removeWxsatMapOverlay(key); window.removeSatMapOverlay(key);
} }
}; };
@@ -5805,8 +5805,8 @@ window.clearMapMarkersByType = function(type) {
return; return;
} }
if (type === "wxsat") { if (type === "sat") {
window.clearWxsatMapOverlays(); window.clearSatMapOverlays();
return; return;
} }
@@ -8612,7 +8612,7 @@ function dispatchDecodeMessage(msg) {
if (msg.type === "ft4" && window.onServerFt4) window.onServerFt4(msg); if (msg.type === "ft4" && window.onServerFt4) window.onServerFt4(msg);
if (msg.type === "ft2" && window.onServerFt2) window.onServerFt2(msg); if (msg.type === "ft2" && window.onServerFt2) window.onServerFt2(msg);
if (msg.type === "wspr" && window.onServerWspr) window.onServerWspr(msg); if (msg.type === "wspr" && window.onServerWspr) window.onServerWspr(msg);
if (msg.type === "wxsat_image" && window.onServerWxsatImage) window.onServerWxsatImage(msg); if (msg.type === "wxsat_image" && window.onServerSatImage) window.onServerSatImage(msg);
if (msg.type === "lrpt_image" && window.onServerLrptImage) window.onServerLrptImage(msg); if (msg.type === "lrpt_image" && window.onServerLrptImage) window.onServerLrptImage(msg);
} }
@@ -515,7 +515,7 @@
<button class="sub-tab" data-subtab="ft2">FT2</button> <button class="sub-tab" data-subtab="ft2">FT2</button>
<button class="sub-tab" data-subtab="wspr">WSPR</button> <button class="sub-tab" data-subtab="wspr">WSPR</button>
<button class="sub-tab" data-subtab="rds">RDS</button> <button class="sub-tab" data-subtab="rds">RDS</button>
<button class="sub-tab" data-subtab="wxsat">SAT</button> <button class="sub-tab" data-subtab="sat">SAT</button>
</div> </div>
<div id="subtab-overview" class="sub-tab-panel"> <div id="subtab-overview" class="sub-tab-panel">
<div class="plugin-item"> <div class="plugin-item">
@@ -801,28 +801,28 @@
</div> </div>
<div id="cw-output"></div> <div id="cw-output"></div>
</div> </div>
<div id="subtab-wxsat" class="sub-tab-panel" style="display:none;"> <div id="subtab-sat" class="sub-tab-panel" style="display:none;">
<div class="ft8-controls"> <div class="ft8-controls">
<button id="wxsat-decode-toggle-btn" type="button">Enable NOAA APT</button> <button id="sat-decode-toggle-btn" type="button">Enable NOAA APT</button>
<button id="lrpt-decode-toggle-btn" type="button">Enable Meteor LRPT</button> <button id="lrpt-decode-toggle-btn" type="button">Enable Meteor LRPT</button>
<small id="wxsat-status" style="color:var(--text-muted);">Waiting for satellite pass</small> <small id="sat-status" style="color:var(--text-muted);">Waiting for satellite pass</small>
</div> </div>
<!-- View selector --> <!-- View selector -->
<div class="wxsat-view-bar"> <div class="sat-view-bar">
<button id="wxsat-view-live" class="wxsat-view-btn wxsat-view-active" type="button">Live</button> <button id="sat-view-live" class="sat-view-btn sat-view-active" type="button">Live</button>
<button id="wxsat-view-history" class="wxsat-view-btn" type="button">History</button> <button id="sat-view-history" class="sat-view-btn" type="button">History</button>
<button id="wxsat-view-predictions" class="wxsat-view-btn" type="button">Predictions</button> <button id="sat-view-predictions" class="sat-view-btn" type="button">Predictions</button>
</div> </div>
<!-- Live view --> <!-- Live view -->
<div id="wxsat-live-view"> <div id="sat-live-view">
<div class="wxsat-live-grid"> <div class="sat-live-grid">
<div class="wxsat-live-card"> <div class="sat-live-card">
<span class="wxsat-live-label">NOAA APT</span> <span class="sat-live-label">NOAA APT</span>
<span id="wxsat-apt-state" class="wxsat-live-value wxsat-state-idle">Idle</span> <span id="sat-apt-state" class="sat-live-value sat-state-idle">Idle</span>
</div> </div>
<div class="wxsat-live-card"> <div class="sat-live-card">
<span class="wxsat-live-label">Meteor LRPT</span> <span class="sat-live-label">Meteor LRPT</span>
<span id="wxsat-lrpt-state" class="wxsat-live-value wxsat-state-idle">Idle</span> <span id="sat-lrpt-state" class="sat-live-value sat-state-idle">Idle</span>
</div> </div>
</div> </div>
<div style="margin:0.5rem 0;"> <div style="margin:0.5rem 0;">
@@ -835,35 +835,35 @@
Multi-channel CCSDS-framed imagery (APIDs 64&ndash;69) with RGB composite output. Multi-channel CCSDS-framed imagery (APIDs 64&ndash;69) with RGB composite output.
</div> </div>
</div> </div>
<div id="wxsat-live-latest" style="margin-top:0.5rem;"></div> <div id="sat-live-latest" style="margin-top:0.5rem;"></div>
</div> </div>
<!-- History view --> <!-- History view -->
<div id="wxsat-history-view" style="display:none;"> <div id="sat-history-view" style="display:none;">
<div class="wxsat-history-controls"> <div class="sat-history-controls">
<input id="wxsat-filter" class="ft8-filter" type="text" placeholder="Filter (e.g. NOAA-18, Meteor, APT)" /> <input id="sat-filter" class="ft8-filter" type="text" placeholder="Filter (e.g. NOAA-18, Meteor, APT)" />
<select id="wxsat-sort" class="wxsat-sort-select"> <select id="sat-sort" class="sat-sort-select">
<option value="newest">Newest first</option> <option value="newest">Newest first</option>
<option value="oldest">Oldest first</option> <option value="oldest">Oldest first</option>
</select> </select>
<select id="wxsat-type-filter" class="wxsat-sort-select"> <select id="sat-type-filter" class="sat-sort-select">
<option value="all">All types</option> <option value="all">All types</option>
<option value="apt">NOAA APT only</option> <option value="apt">NOAA APT only</option>
<option value="lrpt">Meteor LRPT only</option> <option value="lrpt">Meteor LRPT only</option>
</select> </select>
</div> </div>
<div class="wxsat-history-header"> <div class="sat-history-header">
<span class="wxsat-col-time">Time</span> <span class="sat-col-time">Time</span>
<span class="wxsat-col-type">Type</span> <span class="sat-col-type">Type</span>
<span class="wxsat-col-sat">Satellite</span> <span class="sat-col-sat">Satellite</span>
<span class="wxsat-col-ch">Channels</span> <span class="sat-col-ch">Channels</span>
<span class="wxsat-col-lines">Lines</span> <span class="sat-col-lines">Lines</span>
<span class="wxsat-col-link">Image</span> <span class="sat-col-link">Image</span>
</div> </div>
<div id="wxsat-history-list"></div> <div id="sat-history-list"></div>
<small id="wxsat-history-count" style="color:var(--text-muted);font-size:0.75rem;">No images yet</small> <small id="sat-history-count" style="color:var(--text-muted);font-size:0.75rem;">No images yet</small>
</div> </div>
<!-- Predictions view --> <!-- Predictions view -->
<div id="wxsat-predictions-view" style="display:none;"> <div id="sat-predictions-view" style="display:none;">
<div class="sat-pred-header"> <div class="sat-pred-header">
<span class="sat-pred-col-time">AOS (UTC)</span> <span class="sat-pred-col-time">AOS (UTC)</span>
<span class="sat-pred-col-sat">Satellite</span> <span class="sat-pred-col-sat">Satellite</span>
@@ -1122,7 +1122,7 @@
<button id="settings-clear-ft4-history" class="sch-write sch-reset-btn" type="button">Clear full FT4 history</button> <button id="settings-clear-ft4-history" class="sch-write sch-reset-btn" type="button">Clear full FT4 history</button>
<button id="settings-clear-ft2-history" class="sch-write sch-reset-btn" type="button">Clear full FT2 history</button> <button id="settings-clear-ft2-history" class="sch-write sch-reset-btn" type="button">Clear full FT2 history</button>
<button id="settings-clear-wspr-history" class="sch-write sch-reset-btn" type="button">Clear full WSPR history</button> <button id="settings-clear-wspr-history" class="sch-write sch-reset-btn" type="button">Clear full WSPR history</button>
<button id="settings-clear-wxsat-history" class="sch-write sch-reset-btn" type="button">Clear full Sat history</button> <button id="settings-clear-sat-history" class="sch-write sch-reset-btn" type="button">Clear full Sat history</button>
</div> </div>
</div> </div>
</div> </div>
@@ -1195,7 +1195,7 @@
<tr><td>WSPR</td><td id="about-dec-wspr" class="about-status-off">Off</td></tr> <tr><td>WSPR</td><td id="about-dec-wspr" class="about-status-off">Off</td></tr>
<tr><td>CW</td><td id="about-dec-cw" class="about-status-off">Off</td></tr> <tr><td>CW</td><td id="about-dec-cw" class="about-status-off">Off</td></tr>
<tr><td>APRS</td><td id="about-dec-aprs" class="about-status-off">Off</td></tr> <tr><td>APRS</td><td id="about-dec-aprs" class="about-status-off">Off</td></tr>
<tr><td>NOAA APT</td><td id="about-dec-wxsat" class="about-status-off">Off</td></tr> <tr><td>NOAA APT</td><td id="about-dec-sat" class="about-status-off">Off</td></tr>
<tr><td>Meteor LRPT</td><td id="about-dec-lrpt" class="about-status-off">Off</td></tr> <tr><td>Meteor LRPT</td><td id="about-dec-lrpt" class="about-status-off">Off</td></tr>
</table> </table>
</div> </div>
@@ -1287,7 +1287,7 @@
<script src="/ft2.js"></script> <script src="/ft2.js"></script>
<script src="/wspr.js"></script> <script src="/wspr.js"></script>
<script src="/cw.js"></script> <script src="/cw.js"></script>
<script src="/wxsat.js"></script> <script src="/sat.js"></script>
<script src="/bookmarks.js"></script> <script src="/bookmarks.js"></script>
<script src="/scheduler.js"></script> <script src="/scheduler.js"></script>
<script src="/background-decode.js"></script> <script src="/background-decode.js"></script>
@@ -4,27 +4,27 @@
// Predictions view: next 24 h passes for ham satellites // Predictions view: next 24 h passes for ham satellites
// ── DOM references ────────────────────────────────────────────────── // ── DOM references ──────────────────────────────────────────────────
const wxsatStatus = document.getElementById("wxsat-status"); const satStatus = document.getElementById("sat-status");
const wxsatLiveView = document.getElementById("wxsat-live-view"); const satLiveView = document.getElementById("sat-live-view");
const wxsatHistoryView = document.getElementById("wxsat-history-view"); const satHistoryView = document.getElementById("sat-history-view");
const wxsatPredictionsView = document.getElementById("wxsat-predictions-view"); const satPredictionsView = document.getElementById("sat-predictions-view");
const wxsatLiveLatest = document.getElementById("wxsat-live-latest"); const satLiveLatest = document.getElementById("sat-live-latest");
const wxsatHistoryList = document.getElementById("wxsat-history-list"); const satHistoryList = document.getElementById("sat-history-list");
const wxsatHistoryCount = document.getElementById("wxsat-history-count"); const satHistoryCount = document.getElementById("sat-history-count");
const wxsatFilterInput = document.getElementById("wxsat-filter"); const satFilterInput = document.getElementById("sat-filter");
const wxsatSortSelect = document.getElementById("wxsat-sort"); const satSortSelect = document.getElementById("sat-sort");
const wxsatTypeFilter = document.getElementById("wxsat-type-filter"); const satTypeFilter = document.getElementById("sat-type-filter");
const wxsatAptState = document.getElementById("wxsat-apt-state"); const satAptState = document.getElementById("sat-apt-state");
const wxsatLrptState = document.getElementById("wxsat-lrpt-state"); const satLrptState = document.getElementById("sat-lrpt-state");
// ── State ─────────────────────────────────────────────────────────── // ── State ───────────────────────────────────────────────────────────
let wxsatImageHistory = []; let satImageHistory = [];
const WXSAT_MAX_IMAGES = 100; const SAT_MAX_IMAGES = 100;
let wxsatFilterText = ""; let satFilterText = "";
let wxsatActiveView = "live"; // "live" | "history" | "predictions" let satActiveView = "live"; // "live" | "history" | "predictions"
// ── UI scheduler helper ───────────────────────────────────────────── // ── UI scheduler helper ─────────────────────────────────────────────
function scheduleWxsatUi(key, job) { function scheduleSatUi(key, job) {
if (typeof window.trxScheduleUiFrameJob === "function") { if (typeof window.trxScheduleUiFrameJob === "function") {
window.trxScheduleUiFrameJob(key, job); window.trxScheduleUiFrameJob(key, job);
return; return;
@@ -33,51 +33,51 @@ function scheduleWxsatUi(key, job) {
} }
// ── View switching ────────────────────────────────────────────────── // ── View switching ──────────────────────────────────────────────────
const wxsatViewLiveBtn = document.getElementById("wxsat-view-live"); const satViewLiveBtn = document.getElementById("sat-view-live");
const wxsatViewHistoryBtn = document.getElementById("wxsat-view-history"); const satViewHistoryBtn = document.getElementById("sat-view-history");
const wxsatViewPredictionsBtn = document.getElementById("wxsat-view-predictions"); const satViewPredictionsBtn = document.getElementById("sat-view-predictions");
function switchWxsatView(view) { function switchSatView(view) {
wxsatActiveView = view; satActiveView = view;
if (wxsatLiveView) wxsatLiveView.style.display = view === "live" ? "" : "none"; if (satLiveView) satLiveView.style.display = view === "live" ? "" : "none";
if (wxsatHistoryView) wxsatHistoryView.style.display = view === "history" ? "" : "none"; if (satHistoryView) satHistoryView.style.display = view === "history" ? "" : "none";
if (wxsatPredictionsView) wxsatPredictionsView.style.display = view === "predictions" ? "" : "none"; if (satPredictionsView) satPredictionsView.style.display = view === "predictions" ? "" : "none";
if (wxsatViewLiveBtn) wxsatViewLiveBtn.classList.toggle("wxsat-view-active", view === "live"); if (satViewLiveBtn) satViewLiveBtn.classList.toggle("sat-view-active", view === "live");
if (wxsatViewHistoryBtn) wxsatViewHistoryBtn.classList.toggle("wxsat-view-active", view === "history"); if (satViewHistoryBtn) satViewHistoryBtn.classList.toggle("sat-view-active", view === "history");
if (wxsatViewPredictionsBtn) wxsatViewPredictionsBtn.classList.toggle("wxsat-view-active", view === "predictions"); if (satViewPredictionsBtn) satViewPredictionsBtn.classList.toggle("sat-view-active", view === "predictions");
if (view === "history") { if (view === "history") {
renderWxsatHistoryTable(); renderSatHistoryTable();
} else if (view === "predictions") { } else if (view === "predictions") {
loadSatPredictions(); loadSatPredictions();
} }
} }
wxsatViewLiveBtn?.addEventListener("click", () => switchWxsatView("live")); satViewLiveBtn?.addEventListener("click", () => switchSatView("live"));
wxsatViewHistoryBtn?.addEventListener("click", () => switchWxsatView("history")); satViewHistoryBtn?.addEventListener("click", () => switchSatView("history"));
wxsatViewPredictionsBtn?.addEventListener("click", () => switchWxsatView("predictions")); satViewPredictionsBtn?.addEventListener("click", () => switchSatView("predictions"));
// ── Live view: decoder state ──────────────────────────────────────── // ── Live view: decoder state ────────────────────────────────────────
// Updated from app.js render() via window.updateWxsatLiveState // Updated from app.js render() via window.updateSatLiveState
window.updateWxsatLiveState = function (update) { window.updateSatLiveState = function (update) {
if (!wxsatAptState || !wxsatLrptState) return; if (!satAptState || !satLrptState) return;
const aptOn = !!update.wxsat_decode_enabled; const aptOn = !!update.wxsat_decode_enabled;
const lrptOn = !!update.lrpt_decode_enabled; const lrptOn = !!update.lrpt_decode_enabled;
wxsatAptState.textContent = aptOn ? "Listening" : "Idle"; satAptState.textContent = aptOn ? "Listening" : "Idle";
wxsatAptState.className = "wxsat-live-value " + (aptOn ? "wxsat-state-listening" : "wxsat-state-idle"); satAptState.className = "sat-live-value " + (aptOn ? "sat-state-listening" : "sat-state-idle");
wxsatLrptState.textContent = lrptOn ? "Listening" : "Idle"; satLrptState.textContent = lrptOn ? "Listening" : "Idle";
wxsatLrptState.className = "wxsat-live-value " + (lrptOn ? "wxsat-state-listening" : "wxsat-state-idle"); satLrptState.className = "sat-live-value " + (lrptOn ? "sat-state-listening" : "sat-state-idle");
}; };
function renderWxsatLatestCard() { function renderSatLatestCard() {
if (!wxsatLiveLatest) return; if (!satLiveLatest) return;
if (wxsatImageHistory.length === 0) { if (satImageHistory.length === 0) {
wxsatLiveLatest.innerHTML = satLiveLatest.innerHTML =
'<div style="color:var(--text-muted);font-size:0.82rem;">No images decoded yet. Enable a decoder and wait for a satellite pass.</div>'; '<div style="color:var(--text-muted);font-size:0.82rem;">No images decoded yet. Enable a decoder and wait for a satellite pass.</div>';
return; return;
} }
const img = wxsatImageHistory[0]; const img = satImageHistory[0];
const decoder = img._decoder || "unknown"; const decoder = img._decoder || "unknown";
const typeName = decoder === "lrpt" ? "Meteor LRPT" : "NOAA APT"; const typeName = decoder === "lrpt" ? "Meteor LRPT" : "NOAA APT";
const satellite = img.satellite || ""; const satellite = img.satellite || "";
@@ -93,30 +93,30 @@ function renderWxsatLatestCard() {
meta.push(`${lines} ${unit}`); meta.push(`${lines} ${unit}`);
meta.push(`${date} ${ts}`); meta.push(`${date} ${ts}`);
let html = `<div class="wxsat-latest-card">`; let html = `<div class="sat-latest-card">`;
html += `<div class="wxsat-latest-title">Latest decoded image</div>`; html += `<div class="sat-latest-title">Latest decoded image</div>`;
html += `<div class="wxsat-latest-meta">${meta.join(" &middot; ")}</div>`; html += `<div class="sat-latest-meta">${meta.join(" &middot; ")}</div>`;
if (img.path) { if (img.path) {
html += `<a href="${img.path}" target="_blank" style="font-size:0.8rem;color:var(--accent);display:inline-block;margin-top:0.25rem;">Download PNG</a>`; html += `<a href="${img.path}" target="_blank" style="font-size:0.8rem;color:var(--accent);display:inline-block;margin-top:0.25rem;">Download PNG</a>`;
} }
if (img.geo_bounds) { if (img.geo_bounds) {
html += ` <button type="button" class="wxsat-map-btn" onclick="window.wxsatShowOnMap(${img.geo_bounds[0]},${img.geo_bounds[1]},${img.geo_bounds[2]},${img.geo_bounds[3]})" style="font-size:0.8rem;margin-top:0.25rem;margin-left:0.5rem;cursor:pointer;background:none;border:1px solid var(--accent);color:var(--accent);border-radius:3px;padding:1px 6px;">Show on Map</button>`; html += ` <button type="button" class="sat-map-btn" onclick="window.satShowOnMap(${img.geo_bounds[0]},${img.geo_bounds[1]},${img.geo_bounds[2]},${img.geo_bounds[3]})" style="font-size:0.8rem;margin-top:0.25rem;margin-left:0.5rem;cursor:pointer;background:none;border:1px solid var(--accent);color:var(--accent);border-radius:3px;padding:1px 6px;">Show on Map</button>`;
} }
html += `</div>`; html += `</div>`;
wxsatLiveLatest.innerHTML = html; satLiveLatest.innerHTML = html;
} }
// ── History view: table ───────────────────────────────────────────── // ── History view: table ─────────────────────────────────────────────
function getFilteredHistory() { function getSatFilteredHistory() {
let items = wxsatImageHistory; let items = satImageHistory;
// Type filter // Type filter
const typeVal = wxsatTypeFilter ? wxsatTypeFilter.value : "all"; const typeVal = satTypeFilter ? satTypeFilter.value : "all";
if (typeVal === "apt") items = items.filter((i) => i._decoder === "apt"); if (typeVal === "apt") items = items.filter((i) => i._decoder === "apt");
else if (typeVal === "lrpt") items = items.filter((i) => i._decoder === "lrpt"); else if (typeVal === "lrpt") items = items.filter((i) => i._decoder === "lrpt");
// Text filter // Text filter
if (wxsatFilterText) { if (satFilterText) {
items = items.filter((i) => { items = items.filter((i) => {
const haystack = [ const haystack = [
i._decoder === "lrpt" ? "meteor lrpt" : "noaa apt", i._decoder === "lrpt" ? "meteor lrpt" : "noaa apt",
@@ -127,12 +127,12 @@ function getFilteredHistory() {
] ]
.join(" ") .join(" ")
.toUpperCase(); .toUpperCase();
return haystack.includes(wxsatFilterText); return haystack.includes(satFilterText);
}); });
} }
// Sort // Sort
const sortVal = wxsatSortSelect ? wxsatSortSelect.value : "newest"; const sortVal = satSortSelect ? satSortSelect.value : "newest";
if (sortVal === "oldest") { if (sortVal === "oldest") {
items = items.slice().reverse(); items = items.slice().reverse();
} }
@@ -140,13 +140,13 @@ function getFilteredHistory() {
return items; return items;
} }
function renderWxsatHistoryRow(img) { function renderSatHistoryRow(img) {
const row = document.createElement("div"); const row = document.createElement("div");
row.className = "wxsat-history-row"; row.className = "sat-history-row";
const decoder = img._decoder || "unknown"; const decoder = img._decoder || "unknown";
const typeName = decoder === "lrpt" ? "Meteor LRPT" : "NOAA APT"; const typeName = decoder === "lrpt" ? "Meteor LRPT" : "NOAA APT";
const typeClass = decoder === "lrpt" ? "wxsat-type-lrpt" : "wxsat-type-apt"; const typeClass = decoder === "lrpt" ? "sat-type-lrpt" : "sat-type-apt";
const ts = img._ts || "--"; const ts = img._ts || "--";
const date = img._tsMs ? new Date(img._tsMs).toLocaleDateString([], { month: "short", day: "numeric" }) : ""; const date = img._tsMs ? new Date(img._tsMs).toLocaleDateString([], { month: "short", day: "numeric" }) : "";
const satellite = img.satellite || "--"; const satellite = img.satellite || "--";
@@ -157,12 +157,12 @@ function renderWxsatHistoryRow(img) {
? `<a href="${img.path}" target="_blank" style="color:var(--accent);">PNG</a>` ? `<a href="${img.path}" target="_blank" style="color:var(--accent);">PNG</a>`
: "--"; : "--";
if (img.geo_bounds) { if (img.geo_bounds) {
link += ` <a href="javascript:void(0)" onclick="window.wxsatShowOnMap(${img.geo_bounds[0]},${img.geo_bounds[1]},${img.geo_bounds[2]},${img.geo_bounds[3]})" style="color:var(--accent);">Map</a>`; link += ` <a href="javascript:void(0)" onclick="window.satShowOnMap(${img.geo_bounds[0]},${img.geo_bounds[1]},${img.geo_bounds[2]},${img.geo_bounds[3]})" style="color:var(--accent);">Map</a>`;
} }
row.innerHTML = [ row.innerHTML = [
`<span>${date} ${ts}</span>`, `<span>${date} ${ts}</span>`,
`<span class="wxsat-col-type ${typeClass}">${typeName}</span>`, `<span class="sat-col-type ${typeClass}">${typeName}</span>`,
`<span>${satellite}</span>`, `<span>${satellite}</span>`,
`<span>${channels}</span>`, `<span>${channels}</span>`,
`<span>${lines} ${unit}</span>`, `<span>${lines} ${unit}</span>`,
@@ -172,19 +172,19 @@ function renderWxsatHistoryRow(img) {
return row; return row;
} }
function renderWxsatHistoryTable() { function renderSatHistoryTable() {
if (!wxsatHistoryList) return; if (!satHistoryList) return;
const items = getFilteredHistory(); const items = getSatFilteredHistory();
const fragment = document.createDocumentFragment(); const fragment = document.createDocumentFragment();
for (let i = 0; i < items.length; i += 1) { for (let i = 0; i < items.length; i += 1) {
fragment.appendChild(renderWxsatHistoryRow(items[i])); fragment.appendChild(renderSatHistoryRow(items[i]));
} }
wxsatHistoryList.replaceChildren(fragment); satHistoryList.replaceChildren(fragment);
if (wxsatHistoryCount) { if (satHistoryCount) {
const total = wxsatImageHistory.length; const total = satImageHistory.length;
const shown = items.length; const shown = items.length;
wxsatHistoryCount.textContent = satHistoryCount.textContent =
total === 0 total === 0
? "No images yet" ? "No images yet"
: shown === total : shown === total
@@ -194,7 +194,7 @@ function renderWxsatHistoryTable() {
} }
// ── Add image to history ──────────────────────────────────────────── // ── Add image to history ────────────────────────────────────────────
function addWxsatImage(img, decoder) { function addSatImage(img, decoder) {
const tsMs = Number.isFinite(img.ts_ms) ? Number(img.ts_ms) : Date.now(); const tsMs = Number.isFinite(img.ts_ms) ? Number(img.ts_ms) : Date.now();
img._tsMs = tsMs; img._tsMs = tsMs;
img._ts = new Date(tsMs).toLocaleTimeString([], { img._ts = new Date(tsMs).toLocaleTimeString([], {
@@ -204,55 +204,55 @@ function addWxsatImage(img, decoder) {
}); });
img._decoder = decoder; img._decoder = decoder;
wxsatImageHistory.unshift(img); satImageHistory.unshift(img);
if (wxsatImageHistory.length > WXSAT_MAX_IMAGES) { if (satImageHistory.length > SAT_MAX_IMAGES) {
wxsatImageHistory = wxsatImageHistory.slice(0, WXSAT_MAX_IMAGES); satImageHistory = satImageHistory.slice(0, SAT_MAX_IMAGES);
} }
scheduleWxsatUi("wxsat-latest", () => renderWxsatLatestCard()); scheduleSatUi("sat-latest", () => renderSatLatestCard());
if (wxsatActiveView === "history") { if (satActiveView === "history") {
scheduleWxsatUi("wxsat-history", () => renderWxsatHistoryTable()); scheduleSatUi("sat-history", () => renderSatHistoryTable());
} }
} }
// ── Server callbacks ──────────────────────────────────────────────── // ── Server callbacks ────────────────────────────────────────────────
window.onServerWxsatImage = function (msg) { window.onServerSatImage = function (msg) {
if (wxsatStatus) wxsatStatus.textContent = "Image received (NOAA APT)"; if (satStatus) satStatus.textContent = "Image received (NOAA APT)";
addWxsatImage(msg, "apt"); addSatImage(msg, "apt");
if (msg.geo_bounds && msg.path && window.addWxsatMapOverlay) { if (msg.geo_bounds && msg.path && window.addSatMapOverlay) {
window.addWxsatMapOverlay(msg); window.addSatMapOverlay(msg);
} }
}; };
window.onServerLrptImage = function (msg) { window.onServerLrptImage = function (msg) {
if (wxsatStatus) wxsatStatus.textContent = "Image received (Meteor LRPT)"; if (satStatus) satStatus.textContent = "Image received (Meteor LRPT)";
addWxsatImage(msg, "lrpt"); addSatImage(msg, "lrpt");
if (msg.geo_bounds && msg.path && window.addWxsatMapOverlay) { if (msg.geo_bounds && msg.path && window.addSatMapOverlay) {
window.addWxsatMapOverlay(msg); window.addSatMapOverlay(msg);
} }
}; };
window.resetWxsatHistoryView = function () { window.resetSatHistoryView = function () {
wxsatImageHistory = []; satImageHistory = [];
if (wxsatHistoryList) wxsatHistoryList.innerHTML = ""; if (satHistoryList) satHistoryList.innerHTML = "";
renderWxsatLatestCard(); renderSatLatestCard();
renderWxsatHistoryTable(); renderSatHistoryTable();
if (window.clearWxsatMapOverlays) window.clearWxsatMapOverlays(); if (window.clearSatMapOverlays) window.clearSatMapOverlays();
}; };
window.pruneWxsatHistoryView = function () { window.pruneSatHistoryView = function () {
renderWxsatHistoryTable(); renderSatHistoryTable();
renderWxsatLatestCard(); renderSatLatestCard();
}; };
// ── Toggle buttons ────────────────────────────────────────────────── // ── Toggle buttons ──────────────────────────────────────────────────
const wxsatDecodeToggleBtn = document.getElementById("wxsat-decode-toggle-btn"); const satDecodeToggleBtn = document.getElementById("sat-decode-toggle-btn");
wxsatDecodeToggleBtn?.addEventListener("click", async () => { satDecodeToggleBtn?.addEventListener("click", async () => {
try { try {
await window.takeSchedulerControlForDecoderDisable?.(wxsatDecodeToggleBtn); await window.takeSchedulerControlForDecoderDisable?.(satDecodeToggleBtn);
await postPath("/toggle_wxsat_decode"); await postPath("/toggle_wxsat_decode");
} catch (e) { } catch (e) {
console.error("WXSAT toggle failed", e); console.error("SAT toggle failed", e);
} }
}); });
@@ -267,22 +267,22 @@ lrptDecodeToggleBtn?.addEventListener("click", async () => {
}); });
// ── Filter / sort event listeners ─────────────────────────────────── // ── Filter / sort event listeners ───────────────────────────────────
wxsatFilterInput?.addEventListener("input", () => { satFilterInput?.addEventListener("input", () => {
wxsatFilterText = wxsatFilterInput.value.trim().toUpperCase(); satFilterText = satFilterInput.value.trim().toUpperCase();
renderWxsatHistoryTable(); renderSatHistoryTable();
}); });
wxsatSortSelect?.addEventListener("change", () => renderWxsatHistoryTable()); satSortSelect?.addEventListener("change", () => renderSatHistoryTable());
wxsatTypeFilter?.addEventListener("change", () => renderWxsatHistoryTable()); satTypeFilter?.addEventListener("change", () => renderSatHistoryTable());
// ── Settings: clear history ───────────────────────────────────────── // ── Settings: clear history ─────────────────────────────────────────
document document
.getElementById("settings-clear-wxsat-history") .getElementById("settings-clear-sat-history")
?.addEventListener("click", async () => { ?.addEventListener("click", async () => {
try { try {
await postPath("/clear_wxsat_decode"); await postPath("/clear_wxsat_decode");
await postPath("/clear_lrpt_decode"); await postPath("/clear_lrpt_decode");
window.resetWxsatHistoryView(); window.resetSatHistoryView();
} catch (e) { } catch (e) {
console.error("Weather satellite history clear failed", e); console.error("Weather satellite history clear failed", e);
} }
@@ -372,10 +372,10 @@ async function loadSatPredictions() {
} }
// ── Navigate to map centered on satellite image bounds ────────────── // ── Navigate to map centered on satellite image bounds ──────────────
window.wxsatShowOnMap = function (south, west, north, east) { window.satShowOnMap = function (south, west, north, east) {
// Enable wxsat filter if not active // Enable sat filter if not active
if (typeof window.enableMapSourceFilter === "function") { if (typeof window.enableMapSourceFilter === "function") {
window.enableMapSourceFilter("wxsat"); window.enableMapSourceFilter("sat");
} }
// Navigate to the center of the image bounds // Navigate to the center of the image bounds
const lat = (south + north) / 2; const lat = (south + north) / 2;
@@ -386,5 +386,5 @@ window.wxsatShowOnMap = function (south, west, north, east) {
}; };
// ── Initial render ────────────────────────────────────────────────── // ── Initial render ──────────────────────────────────────────────────
renderWxsatLatestCard(); renderSatLatestCard();
renderWxsatHistoryTable(); renderSatHistoryTable();
@@ -4539,28 +4539,28 @@ button:focus-visible, input:focus-visible, select:focus-visible {
} }
} }
/* ── SAT panel ──────────────────────────────────────────────────────── */ /* ── SAT panel ──────────────────────────────────────────────────────── */
.wxsat-view-bar { display: flex; gap: 0; margin-bottom: 0.75rem; border-bottom: 1px solid var(--border); } .sat-view-bar { display: flex; gap: 0; margin-bottom: 0.75rem; border-bottom: 1px solid var(--border); }
.wxsat-view-btn { flex-shrink: 0; background: transparent; border: none; border-bottom: 2px solid transparent; border-radius: 0; padding: 0.3rem 0.9rem; color: var(--text-muted); cursor: pointer; font-size: 0.82rem; } .sat-view-btn { flex-shrink: 0; background: transparent; border: none; border-bottom: 2px solid transparent; border-radius: 0; padding: 0.3rem 0.9rem; color: var(--text-muted); cursor: pointer; font-size: 0.82rem; }
.wxsat-view-active { border-bottom-color: var(--accent-green); color: var(--accent-green); font-weight: 600; } .sat-view-active { border-bottom-color: var(--accent-green); color: var(--accent-green); font-weight: 600; }
.wxsat-view-btn:hover:not(.wxsat-view-active) { color: var(--text); } .sat-view-btn:hover:not(.sat-view-active) { color: var(--text); }
.wxsat-live-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin-bottom: 0.5rem; } .sat-live-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; margin-bottom: 0.5rem; }
.wxsat-live-card { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: 0.35rem; padding: 0.5rem 0.75rem; display: flex; flex-direction: column; gap: 0.15rem; } .sat-live-card { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: 0.35rem; padding: 0.5rem 0.75rem; display: flex; flex-direction: column; gap: 0.15rem; }
.wxsat-live-label { font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; } .sat-live-label { font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.04em; }
.wxsat-live-value { font-size: 0.9rem; font-weight: 600; } .sat-live-value { font-size: 0.9rem; font-weight: 600; }
.wxsat-state-idle { color: var(--text-muted); } .sat-state-idle { color: var(--text-muted); }
.wxsat-state-listening { color: var(--accent-green); } .sat-state-listening { color: var(--accent-green); }
.wxsat-state-decoding { color: #f0a020; } .sat-state-decoding { color: #f0a020; }
.wxsat-history-controls { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.5rem; flex-wrap: wrap; } .sat-history-controls { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.5rem; flex-wrap: wrap; }
.wxsat-sort-select { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: 0.25rem; color: var(--text); padding: 0.25rem 0.4rem; font-size: 0.82rem; } .sat-sort-select { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: 0.25rem; color: var(--text); padding: 0.25rem 0.4rem; font-size: 0.82rem; }
.wxsat-history-header { display: grid; grid-template-columns: 7rem 5.5rem 9rem 6rem 4.5rem 1fr; gap: 0.25rem; padding: 0.25rem 0.4rem; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; border-bottom: 1px solid var(--border); } .sat-history-header { display: grid; grid-template-columns: 7rem 5.5rem 9rem 6rem 4.5rem 1fr; gap: 0.25rem; padding: 0.25rem 0.4rem; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; border-bottom: 1px solid var(--border); }
.wxsat-history-row { display: grid; grid-template-columns: 7rem 5.5rem 9rem 6rem 4.5rem 1fr; gap: 0.25rem; padding: 0.35rem 0.4rem; font-size: 0.82rem; border-bottom: 1px solid var(--border-faint, rgba(255,255,255,0.04)); } .sat-history-row { display: grid; grid-template-columns: 7rem 5.5rem 9rem 6rem 4.5rem 1fr; gap: 0.25rem; padding: 0.35rem 0.4rem; font-size: 0.82rem; border-bottom: 1px solid var(--border-faint, rgba(255,255,255,0.04)); }
.wxsat-history-row:hover { background: var(--bg-hover, rgba(255,255,255,0.02)); } .sat-history-row:hover { background: var(--bg-hover, rgba(255,255,255,0.02)); }
.wxsat-col-type { font-weight: 500; } .sat-col-type { font-weight: 500; }
.wxsat-type-apt { color: #6ec6ff; } .sat-type-apt { color: #6ec6ff; }
.wxsat-type-lrpt { color: #b39ddb; } .sat-type-lrpt { color: #b39ddb; }
.wxsat-latest-card { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: 0.4rem; padding: 0.6rem 0.75rem; } .sat-latest-card { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: 0.4rem; padding: 0.6rem 0.75rem; }
.wxsat-latest-card .wxsat-latest-title { font-size: 0.82rem; font-weight: 600; margin-bottom: 0.25rem; } .sat-latest-card .sat-latest-title { font-size: 0.82rem; font-weight: 600; margin-bottom: 0.25rem; }
.wxsat-latest-card .wxsat-latest-meta { font-size: 0.78rem; color: var(--text-muted); } .sat-latest-card .sat-latest-meta { font-size: 0.78rem; color: var(--text-muted); }
.sat-pred-header { display: grid; grid-template-columns: 6rem 1fr 4.5rem 5rem 6rem; gap: 0.25rem; padding: 0.25rem 0.4rem; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; border-bottom: 1px solid var(--border); } .sat-pred-header { display: grid; grid-template-columns: 6rem 1fr 4.5rem 5rem 6rem; gap: 0.25rem; padding: 0.25rem 0.4rem; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.03em; border-bottom: 1px solid var(--border); }
.sat-pred-row { display: grid; grid-template-columns: 6rem 1fr 4.5rem 5rem 6rem; gap: 0.25rem; padding: 0.35rem 0.4rem; font-size: 0.82rem; border-bottom: 1px solid var(--border-faint, rgba(255,255,255,0.04)); } .sat-pred-row { display: grid; grid-template-columns: 6rem 1fr 4.5rem 5rem 6rem; gap: 0.25rem; padding: 0.35rem 0.4rem; font-size: 0.82rem; border-bottom: 1px solid var(--border-faint, rgba(255,255,255,0.04)); }
.sat-pred-row:hover { background: var(--bg-hover, rgba(255,255,255,0.02)); } .sat-pred-row:hover { background: var(--bg-hover, rgba(255,255,255,0.02)); }
@@ -4573,8 +4573,8 @@ button:focus-visible, input:focus-visible, select:focus-visible {
.sat-pred-el-mid { color: #f0a020; } .sat-pred-el-mid { color: #f0a020; }
.sat-pred-el-low { color: var(--text-muted); } .sat-pred-el-low { color: var(--text-muted); }
@media (max-width: 600px) { @media (max-width: 600px) {
.wxsat-live-grid { grid-template-columns: 1fr; } .sat-live-grid { grid-template-columns: 1fr; }
.wxsat-history-header, .wxsat-history-row { grid-template-columns: 5rem 4rem 6rem 4rem 3.5rem 1fr; font-size: 0.75rem; } .sat-history-header, .sat-history-row { grid-template-columns: 5rem 4rem 6rem 4rem 3.5rem 1fr; font-size: 0.75rem; }
.sat-pred-header, .sat-pred-row { grid-template-columns: 5.5rem 1fr 4rem 4.5rem; font-size: 0.75rem; } .sat-pred-header, .sat-pred-row { grid-template-columns: 5.5rem 1fr 4rem 4.5rem; font-size: 0.75rem; }
.sat-pred-col-dir { display: none; } .sat-pred-col-dir { display: none; }
} }
@@ -2174,7 +2174,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
.service(ft2_js) .service(ft2_js)
.service(wspr_js) .service(wspr_js)
.service(cw_js) .service(cw_js)
.service(wxsat_js) .service(sat_js)
.service(bookmarks_js) .service(bookmarks_js)
.service(scheduler_js) .service(scheduler_js)
.service(background_decode_js) .service(background_decode_js)
@@ -2318,9 +2318,9 @@ async fn cw_js() -> impl Responder {
no_cache_response("application/javascript; charset=utf-8", status::CW_JS) no_cache_response("application/javascript; charset=utf-8", status::CW_JS)
} }
#[get("/wxsat.js")] #[get("/sat.js")]
async fn wxsat_js() -> impl Responder { async fn sat_js() -> impl Responder {
no_cache_response("application/javascript; charset=utf-8", status::WXSAT_JS) no_cache_response("application/javascript; charset=utf-8", status::SAT_JS)
} }
#[get("/bookmarks.js")] #[get("/bookmarks.js")]
@@ -22,7 +22,7 @@ pub const FT4_JS: &str = include_str!("../assets/web/plugins/ft4.js");
pub const FT2_JS: &str = include_str!("../assets/web/plugins/ft2.js"); pub const FT2_JS: &str = include_str!("../assets/web/plugins/ft2.js");
pub const WSPR_JS: &str = include_str!("../assets/web/plugins/wspr.js"); pub const WSPR_JS: &str = include_str!("../assets/web/plugins/wspr.js");
pub const CW_JS: &str = include_str!("../assets/web/plugins/cw.js"); pub const CW_JS: &str = include_str!("../assets/web/plugins/cw.js");
pub const WXSAT_JS: &str = include_str!("../assets/web/plugins/wxsat.js"); pub const SAT_JS: &str = include_str!("../assets/web/plugins/sat.js");
pub const BOOKMARKS_JS: &str = include_str!("../assets/web/plugins/bookmarks.js"); pub const BOOKMARKS_JS: &str = include_str!("../assets/web/plugins/bookmarks.js");
pub const SCHEDULER_JS: &str = include_str!("../assets/web/plugins/scheduler.js"); pub const SCHEDULER_JS: &str = include_str!("../assets/web/plugins/scheduler.js");
pub const BACKGROUND_DECODE_JS: &str = include_str!("../assets/web/plugins/background-decode.js"); pub const BACKGROUND_DECODE_JS: &str = include_str!("../assets/web/plugins/background-decode.js");
+10 -3
View File
@@ -72,8 +72,15 @@ pub struct PassPrediction {
pub duration_s: u64, pub duration_s: u64,
} }
/// Well-known amateur satellites: (display name, NORAD ID). /// Satellites included in pass predictions: weather + amateur.
const HAM_SATS: &[(&str, u32)] = &[ const PREDICTION_SATS: &[(&str, u32)] = &[
// Weather satellites (TLEs from CelesTrak weather group)
("NOAA-15", 25338),
("NOAA-18", 28654),
("NOAA-19", 33591),
("Meteor-M N2-3", 57166),
("Meteor-M N2-4", 59051),
// Amateur satellites (TLEs from CelesTrak amateur group)
("ISS (ARISS)", 25544), ("ISS (ARISS)", 25544),
("AO-91 (RadFxSat)", 43017), ("AO-91 (RadFxSat)", 43017),
("AO-92 (Fox-1D)", 43137), ("AO-92 (Fox-1D)", 43137),
@@ -457,7 +464,7 @@ pub fn compute_upcoming_passes(
let mut all_passes = Vec::new(); let mut all_passes = Vec::new();
for &(name, norad_id) in HAM_SATS { for &(name, norad_id) in PREDICTION_SATS {
let tle = guard let tle = guard
.as_ref() .as_ref()
.and_then(|s| s.get(&norad_id)) .and_then(|s| s.get(&norad_id))
+9 -12
View File
@@ -2648,14 +2648,12 @@ async fn finalize_wxsat_pass(
.or_else(|| { .or_else(|| {
// Fallback: use station location if available // Fallback: use station location if available
match (station_lat, station_lon) { match (station_lat, station_lon) {
(Some(lat), Some(lon)) => Some( (Some(lat), Some(lon)) => Some(trx_core::geo::estimate_pass_geo_from_station(
trx_core::geo::estimate_pass_geo_from_station(
apt_image.first_line_ms, apt_image.first_line_ms,
pass_end_ms, pass_end_ms,
lat, lat,
lon, lon,
), )),
),
_ => None, _ => None,
} }
}); });
@@ -2877,18 +2875,14 @@ async fn finalize_lrpt_pass(
station_lon, station_lon,
) )
}) })
.or_else(|| { .or_else(|| match (station_lat, station_lon) {
match (station_lat, station_lon) { (Some(lat), Some(lon)) => Some(trx_core::geo::estimate_pass_geo_from_station(
(Some(lat), Some(lon)) => Some(
trx_core::geo::estimate_pass_geo_from_station(
pass_start_ms, pass_start_ms,
pass_end_ms, pass_end_ms,
lat, lat,
lon, lon,
), )),
),
_ => None, _ => None,
}
}); });
let (geo_bounds, ground_track) = match pass_geo { let (geo_bounds, ground_track) = match pass_geo {
Some(geo) => (Some(geo.bounds), Some(geo.ground_track)), Some(geo) => (Some(geo.bounds), Some(geo.ground_track)),
@@ -2907,7 +2901,10 @@ async fn finalize_lrpt_pass(
ground_track, ground_track,
}; };
if geo_bounds.is_some() { if geo_bounds.is_some() {
info!("LRPT: geo-referenced {} image overlay", sat_name.as_deref().unwrap_or("unknown")); info!(
"LRPT: geo-referenced {} image overlay",
sat_name.as_deref().unwrap_or("unknown")
);
} }
histories.record_lrpt_image(img.clone()); histories.record_lrpt_image(img.clone());
let _ = decode_tx.send(DecodedMessage::LrptImage(img)); let _ = decode_tx.send(DecodedMessage::LrptImage(img));