import db from '../lib/db'; import * as satellite from 'satellite.js'; import path from 'path'; import fs from 'fs'; import { CosmicEvent } from '@/types/space'; const MY_LAT = 55.6683; const MY_LON = 12.5333; const MY_ALT = 0; async function updateISSData() { console.log("🛰️ Fetching TLE data..."); const response = await fetch('https://data.ivanstanojevic.me/api/tle/25544'); if (!response.ok) { throw new Error(`Mirror API Error: ${response.status}`); } const data = await response.json(); const line1 = data.line1; const line2 = data.line2; if (!line1 || !line2) { throw new Error("Invalid TLE data received from mirror"); } const satrec = satellite.twoline2satrec(line1, line2); const observerGd = { longitude: satellite.degreesToRadians(MY_LON), latitude: satellite.degreesToRadians(MY_LAT), height: MY_ALT }; const passesFound: { start: Date, end: Date }[] = []; const now = new Date(); const searchTime = new Date(now.getTime()); const maxSearchMinutes = 2880; let minutesSearched = 0; while (passesFound.length < 2 && minutesSearched < maxSearchMinutes) { const checkTime = new Date(searchTime.getTime() + (minutesSearched * 60000)); const positionAndVelocity = satellite.propagate(satrec, checkTime); const gmst = satellite.gstime(checkTime); if (typeof positionAndVelocity?.position !== 'boolean') { const positionEcf = satellite.eciToEcf(positionAndVelocity!.position, gmst); const lookAngles = satellite.ecfToLookAngles(observerGd, positionEcf); const elevation = satellite.radiansToDegrees(lookAngles.elevation); if (elevation > 0) { const aos = checkTime; let los = new Date(aos.getTime() + 5 * 60000); for (let j = 1; j < 20; j++) { const exitTime = new Date(aos.getTime() + j * 60000); const pos = satellite.propagate(satrec, exitTime); const gmstExit = satellite.gstime(exitTime); if (typeof pos?.position !== 'boolean') { const lookExit = satellite.ecfToLookAngles(observerGd, satellite.eciToEcf(pos!.position, gmstExit)); if (satellite.radiansToDegrees(lookExit.elevation) < 0) { los = exitTime; break; } } } passesFound.push({ start: aos, end: los }); minutesSearched += 90; continue; } } minutesSearched++; } db.prepare('DELETE FROM iss_passes').run(); const insert = db.prepare('INSERT INTO iss_passes (pass_time, end_time) VALUES (?, ?)'); for (const pass of passesFound) { insert.run(pass.start.toISOString(), pass.end.toISOString()); } console.log(`✅ Stored ${passesFound.length} future passes with start and end times.`); } function updateMoonPhase() { const LUNAR_CYCLE = 29.53059; const KNOWN_NEW_MOON = new Date('2024-01-11T11:57:00Z'); const now = new Date(); const daysSince = (now.getTime() - KNOWN_NEW_MOON.getTime()) / 86400000; const progress = daysSince % LUNAR_CYCLE; const daysToFull = (LUNAR_CYCLE / 2) - progress; const daysToNew = LUNAR_CYCLE - progress; const moon = daysToFull > 0 ? { title: "Next Full Moon", date: new Date(now.getTime() + daysToFull * 86400000) } : { title: "Next New Moon", date: new Date(now.getTime() + daysToNew * 86400000) }; db.prepare(` INSERT INTO global_events (id, title, event_time) VALUES ('moon_phase', ?, ?) ON CONFLICT(id) DO UPDATE SET title=excluded.title, event_time=excluded.event_time `).run(moon.title, moon.date.toISOString()); console.log(`🌙 Moon: ${moon.title}`); } function updateCosmicEvents() { const configPath = path.resolve(process.cwd(), 'config/events.json'); if (!fs.existsSync(configPath)) return; const events: CosmicEvent[] = JSON.parse(fs.readFileSync(configPath, 'utf-8')); const now = new Date(); const next = events .filter((e) => new Date(e.event_time) > now) .sort((a, b) => new Date(a.event_time).getTime() - new Date(b.event_time).getTime())[0]; if (next) { db.prepare(` INSERT INTO global_events (id, title, event_time) VALUES ('next_cosmic_event', ?, ?) ON CONFLICT(id) DO UPDATE SET title=excluded.title, event_time=excluded.event_time `).run(next.title, next.event_time); console.log(`✨ Cosmic: ${next.title}`); } } async function updateRocketLaunches() { try { const nowISO = new Date().toISOString(); const url = `https://lldev.thespacedevs.com/2.2.0/launch/upcoming/?limit=1&mode=list&net__gt=${nowISO}`; const response = await fetch(url); const data = await response.json(); if (data.results && data.results.length > 0) { const launch = data.results[0]; db.prepare(` INSERT INTO global_events (id, title, event_time) VALUES ('next_launch', ?, ?) ON CONFLICT(id) DO UPDATE SET title=excluded.title, event_time=excluded.event_time `).run( `Launch: ${launch.name}`, launch.net ); console.log(`🚀 Rocket Sync: ${launch.name}`); } } catch (error) { console.error("❌ Rocket Fetch Error:", error); } } async function updateAllData() { console.log("📡 Starting Ground Station sync..."); try { await updateISSData(); } catch (e) { console.error("⚠️ ISS sync failed (Timeout/Network)", e); } try { updateMoonPhase(); } catch (e) { console.error("⚠️ Moon sync failed", e); } try { updateCosmicEvents(); } catch (e) { console.error("⚠️ Cosmic sync failed", e); } try { await updateRocketLaunches(); } catch (e) { console.error("⚠️ Rocket sync failed", e); } } async function startWorker() { console.log("🛰️ Ground Station Worker started..."); while (true) { try { await updateAllData(); console.log("✅ Sync complete. Sleeping for 1 hour..."); } catch (err) { console.error("❌ Worker loop error:", err); } // Sleep for 3600000ms (1 hour) await new Promise(resolve => setTimeout(resolve, 3600000)); } } startWorker();