diff --git a/static/css/modes/sstv.css b/static/css/modes/sstv.css
index da2e97a..c2a336f 100644
--- a/static/css/modes/sstv.css
+++ b/static/css/modes/sstv.css
@@ -449,108 +449,126 @@
}
/* ============================================
- ISS ROW (Globe + Pass Info)
+ ISS MAP ROW
============================================ */
-.sstv-iss-row {
- display: flex;
- gap: 16px;
- align-items: stretch;
+.sstv-map-row {
+ margin-bottom: 16px;
}
-.sstv-globe-container {
- display: flex;
- flex-direction: column;
- align-items: center;
+.sstv-map-container {
+ position: relative;
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 8px;
- padding: 12px;
- min-width: 320px;
+ overflow: hidden;
}
-#sstvGlobe {
- border-radius: 4px;
+.sstv-iss-map {
+ width: 100%;
+ height: 200px;
background: #0a1628;
- box-shadow: 0 0 10px rgba(0, 212, 255, 0.2);
}
-.sstv-globe-info {
- text-align: center;
- margin-top: 8px;
+.sstv-map-overlay {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 12px;
+ background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
+ pointer-events: none;
+ z-index: 1000;
}
-.sstv-globe-label {
+.sstv-map-info {
+ display: flex;
+ align-items: center;
+ gap: 12px;
font-family: 'JetBrains Mono', monospace;
+}
+
+.sstv-map-label {
+ font-size: 10px;
+ font-weight: bold;
+ color: #ffcc00;
+ background: rgba(255, 204, 0, 0.2);
+ padding: 2px 6px;
+ border-radius: 3px;
+}
+
+.sstv-map-coords {
+ font-size: 11px;
+ color: var(--accent-cyan);
+}
+
+.sstv-map-alt {
+ font-size: 10px;
+ color: var(--text-secondary);
+}
+
+.sstv-pass-info {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-family: 'JetBrains Mono', monospace;
+}
+
+.sstv-pass-label {
font-size: 9px;
color: var(--text-dim);
text-transform: uppercase;
- letter-spacing: 1px;
}
-.sstv-globe-coords {
- font-family: 'JetBrains Mono', monospace;
- font-size: 12px;
- color: var(--accent-cyan);
- margin-top: 4px;
-}
-
-.sstv-globe-alt {
- font-family: 'JetBrains Mono', monospace;
- font-size: 10px;
- color: var(--text-secondary);
- margin-top: 2px;
-}
-
-.sstv-pass-info-container {
- flex: 1;
- display: flex;
- align-items: center;
+.sstv-pass-value {
+ font-size: 11px;
+ color: var(--text-primary);
}
/* ============================================
- ISS PASS INFO
+ ISS MAP MARKER
============================================ */
-.sstv-iss-info {
+.sstv-iss-marker {
display: flex;
+ flex-direction: column;
align-items: center;
- gap: 16px;
- padding: 10px 14px;
- background: rgba(0, 212, 255, 0.05);
- border: 1px solid rgba(0, 212, 255, 0.2);
- border-radius: 6px;
- flex: 1;
+ justify-content: center;
}
-.sstv-iss-icon {
- width: 32px;
- height: 32px;
- color: var(--accent-cyan);
- flex-shrink: 0;
-}
-
-.sstv-iss-details {
- flex: 1;
- min-width: 0;
+.sstv-iss-dot {
+ width: 16px;
+ height: 16px;
+ background: #ffcc00;
+ border: 2px solid #fff;
+ border-radius: 50%;
+ box-shadow: 0 0 15px rgba(255, 204, 0, 0.8), 0 0 30px rgba(255, 204, 0, 0.4);
+ animation: iss-pulse 2s ease-in-out infinite;
}
.sstv-iss-label {
- font-family: 'JetBrains Mono', monospace;
- font-size: 9px;
- color: var(--text-dim);
- text-transform: uppercase;
- letter-spacing: 0.5px;
-}
-
-.sstv-iss-value {
- font-family: 'JetBrains Mono', monospace;
- font-size: 12px;
- color: var(--text-primary);
-}
-
-.sstv-iss-note {
font-family: 'JetBrains Mono', monospace;
font-size: 10px;
- color: var(--accent-orange);
+ font-weight: bold;
+ color: #ffcc00;
+ text-shadow: 0 0 3px rgba(0, 0, 0, 0.8), 0 0 6px rgba(0, 0, 0, 0.5);
+ margin-top: 2px;
+}
+
+@keyframes iss-pulse {
+ 0%, 100% {
+ box-shadow: 0 0 15px rgba(255, 204, 0, 0.8), 0 0 30px rgba(255, 204, 0, 0.4);
+ }
+ 50% {
+ box-shadow: 0 0 25px rgba(255, 204, 0, 1), 0 0 50px rgba(255, 204, 0, 0.6);
+ }
+}
+
+/* Override Leaflet default marker styles */
+.leaflet-marker-icon.sstv-iss-marker {
+ background: transparent;
+ border: none;
}
/* ============================================
@@ -618,21 +636,14 @@
}
@media (max-width: 1024px) {
- .sstv-iss-row {
+ .sstv-iss-map {
+ height: 180px;
+ }
+
+ .sstv-map-overlay {
flex-direction: column;
- }
-
- .sstv-globe-container {
- flex-direction: row;
- min-width: auto;
- width: 100%;
- justify-content: center;
- gap: 16px;
- }
-
- .sstv-globe-info {
- margin-top: 0;
- text-align: left;
+ align-items: flex-start;
+ gap: 4px;
}
}
@@ -640,6 +651,7 @@
.sstv-stats-strip {
padding: 8px 12px;
gap: 8px;
+ flex-wrap: wrap;
}
.sstv-strip-divider {
@@ -660,17 +672,16 @@
padding: 8px;
}
- .sstv-iss-info {
- flex-direction: column;
- text-align: center;
+ .sstv-iss-map {
+ height: 150px;
}
- .sstv-globe-container {
- flex-direction: column;
+ .sstv-map-info {
+ gap: 8px;
}
- .sstv-globe-info {
- text-align: center;
+ .sstv-map-overlay {
+ padding: 6px 10px;
}
}
diff --git a/static/js/modes/sstv.js b/static/js/modes/sstv.js
index f6f7775..5d83c7e 100644
--- a/static/js/modes/sstv.js
+++ b/static/js/modes/sstv.js
@@ -10,7 +10,9 @@ const SSTV = (function() {
let images = [];
let currentMode = null;
let progress = 0;
- let globeAnimationId = null;
+ let issMap = null;
+ let issMarker = null;
+ let issTrackLine = null;
let issPosition = null;
let issUpdateInterval = null;
@@ -25,7 +27,7 @@ const SSTV = (function() {
loadImages();
loadLocationInputs();
loadIssSchedule();
- initGlobe();
+ initMap();
startIssTracking();
}
@@ -139,13 +141,51 @@ const SSTV = (function() {
}
/**
- * Initialize 3D globe
+ * Initialize Leaflet map for ISS tracking
*/
- function initGlobe() {
- const canvas = document.getElementById('sstvGlobe');
- if (!canvas) return;
+ function initMap() {
+ const mapContainer = document.getElementById('sstvIssMap');
+ if (!mapContainer || issMap) return;
- renderGlobe();
+ // Create map
+ issMap = L.map('sstvIssMap', {
+ center: [0, 0],
+ zoom: 1,
+ minZoom: 1,
+ maxZoom: 6,
+ zoomControl: true,
+ attributionControl: false,
+ worldCopyJump: true
+ });
+
+ // Add tile layer using settings manager if available
+ if (typeof Settings !== 'undefined' && Settings.createTileLayer) {
+ Settings.createTileLayer().addTo(issMap);
+ } else {
+ // Fallback to dark theme tiles
+ L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
+ maxZoom: 19
+ }).addTo(issMap);
+ }
+
+ // Create ISS icon
+ const issIcon = L.divIcon({
+ className: 'sstv-iss-marker',
+ html: `
ISS
`,
+ iconSize: [40, 40],
+ iconAnchor: [20, 20]
+ });
+
+ // Create ISS marker (will be positioned when we get data)
+ issMarker = L.marker([0, 0], { icon: issIcon }).addTo(issMap);
+
+ // Create ground track line
+ issTrackLine = L.polyline([], {
+ color: '#00d4ff',
+ weight: 2,
+ opacity: 0.6,
+ dashArray: '5, 5'
+ }).addTo(issMap);
}
/**
@@ -166,10 +206,6 @@ const SSTV = (function() {
clearInterval(issUpdateInterval);
issUpdateInterval = null;
}
- if (globeAnimationId) {
- cancelAnimationFrame(globeAnimationId);
- globeAnimationId = null;
- }
}
/**
@@ -187,7 +223,7 @@ const SSTV = (function() {
if (data.status === 'ok') {
issPosition = data;
updateIssDisplay();
- renderGlobe();
+ updateMap();
console.log('ISS position updated:', data.lat.toFixed(1), data.lon.toFixed(1));
} else {
console.warn('ISS position error:', data.message);
@@ -212,345 +248,72 @@ const SSTV = (function() {
if (altEl) altEl.textContent = Math.round(issPosition.altitude);
}
- // Accurate world map continent outlines (lon, lat pairs)
- const continents = {
- // North America mainland
- northAmerica: [
- [-168, 66], [-166, 62], [-164, 60], [-160, 59], [-152, 60], [-146, 61],
- [-141, 60], [-139, 60], [-137, 59], [-135, 56], [-133, 55], [-130, 54],
- [-127, 51], [-124, 48], [-124, 42], [-120, 39], [-117, 33], [-114, 31],
- [-110, 31], [-108, 31], [-105, 29], [-101, 26], [-97, 26], [-97, 28],
- [-94, 29], [-90, 29], [-89, 30], [-85, 29], [-83, 29], [-81, 25],
- [-80, 25], [-81, 28], [-81, 31], [-77, 35], [-75, 36], [-75, 38],
- [-73, 41], [-70, 42], [-70, 44], [-67, 45], [-66, 44], [-64, 46],
- [-61, 47], [-64, 52], [-59, 48], [-55, 52], [-57, 58], [-62, 58],
- [-68, 60], [-73, 62], [-77, 64], [-78, 69], [-85, 70], [-95, 70],
- [-102, 72], [-115, 72], [-125, 72], [-135, 70], [-145, 70], [-155, 71],
- [-165, 68], [-168, 66]
- ],
- // Greenland
- greenland: [
- [-45, 60], [-43, 60], [-40, 62], [-38, 65], [-30, 68], [-25, 72],
- [-20, 76], [-22, 80], [-35, 83], [-50, 82], [-60, 78], [-68, 76],
- [-72, 73], [-60, 70], [-52, 66], [-48, 62], [-45, 60]
- ],
- // Central America
- centralAmerica: [
- [-97, 26], [-97, 22], [-95, 19], [-92, 18], [-90, 16], [-88, 16],
- [-86, 14], [-84, 11], [-82, 9], [-79, 8], [-77, 9], [-80, 9],
- [-83, 10], [-86, 12], [-88, 14], [-90, 15], [-92, 16], [-95, 17],
- [-97, 20], [-97, 26]
- ],
- // South America
- southAmerica: [
- [-79, 8], [-77, 9], [-73, 11], [-72, 12], [-67, 11], [-63, 10],
- [-60, 8], [-57, 6], [-52, 5], [-50, 2], [-50, 0], [-48, -2],
- [-44, -3], [-39, -4], [-35, -7], [-35, -10], [-37, -13], [-39, -18],
- [-41, -22], [-44, -23], [-47, -24], [-49, -29], [-52, -33], [-54, -34],
- [-57, -38], [-62, -39], [-65, -41], [-66, -45], [-66, -52], [-68, -55],
- [-72, -53], [-74, -50], [-75, -47], [-75, -41], [-73, -37], [-71, -33],
- [-71, -29], [-70, -24], [-70, -18], [-75, -15], [-76, -12], [-81, -6],
- [-81, -2], [-80, 1], [-79, 8]
- ],
- // UK and Ireland
- ukIreland: [
- [-10, 51], [-9, 52], [-10, 54], [-8, 55], [-6, 55], [-6, 58],
- [-3, 59], [0, 58], [2, 53], [1, 51], [-2, 50], [-5, 50], [-6, 52],
- [-10, 51]
- ],
- // Iceland
- iceland: [
- [-24, 64], [-22, 66], [-18, 66], [-14, 65], [-14, 64], [-18, 63],
- [-22, 64], [-24, 64]
- ],
- // Europe mainland
- europe: [
- [-10, 36], [-9, 38], [-9, 43], [-2, 44], [3, 43], [4, 44], [1, 46],
- [-2, 47], [-5, 48], [-3, 49], [2, 51], [4, 52], [7, 54], [8, 55],
- [12, 55], [14, 54], [19, 55], [23, 55], [28, 56], [28, 60], [24, 60],
- [23, 64], [26, 66], [25, 70], [21, 70], [18, 69], [15, 69], [11, 64],
- [12, 58], [10, 58], [8, 58], [6, 58], [5, 62], [7, 65], [15, 69],
- [25, 71], [30, 70], [28, 66], [31, 65], [29, 60], [32, 55], [40, 55],
- [50, 55], [60, 55], [68, 56], [70, 66], [60, 70], [50, 68], [40, 67],
- [32, 70], [28, 70], [25, 71], [21, 70], [17, 68], [10, 64], [12, 56],
- [8, 54], [5, 54], [4, 52], [2, 51], [-3, 49], [-5, 48], [-2, 47],
- [1, 46], [4, 44], [3, 43], [-2, 44], [-9, 43], [-9, 41], [-8, 40],
- [-9, 38], [-7, 37], [-6, 37], [-5, 36], [-2, 36], [0, 38], [3, 42],
- [6, 43], [8, 44], [13, 44], [14, 42], [16, 41], [14, 38], [12, 38],
- [15, 37], [18, 40], [20, 40], [24, 38], [26, 39], [28, 41], [26, 42],
- [29, 45], [22, 45], [20, 42], [16, 42], [14, 44], [10, 46], [7, 46],
- [7, 48], [10, 48], [15, 47], [17, 49], [15, 51], [14, 53], [10, 54],
- [7, 54], [4, 52]
- ],
- // Scandinavia (simplified)
- scandinavia: [
- [5, 58], [6, 62], [8, 64], [14, 66], [18, 68], [20, 70], [28, 71],
- [31, 70], [30, 67], [27, 65], [24, 60], [18, 60], [16, 57], [11, 56],
- [8, 56], [5, 58]
- ],
- // Africa
- africa: [
- [-17, 21], [-17, 15], [-16, 13], [-15, 11], [-8, 5], [-5, 5],
- [0, 5], [2, 6], [10, 4], [10, 1], [9, -1], [12, -5], [14, -5],
- [17, -12], [23, -18], [26, -23], [28, -28], [28, -33], [23, -35],
- [18, -34], [16, -29], [14, -22], [12, -17], [14, -10], [20, -3],
- [30, 5], [35, 5], [42, 11], [44, 11], [49, 12], [51, 11], [43, 5],
- [41, -2], [40, -10], [36, -20], [33, -26], [28, -33], [23, -35],
- [18, -34], [16, -29], [13, -25], [10, -18], [9, -6], [5, 4],
- [-5, 5], [-10, 8], [-17, 15], [-17, 21], [-13, 24], [-8, 28],
- [-2, 35], [3, 37], [10, 37], [11, 34], [9, 31], [10, 28], [17, 32],
- [25, 32], [32, 31], [35, 32], [36, 30], [33, 27], [35, 22], [43, 13],
- [42, 11], [35, 5], [33, 10], [31, 10], [30, 5], [20, -3], [14, -10],
- [12, -17], [17, -12], [14, -5], [12, -5], [9, -1], [10, 1], [10, 4],
- [2, 6], [0, 5], [-5, 5], [-8, 5], [-15, 11], [-16, 13], [-17, 15],
- [-17, 21]
- ],
- // Madagascar
- madagascar: [
- [50, -12], [50, -16], [47, -24], [44, -25], [44, -20], [47, -15],
- [49, -12], [50, -12]
- ],
- // Middle East / Arabian Peninsula
- middleEast: [
- [35, 32], [36, 30], [40, 29], [48, 30], [52, 26], [56, 25], [57, 21],
- [55, 17], [52, 13], [44, 13], [43, 13], [35, 22], [33, 27], [35, 32]
- ],
- // Asia mainland
- asia: [
- [60, 55], [70, 55], [80, 55], [90, 55], [100, 55], [110, 55], [120, 53],
- [130, 48], [135, 45], [135, 42], [130, 43], [123, 40], [120, 35],
- [117, 30], [118, 25], [118, 22], [110, 20], [108, 22], [107, 17],
- [103, 10], [100, 14], [99, 7], [104, 2], [104, -2], [117, -8],
- [120, -10], [115, -8], [107, -6], [105, -6], [106, -2], [103, 1],
- [99, 7], [100, 14], [103, 10], [105, 12], [107, 17], [108, 22],
- [105, 22], [102, 22], [98, 24], [90, 22], [89, 26], [92, 28],
- [88, 28], [84, 28], [80, 30], [77, 35], [72, 37], [68, 37],
- [60, 40], [52, 42], [50, 46], [55, 50], [60, 55]
- ],
- // India
- india: [
- [68, 24], [70, 22], [72, 21], [73, 17], [75, 12], [77, 8], [80, 10],
- [80, 14], [83, 15], [86, 20], [90, 22], [89, 26], [88, 28], [84, 28],
- [80, 30], [77, 30], [75, 25], [72, 25], [68, 24]
- ],
- // Southeast Asia
- southeastAsia: [
- [100, 14], [103, 10], [105, 12], [107, 17], [108, 22], [105, 22],
- [102, 22], [98, 24], [98, 19], [100, 14]
- ],
- // Japan
- japan: [
- [130, 32], [131, 34], [135, 35], [137, 37], [140, 38], [141, 41],
- [141, 43], [145, 44], [145, 42], [142, 39], [140, 36], [137, 35],
- [135, 34], [130, 32]
- ],
- // Korea
- korea: [
- [126, 34], [126, 38], [129, 38], [130, 43], [128, 42], [124, 40],
- [125, 37], [126, 34]
- ],
- // Philippines
- philippines: [
- [117, 7], [120, 10], [122, 13], [124, 17], [122, 19], [120, 16],
- [118, 12], [117, 7]
- ],
- // Indonesia (simplified)
- indonesia: [
- [95, 6], [98, 4], [103, 1], [106, -2], [106, -6], [110, -7],
- [115, -8], [120, -10], [127, -8], [131, -2], [136, -2], [141, -5],
- [141, -9], [131, -8], [120, -10], [115, -8], [110, -7], [106, -6],
- [106, -2], [103, 1], [98, 4], [95, 6]
- ],
- // Australia
- australia: [
- [114, -22], [114, -26], [115, -32], [117, -35], [122, -34], [129, -32],
- [132, -32], [134, -33], [137, -35], [140, -38], [144, -38], [147, -38],
- [150, -37], [153, -29], [153, -25], [149, -21], [145, -15], [142, -11],
- [136, -12], [130, -15], [129, -17], [123, -17], [119, -20], [114, -22]
- ],
- // New Zealand
- newZealand: [
- [166, -46], [168, -45], [171, -41], [175, -37], [178, -37], [178, -42],
- [174, -41], [170, -43], [167, -44], [166, -46]
- ],
- // Taiwan
- taiwan: [
- [120, 22], [121, 23], [122, 25], [121, 25], [120, 24], [120, 22]
- ],
- // Sri Lanka
- sriLanka: [
- [80, 6], [80, 8], [82, 10], [82, 7], [80, 6]
- ]
- };
-
/**
- * Convert lat/lon to canvas x/y (equirectangular projection)
- * This is a simple, accurate 1:1 mapping
+ * Update map with ISS position
*/
- function latLonToXY(lat, lon, width, height, padding) {
- // Simple linear mapping - guaranteed accurate
- // Longitude: -180 to 180 maps to padding to width-padding
- // Latitude: 90 to -90 maps to padding to height-padding
- const mapWidth = width - 2 * padding;
- const mapHeight = height - 2 * padding;
+ function updateMap() {
+ if (!issMap || !issPosition) return;
- const x = padding + ((lon + 180) / 360) * mapWidth;
- const y = padding + ((90 - lat) / 180) * mapHeight;
+ const lat = issPosition.lat;
+ const lon = issPosition.lon;
- return { x, y };
- }
-
- /**
- * Render 2D world map with ISS position
- */
- function renderGlobe() {
- const canvas = document.getElementById('sstvGlobe');
- if (!canvas) return;
-
- const ctx = canvas.getContext('2d');
- const width = canvas.width;
- const height = canvas.height;
- const padding = 5;
-
- // Clear canvas
- ctx.clearRect(0, 0, width, height);
-
- // Draw ocean background
- ctx.fillStyle = '#0a1628';
- ctx.fillRect(0, 0, width, height);
-
- // Inner map area
- ctx.fillStyle = '#0d2847';
- ctx.fillRect(padding, padding, width - 2 * padding, height - 2 * padding);
-
- // Draw grid lines
- ctx.strokeStyle = 'rgba(0, 212, 255, 0.15)';
- ctx.lineWidth = 0.5;
-
- // Latitude lines every 30 degrees
- for (let lat = -60; lat <= 60; lat += 30) {
- const p = latLonToXY(lat, -180, width, height, padding);
- const p2 = latLonToXY(lat, 180, width, height, padding);
- ctx.beginPath();
- ctx.moveTo(p.x, p.y);
- ctx.lineTo(p2.x, p2.y);
- ctx.stroke();
+ // Update marker position
+ if (issMarker) {
+ issMarker.setLatLng([lat, lon]);
}
- // Longitude lines every 30 degrees
- for (let lon = -180; lon <= 180; lon += 30) {
- const p = latLonToXY(90, lon, width, height, padding);
- const p2 = latLonToXY(-90, lon, width, height, padding);
- ctx.beginPath();
- ctx.moveTo(p.x, p.y);
- ctx.lineTo(p2.x, p2.y);
- ctx.stroke();
- }
+ // Calculate and draw ground track
+ if (issTrackLine) {
+ const trackPoints = [];
+ const inclination = 51.6; // ISS orbital inclination in degrees
- // Draw equator slightly brighter
- ctx.strokeStyle = 'rgba(0, 212, 255, 0.3)';
- const eq1 = latLonToXY(0, -180, width, height, padding);
- const eq2 = latLonToXY(0, 180, width, height, padding);
- ctx.beginPath();
- ctx.moveTo(eq1.x, eq1.y);
- ctx.lineTo(eq2.x, eq2.y);
- ctx.stroke();
+ // Generate orbit track points
+ for (let offset = -180; offset <= 180; offset += 3) {
+ let trackLon = lon + offset;
- // Draw continents
- ctx.fillStyle = 'rgba(34, 139, 87, 0.6)';
- ctx.strokeStyle = 'rgba(50, 180, 120, 0.8)';
- ctx.lineWidth = 1;
+ // Normalize longitude
+ while (trackLon > 180) trackLon -= 360;
+ while (trackLon < -180) trackLon += 360;
- for (const [name, coords] of Object.entries(continents)) {
- ctx.beginPath();
- for (let i = 0; i < coords.length; i++) {
- const [lon, lat] = coords[i];
- const p = latLonToXY(lat, lon, width, height, padding);
- if (i === 0) {
- ctx.moveTo(p.x, p.y);
- } else {
- ctx.lineTo(p.x, p.y);
- }
- }
- ctx.closePath();
- ctx.fill();
- ctx.stroke();
- }
-
- // Draw ISS ground track (orbit path)
- if (issPosition) {
- // Draw approximate orbit path (ISS completes orbit in ~92 minutes)
- // Orbit is inclined at 51.6 degrees
- ctx.strokeStyle = 'rgba(255, 200, 0, 0.3)';
- ctx.lineWidth = 1;
- ctx.setLineDash([3, 3]);
- ctx.beginPath();
-
- let lastX = null;
- for (let offset = -180; offset <= 180; offset += 2) {
- // ISS moves ~360° longitude per 92 minutes, latitude oscillates ±51.6°
- const orbitLon = issPosition.lon + offset;
- // Normalize longitude to -180 to 180
- let normLon = orbitLon;
- while (normLon > 180) normLon -= 360;
- while (normLon < -180) normLon += 360;
-
- // Calculate latitude based on orbit (sinusoidal pattern)
+ // Calculate latitude based on orbital inclination
const phase = (offset / 360) * 2 * Math.PI;
- const orbitLat = 51.6 * Math.sin(phase + Math.asin(issPosition.lat / 51.6));
+ const currentPhase = Math.asin(Math.max(-1, Math.min(1, lat / inclination)));
+ let trackLat = inclination * Math.sin(phase + currentPhase);
- // Clamp latitude to valid range
- const clampedLat = Math.max(-90, Math.min(90, orbitLat));
+ // Clamp to valid range
+ trackLat = Math.max(-inclination, Math.min(inclination, trackLat));
- const p = latLonToXY(clampedLat, normLon, width, height, padding);
-
- // Handle wrap-around (don't draw line across the map)
- if (lastX !== null && Math.abs(p.x - lastX) > width / 2) {
- ctx.moveTo(p.x, p.y);
- } else if (offset === -180) {
- ctx.moveTo(p.x, p.y);
- } else {
- ctx.lineTo(p.x, p.y);
- }
- lastX = p.x;
+ trackPoints.push([trackLat, trackLon]);
}
- ctx.stroke();
- ctx.setLineDash([]);
- // Draw ISS position marker
- const issP = latLonToXY(issPosition.lat, issPosition.lon, width, height, padding);
+ // Split track at antimeridian to avoid line across map
+ const segments = [];
+ let currentSegment = [];
- // ISS glow
- const issGradient = ctx.createRadialGradient(issP.x, issP.y, 0, issP.x, issP.y, 15);
- issGradient.addColorStop(0, 'rgba(255, 200, 0, 0.9)');
- issGradient.addColorStop(0.4, 'rgba(255, 150, 0, 0.4)');
- issGradient.addColorStop(1, 'rgba(255, 100, 0, 0)');
+ for (let i = 0; i < trackPoints.length; i++) {
+ if (i > 0) {
+ const prevLon = trackPoints[i - 1][1];
+ const currLon = trackPoints[i][1];
+ if (Math.abs(currLon - prevLon) > 180) {
+ // Crossed antimeridian
+ if (currentSegment.length > 0) {
+ segments.push(currentSegment);
+ }
+ currentSegment = [];
+ }
+ }
+ currentSegment.push(trackPoints[i]);
+ }
+ if (currentSegment.length > 0) {
+ segments.push(currentSegment);
+ }
- ctx.beginPath();
- ctx.arc(issP.x, issP.y, 15, 0, Math.PI * 2);
- ctx.fillStyle = issGradient;
- ctx.fill();
-
- // ISS dot
- ctx.beginPath();
- ctx.arc(issP.x, issP.y, 4, 0, Math.PI * 2);
- ctx.fillStyle = '#ffcc00';
- ctx.fill();
- ctx.strokeStyle = '#fff';
- ctx.lineWidth = 1.5;
- ctx.stroke();
-
- // ISS label
- ctx.fillStyle = '#ffcc00';
- ctx.font = 'bold 9px JetBrains Mono, monospace';
- ctx.textAlign = 'center';
- ctx.fillText('ISS', issP.x, issP.y - 10);
+ // Use only the longest segment or combine if needed
+ issTrackLine.setLatLngs(segments.length > 0 ? segments : []);
}
- // Draw border
- ctx.strokeStyle = 'rgba(0, 212, 255, 0.5)';
- ctx.lineWidth = 1;
- ctx.strokeRect(padding, padding, width - 2 * padding, height - 2 * padding);
+ // Pan map to follow ISS
+ issMap.panTo([lat, lon], { animate: true, duration: 0.5 });
}
/**
@@ -870,48 +633,17 @@ const SSTV = (function() {
* Render ISS pass info
*/
function renderIssInfo(nextPass, hasLocation = true) {
- const container = document.getElementById('sstvIssInfo');
- if (!container) return;
+ const passEl = document.getElementById('sstvNextPass');
+ if (!passEl) return;
if (!nextPass) {
- const locationMsg = hasLocation
- ? 'No passes in next 48 hours'
- : 'Set location in Settings > Location tab';
- const noteMsg = hasLocation
- ? 'Check ARISS.org for SSTV event schedules'
- : 'Click the gear icon to open Settings';
-
- container.innerHTML = `
-
-
-
-
Next ISS Pass
-
${locationMsg}
-
${noteMsg}
-
-
- `;
+ passEl.textContent = hasLocation
+ ? 'No passes in 48h'
+ : 'Set location above';
return;
}
- container.innerHTML = `
-
-
-
-
Next ISS Pass
-
${nextPass.startTime} (${nextPass.maxEl}° max elevation)
-
Duration: ${nextPass.duration} min | Check ARISS.org for SSTV events
-
-
- `;
+ passEl.textContent = `${nextPass.startTime} (${nextPass.maxEl}° el, ${nextPass.duration}min)`;
}
/**
diff --git a/templates/index.html b/templates/index.html
index 99d1025..5e81dd4 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1772,31 +1772,19 @@
-
-
-
-
-
-
-
ISS POSITION
-
-
--.-°,
--.-°
+
+
+
+
+
+
+ ISS
+ --.-°, --.-°
+ Alt: --- km
-
Alt: --- km
-
-
-
-
-
-
-
-
Next ISS Pass
-
Loading...
-
Check ARISS.org for SSTV event schedules
+
+ Next Pass:
+ Loading...