From afa806de617c4ca400ab2bce4833b22b333b2ca2 Mon Sep 17 00:00:00 2001 From: Kilian Hofmann Date: Tue, 3 Jun 2025 23:46:16 +0200 Subject: [PATCH] Live CG calculation --- PackageSources/js-bundle/src/App.tsx | 127 +++++++++++++++++- .../js-bundle/src/configs/freighter.ts | 36 +++++ PackageSources/js-bundle/src/configs/pax.ts | 49 +++++++ .../js-bundle/src/configs/shared.ts | 45 +++++++ 4 files changed, 254 insertions(+), 3 deletions(-) create mode 100644 PackageSources/js-bundle/src/configs/freighter.ts create mode 100644 PackageSources/js-bundle/src/configs/pax.ts create mode 100644 PackageSources/js-bundle/src/configs/shared.ts diff --git a/PackageSources/js-bundle/src/App.tsx b/PackageSources/js-bundle/src/App.tsx index 7ca881f..a98791f 100644 --- a/PackageSources/js-bundle/src/App.tsx +++ b/PackageSources/js-bundle/src/App.tsx @@ -1,17 +1,138 @@ import ThemeProvider from '@mui/material/styles/ThemeProvider'; import { FC, StrictMode } from 'react'; import styles from './App.module.scss'; +import { ArmsFreight, PayloadFreight } from './configs/freighter'; +import { ArmsPax, PayloadPax } from './configs/pax'; +import { ArmsFuel, EmptyArm, Fuel, toPercentMAC } from './configs/shared'; interface IAppProps { dataListener: ViewListener.ViewListener; } -const App: FC = ({ dataListener }) => { +const App: FC = () => { + // TODO: Get from EFB settings + const [unit, setUnit] = useState<'lbs' | 'kg'>('lbs'); + // TODO: Look into EFB, infer the same way + const [aircraft, setAircraft] = useState<'f' | 'pax'>('f'); + const [payload, setPayload] = useState(); + const [fuel, setFuel] = useState(); + + const [ZFWCG, setZFWCG] = useState(0); + const [TOCG, setTOCG] = useState(0); + + const requestRef = useRef(undefined); + + const mainLoop = () => { + try { + if (SimVar.IsReady()) { + const conversion = SimVar.GetSimVarValue('FUEL WEIGHT PER GALLON', unit); + const emptyWeight = SimVar.GetSimVarValue('EMPTY WEIGHT', unit); + + const payload: PayloadPax | PayloadFreight = { + pilot: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:1', unit), + firstOfficer: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:2', unit), + engineer: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:3', unit), + upper1Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:4', unit), + cabinCrewFront: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:4', unit), + upper1Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:5', unit), + business1Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:5', unit), + upper2Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:6', unit), + business1Center: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:6', unit), + upper2Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:7', unit), + business1Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:7', unit), + upper3Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:8', unit), + business2Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:8', unit), + upper3Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:9', unit), + business2Center: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:9', unit), + upper4Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:10', unit), + business2Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:10', unit), + upper4Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:11', unit), + economy1Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:11', unit), + lowerForward: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:12', unit), + economy1Center: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:12', unit), + lowerRear: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:13', unit), + economy1Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:13', unit), + leftAuxF: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:14', unit), + economy2Left: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:14', unit), + rightAuxF: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:15', unit), + economy2Center: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:15', unit), + economy2Right: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:16', unit), + cabinCrewRear: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:17', unit), + forwardCargo: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:18', unit), + rearCargo: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:19', unit), + leftAuxPax: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:20', unit), + rightAuxPax: SimVar.GetSimVarValue('PAYLOAD STATION WEIGHT:21', unit), + }; + + const fuel = { + main1: SimVar.GetSimVarValue('FUEL TANK LEFT MAIN QUANTITY', 'gal') * conversion, + main3: SimVar.GetSimVarValue('FUEL TANK RIGHT MAIN QUANTITY', 'gal') * conversion, + main2: SimVar.GetSimVarValue('FUEL TANK CENTER QUANTITY', 'gal') * conversion, + upperAux: SimVar.GetSimVarValue('FUEL TANK CENTER2 QUANTITY', 'gal') * conversion, + lowerAux: SimVar.GetSimVarValue('FUEL TANK CENTER3 QUANTITY', 'gal') * conversion, + main1Tip: SimVar.GetSimVarValue('FUEL TANK LEFT TIP QUANTITY', 'gal') * conversion, + main3Tip: SimVar.GetSimVarValue('FUEL TANK RIGHT TIP QUANTITY', 'gal') * conversion, + tail: SimVar.GetSimVarValue('FUEL TANK EXTERNAL1 QUANTITY', 'gal') * conversion, + forwardAux1: SimVar.GetSimVarValue('FUEL TANK LEFT AUX QUANTITY', 'gal') * conversion, + forwardAux2: SimVar.GetSimVarValue('FUEL TANK RIGHT AUX QUANTITY', 'gal') * conversion, + }; + + let totalMoment = EmptyArm * emptyWeight; + let totalWeight = emptyWeight; + const arms = aircraft === 'f' ? ArmsFreight : ArmsPax; + for (const key in arms) { + if (Object.prototype.hasOwnProperty.call(payload, key)) { + const stationWeight = payload[key as keyof (PayloadFreight | PayloadPax)]; + const arm = arms[key as keyof typeof arms]; + + totalWeight += stationWeight; + totalMoment += arm * stationWeight; + } + } + setZFWCG(toPercentMAC(totalMoment / totalWeight)); + + for (const key in ArmsFuel) { + if (Object.prototype.hasOwnProperty.call(fuel, key)) { + const stationWeight = fuel[key as keyof Fuel]; + const arm = ArmsFuel[key as keyof typeof ArmsFuel]; + + totalWeight += stationWeight; + totalMoment += arm * stationWeight; + } + } + setTOCG(toPercentMAC(totalMoment / totalWeight)); + + setPayload(payload); + setFuel(fuel); + } + } catch {} + + requestRef.current = requestAnimationFrame(mainLoop); + }; + + useEffect(() => { + requestRef.current = requestAnimationFrame(mainLoop); + + if (requestRef.current !== undefined) return () => cancelAnimationFrame(requestRef.current as number); + }, [unit, aircraft]); + return (
- - + + +
+
+
{JSON.stringify(payload, null, 2)}
+ ZFWCG: {ZFWCG} +
+
+
{JSON.stringify(fuel, null, 2)}
+ TOCG: {TOCG} +
+
); diff --git a/PackageSources/js-bundle/src/configs/freighter.ts b/PackageSources/js-bundle/src/configs/freighter.ts new file mode 100644 index 0000000..6ca83bb --- /dev/null +++ b/PackageSources/js-bundle/src/configs/freighter.ts @@ -0,0 +1,36 @@ +export interface PayloadFreight { + pilot: number; + firstOfficer: number; + engineer: number; + upper1Left: number; + upper1Right: number; + upper2Left: number; + upper2Right: number; + upper3Left: number; + upper3Right: number; + upper4Left: number; + upper4Right: number; + lowerForward: number; + lowerRear: number; + leftAuxF: number; + rightAuxF: number; +} + +// TODO: Extract from CFG at runtime. +export const ArmsFreight = { + pilot: 984, + firstOfficer: 984, + engineer: 960, + upper1Left: 660, + upper1Right: 660, + upper2Left: 240, + upper2Right: 240, + upper3Left: -240, + upper3Right: -240, + upper4Left: -600, + upper4Right: -600, + lowerForward: 360, + lowerRear: -360, + leftAuxF: 60, + rightAuxF: 60, +} \ No newline at end of file diff --git a/PackageSources/js-bundle/src/configs/pax.ts b/PackageSources/js-bundle/src/configs/pax.ts new file mode 100644 index 0000000..9af17d6 --- /dev/null +++ b/PackageSources/js-bundle/src/configs/pax.ts @@ -0,0 +1,49 @@ +export interface PayloadPax { + pilot: number; + firstOfficer: number; + engineer: number; + cabinCrewFront: number; + business1Left: number; + business1Center: number; + business1Right: number; + business2Left: number; + business2Center: number; + business2Right: number; + economy1Left: number; + economy1Center: number; + economy1Right: number; + economy2Left: number; + economy2Center: number; + economy2Right: number; + cabinCrewRear: number; + forwardCargo: number; + rearCargo: number; + leftAuxPax: number; + rightAuxPax: number; +} + +// TODO: Extract from CFG at runtime. +// FIXME: Copy over values +export const ArmsPax = { + pilot: 0, + firstOfficer: 0, + engineer: 0, + cabinCrewFront: 0, + business1Left: 0, + business1Center: 0, + business1Right: 0, + business2Left: 0, + business2Center: 0, + business2Right: 0, + economy1Left: 0, + economy1Center: 0, + economy1Right: 0, + economy2Left: 0, + economy2Center: 0, + economy2Right: 0, + cabinCrewRear: 0, + forwardCargo: 0, + rearCargo: 0, + leftAuxPax: 0, + rightAuxPax: 0, +}; diff --git a/PackageSources/js-bundle/src/configs/shared.ts b/PackageSources/js-bundle/src/configs/shared.ts new file mode 100644 index 0000000..1aa1114 --- /dev/null +++ b/PackageSources/js-bundle/src/configs/shared.ts @@ -0,0 +1,45 @@ +export interface Fuel { + main1: number; + main3: number; + main2: number; + upperAux: number; + lowerAux: number; + main1Tip: number; + main3Tip: number; + tail: number; + forwardAux1: number; + forwardAux2: number; +} + +// TODO: Extract from CFG at runtime. +export const EmptyArm = -159.6; + +// TODO: Extract from CFG at runtime. +export const ArmsFuel = { + main1: -240, + main3: -240, + main2: 120, + upperAux: 0, + lowerAux: 0, + main1Tip: -468, + main3Tip: -468, + tail: -840, + forwardAux1: 60, + forwardAux2: 60, +}; + +// TODO: Extract following four from CFG at runtime +const rootChord = 34.68; +const wingSpan = 170.5; +const wingArea = 3648; +const quarterMAC = -165; //aero_center + +const tipChord = (2 * wingArea) / wingSpan - rootChord; +const taperRatio = tipChord / rootChord; + +const MAC = (2 / 3) * rootChord * ((1 + taperRatio + taperRatio ** 2) / (1 + taperRatio)) * 12; +const LEMAC = quarterMAC + (1 / 4) * MAC; + +export const toPercentMAC = (positionInInches: number) => { + return Math.abs(((positionInInches - LEMAC) / MAC) * 100); +};