init
This commit is contained in:
69
server/space.mjs
Normal file
69
server/space.mjs
Normal file
@@ -0,0 +1,69 @@
|
||||
import { ssnFromFlux, sunActivityLabel } from './celestial.mjs'
|
||||
|
||||
const CACHE = {}
|
||||
const CACHE_MS = 10 * 60 * 1000
|
||||
|
||||
async function cachedFetch(key, url) {
|
||||
const now = Date.now()
|
||||
if (CACHE[key] && now - CACHE[key].ts < CACHE_MS) return CACHE[key].data
|
||||
const res = await fetch(url)
|
||||
const data = await res.json()
|
||||
CACHE[key] = { ts: now, data }
|
||||
return data
|
||||
}
|
||||
|
||||
async function getKpAp() {
|
||||
const data = await cachedFetch('kp', 'https://services.swpc.noaa.gov/products/noaa-planetary-k-index.json')
|
||||
// First row is always the header
|
||||
const rows = Array.isArray(data) ? data.slice(1).filter(r => Array.isArray(r)) : []
|
||||
if (!rows.length) return {}
|
||||
const latest = rows[rows.length - 1]
|
||||
const kp = parseFloat(latest[1])
|
||||
const ap = parseFloat(latest[2])
|
||||
return {
|
||||
space_kp_index: isNaN(kp) ? null : kp,
|
||||
space_ap_index: isNaN(ap) ? null : ap,
|
||||
space_geomagnetic_storm: kp >= 7 ? 'Severe' : kp >= 6 ? 'Strong' : kp >= 5 ? 'Moderate' : kp >= 4 ? 'Minor' : 'None',
|
||||
}
|
||||
}
|
||||
|
||||
async function getSolarWind() {
|
||||
const [plasma, mag] = await Promise.all([
|
||||
cachedFetch('plasma', 'https://services.swpc.noaa.gov/products/solar-wind/plasma.json'),
|
||||
cachedFetch('mag', 'https://services.swpc.noaa.gov/products/solar-wind/mag.json'),
|
||||
])
|
||||
// Skip header row, find last valid row
|
||||
const pRows = Array.isArray(plasma) ? plasma.slice(1).filter(r => Array.isArray(r) && r[2] !== null) : []
|
||||
const mRows = Array.isArray(mag) ? mag.slice(1).filter(r => Array.isArray(r) && r[3] !== null) : []
|
||||
const p = pRows[pRows.length - 1] || []
|
||||
const m = mRows[mRows.length - 1] || []
|
||||
return {
|
||||
space_solar_wind_speed_kms: p[2] != null ? parseFloat(p[2]) : null,
|
||||
space_solar_wind_density: p[1] != null ? parseFloat(p[1]) : null,
|
||||
space_solar_wind_temp: p[3] != null ? parseFloat(p[3]) : null,
|
||||
space_imf_bz: m[3] != null ? parseFloat(m[3]) : null,
|
||||
space_imf_bt: m[6] != null ? parseFloat(m[6]) : null,
|
||||
}
|
||||
}
|
||||
|
||||
async function getSolarFlux() {
|
||||
const data = await cachedFetch('flux', 'https://services.swpc.noaa.gov/products/10cm-flux-30-day.json')
|
||||
// Format: [["yyyy-mm-dd", flux, ...], ...] — no header row on this one
|
||||
const rows = Array.isArray(data) ? data.filter(r => Array.isArray(r) && !isNaN(parseFloat(r[1]))) : []
|
||||
if (!rows.length) return {}
|
||||
const f107 = parseFloat(rows[rows.length - 1][1])
|
||||
return {
|
||||
space_solar_flux_f107: isNaN(f107) ? null : f107,
|
||||
sun_ssn: ssnFromFlux(f107),
|
||||
sun_activity_label: sunActivityLabel(f107),
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSpaceWeather() {
|
||||
const [kpAp, wind, flux] = await Promise.allSettled([getKpAp(), getSolarWind(), getSolarFlux()])
|
||||
return {
|
||||
...(kpAp.status === 'fulfilled' ? kpAp.value : {}),
|
||||
...(wind.status === 'fulfilled' ? wind.value : {}),
|
||||
...(flux.status === 'fulfilled' ? flux.value : {}),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user