This commit is contained in:
2026-09-10 22:52:44 -04:00
parent 2c25251864
commit 79acad8c07
9 changed files with 314 additions and 30 deletions

69
server/src/sats.mjs Normal file
View File

@@ -0,0 +1,69 @@
import {cfg} from './config.mjs';
import {isEqual} from '@ztimson/utils';
const HISTORY_LIMIT = 500
const POLL_MS = 5_000
const TTL_MS = 15 * 60_000 // drop a satellite's bucket if nothing new for 15 min
function parseWm(raw) {
const f = raw.replace(/<[^>]+>/g, '').split(',').map(s => s.trim());
const [latitude, longitude] = f[15].split('/').map(s => parseFloat(s));
const [az, el] = f[16].split('/').map(s => parseFloat(s));
return {
timestamp: Date.now(),
freqMHz: parseFloat(f[3]),
satellite: f[14],
latitude, longitude,
az, el,
packetRssi: parseFloat(f[21]),
packetSnr: parseFloat(f[22]),
freqError: parseFloat(f[23]),
crcOk: !/CRC ERROR/i.test(f[24]),
}
}
const satellites = new Map();
function pruneStale() {
const now = Date.now();
for (const [name, bucket] of satellites) {
if (now - bucket.lastSeen > TTL_MS) satellites.delete(name);
}
}
export async function pollTinyGS() {
const { TINYGS_URL, TINYGS_AUTH } = cfg();
try {
const raw = await fetch(TINYGS_URL + '/wm', {
headers: TINYGS_AUTH ? {Authorization: `Basic ${TINYGS_AUTH}`} : {}
}).then(r => r.text());
const reading = parseWm(raw);
if (!reading.satellite || reading.satellite === '-') return;
let bucket = satellites.get(reading.satellite);
if (!bucket) {
bucket = { history: [], lastSeen: 0 };
satellites.set(reading.satellite, bucket);
}
bucket.lastSeen = reading.timestamp;
if (!isEqual(bucket.history.at(-1), reading)) {
bucket.history.push(reading);
if (bucket.history.length > HISTORY_LIMIT) bucket.history.shift();
}
pruneStale();
} catch (err) {
console.error('[tinygs] poll failed', err);
}
}
setInterval(pollTinyGS, POLL_MS);
export const getTinyGSData = () => {
pruneStale();
return Array.from(satellites.entries()).map(([name, bucket]) => ({
...bucket.history.at(-1),
history: bucket.history.slice(0, -1),
}));
}