diff --git a/static/css/modes/weather-satellite.css b/static/css/modes/weather-satellite.css index bbdc3b8..00b078d 100644 --- a/static/css/modes/weather-satellite.css +++ b/static/css/modes/weather-satellite.css @@ -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; diff --git a/static/js/modes/weather-satellite.js b/static/js/modes/weather-satellite.js index 4fc3db5..44f47cf 100644 --- a/static/js/modes/weather-satellite.js +++ b/static/js/modes/weather-satellite.js @@ -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 = `
${hasLocation ? 'No passes in next 24h' : 'Set location to see pass predictions'}
+ ++ ${hasLocation ? 'No passes in next 24 hours' : 'Set your location'} +
++ ${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'} +
Set location to see pass predictions
+ +Set your location
+Enter lat/lon in the strip bar above or click the GPS button to load pass predictions
+ Russia's Meteor-M2-3 and Meteor-M2-4 + are polar-orbiting weather satellites that continuously transmit real-time color imagery (clouds, land, sea) at 137.900 MHz + using the LRPT digital format. Unlike old analog NOAA APT, LRPT produces sharp, full-color images. +
++ 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 4–8 usable passes per day, + each lasting 8–15 minutes as the satellite crosses your sky. +
+| SDR receiver | +RTL-SDR V3/V4 ($25-35) | +
| Antenna | +137 MHz V-dipole ($5 DIY) or QFH ($20-30) | +
| LNA (optional) | +137 MHz filtered, at antenna ($15-25) | +
| Location | +Outdoors, clear sky view | +
| No hardware? | +Use Load Demo Data below to explore the UI | +
137 MHz band — your stock SDR antenna will NOT work. @@ -174,14 +245,22 @@