185 lines
5.0 KiB
JavaScript
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,
|
|
}
|
|
}
|