Files
wetter_station/frontend/scripts/fetch-usno-reference.mjs
T
admin b03bd8589f V 1.7.1 fix: Astro-Werte gegen USNO geprueft, Hoehenkorrektur entfernt
Die Pruefung in 1.7.0 lief gegen selbst geschriebene Referenzen desselben
Algorithmus — ein gemeinsamer Denkfehler faellt so nicht auf. Jetzt gegen die
Astronomical Applications API des U.S. Naval Observatory (JPL-Ephemeriden,
gleiche Grundlage wie der Astronomical Almanac), und dabei zwei Fehler
gefunden:

- Hoehenkorrektur -2.076*sqrt(547)/60 entfernt. Sie gilt fuer den Blick von
  einer Erhebung auf einen fernen Meereshorizont; die Station liegt in
  ringsum aehnlich hohem Gelaende. Sie hat die Zeiten um 5-6 min verschoben,
  weg von Almanach und USNO. Rest-Abweichung jetzt <= 45 s ueber 365 Tage.
- getMoonPhase() leitet die Zyklusposition aus der ekliptikalen Laengen-
  differenz Mond-Sonne ab statt aus dem Phasenwinkel. Das ist die Definition
  von Almanach und USNO; der Phasenwinkel laeuft davon ab, sobald der Mond
  weit vom Knoten steht. Neu-/Vollmond lagen dadurch bis zu 3,8 h daneben,
  jetzt <= 28 min. Die Viertel waren nie betroffen.

Belegt durch astro.test.js gegen ein eingechecktes Fixture (365 Tage plus 50
Phasenereignisse 2026). Der Test geht nicht ins Netz; neu erzeugen laesst er
sich mit scripts/fetch-usno-reference.mjs. Testrunner ist node:test, keine
neue Abhaengigkeit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 00:18:09 +02:00

99 lines
3.4 KiB
JavaScript

// 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}`
)