Files
rocketry/src/hooks/useSatelliteDesign.js
2026-03-15 15:22:04 +00:00

185 lines
5.0 KiB
JavaScript

import { useState, useEffect, useMemo } from 'react'
import {
calcOrbit,
calcPowerBudget,
calcLinkBudget,
calcThermalBalance,
calcMassBudget,
} from '../engine/satelliteCalcs.js'
const LS_KEY = 'rockettools_satellite'
const DEFAULTS = {
orbitInputs: {
altitudeKm: 500,
betaAngleDeg: 0,
},
powerInputs: {
formFactor: '3U',
solarCellType: 'GaAs',
etaCell: 0.28,
etaPacking: 0.85,
deployablePanelArea: 0,
etaCharge: 0.95,
etaDischarge: 0.97,
DOD: 0.30,
P_sunlight_load: 3.0,
P_eclipse_load: 2.0,
degradationPerYear: -0.03,
designLife: 2,
},
linkInputs: {
P_tx_W: 1.0,
G_tx_dBi: 6.0,
L_feed_dB: 1.0,
frequencyMHz: 437,
modulation: 'BPSK',
elevationAngleDeg: 5,
L_atm_dB: 0.5,
L_point_dB: 1.0,
G_rx_dBi: 12.0,
T_sys_K: 290,
dataRateBps: 9600,
Eb_N0_req_dB: 9.6,
},
thermalInputs: {
alpha: 0.90,
epsilon: 0.85,
A_sun: 0.01,
A_earth: 0.01,
A_rad: 0.02,
Q_int: 3.0,
},
subsystems: {
adcs: {
controlMode: 'sun-pointing',
pointingAccuracyDeg: 5,
reactionWheels: false,
magnetorquers: true,
mass: 200,
avgPower: 0.5,
peakPower: 1.0,
},
comms: {
frequencyMHz: 437,
P_tx_W: 1.0,
antennaType: 'dipole',
modulation: 'BPSK',
mass: 100,
avgPower: 1.0,
peakPower: 2.0,
},
cdh: {
processor: 'ARM Cortex-M4',
storageGB: 2,
housekeepingRateHz: 1,
mass: 120,
avgPower: 0.8,
peakPower: 1.2,
},
eps: {
batteryChemistry: 'Li-ion',
solarCellType: 'GaAs',
mass: 300,
},
structure: {
materialId: 'al_6061_t6',
configuration: 'standard',
mass: 400,
},
propulsion: {
type: 'none',
propellant: '',
mass: 0,
avgPower: 0,
peakPower: 0,
},
payload: {
name: '',
type: '',
mass: 0,
avgPower: 0,
peakPower: 0,
interface: 'UART',
},
},
}
function loadFromStorage() {
try {
const raw = localStorage.getItem(LS_KEY)
return raw ? JSON.parse(raw) : null
} catch {
return null
}
}
export function useSatelliteDesign() {
const saved = useMemo(() => loadFromStorage(), [])
const [orbitInputs, setOrbitInputs] = useState(saved?.orbitInputs ?? DEFAULTS.orbitInputs)
const [powerInputs, setPowerInputs] = useState(saved?.powerInputs ?? DEFAULTS.powerInputs)
const [linkInputs, setLinkInputs] = useState(saved?.linkInputs ?? DEFAULTS.linkInputs)
const [thermalInputs, setThermalInputs] = useState(saved?.thermalInputs ?? DEFAULTS.thermalInputs)
const [subsystems, setSubsystems] = useState(saved?.subsystems ?? DEFAULTS.subsystems)
// Persist all state slices on any change
useEffect(() => {
try {
localStorage.setItem(LS_KEY, JSON.stringify({
orbitInputs, powerInputs, linkInputs, thermalInputs, subsystems,
}))
} catch {}
}, [orbitInputs, powerInputs, linkInputs, thermalInputs, subsystems])
// ── Calculation chain (pure useMemo, no side effects) ──
const orbitData = useMemo(() => calcOrbit(orbitInputs), [orbitInputs])
const powerBudget = useMemo(() => calcPowerBudget(orbitData, powerInputs), [orbitData, powerInputs])
const linkBudget = useMemo(() => calcLinkBudget(orbitData, linkInputs), [orbitData, linkInputs])
const thermalBalance = useMemo(() => calcThermalBalance(orbitData, thermalInputs), [orbitData, thermalInputs])
const massBudget = useMemo(() => calcMassBudget(subsystems, powerInputs.formFactor), [subsystems, powerInputs.formFactor])
// ── Setters ──
/** Patch a single key in orbitInputs */
function setOrbitInput(key, value) {
setOrbitInputs(prev => ({ ...prev, [key]: value }))
}
/** Patch a single key in powerInputs */
function setPowerInput(key, value) {
setPowerInputs(prev => ({ ...prev, [key]: value }))
}
/** Patch a single key in linkInputs */
function setLinkInput(key, value) {
setLinkInputs(prev => ({ ...prev, [key]: value }))
}
/** Patch a single key in thermalInputs */
function setThermalInput(key, value) {
setThermalInputs(prev => ({ ...prev, [key]: value }))
}
/** Patch one or more fields of a specific subsystem */
function setSubsystem(key, patch) {
setSubsystems(prev => ({ ...prev, [key]: { ...prev[key], ...patch } }))
}
return {
// State slices
orbitInputs, setOrbitInput,
powerInputs, setPowerInput,
linkInputs, setLinkInput,
thermalInputs, setThermalInput,
subsystems, setSubsystem,
// Computed results
orbitData,
powerBudget,
linkBudget,
thermalBalance,
massBudget,
}
}