sat updates

pull/6/head
accius 3 days ago
parent 1529207e7d
commit 5f29957704

@ -1509,18 +1509,67 @@ app.get('/api/myspots/:callsign', async (req, res) => {
// SATELLITE TRACKING API
// ============================================
// Ham radio satellites - NORAD IDs
// Comprehensive ham radio satellites - NORAD IDs
// Updated list of active amateur radio satellites
const HAM_SATELLITES = {
'ISS': { norad: 25544, name: 'ISS (ZARYA)', color: '#00ffff', priority: 1 },
'AO-91': { norad: 43017, name: 'AO-91 (Fox-1B)', color: '#ff6600', priority: 2 },
'AO-92': { norad: 43137, name: 'AO-92 (Fox-1D)', color: '#ff9900', priority: 2 },
'SO-50': { norad: 27607, name: 'SO-50 (SaudiSat)', color: '#00ff00', priority: 2 },
'RS-44': { norad: 44909, name: 'RS-44 (DOSAAF)', color: '#ff0066', priority: 2 },
'IO-117': { norad: 53106, name: 'IO-117 (GreenCube)', color: '#00ff99', priority: 3 },
'CAS-4A': { norad: 42761, name: 'CAS-4A (ZHUHAI-1 01)', color: '#9966ff', priority: 3 },
'CAS-4B': { norad: 42759, name: 'CAS-4B (ZHUHAI-1 02)', color: '#9933ff', priority: 3 },
'PO-101': { norad: 43678, name: 'PO-101 (Diwata-2)', color: '#ff3399', priority: 3 },
'TEVEL': { norad: 50988, name: 'TEVEL-1', color: '#66ccff', priority: 4 }
// High Priority - Popular FM satellites
'ISS': { norad: 25544, name: 'ISS (ZARYA)', color: '#00ffff', priority: 1, mode: 'FM/APRS/SSTV' },
'SO-50': { norad: 27607, name: 'SO-50', color: '#00ff00', priority: 1, mode: 'FM' },
'AO-91': { norad: 43017, name: 'AO-91 (Fox-1B)', color: '#ff6600', priority: 1, mode: 'FM' },
'AO-92': { norad: 43137, name: 'AO-92 (Fox-1D)', color: '#ff9900', priority: 1, mode: 'FM/L-band' },
'PO-101': { norad: 43678, name: 'PO-101 (Diwata-2)', color: '#ff3399', priority: 1, mode: 'FM' },
// Linear Transponder Satellites
'RS-44': { norad: 44909, name: 'RS-44 (DOSAAF)', color: '#ff0066', priority: 1, mode: 'Linear' },
'AO-7': { norad: 7530, name: 'AO-7', color: '#ffcc00', priority: 2, mode: 'Linear (daylight)' },
'FO-29': { norad: 24278, name: 'FO-29 (JAS-2)', color: '#ff6699', priority: 2, mode: 'Linear' },
'FO-99': { norad: 43937, name: 'FO-99 (NEXUS)', color: '#ff99cc', priority: 2, mode: 'Linear' },
'JO-97': { norad: 43803, name: 'JO-97 (JY1Sat)', color: '#cc99ff', priority: 2, mode: 'Linear/FM' },
'XW-2A': { norad: 40903, name: 'XW-2A (CAS-3A)', color: '#66ff99', priority: 2, mode: 'Linear' },
'XW-2B': { norad: 40911, name: 'XW-2B (CAS-3B)', color: '#66ffcc', priority: 2, mode: 'Linear' },
'XW-2C': { norad: 40906, name: 'XW-2C (CAS-3C)', color: '#99ffcc', priority: 2, mode: 'Linear' },
'XW-2D': { norad: 40907, name: 'XW-2D (CAS-3D)', color: '#99ff99', priority: 2, mode: 'Linear' },
'XW-2E': { norad: 40909, name: 'XW-2E (CAS-3E)', color: '#ccff99', priority: 2, mode: 'Linear' },
'XW-2F': { norad: 40910, name: 'XW-2F (CAS-3F)', color: '#ccffcc', priority: 2, mode: 'Linear' },
// CAS (Chinese Amateur Satellites)
'CAS-4A': { norad: 42761, name: 'CAS-4A', color: '#9966ff', priority: 2, mode: 'Linear' },
'CAS-4B': { norad: 42759, name: 'CAS-4B', color: '#9933ff', priority: 2, mode: 'Linear' },
'CAS-6': { norad: 44881, name: 'CAS-6 (TO-108)', color: '#cc66ff', priority: 2, mode: 'Linear' },
// GreenCube / IO satellites
'IO-117': { norad: 53106, name: 'IO-117 (GreenCube)', color: '#00ff99', priority: 2, mode: 'Digipeater' },
// TEVEL constellation
'TEVEL-1': { norad: 50988, name: 'TEVEL-1', color: '#66ccff', priority: 3, mode: 'FM' },
'TEVEL-2': { norad: 50989, name: 'TEVEL-2', color: '#66ddff', priority: 3, mode: 'FM' },
'TEVEL-3': { norad: 50994, name: 'TEVEL-3', color: '#66eeff', priority: 3, mode: 'FM' },
'TEVEL-4': { norad: 50998, name: 'TEVEL-4', color: '#77ccff', priority: 3, mode: 'FM' },
'TEVEL-5': { norad: 51062, name: 'TEVEL-5', color: '#77ddff', priority: 3, mode: 'FM' },
'TEVEL-6': { norad: 51063, name: 'TEVEL-6', color: '#77eeff', priority: 3, mode: 'FM' },
'TEVEL-7': { norad: 51069, name: 'TEVEL-7', color: '#88ccff', priority: 3, mode: 'FM' },
'TEVEL-8': { norad: 51084, name: 'TEVEL-8', color: '#88ddff', priority: 3, mode: 'FM' },
// OSCAR satellites
'AO-27': { norad: 22825, name: 'AO-27', color: '#ff9966', priority: 3, mode: 'FM' },
'AO-73': { norad: 39444, name: 'AO-73 (FUNcube-1)', color: '#ffcc66', priority: 3, mode: 'Linear/Telemetry' },
'EO-88': { norad: 42017, name: 'EO-88 (Nayif-1)', color: '#ffaa66', priority: 3, mode: 'Linear/Telemetry' },
// Russian satellites
'RS-15': { norad: 23439, name: 'RS-15', color: '#ff6666', priority: 3, mode: 'Linear' },
// QO-100 (Geostationary - special)
'QO-100': { norad: 43700, name: 'QO-100 (Es\'hail-2)', color: '#ffff00', priority: 1, mode: 'Linear (GEO)' },
// APRS Digipeaters
'ARISS': { norad: 25544, name: 'ARISS (ISS)', color: '#00ffff', priority: 1, mode: 'APRS' },
// Cubesats with amateur payloads
'UVSQ-SAT': { norad: 47438, name: 'UVSQ-SAT', color: '#ff66ff', priority: 4, mode: 'Telemetry' },
'MEZNSAT': { norad: 46489, name: 'MeznSat', color: '#66ff66', priority: 4, mode: 'Telemetry' },
// SSTV/Slow Scan
'SSTV-ISS': { norad: 25544, name: 'ISS SSTV', color: '#00ffff', priority: 2, mode: 'SSTV' }
};
// Cache for TLE data (refresh every 6 hours)

@ -342,6 +342,9 @@ export const WorldMap = ({
if (showSatellites && satellites && satellites.length > 0) {
satellites.forEach(sat => {
const satColor = sat.color || '#00ffff';
const satColorDark = sat.visible ? satColor : '#446666';
// Draw orbit track if available
if (sat.track && sat.track.length > 1) {
// Split track into segments to handle date line crossing
@ -364,7 +367,7 @@ export const WorldMap = ({
segments.forEach(segment => {
if (segment.length > 1) {
const trackLine = L.polyline(segment, {
color: sat.visible ? '#00ffff' : '#006688',
color: sat.visible ? satColor : satColorDark,
weight: 2,
opacity: sat.visible ? 0.8 : 0.4,
dashArray: sat.visible ? null : '5, 5'
@ -374,14 +377,14 @@ export const WorldMap = ({
});
}
// Draw footprint circle if available
if (sat.footprintRadius && sat.lat && sat.lon) {
// Draw footprint circle if available and satellite is visible
if (sat.footprintRadius && sat.lat && sat.lon && sat.visible) {
const footprint = L.circle([sat.lat, sat.lon], {
radius: sat.footprintRadius * 1000, // Convert km to meters
color: '#00ffff',
color: satColor,
weight: 1,
opacity: 0.5,
fillColor: '#00ffff',
fillColor: satColor,
fillOpacity: 0.1
}).addTo(map);
satTracksRef.current.push(footprint);
@ -390,7 +393,7 @@ export const WorldMap = ({
// Add satellite marker icon
const icon = L.divIcon({
className: '',
html: `<span style="display:inline-block;background:${sat.visible ? '#00ffff' : '#006688'};color:${sat.visible ? '#000' : '#fff'};padding:4px 8px;border-radius:4px;font-size:11px;font-family:'JetBrains Mono',monospace;white-space:nowrap;border:2px solid ${sat.visible ? '#fff' : '#00aaaa'};font-weight:bold;box-shadow:0 2px 4px rgba(0,0,0,0.4);">🛰 ${sat.name}</span>`,
html: `<span style="display:inline-block;background:${sat.visible ? satColor : satColorDark};color:${sat.visible ? '#000' : '#fff'};padding:4px 8px;border-radius:4px;font-size:11px;font-family:'JetBrains Mono',monospace;white-space:nowrap;border:2px solid ${sat.visible ? '#fff' : '#666'};font-weight:bold;box-shadow:0 2px 4px rgba(0,0,0,0.4);">🛰 ${sat.name}</span>`,
iconSize: null,
iconAnchor: [0, 0]
});
@ -399,11 +402,12 @@ export const WorldMap = ({
.bindPopup(`
<b>🛰 ${sat.name}</b><br>
<table style="font-size: 11px;">
<tr><td>Mode:</td><td><b>${sat.mode || 'Unknown'}</b></td></tr>
<tr><td>Alt:</td><td>${sat.alt} km</td></tr>
<tr><td>Az:</td><td>${sat.azimuth}°</td></tr>
<tr><td>El:</td><td>${sat.elevation}°</td></tr>
<tr><td>Range:</td><td>${sat.range} km</td></tr>
<tr><td>Status:</td><td>${sat.visible ? '<span style="color:green">Visible</span>' : '<span style="color:gray">Below horizon</span>'}</td></tr>
<tr><td>Status:</td><td>${sat.visible ? '<span style="color:green">Visible</span>' : '<span style="color:gray">Below horizon</span>'}</td></tr>
</table>
`)
.addTo(map);

@ -6,20 +6,6 @@
import { useState, useEffect, useCallback } from 'react';
import * as satellite from 'satellite.js';
// List of popular amateur radio satellites
const AMATEUR_SATS = [
'ISS (ZARYA)',
'SO-50',
'AO-91',
'AO-92',
'CAS-4A',
'CAS-4B',
'XW-2A',
'XW-2B',
'JO-97',
'RS-44'
];
export const useSatellites = (observerLocation) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
@ -92,54 +78,57 @@ export const useSatellites = (observerLocation) => {
const elevation = satellite.radiansToDegrees(lookAngles.elevation);
const rangeSat = lookAngles.rangeSat;
// Only include if above horizon or popular sat
const isPopular = AMATEUR_SATS.some(s => name.includes(s));
if (elevation > -5 || isPopular) {
// Calculate orbit track (past 45 min and future 45 min = 90 min total)
const track = [];
const trackMinutes = 90;
const stepMinutes = 1;
// Include all satellites we get TLE for (they're all ham sats)
// Calculate orbit track (past 45 min and future 45 min = 90 min total)
const track = [];
const trackMinutes = 90;
const stepMinutes = 1;
for (let m = -trackMinutes/2; m <= trackMinutes/2; m += stepMinutes) {
const trackTime = new Date(now.getTime() + m * 60 * 1000);
const trackPV = satellite.propagate(satrec, trackTime);
for (let m = -trackMinutes/2; m <= trackMinutes/2; m += stepMinutes) {
const trackTime = new Date(now.getTime() + m * 60 * 1000);
const trackPV = satellite.propagate(satrec, trackTime);
if (trackPV.position) {
const trackGmst = satellite.gstime(trackTime);
const trackGd = satellite.eciToGeodetic(trackPV.position, trackGmst);
const trackLat = satellite.degreesLat(trackGd.latitude);
const trackLon = satellite.degreesLong(trackGd.longitude);
track.push([trackLat, trackLon]);
}
if (trackPV.position) {
const trackGmst = satellite.gstime(trackTime);
const trackGd = satellite.eciToGeodetic(trackPV.position, trackGmst);
const trackLat = satellite.degreesLat(trackGd.latitude);
const trackLon = satellite.degreesLong(trackGd.longitude);
track.push([trackLat, trackLon]);
}
// Calculate footprint radius (visibility circle)
// Formula: radius = Earth_radius * arccos(Earth_radius / (Earth_radius + altitude))
const earthRadius = 6371; // km
const footprintRadius = earthRadius * Math.acos(earthRadius / (earthRadius + alt));
positions.push({
name,
lat,
lon,
alt: Math.round(alt),
azimuth: Math.round(azimuth),
elevation: Math.round(elevation),
range: Math.round(rangeSat),
visible: elevation > 0,
isPopular,
track,
footprintRadius: Math.round(footprintRadius)
});
}
// Calculate footprint radius (visibility circle)
// Formula: radius = Earth_radius * arccos(Earth_radius / (Earth_radius + altitude))
const earthRadius = 6371; // km
const footprintRadius = earthRadius * Math.acos(earthRadius / (earthRadius + alt));
positions.push({
name: tle.name || name,
lat,
lon,
alt: Math.round(alt),
azimuth: Math.round(azimuth),
elevation: Math.round(elevation),
range: Math.round(rangeSat),
visible: elevation > 0,
isPopular: tle.priority <= 2,
track,
footprintRadius: Math.round(footprintRadius),
mode: tle.mode || 'Unknown',
color: tle.color || '#00ffff'
});
} catch (e) {
// Skip satellites with invalid TLE
}
});
// Sort by elevation (highest first) and limit
positions.sort((a, b) => b.elevation - a.elevation);
setData(positions.slice(0, 15));
// Sort by visibility first (visible on top), then by elevation
positions.sort((a, b) => {
if (a.visible !== b.visible) return b.visible - a.visible;
return b.elevation - a.elevation;
});
// Show all satellites (no limit for ham sats)
setData(positions);
setLoading(false);
} catch (err) {
console.error('Satellite calculation error:', err);

Loading…
Cancel
Save

Powered by TurnKey Linux.