// Holt Referenzwerte vom U.S. Naval Observatory und schreibt sie als Fixture // fuer astro.test.js. Muss nur laufen, wenn man das Referenzjahr wechselt — // der Test selbst geht nie ins Netz. // // node scripts/fetch-usno-reference.mjs [Jahr] // // Quelle: Astronomical Applications API v4 des USNO, https://aa.usno.navy.mil/data/api // Die API rechnet mit denselben JPL-Ephemeriden wie der Astronomical Almanac // und ist damit eine von unserer Implementierung unabhaengige Instanz. import { writeFile } from 'node:fs/promises' import { setTimeout as sleep } from 'node:timers/promises' const YEAR = Number(process.argv[2] ?? 2026) // Station Wetterstation — dieselben Werte wie STATION in App.jsx const LAT = 48 + 52 / 60 + 27 / 3600 const LON = 9 + 35 / 60 + 46 / 3600 const API = 'https://aa.usno.navy.mil/api' const COORDS = `${LAT.toFixed(4)},${LON.toFixed(4)}` // Pause zwischen den Requests. Es sind 365 Stueck an einen fremden Server — // nicht draengeln. const THROTTLE_MS = 200 const iso = d => d.toISOString().slice(0, 10) async function getJson(url) { for (let versuch = 1; versuch <= 3; versuch++) { try { const res = await fetch(url, { signal: AbortSignal.timeout(30000) }) if (!res.ok) throw new Error(`HTTP ${res.status}`) return await res.json() } catch (err) { if (versuch === 3) throw new Error(`${url}: ${err.message}`) await sleep(1000 * versuch) } } } // Sonnenauf-/untergang fuer jeden Tag des Jahres, in UTC. // // tz=0&dst=false laesst die API in Weltzeit antworten. Das haelt das Fixture // frei von jeder Sommerzeit-Annahme: der Test vergleicht absolute Zeitpunkte, // die Zeitzone ist allein Sache der Anzeige. async function fetchSonne() { const sonne = {} const tag = new Date(Date.UTC(YEAR, 0, 1)) while (tag.getUTCFullYear() === YEAR) { const datum = iso(tag) const json = await getJson( `${API}/rstt/oneday?date=${datum}&coords=${COORDS}&tz=0&dst=false` ) const daten = json?.properties?.data?.sundata ?? [] const zeit = phen => daten.find(e => e.phen === phen)?.time ?? null sonne[datum] = [zeit('Rise'), zeit('Set')] if (tag.getUTCDate() === 1) console.error(` ${datum} ...`) tag.setUTCDate(tag.getUTCDate() + 1) await sleep(THROTTLE_MS) } return sonne } // Alle Hauptphasen des Jahres — ein einziger Request. Die Zeiten sind UTC. async function fetchMondphasen() { const json = await getJson(`${API}/moon/phases/year?year=${YEAR}`) return json.phasedata.map(p => ({ phase: p.phase, utc: `${p.year}-${String(p.month).padStart(2, '0')}-${String(p.day).padStart(2, '0')}T${p.time}:00Z`, })) } console.error(`Hole USNO-Referenz fuer ${YEAR} (${COORDS}) ...`) const [sonne, mondphasen] = [await fetchSonne(), await fetchMondphasen()] const fixture = { quelle: 'U.S. Naval Observatory, Astronomical Applications API v4 (https://aa.usno.navy.mil/data/api)', abgerufen: iso(new Date()), hinweis: 'Alle Zeiten UTC. Die API rundet auf ganze Minuten — der Test rechnet das ein. ' + 'Auf-/Untergang nach Almanach-Konvention: Meereshorizont, ohne Beobachterhoehe.', jahr: YEAR, station: { lat: LAT, lon: LON }, sonne, mondphasen, } const ziel = new URL('../src/utils/__fixtures__/usno-reference.json', import.meta.url) await writeFile(ziel, JSON.stringify(fixture, null, 2) + '\n') console.error( `Fertig: ${Object.keys(sonne).length} Tage, ${mondphasen.length} Mondphasen -> ${ziel.pathname}` )