mirror of
https://github.com/smittix/intercept.git
synced 2026-06-11 07:23:30 -07:00
Enhance weather satellite UX with pass geometry, guides, and wider predictions
Pass prediction improvements: - Widen prediction window to 48h at 5° min elevation (was 24h/15°) - Add AOS/TCA/LOS pass geometry detail panel with times and bearings - Fix duration display (was showing seconds labeled as minutes) - Enhanced pass cards with AOS/LOS times, bearings, and directions - Add REFRESH button in passes panel header - Better empty state with clear "set your location" prompt and icon Countdown and visual: - Pulse animation on countdown when pass is imminent or active - Countdown numbers scale up and change color for urgency Sidebar getting started guide: - New "Getting Started" section explaining what Meteor satellites are, polar orbits, 4-8 passes/day, step-by-step workflow - "When to look" tips (elevation, day vs night, pass direction) - "What you need" equipment table with costs - Collapsed antenna guide by default to reduce initial overwhelm - Improved offline decode section with clear instructions on where to get IQ recordings Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -408,6 +408,87 @@
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
/* ===== Pass Geometry Detail ===== */
|
||||
.wxsat-pass-geometry {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 10px 14px;
|
||||
background: var(--bg-primary, #0d1117);
|
||||
border-bottom: 1px solid var(--border-color, #2a3040);
|
||||
font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif;
|
||||
}
|
||||
|
||||
.wxsat-geom-event {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.wxsat-geom-event.wxsat-geom-tca {
|
||||
color: var(--neon-green);
|
||||
}
|
||||
|
||||
.wxsat-geom-label {
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
color: var(--text-dim, #666);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.wxsat-geom-tca .wxsat-geom-label {
|
||||
color: var(--neon-green);
|
||||
}
|
||||
|
||||
.wxsat-geom-time {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary, #e0e0e0);
|
||||
}
|
||||
|
||||
.wxsat-geom-tca .wxsat-geom-time {
|
||||
color: var(--neon-green);
|
||||
}
|
||||
|
||||
.wxsat-geom-az {
|
||||
font-size: 10px;
|
||||
color: var(--text-dim, #666);
|
||||
}
|
||||
|
||||
.wxsat-geom-arrow {
|
||||
font-size: 14px;
|
||||
color: var(--text-dim, #444);
|
||||
}
|
||||
|
||||
.wxsat-geom-meta {
|
||||
font-size: 10px;
|
||||
color: var(--text-dim, #666);
|
||||
margin-left: 8px;
|
||||
padding-left: 8px;
|
||||
border-left: 1px solid var(--border-color, #2a3040);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ===== Countdown Pulse Animation ===== */
|
||||
.wxsat-countdown-box.imminent .wxsat-cd-value {
|
||||
animation: wxsat-count-pulse 1s ease-in-out infinite;
|
||||
color: var(--accent-yellow);
|
||||
}
|
||||
|
||||
.wxsat-countdown-box.active .wxsat-cd-value {
|
||||
animation: wxsat-count-pulse 1s ease-in-out infinite;
|
||||
color: var(--neon-green);
|
||||
}
|
||||
|
||||
@keyframes wxsat-count-pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.15); opacity: 0.8; }
|
||||
}
|
||||
|
||||
/* ===== Pass Predictions Panel ===== */
|
||||
.wxsat-passes-panel {
|
||||
flex: 0 0 280px;
|
||||
|
||||
@@ -751,7 +751,7 @@ const WeatherSat = (function() {
|
||||
}
|
||||
|
||||
try {
|
||||
const url = `/weather-sat/passes?latitude=${storedLat}&longitude=${storedLon}&hours=24&min_elevation=15&trajectory=true&ground_track=true`;
|
||||
const url = `/weather-sat/passes?latitude=${storedLat}&longitude=${storedLon}&hours=48&min_elevation=5&trajectory=true&ground_track=true`;
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
|
||||
@@ -815,6 +815,42 @@ const WeatherSat = (function() {
|
||||
// Update polar panel subtitle
|
||||
const polarSat = document.getElementById('wxsatPolarSat');
|
||||
if (polarSat) polarSat.textContent = `${pass.name} ${pass.maxEl}\u00b0`;
|
||||
|
||||
// Update pass geometry detail panel
|
||||
updatePassGeometry(pass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the AOS/TCA/LOS pass geometry detail panel.
|
||||
*/
|
||||
function updatePassGeometry(pass) {
|
||||
const panel = document.getElementById('wxsatPassGeometry');
|
||||
if (!panel) return;
|
||||
|
||||
if (!pass) {
|
||||
panel.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
panel.style.display = 'flex';
|
||||
|
||||
const aosTime = document.getElementById('wxsatGeomAosTime');
|
||||
const aosAz = document.getElementById('wxsatGeomAosAz');
|
||||
const tcaEl = document.getElementById('wxsatGeomTcaEl');
|
||||
const tcaAz = document.getElementById('wxsatGeomTcaAz');
|
||||
const losTime = document.getElementById('wxsatGeomLosTime');
|
||||
const losAz = document.getElementById('wxsatGeomLosAz');
|
||||
const meta = document.getElementById('wxsatGeomMeta');
|
||||
|
||||
const tzLabel = getTZLabel();
|
||||
if (aosTime) aosTime.textContent = formatShortTime(pass.startTimeISO) + tzLabel;
|
||||
if (aosAz) aosAz.textContent = `${Math.round(pass.riseAz || 0)}\u00b0 ${azToDir(pass.riseAz)}`;
|
||||
if (tcaEl) tcaEl.textContent = `${pass.maxEl}\u00b0 el`;
|
||||
if (tcaAz) tcaAz.textContent = `${Math.round(pass.maxElAz || pass.tcaAz || 0)}\u00b0 ${azToDir(pass.maxElAz || pass.tcaAz)}`;
|
||||
if (losTime) losTime.textContent = formatShortTime(pass.endTimeISO) + tzLabel;
|
||||
if (losAz) losAz.textContent = `${Math.round(pass.setAz || 0)}\u00b0 ${azToDir(pass.setAz)}`;
|
||||
|
||||
const durMin = Math.round((pass.duration || 0) / 60);
|
||||
if (meta) meta.textContent = `${durMin} min / ${pass.quality}`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -829,12 +865,28 @@ const WeatherSat = (function() {
|
||||
if (!container) return;
|
||||
|
||||
if (passList.length === 0) {
|
||||
const hasLocation = localStorage.getItem('observerLat') !== null;
|
||||
const hasLocation = localStorage.getItem('observerLat') !== null ||
|
||||
(window.ObserverLocation && ObserverLocation.isSharedEnabled() && ObserverLocation.getShared()?.lat);
|
||||
container.innerHTML = `
|
||||
<div class="wxsat-gallery-empty">
|
||||
<p>${hasLocation ? 'No passes in next 24h' : 'Set location to see pass predictions'}</p>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width: 32px; height: 32px; margin-bottom: 8px; opacity: 0.3;">
|
||||
${hasLocation
|
||||
? '<circle cx="12" cy="12" r="10"/><path d="M2 12h20"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>'
|
||||
: '<circle cx="12" cy="12" r="10"/><path d="M12 2v4m0 12v4M2 12h4m12 0h4"/>'}
|
||||
</svg>
|
||||
<p style="font-size: 12px; font-weight: 600; color: var(--text-secondary);">
|
||||
${hasLocation ? 'No passes in next 24 hours' : 'Set your location'}
|
||||
</p>
|
||||
<p style="font-size: 11px; margin-top: 4px;">
|
||||
${hasLocation
|
||||
? 'All Meteor passes may be below the minimum elevation. Try again later.'
|
||||
: 'Enter lat/lon in the strip bar above or click GPS to load pass predictions'}
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
// Hide geometry panel when no passes
|
||||
const geom = document.getElementById('wxsatPassGeometry');
|
||||
if (geom) geom.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -866,6 +918,10 @@ const WeatherSat = (function() {
|
||||
const riseDir = azToDir(pass.riseAz);
|
||||
const setDir = azToDir(pass.setAz);
|
||||
const bestBadge = isBest ? '<span class="wxsat-pass-best-badge">BEST</span>' : '';
|
||||
const durMin = Math.round((pass.duration || 0) / 60);
|
||||
const aosStr = formatShortTime(pass.startTimeISO);
|
||||
const losStr = formatShortTime(pass.endTimeISO);
|
||||
const tzLabel = getTZLabel();
|
||||
|
||||
return `
|
||||
<div class="wxsat-pass-card${isSelected ? ' selected' : ''}" onclick="WeatherSat.selectPass(${idx})">
|
||||
@@ -874,13 +930,13 @@ const WeatherSat = (function() {
|
||||
<span class="wxsat-pass-mode ${modeClass}">${escapeHtml(pass.mode)}</span>
|
||||
</div>
|
||||
<div class="wxsat-pass-details">
|
||||
<span class="wxsat-pass-detail-label">Time</span>
|
||||
<span class="wxsat-pass-detail-value">${escapeHtml(timeStr)}</span>
|
||||
<span class="wxsat-pass-detail-label">Max El</span>
|
||||
<span class="wxsat-pass-detail-value">${pass.maxEl}°</span>
|
||||
<span class="wxsat-pass-detail-label">Duration</span>
|
||||
<span class="wxsat-pass-detail-value">${pass.duration} min</span>
|
||||
<span class="wxsat-pass-detail-label">Direction</span>
|
||||
<span class="wxsat-pass-detail-label">AOS</span>
|
||||
<span class="wxsat-pass-detail-value">${escapeHtml(aosStr)}${escapeHtml(tzLabel)} · ${Math.round(pass.riseAz || 0)}° ${riseDir}</span>
|
||||
<span class="wxsat-pass-detail-label">LOS</span>
|
||||
<span class="wxsat-pass-detail-value">${escapeHtml(losStr)}${escapeHtml(tzLabel)} · ${Math.round(pass.setAz || 0)}° ${setDir}</span>
|
||||
<span class="wxsat-pass-detail-label">Peak</span>
|
||||
<span class="wxsat-pass-detail-value">${pass.maxEl}° el · ${durMin} min</span>
|
||||
<span class="wxsat-pass-detail-label">Track</span>
|
||||
<span class="wxsat-pass-detail-value">${riseDir} <span class="wxsat-dir-arrow">→</span> ${setDir}</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-top: 4px;">
|
||||
@@ -1456,10 +1512,11 @@ const WeatherSat = (function() {
|
||||
detailEl.textContent = `ACTIVE - ${nextPass.maxEl}\u00b0 max el`;
|
||||
} else {
|
||||
const bestPass = findBestPass(filtered);
|
||||
const durMin = Math.round((nextPass.duration || 0) / 60);
|
||||
const bestNote = bestPass && bestPass.startTimeISO !== nextPass.startTimeISO
|
||||
? ` | Best: ${bestPass.name} ${formatShortTime(bestPass.startTimeISO)}${getTZLabel()} (${bestPass.maxEl}\u00b0)`
|
||||
: '';
|
||||
detailEl.textContent = `${passTimeStr} / ${nextPass.maxEl}\u00b0 max el / ${nextPass.duration} min${bestNote}`;
|
||||
detailEl.textContent = `${passTimeStr} / ${nextPass.maxEl}\u00b0 max el / ${durMin} min${bestNote}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1592,7 +1649,8 @@ const WeatherSat = (function() {
|
||||
if (bestEl) {
|
||||
if (best) {
|
||||
const t = formatShortTime(best.startTimeISO) + getTZLabel();
|
||||
bestEl.textContent = `Best: ${best.name} at ${t} (${best.maxEl}\u00b0 el, ${best.duration} min)`;
|
||||
const bestDurMin = Math.round((best.duration || 0) / 60);
|
||||
bestEl.textContent = `Best: ${best.name} at ${t} (${best.maxEl}\u00b0 el, ${bestDurMin} min)`;
|
||||
} else {
|
||||
bestEl.textContent = 'No upcoming passes';
|
||||
}
|
||||
@@ -2343,13 +2401,13 @@ const WeatherSat = (function() {
|
||||
|
||||
const offsets = [25, 95, 200, 340, 510, 720, 880, 1020];
|
||||
const elevations = [72, 45, 28, 63, 18, 55, 82, 35];
|
||||
const durations = [14, 12, 8, 13, 6, 11, 15, 10];
|
||||
const durations = [840, 720, 480, 780, 360, 660, 900, 600]; // seconds
|
||||
const riseAzs = [350, 15, 200, 310, 170, 40, 280, 90];
|
||||
const setAzs = [170, 195, 20, 130, 350, 220, 100, 270];
|
||||
|
||||
offsets.forEach((offset, i) => {
|
||||
const start = new Date(now.getTime() + offset * 60000);
|
||||
const end = new Date(start.getTime() + durations[i] * 60000);
|
||||
const end = new Date(start.getTime() + durations[i] * 1000);
|
||||
const sat = demoSats[i % 2];
|
||||
const el = elevations[i];
|
||||
const quality = el >= 60 ? 'excellent' : el >= 30 ? 'good' : 'fair';
|
||||
|
||||
+27
-1
@@ -2612,10 +2612,36 @@
|
||||
<div class="wxsat-passes-header">
|
||||
<span class="wxsat-passes-title">Upcoming Passes</span>
|
||||
<span class="wxsat-passes-count" id="wxsatPassesCount">0</span>
|
||||
<button class="wxsat-strip-btn" onclick="WeatherSat.loadPasses()" title="Refresh pass predictions" style="margin-left: auto; font-size: 9px; padding: 2px 6px;">REFRESH</button>
|
||||
</div>
|
||||
<!-- Pass geometry detail (shown when pass selected) -->
|
||||
<div class="wxsat-pass-geometry" id="wxsatPassGeometry" style="display: none;">
|
||||
<div class="wxsat-geom-event">
|
||||
<span class="wxsat-geom-label">AOS</span>
|
||||
<span class="wxsat-geom-time" id="wxsatGeomAosTime">--</span>
|
||||
<span class="wxsat-geom-az" id="wxsatGeomAosAz">--</span>
|
||||
</div>
|
||||
<div class="wxsat-geom-arrow">→</div>
|
||||
<div class="wxsat-geom-event wxsat-geom-tca">
|
||||
<span class="wxsat-geom-label">TCA</span>
|
||||
<span class="wxsat-geom-time" id="wxsatGeomTcaEl">--</span>
|
||||
<span class="wxsat-geom-az" id="wxsatGeomTcaAz">--</span>
|
||||
</div>
|
||||
<div class="wxsat-geom-arrow">→</div>
|
||||
<div class="wxsat-geom-event">
|
||||
<span class="wxsat-geom-label">LOS</span>
|
||||
<span class="wxsat-geom-time" id="wxsatGeomLosTime">--</span>
|
||||
<span class="wxsat-geom-az" id="wxsatGeomLosAz">--</span>
|
||||
</div>
|
||||
<div class="wxsat-geom-meta" id="wxsatGeomMeta">--</div>
|
||||
</div>
|
||||
<div class="wxsat-passes-list" id="wxsatPassesList">
|
||||
<div class="wxsat-gallery-empty">
|
||||
<p>Set location to see pass predictions</p>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="width: 32px; height: 32px; margin-bottom: 8px; opacity: 0.3;">
|
||||
<circle cx="12" cy="12" r="10"/><path d="M12 2v4m0 12v4M2 12h4m12 0h4"/>
|
||||
</svg>
|
||||
<p style="font-size: 12px; font-weight: 600; color: var(--text-secondary);">Set your location</p>
|
||||
<p style="font-size: 11px; margin-top: 4px;">Enter lat/lon in the strip bar above or click the GPS button to load pass predictions</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,74 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Getting Started Guide -->
|
||||
<div class="section">
|
||||
<h3>Getting Started</h3>
|
||||
<div style="font-size: 11px; color: var(--text-dim); line-height: 1.6;">
|
||||
|
||||
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px; margin-bottom: 10px;">
|
||||
<strong style="color: var(--accent-cyan); font-size: 12px;">What are Meteor satellites?</strong>
|
||||
<p style="margin-top: 6px;">
|
||||
Russia's <strong style="color: var(--text-primary);">Meteor-M2-3</strong> and <strong style="color: var(--text-primary);">Meteor-M2-4</strong>
|
||||
are polar-orbiting weather satellites that continuously transmit real-time color imagery (clouds, land, sea) at <strong style="color: var(--text-primary);">137.900 MHz</strong>
|
||||
using the LRPT digital format. Unlike old analog NOAA APT, LRPT produces sharp, full-color images.
|
||||
</p>
|
||||
<p style="margin-top: 6px;">
|
||||
They orbit ~830 km high, circling the Earth every ~100 minutes in a near-polar sun-synchronous orbit.
|
||||
From any location, you'll typically get <strong style="color: var(--text-primary);">4–8 usable passes per day</strong>,
|
||||
each lasting 8–15 minutes as the satellite crosses your sky.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px; margin-bottom: 10px;">
|
||||
<strong style="color: var(--accent-cyan); font-size: 12px;">Step-by-step</strong>
|
||||
<ol style="margin: 6px 0 0 16px; padding: 0;">
|
||||
<li style="margin-bottom: 4px;"><strong style="color: var(--text-primary);">Set your location</strong> — Enter your lat/lon in the strip bar above (or click GPS). This is required for pass predictions.</li>
|
||||
<li style="margin-bottom: 4px;"><strong style="color: var(--text-primary);">Check upcoming passes</strong> — The pass list shows when each satellite will be overhead. Higher max elevation = better signal. Passes above 30° are "good", above 60° are "excellent".</li>
|
||||
<li style="margin-bottom: 4px;"><strong style="color: var(--text-primary);">Prepare your antenna</strong> — You need a 137 MHz antenna outdoors with clear sky (see Antenna Guide below). A $5 V-dipole works for high passes.</li>
|
||||
<li style="margin-bottom: 4px;"><strong style="color: var(--text-primary);">Click Capture</strong> on a pass card when it's about to start, or enable <strong style="color: var(--text-primary);">AUTO</strong> to let the scheduler capture automatically.</li>
|
||||
<li style="margin-bottom: 4px;"><strong style="color: var(--text-primary);">Wait for images</strong> — SatDump will tune, lock the signal, and decode. Decoded images appear in the gallery after the pass completes.</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px; margin-bottom: 10px;">
|
||||
<strong style="color: var(--accent-cyan); font-size: 12px;">When to look</strong>
|
||||
<ul style="margin: 6px 0 0 14px; padding: 0;">
|
||||
<li><strong style="color: var(--text-primary);">Best passes:</strong> When the satellite is high overhead (>30° elevation). The countdown timer shows the next one.</li>
|
||||
<li><strong style="color: var(--text-primary);">Day vs night:</strong> Daytime passes produce visible-light imagery. Night passes still work but only produce infrared/thermal images.</li>
|
||||
<li><strong style="color: var(--text-primary);">Both satellites share 137.9 MHz</strong> so they won't transmit at the same time. You'll see separate pass predictions for each.</li>
|
||||
<li><strong style="color: var(--text-primary);">Pass direction:</strong> Meteor satellites travel roughly north→south or south→north. The pass cards show the exact rise/set direction.</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px;">
|
||||
<strong style="color: var(--accent-cyan); font-size: 12px;">What you need</strong>
|
||||
<table style="width: 100%; margin-top: 6px; font-size: 10px; border-collapse: collapse;">
|
||||
<tr style="border-bottom: 1px solid var(--border-color);">
|
||||
<td style="padding: 3px 4px; color: var(--text-dim);">SDR receiver</td>
|
||||
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">RTL-SDR V3/V4 ($25-35)</td>
|
||||
</tr>
|
||||
<tr style="border-bottom: 1px solid var(--border-color);">
|
||||
<td style="padding: 3px 4px; color: var(--text-dim);">Antenna</td>
|
||||
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">137 MHz V-dipole ($5 DIY) or QFH ($20-30)</td>
|
||||
</tr>
|
||||
<tr style="border-bottom: 1px solid var(--border-color);">
|
||||
<td style="padding: 3px 4px; color: var(--text-dim);">LNA (optional)</td>
|
||||
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">137 MHz filtered, at antenna ($15-25)</td>
|
||||
</tr>
|
||||
<tr style="border-bottom: 1px solid var(--border-color);">
|
||||
<td style="padding: 3px 4px; color: var(--text-dim);">Location</td>
|
||||
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">Outdoors, clear sky view</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 3px 4px; color: var(--text-dim);">No hardware?</td>
|
||||
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">Use <em>Load Demo Data</em> below to explore the UI</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Satellite</h3>
|
||||
<div class="form-group">
|
||||
@@ -33,10 +101,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Antenna Guide - detailed -->
|
||||
<!-- Antenna Guide - detailed (collapsed by default) -->
|
||||
<div class="section">
|
||||
<h3>Antenna Guide</h3>
|
||||
<div style="font-size: 11px; color: var(--text-dim); line-height: 1.5;">
|
||||
<h3 onclick="this.parentElement.querySelector('.wxsat-antenna-body').classList.toggle('collapsed'); this.querySelector('.wxsat-collapse-icon').classList.toggle('collapsed')" style="cursor: pointer; display: flex; align-items: center; justify-content: space-between; user-select: none;">
|
||||
Antenna Guide
|
||||
<span class="wxsat-collapse-icon collapsed" style="font-size: 10px; transition: transform 0.2s; display: inline-block;">▼</span>
|
||||
</h3>
|
||||
<div class="wxsat-antenna-body wxsat-test-decode-body collapsed" style="font-size: 11px; color: var(--text-dim); line-height: 1.5;">
|
||||
|
||||
<p style="margin-bottom: 10px; color: var(--accent-cyan); font-weight: 600;">
|
||||
137 MHz band — your stock SDR antenna will NOT work.
|
||||
@@ -174,14 +245,22 @@
|
||||
|
||||
<div class="section">
|
||||
<h3 onclick="this.parentElement.querySelector('.wxsat-test-decode-body').classList.toggle('collapsed'); this.querySelector('.wxsat-collapse-icon').classList.toggle('collapsed')" style="cursor: pointer; display: flex; align-items: center; justify-content: space-between; user-select: none;">
|
||||
Test Decode (File)
|
||||
Offline Decode (IQ File)
|
||||
<span class="wxsat-collapse-icon collapsed" style="font-size: 10px; transition: transform 0.2s; display: inline-block;">▼</span>
|
||||
</h3>
|
||||
<div class="wxsat-test-decode-body collapsed" style="overflow: hidden;">
|
||||
<p class="info-text" style="font-size: 11px; color: var(--text-dim); margin-bottom: 8px;">
|
||||
Decode a pre-recorded Meteor IQ file without SDR hardware.
|
||||
Shared ground-station recordings are also accepted by the backend.
|
||||
Decode a pre-recorded Meteor IQ baseband file without SDR hardware.
|
||||
You need an actual <code>.raw</code>, <code>.sigmf-data</code>, or <code>.wav</code> recording of a Meteor pass.
|
||||
</p>
|
||||
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 8px; margin-bottom: 10px; font-size: 10px; color: var(--text-dim); line-height: 1.5;">
|
||||
<strong style="color: var(--text-secondary);">Where to get a test file:</strong>
|
||||
<ul style="margin: 4px 0 0 14px; padding: 0;">
|
||||
<li>Record one yourself with <code>rtl_sdr -f 137900000 -s 2400000 meteor.raw</code> during a pass</li>
|
||||
<li>Download samples from <a href="https://www.sigidwiki.com/wiki/Meteor-M_LRPT" target="_blank" rel="noopener" style="color: var(--accent-cyan);">SigID Wiki</a> or <a href="https://www.sondehub.org/" target="_blank" rel="noopener" style="color: var(--accent-cyan);">community forums</a></li>
|
||||
<li>Place the file in <code>data/weather_sat/</code> on the server</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Satellite</label>
|
||||
<select id="wxsatTestSatSelect" class="mode-select">
|
||||
@@ -191,8 +270,8 @@
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>File Path (server-side)</label>
|
||||
<input type="text" id="wxsatTestFilePath" value="data/weather_sat/samples/meteor_lrpt.sigmf-data" style="font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif; font-size: 11px;">
|
||||
<label>File Path (server-side, relative to app root)</label>
|
||||
<input type="text" id="wxsatTestFilePath" placeholder="data/weather_sat/my_recording.raw" style="font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif; font-size: 11px;">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Sample Rate</label>
|
||||
@@ -203,7 +282,7 @@
|
||||
</select>
|
||||
</div>
|
||||
<button class="mode-btn" onclick="WeatherSat.testDecode()" style="width: 100%; margin-top: 4px;">
|
||||
Test Decode
|
||||
Decode File
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user