-
- {icon}
+
+ {isLive ? (
+
+
+
+ {id === 'iss' ? 'Look Up!' : 'Event in Progress'}
+
+
+ {id === 'iss'
+ ? 'The ISS is directly above. Give them a wave! 👋'
+ : `The ${title} is occurring right now.`}
+
+
+ ) : (
+ <>
+
+
+ {icon}
+
+
+ {title}
+
-
- {title}
-
-
-
- {timeLeft.h}
- :
- {timeLeft.m}
- :
- {timeLeft.s}
-
-
-
- T-Minus to Horizon
-
+
+ {parseInt(timeLeft.d) >= 2 ? (
+ /* Case 1: More than 2 days - Keep it super simple */
+ <>
+ {timeLeft.d}
+ Days
+ >
+ ) : parseInt(timeLeft.d) === 1 ? (
+ /* Case 2: Between 24 and 48 hours - Show Day + Hours */
+ <>
+ 01d
+ :
+ {timeLeft.h}h
+ >
+ ) : (
+ /* Case 3: Under 24 hours - The full high-precision clock */
+ <>
+ {timeLeft.h}
+ :
+ {timeLeft.m}
+ :
+ {timeLeft.s}
+ >
+ )}
+
+
+
+ {parseInt(timeLeft.d) > 0 ? "Until event" : "T-Minus to Horizon"}
+
+ >
+ )}
);
}
\ No newline at end of file
diff --git a/components/MissionControl.tsx b/components/MissionControl.tsx
index 3cc1142..a27d312 100644
--- a/components/MissionControl.tsx
+++ b/components/MissionControl.tsx
@@ -4,47 +4,48 @@ import { useMemo } from 'react';
import dynamic from 'next/dynamic';
import EventCard from '@/components/EventCard';
import { Satellite, Rocket, Moon, Sparkles } from 'lucide-react';
+import { MissionControlProps } from '@/types/space';
+
const Starfield = dynamic(() => import('@/components/Starfield'), {
ssr: false
});
-// 1. Define the interface
-interface MissionControlProps {
- initialIssPass: Date;
-}
-// 2. Accept the prop here
-export default function MissionControl({ initialIssPass }: MissionControlProps) {
+export default function MissionControl({ iss, moon }: MissionControlProps) {
const BASE_TIME = 1769610273000;
const events = useMemo(() => [
{
- id: 'iss', // Add an ID to distinguish the ISS card
+ id: 'iss',
title: "ISS Overhead: Home",
- date: initialIssPass, // Use the real data from SQLite here!
+ date: iss.start,
+ endDate: iss.end,
icon:
,
},
{
id: 'moon',
- title: "Next Lunar Phase: Full",
- date: new Date(BASE_TIME + 1000 * 60 * 60 * 24 * 3),
+ title: moon.title,
+ date: moon.start,
+ endDate: moon.end,
icon:
,
},
{
id: 'starlink',
title: "Starlink Train",
date: new Date(BASE_TIME + 1000 * 60 * 45),
+ endDate: new Date(BASE_TIME + 1000 * 60 * 55),
icon:
,
},
{
id: 'meteor',
title: "Meteor Shower",
date: new Date(BASE_TIME + 1000 * 60 * 60 * 24 * 10),
+ endDate: new Date(BASE_TIME + 1000 * 60 * 60 * 24 * 10.1),
icon:
,
}
- ], [initialIssPass]); // Re-memoize if the pass updates
+ ], [iss, moon]);
return (
@@ -62,9 +63,11 @@ export default function MissionControl({ initialIssPass }: MissionControlProps)
{events.map((event) => (
))}
diff --git a/lib/db.ts b/lib/db.ts
index 09f81b4..985e69e 100644
--- a/lib/db.ts
+++ b/lib/db.ts
@@ -17,8 +17,19 @@ const db = new Database(dbPath);
db.exec(`
CREATE TABLE IF NOT EXISTS iss_passes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
- pass_time TEXT NOT NULL
+ pass_time TEXT NOT NULL,
+ end_time TEXT NOT NULL
)
`);
+db.exec(`
+ CREATE TABLE IF NOT EXISTS global_events (
+ id TEXT PRIMARY KEY,
+ title TEXT NOT NULL,
+ event_time TEXT NOT NULL
+ );
+`);
+
+
+
export default db;
\ No newline at end of file
diff --git a/scripts/update-space.ts b/scripts/update-space.ts
index afd93d6..03dd115 100644
--- a/scripts/update-space.ts
+++ b/scripts/update-space.ts
@@ -3,7 +3,7 @@ import * as satellite from 'satellite.js';
const MY_LAT = 55.6683;
const MY_LON = 12.5333;
-const MY_ALT = 0; // Altitude in kilometers
+const MY_ALT = 0;
async function updateISSData() {
console.log("🛰️ Fetching TLE data...");
@@ -14,47 +14,105 @@ async function updateISSData() {
const line1 = lines[1];
const line2 = lines[2];
-
- // 1. Initialize satellite record from TLE
const satrec = satellite.twoline2satrec(line1, line2);
- // 2. Observer coordinates in radians
const observerGd = {
longitude: satellite.degreesToRadians(MY_LON),
latitude: satellite.degreesToRadians(MY_LAT),
height: MY_ALT
};
- let nextPassDate: Date | null = null;
+ const passesFound: { start: Date, end: Date }[] = [];
const now = new Date();
+ const searchTime = new Date(now.getTime());
- // 3. Look ahead for the next 24 hours (1440 minutes)
- for (let i = 0; i < 1440; i++) {
- const checkTime = new Date(now.getTime() + i * 60000);
+ 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);
- // Calculate Look Angles (Azimuth, Elevation, Range)
if (typeof positionAndVelocity?.position !== 'boolean') {
- const positionEcf = satellite.eciToEcf(positionAndVelocity!.position, gmst);
- const lookAngles = satellite.ecfToLookAngles(observerGd, positionEcf);
- const elevation = satellite.radiansToDegrees(lookAngles.elevation);
+ const positionEcf = satellite.eciToEcf(positionAndVelocity!.position, gmst);
+ const lookAngles = satellite.ecfToLookAngles(observerGd, positionEcf);
+ const elevation = satellite.radiansToDegrees(lookAngles.elevation);
- // If elevation > 0, the ISS is above your horizon!
- if (elevation > 0) {
- nextPassDate = checkTime;
- break;
+ 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++;
}
- if (nextPassDate) {
- const upsert = db.prepare('INSERT INTO iss_passes (id, pass_time) VALUES (1, ?) ON CONFLICT(id) DO UPDATE SET pass_time=excluded.pass_time');
- upsert.run(nextPassDate.toISOString());
- console.log(`✅ Real orbital pass found: ${nextPassDate.toISOString()}`);
- } else {
- console.log("❌ No pass found in the next 24 hours (check your TLE or coordinates).");
+ 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.`);
}
-updateISSData().catch(console.error);
\ No newline at end of file
+// Add this to your worker script
+function getNextMoonPhase() {
+ const LUNAR_CYCLE = 29.53059; // Days
+ const KNOWN_NEW_MOON = new Date('2024-01-11T11:57:00Z'); // A reference New Moon
+
+ const now = new Date();
+ const msSinceReference = now.getTime() - KNOWN_NEW_MOON.getTime();
+ const daysSinceReference = msSinceReference / (1000 * 60 * 60 * 24);
+
+ const currentCycleProgress = daysSinceReference % LUNAR_CYCLE;
+ const daysToFullMoon = (LUNAR_CYCLE / 2) - currentCycleProgress;
+ const daysToNewMoon = LUNAR_CYCLE - currentCycleProgress;
+
+ // If we've passed the Full Moon in this cycle, count to New Moon
+ let targetDate, title;
+ if (daysToFullMoon > 0) {
+ targetDate = new Date(now.getTime() + daysToFullMoon * 86400000);
+ title = "Next Full Moon";
+ } else {
+ targetDate = new Date(now.getTime() + daysToNewMoon * 86400000);
+ title = "Next New Moon";
+ }
+
+ return { title, targetDate };
+}
+
+async function updateAllData() {
+ await updateISSData();
+
+ const moon = getNextMoonPhase();
+ const upsertMoon = 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
+ `);
+ upsertMoon.run(moon.title, moon.targetDate.toISOString());
+
+ console.log(`🌙 Updated Moon Phase: ${moon.title}`);
+}
+
+updateAllData().catch(console.error);
diff --git a/types/cards.ts b/types/cards.ts
deleted file mode 100644
index e0385e5..0000000
--- a/types/cards.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-interface EventCardProps {
- title: string;
- targetDate: Date;
- icon?: React.ReactNode;
-}
\ No newline at end of file
diff --git a/types/space.ts b/types/space.ts
index 3beb20c..38b2d20 100644
--- a/types/space.ts
+++ b/types/space.ts
@@ -1,5 +1,3 @@
-import { ReactNode } from "react";
-
export interface ISSPass {
id: string;
timestamp: number;
@@ -24,7 +22,14 @@ export interface Star {
}
export interface EventCardProps {
+ id: string;
title: string;
- targetDate: Date;
- icon?: ReactNode;
+ targetDate: Date | null;
+ endDate: Date | null;
+ icon: React.ReactNode;
+}
+
+export interface MissionControlProps {
+ iss: { start: Date | null; end: Date | null };
+ moon: { title: string; start: Date | null; end: Date | null };
}
\ No newline at end of file