diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 4ad933a5a2a..4373c1f3349 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -84,7 +84,6 @@ 1. [FWC] Implement non-cancellable master warning for overspeed and gear not down - @tracernz (Mike) 1. [EFB] Checklist restructure to add more capabilities and use json configs - @frankkopp (Frank Kopp) 1. [FLIGHTMODEL] Landing lights or RAT extended now have drag - @Crocket63 (crocket) -1. [FADEC] Fadec rewrite/cleanup/commenting using cpp framework - @frankkopp (Frank Kopp) ## 0.11.0 diff --git a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/panel.cfg b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/panel.cfg index 2595ff9116d..2b299cde8ea 100644 --- a/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/panel.cfg +++ b/fbw-a32nx/src/base/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/panel.cfg @@ -132,7 +132,7 @@ background_color = 0,0,0 htmlgauge00 = WasmInstrument/WasmInstrument.html?wasm_module=systems.wasm&wasm_gauge=systems,0,0,1,1 htmlgauge01 = WasmInstrument/WasmInstrument.html?wasm_module=fbw.wasm&wasm_gauge=fbw,0,0,1,1 -htmlgauge02 = WasmInstrument/WasmInstrument.html?wasm_module=fadec-a32nx.wasm&wasm_gauge=Gauge_Fadec,0,0,1,1 +htmlgauge02 = WasmInstrument/WasmInstrument.html?wasm_module=fadec.wasm&wasm_gauge=FadecGauge,0,0,1,1 htmlgauge03 = WasmInstrument/WasmInstrument.html?wasm_module=extra-backend-a32nx.wasm&wasm_gauge=Gauge_Extra_Backend,0,0,1,1 [VCockpit18] diff --git a/fbw-a32nx/src/wasm/CMakeLists.txt b/fbw-a32nx/src/wasm/CMakeLists.txt index e6e907ee080..cdf2102b7ba 100644 --- a/fbw-a32nx/src/wasm/CMakeLists.txt +++ b/fbw-a32nx/src/wasm/CMakeLists.txt @@ -5,10 +5,9 @@ set(OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../out/flybywire-aircraft-a3 add_definitions(-DA32NX) add_subdirectory(extra-backend-a32nx) -add_subdirectory(fadec_a32nx) -# these are only here to allow CMake compatible IDEs (JetBrains' Clion for example) to show systax -# highlighting and allow code navigation +# FIXME: remove the if-clause as soon as all components are using CMake if (WIN32) + add_subdirectory(fadec_a320) add_subdirectory(fbw_a320) endif () diff --git a/fbw-a32nx/src/wasm/fadec_a320/CMakeLists.txt b/fbw-a32nx/src/wasm/fadec_a320/CMakeLists.txt new file mode 100644 index 00000000000..d51e4342832 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a320/CMakeLists.txt @@ -0,0 +1,26 @@ +# This CMakeLists.txt file is used only for syntax highlighting and navigating +# through the code in an IDE. It is not used for building the project. + +cmake_minimum_required(VERSION 3.19) + +project(flybywire-a32nx-fadec) + +set(CMAKE_CXX_STANDARD 20) +set(MSFS_SDK "C:\\MSFS SDK") + +set(CMAKE_CXX_FLAGS "-c -g -DDEBUG -Wno-unused-command-line-argument -Wno-ignored-attributes -Wno-macro-redefined --sysroot \"${MSFS_SDK}/WASM/wasi-sysroot\" -target wasm32-unknown-wasi -flto -D_MSFS_WASM=1 -D__wasi__ -D_LIBCPP_HAS_NO_THREADS -D_WINDLL -D_MBCS -mthread-model single -fno-exceptions -fms-extensions") + +include_directories("${MSFS_SDK}/WASM/include") +include_directories("${MSFS_SDK}/WASM/wasi-sysroot/include") +include_directories("${MSFS_SDK}/SimConnect SDK/include") + +include_directories( + ./src + ${FBW_ROOT}/fbw-common/src/wasm/fadec_common/src + ${FBW_ROOT}/fbw-common/src/wasm/fbw_common/src + ${FBW_ROOT}/fbw-common/src/wasm/fbw_common/src/inih +) + +add_executable(flybywire-a32nx-fadec + ./src/FadecGauge.cpp + ) diff --git a/fbw-a32nx/src/wasm/fadec_a320/README.md b/fbw-a32nx/src/wasm/fadec_a320/README.md new file mode 100644 index 00000000000..baec37e12ac --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a320/README.md @@ -0,0 +1,9 @@ +# Engine & FADEC - A32NX/ A380X Build Options + +By default, the FADEC code will be built for the A32NX aircraft. To build for the A380X, you will have to edit the build.sh file accordingly: + +A380X Option: +Line 44: "${DIR}/a380_fadec/src/FadecGauge.cpp" + +To go back to the A32NX Option: +Line 44: "${DIR}/a320_fadec/src/FadecGauge.cpp" diff --git a/fbw-a32nx/src/wasm/fadec_a320/build.sh b/fbw-a32nx/src/wasm/fadec_a320/build.sh new file mode 100755 index 00000000000..08374a9227c --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a320/build.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# Copyright (c) 2021-2023 FlyByWire Simulations +# +# SPDX-License-Identifier: GPL-3.0 + +# get directory of this script relative to root +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +COMMON_DIR="${DIR}/../../../../fbw-common/src/wasm" +OUTPUT="${DIR}/../../../out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/fadec.wasm" + +if [ "$1" == "--debug" ]; then + WASMLD_ARGS="" + CLANG_ARGS="-g" +else + WASMLD_ARGS="-O2 --lto-O2 --strip-debug" + CLANG_ARGS="-flto -O2 -DNDEBUG" +fi + +set -e + +# create temporary folder for o files +mkdir -p "${DIR}/obj" +pushd "${DIR}/obj" + +# compile c++ code for the A32NX +clang++ \ + -c \ + ${CLANG_ARGS} \ + -std=c++20 \ + -Wno-unused-command-line-argument \ + -Wno-ignored-attributes \ + -Wno-macro-redefined \ + --sysroot "${MSFS_SDK}/WASM/wasi-sysroot" \ + -target wasm32-unknown-wasi \ + -D_MSFS_WASM=1 \ + -D__wasi__ \ + -D_LIBCPP_HAS_NO_THREADS \ + -D_WINDLL \ + -D_MBCS \ + -mthread-model single \ + -fno-exceptions \ + -fms-extensions \ + -fvisibility=hidden \ + -I "${MSFS_SDK}/WASM/include" \ + -I "${MSFS_SDK}/SimConnect SDK/include" \ + -I "${COMMON_DIR}/fadec_common/src" \ + -I "${COMMON_DIR}/fbw_common/src/inih" \ + -I "${DIR}/common" \ + "${DIR}/src/FadecGauge.cpp" + +# restore directory +popd + +wasm-ld \ + --no-entry \ + --allow-undefined \ + -L "${MSFS_SDK}/WASM/wasi-sysroot/lib/wasm32-wasi" \ + -lc "${MSFS_SDK}/WASM/wasi-sysroot/lib/wasm32-wasi/libclang_rt.builtins-wasm32.a" \ + --export __wasm_call_ctors \ + ${WASMLD_ARGS} \ + --export-dynamic \ + --export malloc \ + --export free \ + --export __wasm_call_ctors \ + --export mallinfo \ + --export mchunkit_begin \ + --export mchunkit_next \ + --export get_pages_state \ + --export mark_decommit_pages \ + --export-table \ + --gc-sections \ + -lc++ -lc++abi \ + ${DIR}/obj/*.o \ + -o $OUTPUT diff --git a/fbw-a32nx/src/wasm/fadec_a320/src/EngineControl.h b/fbw-a32nx/src/wasm/fadec_a320/src/EngineControl.h new file mode 100644 index 00000000000..a50ede74ef1 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a320/src/EngineControl.h @@ -0,0 +1,1358 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include "RegPolynomials.h" +#include "SimVars.h" +#include "Tables.h" +#include "ThrustLimits.h" +#include "common.h" + +#include + +#define FILENAME_FADEC_CONF_DIRECTORY "\\work\\AircraftStates\\" +#define FILENAME_FADEC_CONF_FILE_EXTENSION ".ini" +#define CONFIGURATION_SECTION_FUEL "FUEL" + +#define CONFIGURATION_SECTION_FUEL_CENTER_QUANTITY "FUEL_CENTER_QUANTITY" +#define CONFIGURATION_SECTION_FUEL_LEFT_QUANTITY "FUEL_LEFT_QUANTITY" +#define CONFIGURATION_SECTION_FUEL_RIGHT_QUANTITY "FUEL_RIGHT_QUANTITY" +#define CONFIGURATION_SECTION_FUEL_LEFT_AUX_QUANTITY "FUEL_LEFT_AUX_QUANTITY" +#define CONFIGURATION_SECTION_FUEL_RIGHT_AUX_QUANTITY "FUEL_RIGHT_AUX_QUANTITY" + +/* Values in gallons */ +struct Configuration { + double fuelCenter = 0; + double fuelLeft = 411.34; + double fuelRight = fuelLeft; + double fuelLeftAux = 0; + double fuelRightAux = fuelLeftAux; +}; + +class EngineControl { + private: + SimVars* simVars; + EngineRatios* ratios; + Polynomial* poly; + Timer timerLeft; + Timer timerRight; + Timer timerFuel; + + std::string confFilename = FILENAME_FADEC_CONF_DIRECTORY; + + bool simPaused; + double animationDeltaTime; + double timer; + double ambientTemp; + double ambientPressure; + double simOnGround; + double devState; + double isReady; + + int engine; + int egtImbalance; + int ffImbalance; + int n2Imbalance; + double engineState; + double engineStarter; + double engineIgniter; + + double packs; + double nai; + double wai; + + double simCN1; + double simN1; + double simN2; + double thrust; + double simN2LeftPre; + double simN2RightPre; + double deltaN2; + double thermalEnergy1; + double thermalEnergy2; + double oilTemperature; + double oilTemperatureLeftPre; + double oilTemperatureRightPre; + double oilTemperatureMax; + double idleN1; + double idleN2; + double idleFF; + double idleEGT; + double idleOil; + double mach; + double pressAltitude; + double correctedEGT; + double correctedFuelFlow; + double cFbwFF; + double imbalance; + int engineImbalanced; + double paramImbalance; + double prevEngineMasterPos[2] = {0, 0}; + bool prevEngineStarterState[2] = {false, false}; + + const double LBS_TO_KGS = 0.4535934; + const double KGS_TO_LBS = 1 / 0.4535934; + const double FUEL_THRESHOLD = 661; // lbs/sec + + bool isFlexActive = false; + double prevThrustLimitType = 0; + double prevFlexTemperature = 0; + + const double waitTime = 10; + const double transitionTime = 30; + + bool isTransitionActive = false; + double transitionFactor = 0; + double transitionStartTime = 0; + + /// + /// Generate Idle/ Initial Engine Parameters (non-imbalanced) + /// + void generateIdleParameters(double pressAltitude, double mach, double ambientTemp, double ambientPressure) { + double idleCN1; + double idleCFF; + + idleCN1 = iCN1(pressAltitude, mach, ambientTemp); + idleN1 = idleCN1 * sqrt(ratios->theta2(0, ambientTemp)); + idleN2 = iCN2(pressAltitude, mach) * sqrt(ratios->theta(ambientTemp)); + idleCFF = poly->correctedFuelFlow(idleCN1, 0, pressAltitude); // lbs/hr + idleFF = idleCFF * LBS_TO_KGS * ratios->delta2(0, ambientPressure) * sqrt(ratios->theta2(0, ambientTemp)); // Kg/hr + idleEGT = poly->correctedEGT(idleCN1, idleCFF, 0, pressAltitude) * ratios->theta2(0, ambientTemp); + + simVars->setEngineIdleN1(idleN1); + simVars->setEngineIdleN2(idleN2); + simVars->setEngineIdleFF(idleFF); + simVars->setEngineIdleEGT(idleEGT); + } + + double initOil(int minOil, int maxOil) { + double idleOil = (rand() % (maxOil - minOil + 1) + minOil) / 10; + return idleOil; + } + + /// + /// Engine imbalance Coded Digital Word: + /// 0 - Engine, 00 - EGT, 00 - FuelFlow, 00 - N2, 00 - Oil Qty, 00 - Oil PSI, 00 - Oil PSI Rnd, 00 - Oil Max Temp + /// Generates a random engine imbalance. Next steps: make realistic imbalance due to wear + /// + void generateEngineImbalance(int initial) { + int oilQtyImbalance; + int oilPressureImbalance; + int oilPressureIdle; + int oilTemperatureMax; + std::string imbalanceCode; + + if (initial == 1) { + // Decide Engine with imbalance + if ((rand() % 100) + 1 < 50) { + engine = 1; + } else { + engine = 2; + } + // Obtain EGT imbalance (Max 20 degree C) + egtImbalance = (rand() % 20) + 1; + + // Obtain FF imbalance (Max 36 Kg/h) + ffImbalance = (rand() % 36) + 1; + + // Obtain N2 imbalance (Max 0.3%) + n2Imbalance = (rand() % 30) + 1; + + // Obtain Oil Qty imbalance (Max 2.0 qt) + oilQtyImbalance = (rand() % 20) + 1; + + // Obtain Oil Pressure imbalance (Max 3.0 PSI) + oilPressureImbalance = (rand() % 30) + 1; + + // Obtain Oil Pressure Random Idle (-6 to +6 PSI) + oilPressureIdle = (rand() % 12) + 1; + + // Obtain Oil Temperature (85 to 95 Celsius) + oilTemperatureMax = (rand() % 10) + 86; + + // Zero Padding and Merging + imbalanceCode = to_string_with_zero_padding(engine, 2) + to_string_with_zero_padding(egtImbalance, 2) + + to_string_with_zero_padding(ffImbalance, 2) + to_string_with_zero_padding(n2Imbalance, 2) + + to_string_with_zero_padding(oilQtyImbalance, 2) + to_string_with_zero_padding(oilPressureImbalance, 2) + + to_string_with_zero_padding(oilPressureIdle, 2) + to_string_with_zero_padding(oilTemperatureMax, 2); + + simVars->setEngineImbalance(stod(imbalanceCode)); + } + } + + /// + /// Engine State Machine + /// 0 - Engine OFF, 1 - Engine ON, 2 - Engine Starting, 3 - Engine Re-starting & 4 - Engine Shutting + /// returns EngineState + /// + void engineStateMachine(int engine, + double engineIgniter, + double engineStarter, + bool engineStarterTurnedOff, + bool engineMasterTurnedOn, + bool engineMasterTurnedOff, + double simN2, + double idleN2, + double pressAltitude, + double ambientTemp, + double deltaTimeDiff) { + int resetTimer = 0; + double egtFbw = 0; + + switch (engine) { + case 1: + engineState = simVars->getEngine1State(); + egtFbw = simVars->getEngine1EGT(); + break; + case 2: + engineState = simVars->getEngine2State(); + egtFbw = simVars->getEngine2EGT(); + break; + } + // Present State PAUSED + if (deltaTimeDiff == 0 && engineState < 10) { + engineState = engineState + 10; + simPaused = true; + } else if (deltaTimeDiff == 0 && engineState >= 10) { + simPaused = true; + } else { + simPaused = false; + + // Present State OFF + if (engineState == 0 || engineState == 10) { + if (engineIgniter == 1 && engineStarter == 1 && simN2 > 20) { + engineState = 1; + } else if (engineIgniter == 2 && engineMasterTurnedOn) { + engineState = 2; + } else { + engineState = 0; + } + } + // Present State ON + else if (engineState == 1 || engineState == 11) { + if (engineStarter == 1) { + engineState = 1; + } else { + engineState = 4; + } + } + // Present State Starting. + else if (engineState == 2 || engineState == 12) { + if (engineStarter == 1 && simN2 >= (idleN2 - 0.1)) { + engineState = 1; + resetTimer = 1; + } else if (engineStarterTurnedOff || engineMasterTurnedOff) { + engineState = 4; + resetTimer = 1; + } else { + engineState = 2; + } + } + // Present State Re-Starting. + else if (engineState == 3 || engineState == 13) { + if (engineStarter == 1 && simN2 >= (idleN2 - 0.1)) { + engineState = 1; + resetTimer = 1; + } else if (engineStarterTurnedOff || engineMasterTurnedOff) { + engineState = 4; + resetTimer = 1; + } else { + engineState = 3; + } + } + // Present State Shutting + else if (engineState == 4 || engineState == 14) { + if (engineIgniter == 2 && engineMasterTurnedOn) { + engineState = 3; + resetTimer = 1; + } else if (engineStarter == 0 && simN2 < 0.05 && egtFbw <= ambientTemp) { + engineState = 0; + resetTimer = 1; + } else if (engineStarter == 1 && simN2 > 50) { + engineState = 3; + resetTimer = 1; + } else { + engineState = 4; + } + } + } + + switch (engine) { + case 1: + simVars->setEngine1State(engineState); + if (resetTimer == 1) { + simVars->setEngine1Timer(0); + } + break; + case 2: + simVars->setEngine2State(engineState); + if (resetTimer == 1) { + simVars->setEngine2Timer(0); + } + break; + } + } + + /// + /// Engine Start Procedure + /// TIT + void engineStartProcedure(int engine, + double engineState, + double imbalance, + double deltaTime, + double timer, + double simN2, + double pressAltitude, + double ambientTemp) { + double preN2Fbw; + double newN2Fbw; + double preEgtFbw; + double startEgtFbw; + double shutdownEgtFbw; + + n2Imbalance = 0; + ffImbalance = 0; + egtImbalance = 0; + idleN2 = simVars->getEngineIdleN2(); + idleN1 = simVars->getEngineIdleN1(); + idleFF = simVars->getEngineIdleFF(); + idleEGT = simVars->getEngineIdleEGT(); + + // Engine imbalance + engineImbalanced = imbalanceExtractor(imbalance, 1); + + // Checking engine imbalance + if (engineImbalanced == engine) { + ffImbalance = imbalanceExtractor(imbalance, 3); + egtImbalance = imbalanceExtractor(imbalance, 2); + n2Imbalance = imbalanceExtractor(imbalance, 4) / 100; + } + + if (engine == 1) { + // Delay between Engine Master ON and Start Valve Open + + if (simOnGround == 1) { + simVars->setFuelUsedLeft(0); + } + + preN2Fbw = simVars->getEngine1N2(); + preEgtFbw = simVars->getEngine1EGT(); + newN2Fbw = poly->startN2(simN2, preN2Fbw, idleN2 - n2Imbalance); + startEgtFbw = poly->startEGT(newN2Fbw, idleN2 - n2Imbalance, ambientTemp, idleEGT - egtImbalance); + shutdownEgtFbw = poly->shutdownEGT(preEgtFbw, ambientTemp, deltaTime); + + simVars->setEngine1N2(newN2Fbw); + simVars->setEngine1N1(poly->startN1(newN2Fbw, idleN2 - n2Imbalance, idleN1)); + simVars->setEngine1FF(poly->startFF(newN2Fbw, idleN2 - n2Imbalance, idleFF - ffImbalance)); + + if (engineState == 3) { + if (abs(startEgtFbw - preEgtFbw) <= 1.5) { + simVars->setEngine1EGT(startEgtFbw); + simVars->setEngine1State(2); + } else if (startEgtFbw > preEgtFbw) { + simVars->setEngine1EGT(preEgtFbw + (0.75 * deltaTime * (idleN2 - newN2Fbw))); + } else { + simVars->setEngine1EGT(shutdownEgtFbw); + } + } else { + simVars->setEngine1EGT(startEgtFbw); + } + + oilTemperature = poly->startOilTemp(newN2Fbw, idleN2, ambientTemp); + oilTemperatureLeftPre = oilTemperature; + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempLeft, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperature); + + } else { + if (simOnGround == 1) { + simVars->setFuelUsedRight(0); + } + + preN2Fbw = simVars->getEngine2N2(); + preEgtFbw = simVars->getEngine2EGT(); + newN2Fbw = poly->startN2(simN2, preN2Fbw, idleN2 - n2Imbalance); + startEgtFbw = poly->startEGT(newN2Fbw, idleN2 - n2Imbalance, ambientTemp, idleEGT - egtImbalance); + shutdownEgtFbw = poly->shutdownEGT(preEgtFbw, ambientTemp, deltaTime); + + simVars->setEngine2N2(newN2Fbw); + simVars->setEngine2N1(poly->startN1(newN2Fbw, idleN2 - n2Imbalance, idleN1)); + simVars->setEngine2FF(poly->startFF(newN2Fbw, idleN2 - n2Imbalance, idleFF - ffImbalance)); + + if (engineState == 3) { + if (abs(startEgtFbw - preEgtFbw) <= 1.5) { + simVars->setEngine2EGT(startEgtFbw); + simVars->setEngine2State(2); + } else if (startEgtFbw > preEgtFbw) { + simVars->setEngine2EGT(preEgtFbw + (0.75 * deltaTime * (idleN2 - newN2Fbw))); + } else { + simVars->setEngine2EGT(shutdownEgtFbw); + } + } else { + simVars->setEngine2EGT(startEgtFbw); + } + + oilTemperature = poly->startOilTemp(newN2Fbw, idleN2, ambientTemp); + oilTemperatureRightPre = oilTemperature; + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempRight, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperature); + } + } + + /// + /// Engine Shutdown Procedure - TEMPORAL SOLUTION + /// + void engineShutdownProcedure(int engine, double ambientTemp, double simN1, double deltaTime, double timer) { + double preN1Fbw; + double preN2Fbw; + double preEgtFbw; + double newN1Fbw; + double newN2Fbw; + double newEgtFbw; + + if (engine == 1) { + if (timer < 1.8) { + simVars->setEngine1Timer(timer + deltaTime); + } else { + preN1Fbw = simVars->getEngine1N1(); + preN2Fbw = simVars->getEngine1N2(); + preEgtFbw = simVars->getEngine1EGT(); + newN1Fbw = poly->shutdownN1(preN1Fbw, deltaTime); + if (simN1 < 5 && simN1 > newN1Fbw) { // Takes care of windmilling + newN1Fbw = simN1; + } + newN2Fbw = poly->shutdownN2(preN2Fbw, deltaTime); + newEgtFbw = poly->shutdownEGT(preEgtFbw, ambientTemp, deltaTime); + simVars->setEngine1N1(newN1Fbw); + simVars->setEngine1N2(newN2Fbw); + simVars->setEngine1EGT(newEgtFbw); + } + + } else { + if (timer < 1.8) { + simVars->setEngine2Timer(timer + deltaTime); + } else { + preN1Fbw = simVars->getEngine2N1(); + preN2Fbw = simVars->getEngine2N2(); + preEgtFbw = simVars->getEngine2EGT(); + newN1Fbw = poly->shutdownN1(preN1Fbw, deltaTime); + if (simN1 < 5 && simN1 > newN1Fbw) { // Takes care of windmilling + newN1Fbw = simN1; + } + newN2Fbw = poly->shutdownN2(preN2Fbw, deltaTime); + newEgtFbw = poly->shutdownEGT(preEgtFbw, ambientTemp, deltaTime); + simVars->setEngine2N1(newN1Fbw); + simVars->setEngine2N2(newN2Fbw); + simVars->setEngine2EGT(newEgtFbw); + } + } + } + /// + /// FBW Engine RPM (N1 and N2) + /// Updates Engine N1 and N2 with our own algorithm for start-up and shutdown + /// + void updatePrimaryParameters(int engine, double imbalance, double simN1, double simN2) { + // Engine imbalance + engineImbalanced = imbalanceExtractor(imbalance, 1); + paramImbalance = imbalanceExtractor(imbalance, 4) / 100; + + // Checking engine imbalance + if (engineImbalanced != engine) { + paramImbalance = 0; + } + + if (engine == 1) { + simVars->setEngine1N1(simN1); + simVars->setEngine1N2((std::max)(0.0, simN2 - paramImbalance)); + } else { + simVars->setEngine2N1(simN1); + simVars->setEngine2N2((std::max)(0.0, simN2 - paramImbalance)); + } + } + + /// + /// FBW Exhaust Gas Temperature (in degree Celsius) + /// Updates EGT with realistic values visualized in the ECAM + /// + void updateEGT(int engine, + double imbalance, + double deltaTime, + double simOnGround, + double engineState, + double simCN1, + double cFbwFF, + double mach, + double pressAltitude, + double ambientTemp) { + double egtFbwPreviousEng1; + double egtFbwActualEng1; + double egtFbwPreviousEng2; + double egtFbwActualEng2; + + // Engine imbalance timer + engineImbalanced = imbalanceExtractor(imbalance, 1); + paramImbalance = imbalanceExtractor(imbalance, 2); + + correctedEGT = poly->correctedEGT(simCN1, cFbwFF, mach, pressAltitude); + + // Checking engine imbalance + if (engineImbalanced != engine) { + paramImbalance = 0; + } + + if (engine == 1) { + if (simOnGround == 1 && engineState == 0) { + simVars->setEngine1EGT(ambientTemp); + } else { + egtFbwPreviousEng1 = simVars->getEngine1EGT(); + egtFbwActualEng1 = (correctedEGT * ratios->theta2(mach, ambientTemp)) - paramImbalance; + egtFbwActualEng1 = egtFbwActualEng1 + (egtFbwPreviousEng1 - egtFbwActualEng1) * expFBW(-0.1 * deltaTime); + simVars->setEngine1EGT(egtFbwActualEng1); + } + } else { + if (simOnGround == 1 && engineState == 0) { + simVars->setEngine2EGT(ambientTemp); + } else { + egtFbwPreviousEng2 = simVars->getEngine2EGT(); + egtFbwActualEng2 = (correctedEGT * ratios->theta2(mach, ambientTemp)) - paramImbalance; + egtFbwActualEng2 = egtFbwActualEng2 + (egtFbwPreviousEng2 - egtFbwActualEng2) * expFBW(-0.1 * deltaTime); + simVars->setEngine2EGT(egtFbwActualEng2); + } + } + } + + /// + /// FBW Fuel FLow (in Kg/h) + /// Updates Fuel Flow with realistic values + /// + double + updateFF(int engine, double imbalance, double simCN1, double mach, double pressAltitude, double ambientTemp, double ambientPressure) { + double outFlow = 0; + + // Engine imbalance + engineImbalanced = imbalanceExtractor(imbalance, 1); + paramImbalance = imbalanceExtractor(imbalance, 3); + + correctedFuelFlow = poly->correctedFuelFlow(simCN1, mach, pressAltitude); // in lbs/hr. + + // Checking engine imbalance + if (engineImbalanced != engine || correctedFuelFlow < 1) { + paramImbalance = 0; + } + + // Checking Fuel Logic and final Fuel Flow + if (correctedFuelFlow < 1) { + outFlow = 0; + } else { + outFlow = max(0, (correctedFuelFlow * LBS_TO_KGS * ratios->delta2(mach, ambientPressure) * sqrt(ratios->theta2(mach, ambientTemp))) - + paramImbalance); + } + + if (engine == 1) { + simVars->setEngine1FF(outFlow); + } else { + simVars->setEngine2FF(outFlow); + } + + return correctedFuelFlow; + } + + /// + /// FBW Oil Qty, Pressure and Temperature (in Quarts, PSI and degree Celsius) + /// Updates Oil with realistic values visualized in the SD + /// + void updateOil(int engine, double imbalance, double thrust, double simN2, double deltaN2, double deltaTime, double ambientTemp) { + double steadyTemperature; + double thermalEnergy; + double oilTemperaturePre; + double oilQtyActual; + double oilTotalActual; + double oilQtyObjective; + double oilBurn; + double oilIdleRandom; + double oilPressure; + + //-------------------------------------------- + // Engine Reading + //-------------------------------------------- + if (engine == 1) { + steadyTemperature = simVars->getEngine1EGT(); + thermalEnergy = thermalEnergy1; + oilTemperaturePre = oilTemperatureLeftPre; + oilQtyActual = simVars->getEngine1Oil(); + oilTotalActual = simVars->getEngine1OilTotal(); + } else { + steadyTemperature = simVars->getEngine2EGT(); + thermalEnergy = thermalEnergy2; + oilTemperaturePre = oilTemperatureRightPre; + oilQtyActual = simVars->getEngine2Oil(); + oilTotalActual = simVars->getEngine2OilTotal(); + } + + //-------------------------------------------- + // Oil Temperature + //-------------------------------------------- + if (simOnGround == 1 && engineState == 0 && ambientTemp > oilTemperaturePre - 10) { + oilTemperature = ambientTemp; + } else { + if (steadyTemperature > oilTemperatureMax) { + steadyTemperature = oilTemperatureMax; + } + thermalEnergy = (0.995 * thermalEnergy) + (deltaN2 / deltaTime); + oilTemperature = poly->oilTemperature(thermalEnergy, oilTemperaturePre, steadyTemperature, deltaTime); + } + + //-------------------------------------------- + // Oil Quantity + //-------------------------------------------- + // Calculating Oil Qty as a function of thrust + oilQtyObjective = oilTotalActual * (1 - poly->oilGulpPct(thrust)); + oilQtyActual = oilQtyActual - (oilTemperature - oilTemperaturePre); + + // Oil burnt taken into account for tank and total oil + oilBurn = (0.00011111 * deltaTime); + oilQtyActual = oilQtyActual - oilBurn; + oilTotalActual = oilTotalActual - oilBurn; + + //-------------------------------------------- + // Oil Pressure + //-------------------------------------------- + // Engine imbalance + engineImbalanced = imbalanceExtractor(imbalance, 1); + paramImbalance = imbalanceExtractor(imbalance, 6) / 10; + oilIdleRandom = imbalanceExtractor(imbalance, 7) - 6; + + // Checking engine imbalance + if (engineImbalanced != engine) { + paramImbalance = 0; + } + + oilPressure = poly->oilPressure(simN2) - paramImbalance + oilIdleRandom; + + //-------------------------------------------- + // Engine Writing + //-------------------------------------------- + if (engine == 1) { + thermalEnergy1 = thermalEnergy; + oilTemperatureLeftPre = oilTemperature; + simVars->setEngine1Oil(oilQtyActual); + simVars->setEngine1OilTotal(oilTotalActual); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempLeft, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperature); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilPsiLeft, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &oilPressure); + } else { + thermalEnergy2 = thermalEnergy; + oilTemperatureRightPre = oilTemperature; + simVars->setEngine2Oil(oilQtyActual); + simVars->setEngine2OilTotal(oilTotalActual); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempRight, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperature); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilPsiRight, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &oilPressure); + } + } + + /// @brief FBW Fuel Consumption and Tankering + /// Updates Fuel Consumption with realistic values + /// @param deltaTimeSeconds Frame delta time in seconds + void updateFuel(double deltaTimeSeconds) { + double m = 0; + double b = 0; + double fuelBurn1 = 0; + double fuelBurn2 = 0; + double apuBurn1 = 0; + double apuBurn2 = 0; + + double refuelRate = simVars->getRefuelRate(); + double refuelStartedByUser = simVars->getRefuelStartedByUser(); + bool uiFuelTamper = false; + double pumpStateLeft = simVars->getPumpStateLeft(); + double pumpStateRight = simVars->getPumpStateRight(); + bool xfrCenterLeftManual = simVars->getJunctionSetting(4) > 1.5; + bool xfrCenterRightManual = simVars->getJunctionSetting(5) > 1.5; + bool xfrCenterLeftAuto = simVars->getValve(11) > 0.0 && !xfrCenterLeftManual; + bool xfrCenterRightAuto = simVars->getValve(12) > 0.0 && !xfrCenterRightManual; + bool xfrValveCenterLeftOpen = simVars->getValve(9) > 0.0 && (xfrCenterLeftAuto || xfrCenterLeftManual); + bool xfrValveCenterRightOpen = simVars->getValve(10) > 0.0 && (xfrCenterRightAuto || xfrCenterRightManual); + double xfrValveOuterLeft1 = simVars->getValve(6); + double xfrValveOuterLeft2 = simVars->getValve(4); + double xfrValveOuterRight1 = simVars->getValve(7); + double xfrValveOuterRight2 = simVars->getValve(5); + double lineLeftToCenterFlow = simVars->getLineFlow(27); + double lineRightToCenterFlow = simVars->getLineFlow(28); + double lineFlowRatio = 0; + + double engine1PreFF = simVars->getEngine1PreFF(); // KG/H + double engine2PreFF = simVars->getEngine2PreFF(); // KG/H + double engine1FF = simVars->getEngine1FF(); // KG/H + double engine2FF = simVars->getEngine2FF(); // KG/H + + /// weight of one gallon of fuel in pounds + double fuelWeightGallon = simVars->getFuelWeightGallon(); + double fuelUsedLeft = simVars->getFuelUsedLeft(); // Kg + double fuelUsedRight = simVars->getFuelUsedRight(); // Kg + + double fuelLeftPre = simVars->getFuelLeftPre(); // LBS + double fuelRightPre = simVars->getFuelRightPre(); // LBS + double fuelAuxLeftPre = simVars->getFuelAuxLeftPre(); // LBS + double fuelAuxRightPre = simVars->getFuelAuxRightPre(); // LBS + double fuelCenterPre = simVars->getFuelCenterPre(); // LBS + double leftQuantity = simVars->getFuelTankQuantity(2) * fuelWeightGallon; // LBS + double rightQuantity = simVars->getFuelTankQuantity(3) * fuelWeightGallon; // LBS + double leftAuxQuantity = simVars->getFuelTankQuantity(4) * fuelWeightGallon; // LBS + double rightAuxQuantity = simVars->getFuelTankQuantity(5) * fuelWeightGallon; // LBS + double centerQuantity = simVars->getFuelTankQuantity(1) * fuelWeightGallon; // LBS + /// Left inner tank fuel quantity in pounds + double fuelLeft = 0; + /// Right inner tank fuel quantity in pounds + double fuelRight = 0; + double fuelLeftAux = 0; + double fuelRightAux = 0; + double fuelCenter = 0; + double xfrCenterToLeft = 0; + double xfrCenterToRight = 0; + double xfrAuxLeft = 0; + double xfrAuxRight = 0; + double fuelTotalActual = leftQuantity + rightQuantity + leftAuxQuantity + rightAuxQuantity + centerQuantity; // LBS + double fuelTotalPre = fuelLeftPre + fuelRightPre + fuelAuxLeftPre + fuelAuxRightPre + fuelCenterPre; // LBS + double deltaFuelRate = abs(fuelTotalActual - fuelTotalPre) / (fuelWeightGallon * deltaTimeSeconds); // LBS/ sec + + double engine1State = simVars->getEngine1State(); + double engine2State = simVars->getEngine2State(); + + int isTankClosed = 0; + double xFeedValve = simVars->getValve(3); + double leftPump1 = simVars->getPump(2); + double leftPump2 = simVars->getPump(5); + double rightPump1 = simVars->getPump(3); + double rightPump2 = simVars->getPump(6); + + double apuNpercent = simVars->getAPUrpmPercent(); + + // Check Ready & Development State for UI + isReady = simVars->getIsReady(); + devState = simVars->getDeveloperState(); + + /// Delta time for this update in hours + double deltaTime = deltaTimeSeconds / 3600; + + // Pump State Logic for Left Wing + if (pumpStateLeft == 0 && (timerLeft.elapsed() == 0 || timerLeft.elapsed() >= 1000)) { + if (fuelLeftPre - leftQuantity > 0 && leftQuantity == 0) { + timerLeft.reset(); + simVars->setPumpStateLeft(1); + } else if (fuelLeftPre == 0 && leftQuantity - fuelLeftPre > 0) { + timerLeft.reset(); + simVars->setPumpStateLeft(2); + } else { + simVars->setPumpStateLeft(0); + } + } else if (pumpStateLeft == 1 && timerLeft.elapsed() >= 2100) { + simVars->setPumpStateLeft(0); + timerLeft.reset(); + } else if (pumpStateLeft == 2 && timerLeft.elapsed() >= 2700) { + simVars->setPumpStateLeft(0); + timerLeft.reset(); + } + + // Pump State Logic for Right Wing + if (pumpStateRight == 0 && (timerRight.elapsed() == 0 || timerRight.elapsed() >= 1000)) { + if (fuelRightPre - rightQuantity > 0 && rightQuantity == 0) { + timerRight.reset(); + simVars->setPumpStateRight(1); + } else if (fuelRightPre == 0 && rightQuantity - fuelRightPre > 0) { + timerRight.reset(); + simVars->setPumpStateRight(2); + } else { + simVars->setPumpStateRight(0); + } + } else if (pumpStateRight == 1 && timerRight.elapsed() >= 2100) { + simVars->setPumpStateRight(0); + timerRight.reset(); + } else if (pumpStateRight == 2 && timerRight.elapsed() >= 2700) { + simVars->setPumpStateRight(0); + timerRight.reset(); + } + + // Checking for in-game UI Fuel tampering + if ((isReady == 1 && refuelStartedByUser == 0 && deltaFuelRate > FUEL_THRESHOLD) || + (isReady == 1 && refuelStartedByUser == 1 && deltaFuelRate > FUEL_THRESHOLD && refuelRate < 2)) { + uiFuelTamper = true; + } + + if (simPaused || uiFuelTamper && devState == 0) { // Detects whether the Sim is paused or the Fuel UI is being tampered with + simVars->setFuelLeftPre(fuelLeftPre); // in LBS + simVars->setFuelRightPre(fuelRightPre); // in LBS + simVars->setFuelAuxLeftPre(fuelAuxLeftPre); // in LBS + simVars->setFuelAuxRightPre(fuelAuxRightPre); // in LBS + simVars->setFuelCenterPre(fuelCenterPre); // in LBS + + fuelLeft = (fuelLeftPre / fuelWeightGallon); // USG + fuelRight = (fuelRightPre / fuelWeightGallon); // USG + fuelCenter = (fuelCenterPre / fuelWeightGallon); // USG + fuelLeftAux = (fuelAuxLeftPre / fuelWeightGallon); // USG + fuelRightAux = (fuelAuxRightPre / fuelWeightGallon); // USG + + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelCenterMain, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &fuelCenter); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelLeftMain, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &fuelLeft); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelRightMain, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &fuelRight); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelLeftAux, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &fuelLeftAux); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelRightAux, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &fuelRightAux); + } else if (!uiFuelTamper && refuelStartedByUser == 1) { // Detects refueling from the EFB + simVars->setFuelLeftPre(leftQuantity); // in LBS + simVars->setFuelRightPre(rightQuantity); // in LBS + simVars->setFuelAuxLeftPre(leftAuxQuantity); // in LBS + simVars->setFuelAuxRightPre(rightAuxQuantity); // in LBS + simVars->setFuelCenterPre(centerQuantity); // in LBS + } else { + if (uiFuelTamper == 1) { + fuelLeftPre = leftQuantity; // LBS + fuelRightPre = rightQuantity; // LBS + fuelAuxLeftPre = leftAuxQuantity; // LBS + fuelAuxRightPre = rightAuxQuantity; // LBS + fuelCenterPre = centerQuantity; // LBS + } + //----------------------------------------------------------- + // Cross-feed Logic + // isTankClosed = 0, x-feed valve closed + // isTankClosed = 1, left tank does not supply fuel + // isTankClosed = 2, right tank does not supply fuel + // isTankClosed = 3, left & right tanks do not supply fuel + // isTankClosed = 4, both tanks supply fuel + if (xFeedValve > 0.0) { + if (leftPump1 == 0 && leftPump2 == 0 && rightPump1 == 0 && rightPump2 == 0) + isTankClosed = 3; + else if (leftPump1 == 0 && leftPump2 == 0) + isTankClosed = 1; + else if (rightPump1 == 0 && rightPump2 == 0) + isTankClosed = 2; + else + isTankClosed = 4; + } + + //-------------------------------------------- + // Left Engine and Wing routine + if (fuelLeftPre > 0) { + // Cycle Fuel Burn for Engine 1 + if (devState != 2) { + m = (engine1FF - engine1PreFF) / deltaTime; + b = engine1PreFF; + fuelBurn1 = (m * pow(deltaTime, 2) / 2) + (b * deltaTime); // KG + } + + // Fuel transfer routine for Left Wing + if (xfrValveOuterLeft1 > 0.0 || xfrValveOuterLeft2 > 0.0) + xfrAuxLeft = fuelAuxLeftPre - leftAuxQuantity; + } else { + fuelBurn1 = 0; + fuelLeftPre = 0; + } + + //-------------------------------------------- + // Right Engine and Wing routine + if (fuelRightPre > 0) { + // Cycle Fuel Burn for Engine 2 + if (devState != 2) { + m = (engine2FF - engine2PreFF) / deltaTime; + b = engine2PreFF; + fuelBurn2 = (m * pow(deltaTime, 2) / 2) + (b * deltaTime); // KG + } + // Fuel transfer routine for Right Wing + if (xfrValveOuterRight1 > 0.0 || xfrValveOuterRight2 > 0.0) + xfrAuxRight = fuelAuxRightPre - rightAuxQuantity; + } else { + fuelBurn2 = 0; + fuelRightPre = 0; + } + + /// apu fuel consumption for this frame in pounds + double apuFuelConsumption = simVars->getLineFlow(18) * fuelWeightGallon * deltaTime; + + // check if APU is actually running instead of just the ASU which doesnt consume fuel + if (apuNpercent <= 0.0) { + apuFuelConsumption = 0.0; + } + + apuBurn1 = apuFuelConsumption; + apuBurn2 = 0; + + //-------------------------------------------- + // Fuel used accumulators + fuelUsedLeft += fuelBurn1; + fuelUsedRight += fuelBurn2; + + //-------------------------------------------- + // Cross-feed fuel burn routine + // If fuel pumps for a given tank are closed, + // all fuel will be burnt on the other tank + switch (isTankClosed) { + case 1: + fuelBurn2 = fuelBurn1 + fuelBurn2; + fuelBurn1 = 0; + apuBurn1 = 0; + apuBurn2 = apuFuelConsumption; + break; + case 2: + fuelBurn1 = fuelBurn1 + fuelBurn2; + fuelBurn2 = 0; + break; + case 3: + fuelBurn1 = 0; + fuelBurn2 = 0; + apuBurn1 = apuFuelConsumption * 0.5; + apuBurn2 = apuFuelConsumption * 0.5; + break; + case 4: + apuBurn1 = apuFuelConsumption * 0.5; + apuBurn2 = apuFuelConsumption * 0.5; + break; + default: + break; + } + + //-------------------------------------------- + // Center Tank transfer routine + if (xfrValveCenterLeftOpen && xfrValveCenterRightOpen) { + if (lineLeftToCenterFlow < 0.1 && lineRightToCenterFlow < 0.1) + lineFlowRatio = 0.5; + else + lineFlowRatio = lineLeftToCenterFlow / (lineLeftToCenterFlow + lineRightToCenterFlow); + + xfrCenterToLeft = (fuelCenterPre - centerQuantity) * lineFlowRatio; + xfrCenterToRight = (fuelCenterPre - centerQuantity) * (1 - lineFlowRatio); + } else if (xfrValveCenterLeftOpen) + xfrCenterToLeft = fuelCenterPre - centerQuantity; + else if (xfrValveCenterRightOpen) + xfrCenterToRight = fuelCenterPre - centerQuantity; + + //-------------------------------------------- + // Final Fuel levels for left and right inner tanks + fuelLeft = (fuelLeftPre - (fuelBurn1 * KGS_TO_LBS)) + xfrAuxLeft + xfrCenterToLeft - apuBurn1; // LBS + fuelRight = (fuelRightPre - (fuelBurn2 * KGS_TO_LBS)) + xfrAuxRight + xfrCenterToRight - apuBurn2; // LBS + + //-------------------------------------------- + // Setting new pre-cycle conditions + simVars->setEngine1PreFF(engine1FF); + simVars->setEngine2PreFF(engine2FF); + simVars->setFuelUsedLeft(fuelUsedLeft); // in KG + simVars->setFuelUsedRight(fuelUsedRight); // in KG + simVars->setFuelAuxLeftPre(leftAuxQuantity); // in LBS + simVars->setFuelAuxRightPre(rightAuxQuantity); // in LBS + simVars->setFuelCenterPre(centerQuantity); // in LBS + + simVars->setFuelLeftPre(fuelLeft); // in LBS + simVars->setFuelRightPre(fuelRight); // in LBS + + fuelLeft = (fuelLeft / fuelWeightGallon); // USG + fuelRight = (fuelRight / fuelWeightGallon); // USG + + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelLeftMain, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &fuelLeft); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelRightMain, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &fuelRight); + } + + //-------------------------------------------- + // Will save the current fuel quantities if on + // the ground AND engines being shutdown + if (timerFuel.elapsed() >= 1000 && simVars->getSimOnGround() && + (engine1State == 0 || engine1State == 10 || engine1State == 4 || engine1State == 14 || engine2State == 0 || engine2State == 10 || + engine2State == 4 || engine2State == 14)) { + Configuration configuration; + + configuration.fuelLeft = simVars->getFuelLeftPre() / simVars->getFuelWeightGallon(); + configuration.fuelRight = simVars->getFuelRightPre() / simVars->getFuelWeightGallon(); + configuration.fuelCenter = simVars->getFuelCenterPre() / simVars->getFuelWeightGallon(); + configuration.fuelLeftAux = simVars->getFuelAuxLeftPre() / simVars->getFuelWeightGallon(); + configuration.fuelRightAux = simVars->getFuelAuxRightPre() / simVars->getFuelWeightGallon(); + + saveFuelInConfiguration(configuration); + timerFuel.reset(); + } + } + + void updateThrustLimits(double simulationTime, + double altitude, + double ambientTemp, + double ambientPressure, + double mach, + double simN1highest, + double packs, + double nai, + double wai) { + double idle = simVars->getEngineIdleN1(); + double flexTemp = simVars->getFlexTemp(); + double thrustLimitType = simVars->getThrustLimitType(); + double to = 0; + double ga = 0; + double toga = 0; + double clb = 0; + double mct = 0; + double flex_to = 0; + double flex_ga = 0; + double flex = 0; + + // Write all N1 Limits + to = limitN1(0, (std::min)(16600.0, pressAltitude), ambientTemp, ambientPressure, 0, packs, nai, wai); + ga = limitN1(1, (std::min)(16600.0, pressAltitude), ambientTemp, ambientPressure, 0, packs, nai, wai); + if (flexTemp > 0) { + flex_to = limitN1(0, (std::min)(16600.0, pressAltitude), ambientTemp, ambientPressure, flexTemp, packs, nai, wai); + flex_ga = limitN1(1, (std::min)(16600.0, pressAltitude), ambientTemp, ambientPressure, flexTemp, packs, nai, wai); + } + clb = limitN1(2, pressAltitude, ambientTemp, ambientPressure, 0, packs, nai, wai); + mct = limitN1(3, pressAltitude, ambientTemp, ambientPressure, 0, packs, nai, wai); + + // transition between TO and GA limit ----------------------------------------------------------------------------- + double machFactorLow = (std::max)(0.0, (std::min)(1.0, (mach - 0.04) / 0.04)); + toga = to + (ga - to) * machFactorLow; + flex = flex_to + (flex_ga - flex_to) * machFactorLow; + + // adaption of CLB due to FLX limit if necessary ------------------------------------------------------------------ + + if ((prevThrustLimitType != 3 && thrustLimitType == 3) || (prevFlexTemperature == 0 && flexTemp > 0)) { + isFlexActive = true; + } else if ((flexTemp == 0) || (thrustLimitType == 4)) { + isFlexActive = false; + } + + if (isFlexActive && !isTransitionActive && thrustLimitType == 1) { + isTransitionActive = true; + transitionStartTime = simulationTime; + transitionFactor = 0.2; + // transitionFactor = (clb - flex) / transitionTime; + } else if (!isFlexActive) { + isTransitionActive = false; + transitionStartTime = 0; + transitionFactor = 0; + } + + double deltaThrust = 0; + + if (isTransitionActive) { + double timeDifference = (std::max)(0.0, (simulationTime - transitionStartTime) - waitTime); + + if (timeDifference > 0 && clb > flex) { + deltaThrust = (std::min)(clb - flex, timeDifference * transitionFactor); + } + + if (flex + deltaThrust >= clb) { + isFlexActive = false; + isTransitionActive = false; + } + } + + if (isFlexActive) { + clb = (std::min)(clb, flex) + deltaThrust; + } + + prevThrustLimitType = thrustLimitType; + prevFlexTemperature = flexTemp; + + // thrust transitions for MCT and TOGA ---------------------------------------------------------------------------- + + // get factors + double machFactor = (std::max)(0.0, (std::min)(1.0, ((mach - 0.37) / 0.05))); + double altitudeFactorLow = (std::max)(0.0, (std::min)(1.0, ((altitude - 16600) / 500))); + double altitudeFactorHigh = (std::max)(0.0, (std::min)(1.0, ((altitude - 25000) / 500))); + + // adapt thrust limits + if (altitude >= 25000) { + mct = (std::max)(clb, mct + (clb - mct) * altitudeFactorHigh); + toga = mct; + } else { + if (mct > toga) { + mct = toga + (mct - toga) * (std::min)(1.0, altitudeFactorLow + machFactor); + toga = mct; + } else { + toga = toga + (mct - toga) * (std::min)(1.0, altitudeFactorLow + machFactor); + } + } + + // write limits --------------------------------------------------------------------------------------------------- + simVars->setThrustLimitIdle(idle); + simVars->setThrustLimitToga(toga); + simVars->setThrustLimitFlex(flex); + simVars->setThrustLimitClimb(clb); + simVars->setThrustLimitMct(mct); + } + + public: + /// + /// Initialize the FADEC and Fuel model + /// + void initialize(const char* acftRegistration) { + srand((int)time(0)); + + std::cout << "FADEC: Initializing EngineControl" << std::endl; + + simVars = new SimVars(); + double engTime = 0; + ambientTemp = simVars->getAmbientTemperature(); + simN2LeftPre = simVars->getN2(1); + simN2RightPre = simVars->getN2(2); + + confFilename += acftRegistration; + confFilename += FILENAME_FADEC_CONF_FILE_EXTENSION; + + Configuration configuration = getConfigurationFromFile(); + + // One-off Engine imbalance + generateEngineImbalance(1); + imbalance = simVars->getEngineImbalance(); + engineImbalanced = imbalanceExtractor(imbalance, 1); + + // Checking engine imbalance + if (engineImbalanced != engine) { + paramImbalance = 0; + } + + for (engine = 1; engine <= 2; engine++) { + // Obtain Engine Time + engTime = simVars->getEngineTime(engine) + engTime; + + // Checking engine imbalance + paramImbalance = imbalanceExtractor(imbalance, 5) / 10; + + if (engineImbalanced != engine) { + paramImbalance = 0; + } + + // Engine Idle Oil Qty + idleOil = initOil(140, 200); + + // Setting initial Oil + if (engine == 1) { + simVars->setEngine1OilTotal(idleOil - paramImbalance); + } else { + simVars->setEngine2OilTotal(idleOil - paramImbalance); + } + } + + // Setting initial Oil Temperature + thermalEnergy1 = 0; + thermalEnergy2 = 0; + oilTemperatureMax = imbalanceExtractor(imbalance, 8); + simOnGround = simVars->getSimOnGround(); + double engine1Combustion = simVars->getEngineCombustion(1); + double engine2Combustion = simVars->getEngineCombustion(2); + + if (simOnGround == 1 && engine1Combustion == 1 && engine2Combustion == 1) { + oilTemperatureLeftPre = 75; + oilTemperatureRightPre = 75; + } else if (simOnGround == 0 && engine1Combustion == 1 && engine2Combustion == 1) { + oilTemperatureLeftPre = 85; + oilTemperatureRightPre = 85; + + } else { + oilTemperatureLeftPre = ambientTemp; + oilTemperatureRightPre = ambientTemp; + } + + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempLeft, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperatureLeftPre); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempRight, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperatureRightPre); + + // Initialize Engine State + simVars->setEngine1State(10); + simVars->setEngine2State(10); + + // Resetting Engine Timers + simVars->setEngine1Timer(0); + simVars->setEngine2Timer(0); + + // Initialize Fuel Tanks + double centerQuantity = simVars->getFuelTankQuantity(1); // gal + double leftQuantity = simVars->getFuelTankQuantity(2); // gal + double rightQuantity = simVars->getFuelTankQuantity(3); // gal + double leftAuxQuantity = simVars->getFuelTankQuantity(4); // gal + double rightAuxQuantity = simVars->getFuelTankQuantity(5); // gal + + double fuelWeightGallon = simVars->getFuelWeightGallon(); // weight of gallon of jet A in lbs + + if (simVars->getStartState() == 2) { // only loads saved fuel quantity on C/D spawn + simVars->setFuelCenterPre(configuration.fuelCenter * fuelWeightGallon); // in LBS + simVars->setFuelLeftPre(configuration.fuelLeft * fuelWeightGallon); // in LBS + simVars->setFuelRightPre(configuration.fuelRight * fuelWeightGallon); // in LBS + simVars->setFuelAuxLeftPre(configuration.fuelLeftAux * fuelWeightGallon); // in LBS + simVars->setFuelAuxRightPre(configuration.fuelRightAux * fuelWeightGallon); // in LBS + } else { + simVars->setFuelCenterPre(centerQuantity * fuelWeightGallon); // in LBS + simVars->setFuelLeftPre(leftQuantity * fuelWeightGallon); // in LBS + simVars->setFuelRightPre(rightQuantity * fuelWeightGallon); // in LBS + simVars->setFuelAuxLeftPre(leftAuxQuantity * fuelWeightGallon); // in LBS + simVars->setFuelAuxRightPre(rightAuxQuantity * fuelWeightGallon); // in LBS + } + // Initialize Pump State + simVars->setPumpStateLeft(0); + simVars->setPumpStateRight(0); + + // Initialize Thrust Limits + simVars->setThrustLimitIdle(0); + simVars->setThrustLimitToga(0); + simVars->setThrustLimitFlex(0); + simVars->setThrustLimitClimb(0); + simVars->setThrustLimitMct(0); + } + + /// + /// Update cycle at deltaTime + /// + void update(double deltaTime, double simulationTime) { + double prevAnimationDeltaTime; + double simN1highest = 0; + + double engineStarterPressurized; + double engineFuelValveOpen; + + // animationDeltaTimes being used to detect a Paused situation + prevAnimationDeltaTime = animationDeltaTime; + animationDeltaTime = simVars->getAnimDeltaTime(); + + mach = simVars->getMach(); + pressAltitude = simVars->getPressureAltitude(); + ambientTemp = simVars->getAmbientTemperature(); + ambientPressure = simVars->getAmbientPressure(); + simOnGround = simVars->getSimOnGround(); + imbalance = simVars->getEngineImbalance(); + packs = 0; + nai = 0; + wai = 0; + + // Obtain Bleed Variables + if (simVars->getPacksState1() > 0.5 || simVars->getPacksState2() > 0.5) { + packs = 1; + } + if (simVars->getNAI(1) > 0.5 || simVars->getNAI(2) > 0.5) { + nai = 1; + } + wai = simVars->getWAI(); + + generateIdleParameters(pressAltitude, mach, ambientTemp, ambientPressure); + + // Timer timer; + for (engine = 1; engine <= 2; engine++) { + engineStarter = simVars->getEngineStarter(engine); + engineIgniter = simVars->getEngineIgniter(engine); + simCN1 = simVars->getCN1(engine); + simN1 = simVars->getN1(engine); + simN2 = simVars->getN2(engine); + thrust = simVars->getThrust(engine); + engineFuelValveOpen = simVars->getValve(engine); + engineStarterPressurized = simVars->getStarterPressurized(engine); + + if (engine == 1) { + deltaN2 = simN2 - simN2LeftPre; + simN2LeftPre = simN2; + timer = simVars->getEngine1Timer(); + } else { + deltaN2 = simN2 - simN2RightPre; + simN2RightPre = simN2; + timer = simVars->getEngine2Timer(); + } + + // starts engines if Engine Master is turned on and Starter is pressurized or engine is still spinning fast enough + if (!engineStarter && engineFuelValveOpen == 1 && (engineStarterPressurized || simN2 >= 20)) { + std::string command = engine == 1 ? "1 (>K:SET_STARTER1_HELD)" : "1 (>K:SET_STARTER2_HELD)"; + execute_calculator_code(command.c_str(), nullptr, nullptr, nullptr); + engineStarter = 1; + } + // shuts off engines if Engine Master is turned off or starter is depressurized while N2 is below 50 % + else if (engineStarter && (engineFuelValveOpen < 1 || (engineFuelValveOpen && !engineStarterPressurized && simN2 < 20))) { + std::string command1 = engine == 1 ? "0 (>K:SET_STARTER1_HELD)" : "0 (>K:SET_STARTER2_HELD)"; + execute_calculator_code(command1.c_str(), nullptr, nullptr, nullptr); + std::string command2 = engine == 1 ? "0 (>K:STARTER1_SET)" : "0 (>K:STARTER2_SET)"; + execute_calculator_code(command2.c_str(), nullptr, nullptr, nullptr); + engineStarter = 0; + } + + // simulates delay to start valve open through fuel valve travel time + bool engineMasterTurnedOn = prevEngineMasterPos[engine - 1] < 1 && engineFuelValveOpen >= 1; + bool engineMasterTurnedOff = prevEngineMasterPos[engine - 1] == 1 && engineFuelValveOpen < 1; + bool engineStarterTurnedOff = prevEngineStarterState[engine - 1] == 1 && engineStarter == 0; + + // Set & Check Engine Status for this Cycle + engineStateMachine(engine, engineIgniter, engineStarter, engineStarterTurnedOff, engineMasterTurnedOn, engineMasterTurnedOff, simN2, + idleN2, pressAltitude, ambientTemp, animationDeltaTime - prevAnimationDeltaTime); + + switch (int(engineState)) { + case 2: + case 3: + if (engineStarter) { + engineStartProcedure(engine, engineState, imbalance, deltaTime, timer, simN2, pressAltitude, ambientTemp); + break; + } + case 4: + engineShutdownProcedure(engine, ambientTemp, simN1, deltaTime, timer); + cFbwFF = updateFF(engine, imbalance, simCN1, mach, pressAltitude, ambientTemp, ambientPressure); + break; + default: + updatePrimaryParameters(engine, imbalance, simN1, simN2); + cFbwFF = updateFF(engine, imbalance, simCN1, mach, pressAltitude, ambientTemp, ambientPressure); + updateEGT(engine, imbalance, deltaTime, simOnGround, engineState, simCN1, cFbwFF, mach, pressAltitude, ambientTemp); + // updateOil(engine, imbalance, thrust, simN2, deltaN2, deltaTime, ambientTemp); + } + + // set highest N1 from either engine + simN1highest = (std::max)(simN1highest, simN1); + prevEngineMasterPos[engine - 1] = engineFuelValveOpen; + prevEngineStarterState[engine - 1] = engineStarter; + } + + updateFuel(deltaTime); + + updateThrustLimits(simulationTime, pressAltitude, ambientTemp, ambientPressure, mach, simN1highest, packs, nai, wai); + // timer.elapsed(); + } + + void terminate() {} + + Configuration getConfigurationFromFile() { + Configuration configuration; + mINI::INIStructure stInitStructure; + + mINI::INIFile iniFile(confFilename); + + if (!iniFile.read(stInitStructure)) { + std::cout << "EngineControl: failed to read configuration file " << confFilename << " due to error \"" << strerror(errno) + << "\" -> use default main/aux/center: " << configuration.fuelLeft << "/" << configuration.fuelLeftAux << "/" + << configuration.fuelCenter << std::endl; + } else { + configuration = loadConfiguration(stInitStructure); + } + + return configuration; + } + + Configuration loadConfiguration(const mINI::INIStructure& structure) { + return { + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_CENTER_QUANTITY, 0), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_LEFT_QUANTITY, 411.34), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_RIGHT_QUANTITY, 411.34), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_LEFT_AUX_QUANTITY, 0), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_RIGHT_AUX_QUANTITY, 0), + }; + } + + void saveFuelInConfiguration(Configuration configuration) { + mINI::INIStructure stInitStructure; + mINI::INIFile iniFile(confFilename); + + // Do not check a possible error since the file may not exist yet + iniFile.read(stInitStructure); + + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_CENTER_QUANTITY] = std::to_string(configuration.fuelCenter); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_LEFT_QUANTITY] = std::to_string(configuration.fuelLeft); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_RIGHT_QUANTITY] = std::to_string(configuration.fuelRight); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_LEFT_AUX_QUANTITY] = std::to_string(configuration.fuelLeftAux); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_RIGHT_AUX_QUANTITY] = std::to_string(configuration.fuelRightAux); + + if (!iniFile.write(stInitStructure, true)) { + std::cout << "EngineControl: failed to write engine conf " << confFilename << " due to error \"" << strerror(errno) << "\"" + << std::endl; + } + } +}; + +EngineControl EngineControlInstance; diff --git a/fbw-a32nx/src/wasm/fadec_a320/src/FadecGauge.cpp b/fbw-a32nx/src/wasm/fadec_a320/src/FadecGauge.cpp new file mode 100644 index 00000000000..f502997cecc --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a320/src/FadecGauge.cpp @@ -0,0 +1,32 @@ +#include "FadecGauge.h" + +FadecGauge FADEC_GAUGE; + +__attribute__((export_name("FadecGauge_gauge_callback"))) extern "C" bool FadecGauge_gauge_callback(FsContext ctx, + int service_id, + void* pData) { + switch (service_id) { + case PANEL_SERVICE_PRE_INSTALL: { + return true; + } break; + case PANEL_SERVICE_POST_INSTALL: { + return FADEC_GAUGE.initializeFADEC(); + } break; + case PANEL_SERVICE_PRE_DRAW: { + /* Start updating the engines only if all needed data was fetched */ + /* Due to the data fetching feature from the sim being available at this stage only (from what I have observed) */ + /* Event SIMCONNECT_RECV_ID_OPEN received after the first PANEL_SERVICE_POST_INSTALL */ + if (FADEC_GAUGE.isReady()) { + sGaugeDrawData* drawData = static_cast(pData); + return FADEC_GAUGE.onUpdate(drawData->dt); + } else { + return FADEC_GAUGE.fetchNeededData(); + } + } break; + case PANEL_SERVICE_PRE_KILL: { + FADEC_GAUGE.killFADEC(); + return true; + } break; + } + return false; +} diff --git a/fbw-a32nx/src/wasm/fadec_a320/src/FadecGauge.h b/fbw-a32nx/src/wasm/fadec_a320/src/FadecGauge.h new file mode 100644 index 00000000000..811e37092a9 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a320/src/FadecGauge.h @@ -0,0 +1,441 @@ +#pragma once + +#ifndef __INTELLISENSE__ +#define MODULE_EXPORT __attribute__((visibility("default"))) +#define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod))) +#else +#define MODULE_EXPORT +#define MODULE_WASM_MODNAME(mod) +#define __attribute__(x) +#define __restrict__ +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "EngineControl.h" +// #include "ThrustLimits.h" +#include "RegPolynomials.h" +#include "SimVars.h" +#include "Tables.h" +#include "common.h" + +#define DEFAULT_AIRCRAFT_REGISTRATION "ASX320" + +class FadecGauge { + private: + bool isConnected = false; + bool _isReady = false; + double previousSimulationTime = 0; + SimulationData simulationData = {}; + SimulationDataLivery simulationDataLivery = {}; + + /// + /// Initializes the connection to SimConnect + /// + /// True if successful, false otherwise. + bool initializeSimConnect() { + std::cout << "FADEC: Connecting to SimConnect..." << std::endl; + if (SUCCEEDED(SimConnect_Open(&hSimConnect, "FadecGauge", nullptr, 0, 0, 0))) { + std::cout << "FADEC: SimConnect connected." << std::endl; + + // SimConnect Tanker Definitions + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelLeftMain, "FUEL TANK LEFT MAIN QUANTITY", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelRightMain, "FUEL TANK RIGHT MAIN QUANTITY", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelCenterMain, "FUEL TANK CENTER QUANTITY", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelLeftAux, "FUEL TANK LEFT AUX QUANTITY", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelRightAux, "FUEL TANK RIGHT AUX QUANTITY", "Gallons"); + + // SimConnect Oil Temperature Definitions + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::OilTempLeft, "GENERAL ENG OIL TEMPERATURE:1", "Celsius"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::OilTempRight, "GENERAL ENG OIL TEMPERATURE:2", "Celsius"); + + // SimConnect Oil Pressure Definitions + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::OilPsiLeft, "GENERAL ENG OIL PRESSURE:1", "Psi"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::OilPsiRight, "GENERAL ENG OIL PRESSURE:2", "Psi"); + + // SimConnect Engine Start Definitions + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::StartCN2Left, "TURB ENG CORRECTED N2:1", "Percent"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::StartCN2Right, "TURB ENG CORRECTED N2:2", "Percent"); + // Simulation Data + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::SimulationDataTypeId, "SIMULATION TIME", "NUMBER"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::SimulationDataTypeId, "SIMULATION RATE", "NUMBER"); + + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::AcftInfo, "ATC ID", NULL, SIMCONNECT_DATATYPE_STRING32); + // SimConnect Input Event Definitions + addInputDataDefinition(hSimConnect, 0, Events::Engine1StarterToggled, "TOGGLE_STARTER1", true); + addInputDataDefinition(hSimConnect, 0, Events::Engine2StarterToggled, "TOGGLE_STARTER2", true); + + std::cout << "FADEC: SimConnect registrations complete." << std::endl; + return true; + } + + std::cout << "FADEC: SimConnect failed." << std::endl; + + return false; + } + + bool addInputDataDefinition(const HANDLE connectionHandle, + const SIMCONNECT_DATA_DEFINITION_ID groupId, + const SIMCONNECT_CLIENT_EVENT_ID eventId, + const std::string& eventName, + const bool maskEvent) { + HRESULT result = SimConnect_MapClientEventToSimEvent(connectionHandle, eventId, eventName.c_str()); + + if (result != S_OK) { + // failed -> abort + return false; + } + + result = SimConnect_AddClientEventToNotificationGroup(connectionHandle, groupId, eventId, maskEvent); + if (result != S_OK) { + // failed -> abort + return false; + } + + result = SimConnect_SetNotificationGroupPriority(connectionHandle, groupId, SIMCONNECT_GROUP_PRIORITY_HIGHEST_MASKABLE); + + if (result != S_OK) { + // failed -> abort + return false; + } + + // success + return true; + } + + bool isRegistrationFound() { return simulationDataLivery.atc_id[0] != 0; } + + public: + /// + /// Initializes the FADEC control + /// + /// True if successful, false otherwise. + bool initializeFADEC() { + if (!this->initializeSimConnect()) { + std::cout << "FADEC: Init SimConnect failed." << std::endl; + return false; + } + + isConnected = true; + simConnectRequestData(); + + return true; + } + + /// + /// Callback used to update the FADEC at each tick (dt) + /// + /// True if successful, false otherwise. + bool onUpdate(double deltaTime) { + if (isConnected == true) { + // read simulation data from simconnect + + simConnectReadData(); + // detect pause + if ((simulationData.simulationTime == previousSimulationTime) || (simulationData.simulationTime < 0.2)) { + // pause detected -> return + return true; + } + // calculate delta time + double calculatedSampleTime = max(0.002, simulationData.simulationTime - previousSimulationTime); + // store previous simulation time + previousSimulationTime = simulationData.simulationTime; + // update engines + EngineControlInstance.update(calculatedSampleTime, simulationData.simulationTime); + } + + return true; + } + + bool simConnectRequestDataAcftInfo() { + // check if we are connected + if (!isConnected) { + return false; + } + + // request data + return S_OK == + SimConnect_RequestDataOnSimObject(hSimConnect, 8, DataTypesID::AcftInfo, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_ONCE); + } + + bool simConnectRequestData() { + // check if we are connected + if (!isConnected) { + return false; + } + + // request data + HRESULT result = SimConnect_RequestDataOnSimObject(hSimConnect, 0, DataTypesID::SimulationDataTypeId, SIMCONNECT_OBJECT_ID_USER, + SIMCONNECT_PERIOD_VISUAL_FRAME); + + // check result of data request + if (result != S_OK) { + // request failed + return false; + } + + // success + return true; + } + + bool simConnectReadData() { + // check if we are connected + if (!isConnected) { + return false; + } + + // get next dispatch message(s) and process them + DWORD cbData; + SIMCONNECT_RECV* pData; + while (SUCCEEDED(SimConnect_GetNextDispatch(hSimConnect, &pData, &cbData))) { + simConnectProcessDispatchMessage(pData, &cbData); + } + + // success + return true; + } + + void simConnectProcessDispatchMessage(SIMCONNECT_RECV* pData, DWORD* cbData) { + switch (pData->dwID) { + case SIMCONNECT_RECV_ID_OPEN: + // connection established + std::cout << "FADEC: SimConnect connection established" << std::endl; + break; + + case SIMCONNECT_RECV_ID_QUIT: + // connection lost + std::cout << "FADEC: Received SimConnect connection quit message" << std::endl; + break; + case SIMCONNECT_RECV_ID_EVENT: + // get event + std::cout << "FADEC: Received SimConnect event capture message" << std::endl; + simConnectProcessEvent(static_cast(pData)); + break; + + case SIMCONNECT_RECV_ID_SIMOBJECT_DATA: + // process data + simConnectProcessSimObjectData(static_cast(pData)); + break; + + case SIMCONNECT_RECV_ID_EXCEPTION: + // exception + std::cout << "FADEC: Exception in SimConnect connection: "; + std::cout << getSimConnectExceptionString( + static_cast(static_cast(pData)->dwException)); + std::cout << std::endl; + break; + + default: + break; + } + } + + void simConnectProcessEvent(const SIMCONNECT_RECV_EVENT* event) { + switch (event->uEventID) { + // we just want to mask the events, not actually do anything with the information + case Events::Engine1StarterToggled: { + break; + } + case Events::Engine2StarterToggled: { + break; + } + default: + break; + } + } + + void simConnectProcessSimObjectData(const SIMCONNECT_RECV_SIMOBJECT_DATA* data) { + // process depending on request id + switch (data->dwRequestID) { + case 0: + // store aircraft data + simulationData = *((SimulationData*)&data->dwData); + return; + case 8: + simulationDataLivery = *((SimulationDataLivery*)&data->dwData); + if (simulationDataLivery.atc_id[0] == '\0') { + std::cout << "FADEC: Use default aircraft registration " << DEFAULT_AIRCRAFT_REGISTRATION << std::endl; + strncpy(simulationDataLivery.atc_id, DEFAULT_AIRCRAFT_REGISTRATION, sizeof(simulationDataLivery.atc_id)); + } + return; + + default: + // print unknown request id + std::cout << "FADEC: Unknown request id in SimConnect connection: "; + std::cout << data->dwRequestID << std::endl; + return; + } + } + + /// + /// Kills the FADEC and unregisters all LVars + /// + /// True if successful, false otherwise. + bool killFADEC() { + std::cout << "FADEC: Disconnecting ..." << std::endl; + EngineControlInstance.terminate(); + + isConnected = false; + unregister_all_named_vars(); + + std::cout << "FADEC: Disconnected." << std::endl; + return SUCCEEDED(SimConnect_Close(hSimConnect)); + } + + std::string getSimConnectExceptionString(SIMCONNECT_EXCEPTION exception) { + switch (exception) { + case SIMCONNECT_EXCEPTION_NONE: + return "NONE"; + + case SIMCONNECT_EXCEPTION_ERROR: + return "ERROR"; + + case SIMCONNECT_EXCEPTION_SIZE_MISMATCH: + return "SIZE_MISMATCH"; + + case SIMCONNECT_EXCEPTION_UNRECOGNIZED_ID: + return "UNRECOGNIZED_ID"; + + case SIMCONNECT_EXCEPTION_UNOPENED: + return "UNOPENED"; + + case SIMCONNECT_EXCEPTION_VERSION_MISMATCH: + return "VERSION_MISMATCH"; + + case SIMCONNECT_EXCEPTION_TOO_MANY_GROUPS: + return "TOO_MANY_GROUPS"; + + case SIMCONNECT_EXCEPTION_NAME_UNRECOGNIZED: + return "NAME_UNRECOGNIZED"; + + case SIMCONNECT_EXCEPTION_TOO_MANY_EVENT_NAMES: + return "TOO_MANY_EVENT_NAMES"; + + case SIMCONNECT_EXCEPTION_EVENT_ID_DUPLICATE: + return "EVENT_ID_DUPLICATE"; + + case SIMCONNECT_EXCEPTION_TOO_MANY_MAPS: + return "TOO_MANY_MAPS"; + + case SIMCONNECT_EXCEPTION_TOO_MANY_OBJECTS: + return "TOO_MANY_OBJECTS"; + + case SIMCONNECT_EXCEPTION_TOO_MANY_REQUESTS: + return "TOO_MANY_REQUESTS"; + + case SIMCONNECT_EXCEPTION_WEATHER_INVALID_PORT: + return "WEATHER_INVALID_PORT"; + + case SIMCONNECT_EXCEPTION_WEATHER_INVALID_METAR: + return "WEATHER_INVALID_METAR"; + + case SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_GET_OBSERVATION: + return "WEATHER_UNABLE_TO_GET_OBSERVATION"; + + case SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_CREATE_STATION: + return "WEATHER_UNABLE_TO_CREATE_STATION"; + + case SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_REMOVE_STATION: + return "WEATHER_UNABLE_TO_REMOVE_STATION"; + + case SIMCONNECT_EXCEPTION_INVALID_DATA_TYPE: + return "INVALID_DATA_TYPE"; + + case SIMCONNECT_EXCEPTION_INVALID_DATA_SIZE: + return "INVALID_DATA_SIZE"; + + case SIMCONNECT_EXCEPTION_DATA_ERROR: + return "DATA_ERROR"; + + case SIMCONNECT_EXCEPTION_INVALID_ARRAY: + return "INVALID_ARRAY"; + + case SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED: + return "CREATE_OBJECT_FAILED"; + + case SIMCONNECT_EXCEPTION_LOAD_FLIGHTPLAN_FAILED: + return "LOAD_FLIGHTPLAN_FAILED"; + + case SIMCONNECT_EXCEPTION_OPERATION_INVALID_FOR_OBJECT_TYPE: + return "OPERATION_INVALID_FOR_OBJECT_TYPE"; + + case SIMCONNECT_EXCEPTION_ILLEGAL_OPERATION: + return "ILLEGAL_OPERATION"; + + case SIMCONNECT_EXCEPTION_ALREADY_SUBSCRIBED: + return "ALREADY_SUBSCRIBED"; + + case SIMCONNECT_EXCEPTION_INVALID_ENUM: + return "INVALID_ENUM"; + + case SIMCONNECT_EXCEPTION_DEFINITION_ERROR: + return "DEFINITION_ERROR"; + + case SIMCONNECT_EXCEPTION_DUPLICATE_ID: + return "DUPLICATE_ID"; + + case SIMCONNECT_EXCEPTION_DATUM_ID: + return "DATUM_ID"; + + case SIMCONNECT_EXCEPTION_OUT_OF_BOUNDS: + return "OUT_OF_BOUNDS"; + + case SIMCONNECT_EXCEPTION_ALREADY_CREATED: + return "ALREADY_CREATED"; + + case SIMCONNECT_EXCEPTION_OBJECT_OUTSIDE_REALITY_BUBBLE: + return "OBJECT_OUTSIDE_REALITY_BUBBLE"; + + case SIMCONNECT_EXCEPTION_OBJECT_CONTAINER: + return "OBJECT_CONTAINER"; + + case SIMCONNECT_EXCEPTION_OBJECT_AI: + return "OBJECT_AI"; + + case SIMCONNECT_EXCEPTION_OBJECT_ATC: + return "OBJECT_ATC"; + + case SIMCONNECT_EXCEPTION_OBJECT_SCHEDULE: + return "OBJECT_SCHEDULE"; + + default: + return "UNKNOWN"; + } + } + + /* + * This function is to call if some data are needed before initialization of + * the engine (such as aircraft registration). This has to be done here due to + * data fetch feature being available after the first PANEL_SERVICE_PRE_DRAW + * (from what I have observed) This problem was observed when engine + * configuration file was under development Reach Julian Sebline on Discord if + * information needed + * + * Modify this function as needed + * + * @return true if all the requirements to initialize the engine are fulfilled + */ + bool fetchNeededData() { + // This two lines request aircraft registration + simConnectRequestDataAcftInfo(); + simConnectReadData(); + + _isReady = isRegistrationFound(); + + if (_isReady) { + EngineControlInstance.initialize(simulationDataLivery.atc_id); + } + + return _isReady; + } + + bool isReady() { return _isReady; } +}; diff --git a/fbw-a32nx/src/wasm/fadec_a320/src/RegPolynomials.h b/fbw-a32nx/src/wasm/fadec_a320/src/RegPolynomials.h new file mode 100644 index 00000000000..f2d17901a4a --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a320/src/RegPolynomials.h @@ -0,0 +1,268 @@ +#pragma once + +#include "common.h" + +/// +/// A collection of multi-variate regression polynomials for engine parameters +/// +/// True if successful, false otherwise. +class Polynomial { + public: + /// + /// Shutdown polynomials - N2 (%) + /// + double shutdownN2(double preN2, double deltaTime) { + double outN2 = 0; + double k = -0.08183; + + if (preN2 < 30) + k = -0.0515; + + outN2 = preN2 * expFBW(k * deltaTime); + + return outN2; + } + + /// + /// Shutdown polynomials - N1 (%) + /// + double shutdownN1(double preN1, double deltaTime) { + double outN1 = 0; + double k = -0.164; + + if (preN1 < 4) + k = -0.08; + + outN1 = preN1 * expFBW(k * deltaTime); + + return outN1; + } + + /// + /// Shutdown polynomials - EGT (degrees C) + /// + double shutdownEGT(double preEGT, double ambientTemp, double deltaTime) { + double outEGT = 0; + double threshold = ambientTemp + 140; + double k = 0; + double ts = 0; + + if (preEGT > threshold) { + k = 0.0257743; + ts = 135 + ambientTemp; + } else { + k = 0.00072756; + ts = 30 + ambientTemp; + } + + outEGT = ts + (preEGT - ts) * expFBW(-k * deltaTime); + + return outEGT; + } + + /// + /// Start-up polynomials - N2 (%) + /// + double startN2(double n2, double preN2, double idleN2) { + double outN2 = 0; + double normalN2 = 0; + + normalN2 = n2 * 68.2 / idleN2; + + double c_N2[16] = {4.03649879e+00, -9.41981960e-01, 1.98426614e-01, -2.11907840e-02, 1.00777507e-03, -1.57319166e-06, + -2.15034888e-06, 1.08288379e-07, -2.48504632e-09, 2.52307089e-11, -2.06869243e-14, 8.99045761e-16, + -9.94853959e-17, 1.85366499e-18, -1.44869928e-20, 4.31033031e-23}; + + outN2 = c_N2[0] + (c_N2[1] * normalN2) + (c_N2[2] * powFBW(normalN2, 2)) + (c_N2[3] * powFBW(normalN2, 3)) + + (c_N2[4] * powFBW(normalN2, 4)) + (c_N2[5] * powFBW(normalN2, 5)) + (c_N2[6] * powFBW(normalN2, 6)) + + (c_N2[7] * powFBW(normalN2, 7)) + (c_N2[8] * powFBW(normalN2, 8)) + (c_N2[9] * powFBW(normalN2, 9)) + + (c_N2[10] * powFBW(normalN2, 10)) + (c_N2[11] * powFBW(normalN2, 11)) + (c_N2[12] * powFBW(normalN2, 12)) + + (c_N2[13] * powFBW(normalN2, 13)) + (c_N2[14] * powFBW(normalN2, 14)) + (c_N2[15] * powFBW(normalN2, 15)); + + outN2 = outN2 * n2; + + if (outN2 < preN2) { + outN2 = preN2 + 0.002; + } + if (outN2 >= idleN2 + 0.1) { + outN2 = idleN2 + 0.05; + } + + return outN2; + } + + /// + /// Start-up polynomials - N1 (%) + /// + double startN1(double fbwN2, double idleN2, double idleN1) { + double normalN1pre = 0; + double normalN1post = 0; + double normalN2 = fbwN2 / idleN2; + double c_N1[9] = {-2.2812156e-12, -5.9830374e+01, 7.0629094e+02, -3.4580361e+03, 9.1428923e+03, + -1.4097740e+04, 1.2704110e+04, -6.2099935e+03, 1.2733071e+03}; + + normalN1pre = (-2.4698087 * powFBW(normalN2, 3)) + (0.9662026 * powFBW(normalN2, 2)) + (0.0701367 * normalN2); + + normalN1post = c_N1[0] + (c_N1[1] * normalN2) + (c_N1[2] * powFBW(normalN2, 2)) + (c_N1[3] * powFBW(normalN2, 3)) + + (c_N1[4] * powFBW(normalN2, 4)) + (c_N1[5] * powFBW(normalN2, 5)) + (c_N1[6] * powFBW(normalN2, 6)) + + (c_N1[7] * powFBW(normalN2, 7)) + (c_N1[8] * powFBW(normalN2, 8)); + + if (normalN1post >= normalN1pre) + return normalN1post * idleN1; + else + return normalN1pre * idleN1; + } + + /// + /// Start-up polynomials - Fuel Flow (Kg/hr) + /// + double startFF(double fbwN2, double idleN2, double idleFF) { + double normalFF = 0; + double outFF = 0; + double normalN2 = fbwN2 / idleN2; + + if (normalN2 <= 0.37) { + normalFF = 0; + } else { + double c_FF[9] = {3.1110282e-12, 1.0804331e+02, -1.3972629e+03, 7.4874131e+03, -2.1511983e+04, + 3.5957757e+04, -3.5093994e+04, 1.8573033e+04, -4.1220062e+03}; + + normalFF = c_FF[0] + (c_FF[1] * normalN2) + (c_FF[2] * powFBW(normalN2, 2)) + (c_FF[3] * powFBW(normalN2, 3)) + + (c_FF[4] * powFBW(normalN2, 4)) + (c_FF[5] * powFBW(normalN2, 5)) + (c_FF[6] * powFBW(normalN2, 6)) + + (c_FF[7] * powFBW(normalN2, 7)) + (c_FF[8] * powFBW(normalN2, 8)); + } + + if (normalFF < 0) { + normalFF = 0; + } + + return normalFF * idleFF; + } + + /// + /// Start-up polynomials - EGT (Celsius) + /// + double startEGT(double fbwN2, double idleN2, double ambientTemp, double idleEGT) { + double normalEGT = 0; + double outEGT = 0; + double normalN2 = fbwN2 / idleN2; + + if (normalN2 < 0.17) { + normalEGT = 0; + } else if (normalN2 <= 0.4) { + normalEGT = (0.04783 * normalN2) - 0.00813; + } else { + double c_EGT[9] = {-6.8725167e+02, 7.7548864e+03, -3.7507098e+04, 1.0147016e+05, -1.6779273e+05, + 1.7357157e+05, -1.0960924e+05, 3.8591956e+04, -5.7912600e+03}; + + normalEGT = c_EGT[0] + (c_EGT[1] * normalN2) + (c_EGT[2] * powFBW(normalN2, 2)) + (c_EGT[3] * powFBW(normalN2, 3)) + + (c_EGT[4] * powFBW(normalN2, 4)) + (c_EGT[5] * powFBW(normalN2, 5)) + (c_EGT[6] * powFBW(normalN2, 6)) + + (c_EGT[7] * powFBW(normalN2, 7)) + (c_EGT[8] * powFBW(normalN2, 8)); + } + + outEGT = (normalEGT * (idleEGT - (ambientTemp))) + (ambientTemp); + + return outEGT; + } + + /// + /// Start-up polynomials - Oil Temperature (Celsius) + /// + double startOilTemp(double fbwN2, double idleN2, double ambientTemp) { + double outOilTemp = 0; + + if (fbwN2 < 0.79 * idleN2) { + outOilTemp = ambientTemp; + } else if (fbwN2 < 0.98 * idleN2) { + outOilTemp = ambientTemp + 5; + } else { + outOilTemp = ambientTemp + 10; + } + + return outOilTemp; + } + + /// + /// Real-life modeled polynomials - Corrected EGT (Celsius) + /// + double correctedEGT(double cn1, double cff, double mach, double alt) { + double outCEGT = 0; + + double c_EGT[16] = {3.2636e+02, 0.0000e+00, 9.2893e-01, 3.9505e-02, 3.9070e+02, -4.7911e-04, 7.7679e-03, 5.8361e-05, + -2.5566e+00, 5.1227e-06, 1.0178e-07, -7.4602e-03, 1.2106e-07, -5.1639e+01, -2.7356e-03, 1.9312e-08}; + + outCEGT = c_EGT[0] + c_EGT[1] + (c_EGT[2] * cn1) + (c_EGT[3] * cff) + (c_EGT[4] * mach) + (c_EGT[5] * alt) + + (c_EGT[6] * powFBW(cn1, 2)) + (c_EGT[7] * cn1 * cff) + (c_EGT[8] * cn1 * mach) + (c_EGT[9] * cn1 * alt) + + (c_EGT[10] * powFBW(cff, 2)) + (c_EGT[11] * mach * cff) + (c_EGT[12] * cff * alt) + (c_EGT[13] * powFBW(mach, 2)) + + (c_EGT[14] * mach * alt) + (c_EGT[15] * powFBW(alt, 2)); + + return outCEGT; + } + + /// + /// Real-life modeled polynomials - Corrected Fuel Flow (lbs/ hr) + /// + double correctedFuelFlow(double cn1, double mach, double alt) { + double outCFF = 0; + + double c_Flow[21] = {-1.7630e+02, -2.1542e-01, 4.7119e+01, 6.1519e+02, 1.8047e-03, -4.4554e-01, -4.3940e+01, + 4.0459e-05, -3.2912e+01, -6.2894e-03, -1.2544e-07, 1.0938e-02, 4.0936e-01, -5.5841e-06, + -2.3829e+01, 9.3269e-04, 2.0273e-11, -2.4100e+02, 1.4171e-02, -9.5581e-07, 1.2728e-11}; + + outCFF = c_Flow[0] + c_Flow[1] + (c_Flow[2] * cn1) + (c_Flow[3] * mach) + (c_Flow[4] * alt) + (c_Flow[5] * powFBW(cn1, 2)) + + (c_Flow[6] * cn1 * mach) + (c_Flow[7] * cn1 * alt) + (c_Flow[8] * powFBW(mach, 2)) + (c_Flow[9] * mach * alt) + + (c_Flow[10] * powFBW(alt, 2)) + (c_Flow[11] * powFBW(cn1, 3)) + (c_Flow[12] * powFBW(cn1, 2) * mach) + + (c_Flow[13] * powFBW(cn1, 2) * alt) + (c_Flow[14] * cn1 * powFBW(mach, 2)) + (c_Flow[15] * cn1 * mach * alt) + + (c_Flow[16] * cn1 * powFBW(alt, 2)) + (c_Flow[17] * powFBW(mach, 3)) + (c_Flow[18] * powFBW(mach, 2) * alt) + + (c_Flow[19] * mach * powFBW(alt, 2)) + (c_Flow[20] * powFBW(alt, 3)); + + return outCFF; + } + + double oilTemperature(double energy, double preOilTemp, double maxOilTemp, double deltaTime) { + double t_steady = 0; + double k = 0.001; + double dt = 0; + double oilTemp_out; + + dt = energy * deltaTime * 0.002; + + t_steady = ((maxOilTemp * k * deltaTime) + preOilTemp) / (1 + (k * deltaTime)); + + if (t_steady - dt >= maxOilTemp) { + oilTemp_out = maxOilTemp; + } else if (t_steady - dt >= maxOilTemp - 10) { + oilTemp_out = (t_steady - dt) * 0.999997; + } else { + oilTemp_out = (t_steady - dt); + } + + return oilTemp_out; + } + + /// + /// Real-life modeled polynomials - Oil Gulping (%) + /// + double oilGulpPct(double thrust) { + double outOilGulpPct = 0; + + double c_OilGulp[3] = {20.1968848, -1.2270302e-4, 1.78442e-8}; + + outOilGulpPct = c_OilGulp[0] + (c_OilGulp[1] * thrust) + (c_OilGulp[2] * powFBW(thrust, 2)); + + return outOilGulpPct / 100; + } + + /// + /// Real-life modeled polynomials - Oil Pressure (PSI) + /// + double oilPressure(double simN2) { + double outOilPressure = 0; + + double c_OilPress[3] = {-0.88921, 0.23711, 0.00682}; + + outOilPressure = c_OilPress[0] + (c_OilPress[1] * simN2) + (c_OilPress[2] * powFBW(simN2, 2)); + + return outOilPressure; + } +}; diff --git a/fbw-a32nx/src/wasm/fadec_a320/src/SimVars.h b/fbw-a32nx/src/wasm/fadec_a320/src/SimVars.h new file mode 100644 index 00000000000..7fee11865d9 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a320/src/SimVars.h @@ -0,0 +1,407 @@ +#pragma once + +#include + +/// +/// SimConnect data types to send to Sim Updated +/// +enum DataTypesID { + FuelLeftMain, + FuelRightMain, + FuelCenterMain, + FuelLeftAux, + FuelRightAux, + OilTempLeft, + OilTempRight, + OilPsiLeft, + OilPsiRight, + StartCN2Left, + StartCN2Right, + SimulationDataTypeId, + AcftInfo, +}; + +struct SimulationData { + double simulationTime; + double simulationRate; +}; + +struct SimulationDataLivery { + char atc_id[32] = ""; +}; + +enum Events { + Engine1StarterToggled, + Engine2StarterToggled, +}; + +/// +/// A collection of SimVar unit enums. +/// +class Units { + public: + ENUM Percent = get_units_enum("Percent"); + ENUM Pounds = get_units_enum("Pounds"); + ENUM Psi = get_units_enum("Psi"); + ENUM Pph = get_units_enum("Pounds per hour"); + ENUM Gallons = get_units_enum("Gallons"); + ENUM Gph = get_units_enum("Gallons per hour"); + ENUM Feet = get_units_enum("Feet"); + ENUM FootPounds = get_units_enum("Foot pounds"); + ENUM FeetMin = get_units_enum("Feet per minute"); + ENUM Number = get_units_enum("Number"); + ENUM Mach = get_units_enum("Mach"); + ENUM Millibars = get_units_enum("Millibars"); + ENUM SluggerSlugs = get_units_enum("Slug per cubic feet"); + ENUM Celsius = get_units_enum("Celsius"); + ENUM Bool = get_units_enum("Bool"); + ENUM Hours = get_units_enum("Hours"); + ENUM Seconds = get_units_enum("Seconds"); +}; + +/// +/// A collection of SimVars and LVars for the A32NX +/// +class SimVars { + public: + Units* m_Units; + + /// + /// Collection of SimVars for the A32NX + /// + ENUM CorrectedN1 = get_aircraft_var_enum("TURB ENG CORRECTED N1"); + ENUM CorrectedN2 = get_aircraft_var_enum("TURB ENG CORRECTED N2"); + ENUM N1 = get_aircraft_var_enum("TURB ENG N1"); + ENUM N2 = get_aircraft_var_enum("TURB ENG N2"); + ENUM OilPSI = get_aircraft_var_enum("GENERAL ENG OIL PRESSURE"); + ENUM OilTemp = get_aircraft_var_enum("GENERAL ENG OIL TEMPERATURE"); + ENUM Thrust = get_aircraft_var_enum("TURB ENG JET THRUST"); + ENUM correctedFF = get_aircraft_var_enum("TURB ENG CORRECTED FF"); + ENUM PlaneAltitude = get_aircraft_var_enum("PLANE ALTITUDE"); + ENUM PlaneAltitudeAGL = get_aircraft_var_enum("PLANE ALT ABOVE GROUND"); + ENUM PressureAltitude = get_aircraft_var_enum("PRESSURE ALTITUDE"); + ENUM AirSpeedMach = get_aircraft_var_enum("AIRSPEED MACH"); + ENUM AmbientTemp = get_aircraft_var_enum("AMBIENT TEMPERATURE"); + ENUM AmbientPressure = get_aircraft_var_enum("AMBIENT PRESSURE"); + ENUM VerticalSpeed = get_aircraft_var_enum("VERTICAL SPEED"); + ENUM StdTemp = get_aircraft_var_enum("STANDARD ATM TEMPERATURE"); + ENUM SimOnGround = get_aircraft_var_enum("SIM ON GROUND"); + ENUM EngineTime = get_aircraft_var_enum("GENERAL ENG ELAPSED TIME"); + ENUM EngineStarter = get_aircraft_var_enum("GENERAL ENG STARTER"); + ENUM EngineIgniter = get_aircraft_var_enum("TURB ENG IGNITION SWITCH EX1"); + ENUM EngineCombustion = get_aircraft_var_enum("GENERAL ENG COMBUSTION"); + ENUM animDeltaTime = get_aircraft_var_enum("ANIMATION DELTA TIME"); + + ENUM FuelTankQuantity = get_aircraft_var_enum("FUELSYSTEM TANK QUANTITY"); + ENUM EmptyWeight = get_aircraft_var_enum("EMPTY WEIGHT"); + ENUM TotalWeight = get_aircraft_var_enum("TOTAL WEIGHT"); + ENUM FuelTotalQuantity = get_aircraft_var_enum("FUEL TOTAL QUANTITY"); + ENUM FuelWeightGallon = get_aircraft_var_enum("FUEL WEIGHT PER GALLON"); + + ENUM FuelPump = get_aircraft_var_enum("FUELSYSTEM PUMP ACTIVE"); + ENUM FuelValve = get_aircraft_var_enum("FUELSYSTEM VALVE OPEN"); + ENUM FuelLineFlow = get_aircraft_var_enum("FUELSYSTEM LINE FUEL FLOW"); + ENUM FuelJunctionSetting = get_aircraft_var_enum("FUELSYSTEM JUNCTION SETTING"); + + ENUM NacelleAntiIce = get_aircraft_var_enum("ENG ANTI ICE"); + + /// + /// Collection of LVars for the A32NX + /// + ID DevVar; + ID IsReady; + ID FlexTemp; + ID Engine1N2; + ID Engine2N2; + ID Engine1N1; + ID Engine2N1; + ID EngineIdleN1; + ID EngineIdleN2; + ID EngineIdleFF; + ID EngineIdleEGT; + ID Engine1EGT; + ID Engine2EGT; + ID Engine1Oil; + ID Engine2Oil; + ID Engine1OilTotal; + ID Engine2OilTotal; + ID Engine1VibN1; + ID Engine2VibN1; + ID Engine1VibN2; + ID Engine2VibN2; + ID Engine1FF; + ID Engine2FF; + ID Engine1PreFF; + ID Engine2PreFF; + ID EngineCycleTime; + ID EngineImbalance; + ID WingAntiIce; + ID FuelUsedLeft; + ID FuelUsedRight; + ID FuelLeftPre; + ID FuelRightPre; + ID FuelAuxLeftPre; + ID FuelAuxRightPre; + ID FuelCenterPre; + ID RefuelRate; + ID RefuelStartedByUser; + ID FuelOverflowLeft; + ID FuelOverflowRight; + ID Engine1State; + ID Engine2State; + ID Engine1Timer; + ID Engine2Timer; + ID PumpStateLeft; + ID PumpStateRight; + ID ThrustLimitType; + ID ThrustLimitIdle; + ID ThrustLimitToga; + ID ThrustLimitFlex; + ID ThrustLimitClimb; + ID ThrustLimitMct; + ID PacksState1; + ID PacksState2; + ID Eng1StarterPressurized; + ID Eng2StarterPressurized; + ID APUrpmPercent; + ID StartState; + + SimVars() { this->initializeVars(); } + + void initializeVars() { + DevVar = register_named_variable("A32NX_DEVELOPER_STATE"); + IsReady = register_named_variable("A32NX_IS_READY"); + StartState = register_named_variable("A32NX_START_STATE"); + FlexTemp = register_named_variable("AIRLINER_TO_FLEX_TEMP"); + Engine1N2 = register_named_variable("A32NX_ENGINE_N2:1"); + Engine2N2 = register_named_variable("A32NX_ENGINE_N2:2"); + Engine1N1 = register_named_variable("A32NX_ENGINE_N1:1"); + Engine2N1 = register_named_variable("A32NX_ENGINE_N1:2"); + EngineIdleN1 = register_named_variable("A32NX_ENGINE_IDLE_N1"); + EngineIdleN2 = register_named_variable("A32NX_ENGINE_IDLE_N2"); + EngineIdleFF = register_named_variable("A32NX_ENGINE_IDLE_FF"); + EngineIdleEGT = register_named_variable("A32NX_ENGINE_IDLE_EGT"); + Engine1EGT = register_named_variable("A32NX_ENGINE_EGT:1"); + Engine2EGT = register_named_variable("A32NX_ENGINE_EGT:2"); + Engine1Oil = register_named_variable("A32NX_ENGINE_OIL_QTY:1"); + Engine2Oil = register_named_variable("A32NX_ENGINE_OIL_QTY:2"); + Engine1OilTotal = register_named_variable("A32NX_ENGINE_OIL_TOTAL:1"); + Engine2OilTotal = register_named_variable("A32NX_ENGINE_OIL_TOTAL:2"); + Engine1VibN1 = register_named_variable("A32NX_ENGINE_VIB_N1:1"); + Engine2VibN1 = register_named_variable("A32NX_ENGINE_VIB_N1:2"); + Engine1VibN2 = register_named_variable("A32NX_ENGINE_VIB_N2:1"); + Engine2VibN2 = register_named_variable("A32NX_ENGINE_VIB_N2:2"); + Engine1FF = register_named_variable("A32NX_ENGINE_FF:1"); + Engine2FF = register_named_variable("A32NX_ENGINE_FF:2"); + Engine1PreFF = register_named_variable("A32NX_ENGINE_PRE_FF:1"); + Engine2PreFF = register_named_variable("A32NX_ENGINE_PRE_FF:2"); + EngineImbalance = register_named_variable("A32NX_ENGINE_IMBALANCE"); + WingAntiIce = register_named_variable("A32NX_PNEU_WING_ANTI_ICE_SYSTEM_ON"); + FuelUsedLeft = register_named_variable("A32NX_FUEL_USED:1"); + FuelUsedRight = register_named_variable("A32NX_FUEL_USED:2"); + FuelLeftPre = register_named_variable("A32NX_FUEL_LEFT_PRE"); + FuelRightPre = register_named_variable("A32NX_FUEL_RIGHT_PRE"); + FuelAuxLeftPre = register_named_variable("A32NX_FUEL_AUX_LEFT_PRE"); + FuelAuxRightPre = register_named_variable("A32NX_FUEL_AUX_RIGHT_PRE"); + FuelCenterPre = register_named_variable("A32NX_FUEL_CENTER_PRE"); + RefuelRate = register_named_variable("A32NX_EFB_REFUEL_RATE_SETTING"); + RefuelStartedByUser = register_named_variable("A32NX_REFUEL_STARTED_BY_USR"); + Engine1State = register_named_variable("A32NX_ENGINE_STATE:1"); + Engine2State = register_named_variable("A32NX_ENGINE_STATE:2"); + Engine1Timer = register_named_variable("A32NX_ENGINE_TIMER:1"); + Engine2Timer = register_named_variable("A32NX_ENGINE_TIMER:2"); + PumpStateLeft = register_named_variable("A32NX_PUMP_STATE:1"); + PumpStateRight = register_named_variable("A32NX_PUMP_STATE:2"); + Eng1StarterPressurized = register_named_variable("A32NX_PNEU_ENG_1_STARTER_PRESSURIZED"); + Eng2StarterPressurized = register_named_variable("A32NX_PNEU_ENG_2_STARTER_PRESSURIZED"); + APUrpmPercent = register_named_variable("A32NX_APU_N_RAW"); + + ThrustLimitType = register_named_variable("A32NX_AUTOTHRUST_THRUST_LIMIT_TYPE"); + ThrustLimitIdle = register_named_variable("A32NX_AUTOTHRUST_THRUST_LIMIT_IDLE"); + ThrustLimitToga = register_named_variable("A32NX_AUTOTHRUST_THRUST_LIMIT_TOGA"); + ThrustLimitFlex = register_named_variable("A32NX_AUTOTHRUST_THRUST_LIMIT_FLX"); + ThrustLimitClimb = register_named_variable("A32NX_AUTOTHRUST_THRUST_LIMIT_CLB"); + ThrustLimitMct = register_named_variable("A32NX_AUTOTHRUST_THRUST_LIMIT_MCT"); + + PacksState1 = register_named_variable("A32NX_COND_PACK_FLOW_VALVE_1_IS_OPEN"); + PacksState2 = register_named_variable("A32NX_COND_PACK_FLOW_VALVE_2_IS_OPEN"); + + this->setDeveloperState(0); + this->setEngine1N2(0); + this->setEngine2N2(0); + this->setEngine1N1(0); + this->setEngine2N1(0); + this->setEngineIdleN1(0); + this->setEngineIdleN2(0); + this->setEngineIdleFF(0); + this->setEngineIdleEGT(0); + this->setEngine1EGT(0); + this->setEngine2EGT(0); + this->setEngine1Oil(0); + this->setEngine2Oil(0); + this->setEngine1OilTotal(0); + this->setEngine2OilTotal(0); + this->setEngine1VibN1(0); + this->setEngine2VibN1(0); + this->setEngine1VibN2(0); + this->setEngine2VibN2(0); + this->setEngine1FF(0); + this->setEngine2FF(0); + this->setEngine1PreFF(0); + this->setEngine2PreFF(0); + this->setEngineImbalance(0); + this->setFuelUsedLeft(0); + this->setFuelUsedRight(0); + this->setFuelLeftPre(0); + this->setFuelRightPre(0); + this->setFuelAuxLeftPre(0); + this->setFuelAuxRightPre(0); + this->setFuelCenterPre(0); + this->setEngine1State(0); + this->setEngine2State(0); + this->setEngine1Timer(0); + this->setEngine2Timer(0); + this->setPumpStateLeft(0); + this->setPumpStateRight(0); + this->setThrustLimitIdle(0); + this->setThrustLimitToga(0); + this->setThrustLimitFlex(0); + this->setThrustLimitClimb(0); + this->setThrustLimitMct(0); + + m_Units = new Units(); + } + + // Collection of LVar 'set' Functions + void setDeveloperState(FLOAT64 value) { set_named_variable_value(DevVar, value); } + void setEngine1N2(FLOAT64 value) { set_named_variable_value(Engine1N2, value); } + void setEngine2N2(FLOAT64 value) { set_named_variable_value(Engine2N2, value); } + void setEngine1N1(FLOAT64 value) { set_named_variable_value(Engine1N1, value); } + void setEngine2N1(FLOAT64 value) { set_named_variable_value(Engine2N1, value); } + void setEngineIdleN1(FLOAT64 value) { set_named_variable_value(EngineIdleN1, value); } + void setEngineIdleN2(FLOAT64 value) { set_named_variable_value(EngineIdleN2, value); } + void setEngineIdleFF(FLOAT64 value) { set_named_variable_value(EngineIdleFF, value); } + void setEngineIdleEGT(FLOAT64 value) { set_named_variable_value(EngineIdleEGT, value); } + void setEngine1EGT(FLOAT64 value) { set_named_variable_value(Engine1EGT, value); } + void setEngine2EGT(FLOAT64 value) { set_named_variable_value(Engine2EGT, value); } + void setEngine1Oil(FLOAT64 value) { set_named_variable_value(Engine1Oil, value); } + void setEngine2Oil(FLOAT64 value) { set_named_variable_value(Engine2Oil, value); } + void setEngine1OilTotal(FLOAT64 value) { set_named_variable_value(Engine1OilTotal, value); } + void setEngine2OilTotal(FLOAT64 value) { set_named_variable_value(Engine2OilTotal, value); } + void setEngine1VibN1(FLOAT64 value) { set_named_variable_value(Engine1VibN1, value); } + void setEngine2VibN1(FLOAT64 value) { set_named_variable_value(Engine2VibN1, value); } + void setEngine1VibN2(FLOAT64 value) { set_named_variable_value(Engine1VibN2, value); } + void setEngine2VibN2(FLOAT64 value) { set_named_variable_value(Engine2VibN2, value); } + void setEngine1FF(FLOAT64 value) { set_named_variable_value(Engine1FF, value); } + void setEngine2FF(FLOAT64 value) { set_named_variable_value(Engine2FF, value); } + void setEngine1PreFF(FLOAT64 value) { set_named_variable_value(Engine1PreFF, value); } + void setEngine2PreFF(FLOAT64 value) { set_named_variable_value(Engine2PreFF, value); } + void setEngineImbalance(FLOAT64 value) { set_named_variable_value(EngineImbalance, value); } + void setFuelUsedLeft(FLOAT64 value) { set_named_variable_value(FuelUsedLeft, value); } + void setFuelUsedRight(FLOAT64 value) { set_named_variable_value(FuelUsedRight, value); } + void setFuelLeftPre(FLOAT64 value) { set_named_variable_value(FuelLeftPre, value); } + void setFuelRightPre(FLOAT64 value) { set_named_variable_value(FuelRightPre, value); } + void setFuelAuxLeftPre(FLOAT64 value) { set_named_variable_value(FuelAuxLeftPre, value); } + void setFuelAuxRightPre(FLOAT64 value) { set_named_variable_value(FuelAuxRightPre, value); } + void setFuelCenterPre(FLOAT64 value) { set_named_variable_value(FuelCenterPre, value); } + void setEngine1State(FLOAT64 value) { set_named_variable_value(Engine1State, value); } + void setEngine2State(FLOAT64 value) { set_named_variable_value(Engine2State, value); } + void setEngine1Timer(FLOAT64 value) { set_named_variable_value(Engine1Timer, value); } + void setEngine2Timer(FLOAT64 value) { set_named_variable_value(Engine2Timer, value); } + void setPumpStateLeft(FLOAT64 value) { set_named_variable_value(PumpStateLeft, value); } + void setPumpStateRight(FLOAT64 value) { set_named_variable_value(PumpStateRight, value); } + void setThrustLimitIdle(FLOAT64 value) { set_named_variable_value(ThrustLimitIdle, value); } + void setThrustLimitToga(FLOAT64 value) { set_named_variable_value(ThrustLimitToga, value); } + void setThrustLimitFlex(FLOAT64 value) { set_named_variable_value(ThrustLimitFlex, value); } + void setThrustLimitClimb(FLOAT64 value) { set_named_variable_value(ThrustLimitClimb, value); } + void setThrustLimitMct(FLOAT64 value) { set_named_variable_value(ThrustLimitMct, value); } + + // Collection of SimVar/LVar 'get' Functions + FLOAT64 getDeveloperState() { return get_named_variable_value(DevVar); } + FLOAT64 getIsReady() { return get_named_variable_value(IsReady); } + FLOAT64 getStartState() { return get_named_variable_value(StartState); } + FLOAT64 getFlexTemp() { return get_named_variable_value(FlexTemp); } + FLOAT64 getEngine1N2() { return get_named_variable_value(Engine1N2); } + FLOAT64 getEngine2N2() { return get_named_variable_value(Engine2N2); } + FLOAT64 getEngine1N1() { return get_named_variable_value(Engine1N1); } + FLOAT64 getEngine2N1() { return get_named_variable_value(Engine2N1); } + FLOAT64 getEngineIdleN1() { return get_named_variable_value(EngineIdleN1); } + FLOAT64 getEngineIdleN2() { return get_named_variable_value(EngineIdleN2); } + FLOAT64 getEngineIdleFF() { return get_named_variable_value(EngineIdleFF); } + FLOAT64 getEngineIdleEGT() { return get_named_variable_value(EngineIdleEGT); } + FLOAT64 getEngine1FF() { return get_named_variable_value(Engine1FF); } + FLOAT64 getEngine2FF() { return get_named_variable_value(Engine2FF); } + FLOAT64 getEngine1EGT() { return get_named_variable_value(Engine1EGT); } + FLOAT64 getEngine2EGT() { return get_named_variable_value(Engine2EGT); } + FLOAT64 getEngine1Oil() { return get_named_variable_value(Engine1Oil); } + FLOAT64 getEngine2Oil() { return get_named_variable_value(Engine2Oil); } + FLOAT64 getEngine1OilTotal() { return get_named_variable_value(Engine1OilTotal); } + FLOAT64 getEngine2OilTotal() { return get_named_variable_value(Engine2OilTotal); } + FLOAT64 getEngine1VibN1() { return get_named_variable_value(Engine1VibN1); } + FLOAT64 getEngine2VibN1() { return get_named_variable_value(Engine2VibN1); } + FLOAT64 getEngine1VibN2() { return get_named_variable_value(Engine1VibN2); } + FLOAT64 getEngine2VibN2() { return get_named_variable_value(Engine2VibN2); } + FLOAT64 getEngine1PreFF() { return get_named_variable_value(Engine1PreFF); } + FLOAT64 getEngine2PreFF() { return get_named_variable_value(Engine2PreFF); } + FLOAT64 getEngineImbalance() { return get_named_variable_value(EngineImbalance); } + FLOAT64 getWAI() { return get_named_variable_value(WingAntiIce); } + FLOAT64 getFuelUsedLeft() { return get_named_variable_value(FuelUsedLeft); } + FLOAT64 getFuelUsedRight() { return get_named_variable_value(FuelUsedRight); } + FLOAT64 getFuelLeftPre() { return get_named_variable_value(FuelLeftPre); } + FLOAT64 getFuelRightPre() { return get_named_variable_value(FuelRightPre); } + FLOAT64 getFuelAuxLeftPre() { return get_named_variable_value(FuelAuxLeftPre); } + FLOAT64 getFuelAuxRightPre() { return get_named_variable_value(FuelAuxRightPre); } + FLOAT64 getFuelCenterPre() { return get_named_variable_value(FuelCenterPre); } + FLOAT64 getRefuelRate() { return get_named_variable_value(RefuelRate); } + FLOAT64 getRefuelStartedByUser() { return get_named_variable_value(RefuelStartedByUser); } + FLOAT64 getPumpStateLeft() { return get_named_variable_value(PumpStateLeft); } + FLOAT64 getPumpStateRight() { return get_named_variable_value(PumpStateRight); } + FLOAT64 getPacksState1() { return get_named_variable_value(PacksState1); } + FLOAT64 getPacksState2() { return get_named_variable_value(PacksState2); } + FLOAT64 getThrustLimitType() { return get_named_variable_value(ThrustLimitType); } + FLOAT64 getStarterPressurized(int engine) { + return get_named_variable_value(engine == 1 ? Eng1StarterPressurized : Eng2StarterPressurized); + } + FLOAT64 getRightSystemPressure() { return get_named_variable_value(Eng2StarterPressurized); } + FLOAT64 getAPUrpmPercent() { return get_named_variable_value(APUrpmPercent); } + + FLOAT64 getCN1(int index) { return aircraft_varget(CorrectedN1, m_Units->Percent, index); } + FLOAT64 getCN2(int index) { return aircraft_varget(CorrectedN2, m_Units->Percent, index); } + FLOAT64 getN1(int index) { return aircraft_varget(N1, m_Units->Percent, index); } + FLOAT64 getN2(int index) { return aircraft_varget(N2, m_Units->Percent, index); } + FLOAT64 getOilPsi(int index) { return aircraft_varget(OilPSI, m_Units->Psi, index); } + FLOAT64 getOilTemp(int index) { return aircraft_varget(OilTemp, m_Units->Celsius, index); } + FLOAT64 getThrust(int index) { return aircraft_varget(Thrust, m_Units->Pounds, index); } + FLOAT64 getEngine1State() { return get_named_variable_value(Engine1State); } + FLOAT64 getEngine2State() { return get_named_variable_value(Engine2State); } + FLOAT64 getEngine1Timer() { return get_named_variable_value(Engine1Timer); } + FLOAT64 getEngine2Timer() { return get_named_variable_value(Engine2Timer); } + FLOAT64 getFF(int index) { return aircraft_varget(correctedFF, m_Units->Pph, index); } + FLOAT64 getMach() { return aircraft_varget(AirSpeedMach, m_Units->Mach, 0); } + FLOAT64 getPlaneAltitude() { return aircraft_varget(PlaneAltitude, m_Units->Feet, 0); } + FLOAT64 getPlaneAltitudeAGL() { return aircraft_varget(PlaneAltitudeAGL, m_Units->Feet, 0); } + FLOAT64 getPressureAltitude() { return aircraft_varget(PressureAltitude, m_Units->Feet, 0); } + FLOAT64 getVerticalSpeed() { return aircraft_varget(VerticalSpeed, m_Units->FeetMin, 0); } + FLOAT64 getAmbientTemperature() { return aircraft_varget(AmbientTemp, m_Units->Celsius, 0); } + FLOAT64 getAmbientPressure() { return aircraft_varget(AmbientPressure, m_Units->Millibars, 0); } + FLOAT64 getStdTemperature() { return aircraft_varget(StdTemp, m_Units->Celsius, 0); } + FLOAT64 getSimOnGround() { return aircraft_varget(SimOnGround, m_Units->Bool, 0); } + FLOAT64 getFuelTankQuantity(int index) { return aircraft_varget(FuelTankQuantity, m_Units->Gallons, index); } + FLOAT64 getFuelTotalQuantity() { return aircraft_varget(FuelTotalQuantity, m_Units->Gallons, 0); } + FLOAT64 getEmptyWeight() { return aircraft_varget(EmptyWeight, m_Units->Pounds, 0); } + FLOAT64 getTotalWeight() { return aircraft_varget(TotalWeight, m_Units->Pounds, 0); } + FLOAT64 getFuelWeightGallon() { return aircraft_varget(FuelWeightGallon, m_Units->Pounds, 0); } + FLOAT64 getEngineTime(int index) { return aircraft_varget(EngineTime, m_Units->Seconds, index); } + FLOAT64 getEngineStarter(int index) { return aircraft_varget(EngineStarter, m_Units->Bool, index); } + FLOAT64 getEngineIgniter(int index) { return aircraft_varget(EngineIgniter, m_Units->Number, index); } + FLOAT64 getEngineCombustion(int index) { return aircraft_varget(EngineCombustion, m_Units->Bool, index); } + FLOAT64 getAnimDeltaTime() { return aircraft_varget(animDeltaTime, m_Units->Seconds, 0); } + FLOAT64 getNAI(int index) { return aircraft_varget(NacelleAntiIce, m_Units->Bool, index); } + FLOAT64 getPump(int index) { return aircraft_varget(FuelPump, m_Units->Number, index); } + FLOAT64 getValve(int index) { return aircraft_varget(FuelValve, m_Units->Number, index); } + /// @brief Gets a fuel line flow rate in gallons/hour + /// @param index Index of the fuel line + /// @return Fuel line flow rate in gallons/hour + FLOAT64 getLineFlow(int index) { return aircraft_varget(FuelLineFlow, m_Units->Gph, index); } + FLOAT64 getJunctionSetting(int index) { return aircraft_varget(FuelJunctionSetting, m_Units->Number, index); } +}; diff --git a/fbw-a32nx/src/wasm/fadec_a320/src/Tables.h b/fbw-a32nx/src/wasm/fadec_a320/src/Tables.h new file mode 100644 index 00000000000..976c37ff842 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a320/src/Tables.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +#include "SimVars.h" +#include "common.h" + +EngineRatios* ratios; + +/// +/// Table 1502 (CN2 vs correctedN1) representations with FSX nomenclature +/// +/// Returns CN2 - correctedN1 pair. +double table1502(int i, int j) { + double t[13][4] = {{18.20, 0.00, 0.00, 17.00}, {22.00, 1.90, 1.90, 17.40}, {26.00, 2.50, 2.50, 18.20}, + {57.00, 12.80, 12.80, 27.00}, {68.20, 19.60, 19.60, 34.83}, {77.00, 26.00, 26.00, 40.84}, + {83.00, 31.42, 31.42, 44.77}, {89.00, 40.97, 40.97, 50.09}, {92.80, 51.00, 51.00, 55.04}, + {97.00, 65.00, 65.00, 65.00}, {100.00, 77.00, 77.00, 77.00}, {104.00, 85.00, 85.00, 85.50}, + {116.50, 101.00, 101.00, 101.00}}; + + return t[i][j]; +} + +/// +/// Calculate expected CN2 at Idle +/// +double iCN2(double pressAltitude, double mach) { + double cn2 = 0; + + cn2 = 68.2 / (sqrt((288.15 - (1.98 * pressAltitude / 1000)) / 288.15) * sqrt(1 + (0.2 * powFBW(mach, 2)))); + + return cn2; +} + +/// +/// Calculate expected correctedN1 at Idle +/// +double iCN1(double pressAltitude, double mach, double ambientTemp) { + int i; + double cn1_lo = 0, cn1_hi = 0, cn1 = 0; + double cn2 = iCN2(pressAltitude, mach); + double cell = 0; + double cn2lo = 0, cn2hi = 0; + double cn1lolo = 0, cn1hilo = 0, cn1lohi = 0, cn1hihi = 0; + + for (i = 0; i < 13; i++) { + cell = table1502(i, 0); + if (cell > cn2) { + break; + } + } + + cn2lo = table1502(i - 1, 0); + cn2hi = table1502(i, 0); + + cn1lolo = table1502(i - 1, 1); + cn1hilo = table1502(i, 1); + + if (mach <= 0.2) { + cn1 = interpolate(cn2, cn2lo, cn2hi, cn1lolo, cn1hilo); + } else { + cn1lohi = table1502(i - 1, 3); + cn1hihi = table1502(i, 3); + + cn1_lo = interpolate(cn2, cn2lo, cn2hi, cn1lolo, cn1hilo); + cn1_hi = interpolate(cn2, cn2lo, cn2hi, cn1lohi, cn1hihi); + cn1 = interpolate(mach, 0.2, 0.9, cn1_lo, cn1_hi); + } + + return cn1; +} diff --git a/fbw-a32nx/src/wasm/fadec_a320/src/ThrustLimits.h b/fbw-a32nx/src/wasm/fadec_a320/src/ThrustLimits.h new file mode 100644 index 00000000000..33e2dd57f22 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a320/src/ThrustLimits.h @@ -0,0 +1,260 @@ +#pragma once + +#include + +#include "Tables.h" +#include "SimVars.h" +#include "common.h" + +double cas2mach(double cas, double ambientPressure) { + double k = 2188648.141; + double delta = ambientPressure / 1013; + double mach = sqrt((5 * pow(((pow(((pow(cas, 2) / k) + 1), 3.5) * (1 / delta)) - (1 / delta) + 1), 0.285714286)) - 5); + + return mach; +} + +static constexpr double limits[72][6] = { + {-2000, 48.000, 55.000, 81.351, 79.370, 61.535}, {-1000, 46.000, 55.000, 82.605, 80.120, 62.105}, + {0, 44.000, 55.000, 83.832, 80.776, 62.655}, {500, 42.000, 52.000, 84.210, 81.618, 62.655}, + {1000, 42.000, 52.000, 84.579, 81.712, 62.655}, {2000, 40.000, 50.000, 85.594, 82.720, 62.655}, + {3000, 36.000, 48.000, 86.657, 83.167, 61.960}, {4000, 32.000, 46.000, 87.452, 83.332, 61.206}, + {5000, 29.000, 44.000, 88.833, 84.166, 61.206}, {6000, 25.000, 42.000, 90.232, 84.815, 61.206}, + {7000, 21.000, 40.000, 91.711, 85.565, 61.258}, {8000, 17.000, 38.000, 93.247, 86.225, 61.777}, + {9000, 15.000, 36.000, 94.031, 86.889, 60.968}, {10000, 13.000, 34.000, 94.957, 88.044, 60.935}, + {11000, 12.000, 32.000, 95.295, 88.526, 59.955}, {12000, 11.000, 30.000, 95.568, 88.818, 58.677}, + {13000, 10.000, 28.000, 95.355, 88.819, 59.323}, {14000, 10.000, 26.000, 95.372, 89.311, 59.965}, + {15000, 8.000, 24.000, 95.686, 89.907, 58.723}, {16000, 5.000, 22.000, 96.160, 89.816, 57.189}, + {16600, 5.000, 22.000, 96.560, 89.816, 57.189}, {-2000, 47.751, 54.681, 84.117, 81.901, 63.498}, + {-1000, 45.771, 54.681, 85.255, 82.461, 63.920}, {0, 43.791, 54.681, 86.411, 83.021, 64.397}, + {500, 42.801, 52.701, 86.978, 83.740, 64.401}, {1000, 41.811, 52.701, 87.568, 83.928, 64.525}, + {2000, 38.841, 50.721, 88.753, 84.935, 64.489}, {3000, 36.861, 48.741, 89.930, 85.290, 63.364}, + {4000, 32.901, 46.761, 91.004, 85.836, 62.875}, {5000, 28.941, 44.781, 92.198, 86.293, 62.614}, + {6000, 24.981, 42.801, 93.253, 86.563, 62.290}, {7000, 21.022, 40.821, 94.273, 86.835, 61.952}, + {8000, 17.062, 38.841, 94.919, 87.301, 62.714}, {9000, 15.082, 36.861, 95.365, 87.676, 61.692}, + {10000, 13.102, 34.881, 95.914, 88.150, 60.906}, {11000, 12.112, 32.901, 96.392, 88.627, 59.770}, + {12000, 11.122, 30.921, 96.640, 89.206, 58.933}, {13000, 10.132, 28.941, 96.516, 89.789, 60.503}, + {14000, 9.142, 26.961, 96.516, 90.475, 62.072}, {15000, 9.142, 24.981, 96.623, 90.677, 59.333}, + {16000, 7.162, 23.001, 96.845, 90.783, 58.045}, {16600, 5.182, 21.022, 97.366, 91.384, 58.642}, + {-2000, 30.800, 56.870, 80.280, 72.000, 0.000}, {2000, 20.990, 48.157, 82.580, 74.159, 0.000}, + {5000, 16.139, 43.216, 84.642, 75.737, 0.000}, {8000, 7.342, 38.170, 86.835, 77.338, 0.000}, + {10000, 4.051, 34.518, 88.183, 77.999, 0.000}, {10000.1, 4.051, 34.518, 87.453, 77.353, 0.000}, + {12000, 0.760, 30.865, 88.303, 78.660, 0.000}, {15000, -4.859, 25.039, 89.748, 79.816, 0.000}, + {17000, -9.934, 19.813, 90.668, 80.895, 0.000}, {20000, -15.822, 13.676, 92.106, 81.894, 0.000}, + {24000, -22.750, 6.371, 93.651, 82.716, 0.000}, {27000, -29.105, -0.304, 93.838, 83.260, 0.000}, + {29314, -32.049, -3.377, 93.502, 82.962, 0.000}, {31000, -34.980, -6.452, 95.392, 84.110, 0.000}, + {35000, -45.679, -17.150, 96.104, 85.248, 0.000}, {39000, -45.679, -17.150, 96.205, 84.346, 0.000}, + {41500, -45.679, -17.150, 95.676, 83.745, 0.000}, {-1000, 26.995, 54.356, 82.465, 74.086, 0.000}, + {3000, 18.170, 45.437, 86.271, 77.802, 0.000}, {7000, 9.230, 40.266, 89.128, 79.604, 0.000}, + {11000, 4.019, 31.046, 92.194, 82.712, 0.000}, {15000, -5.226, 21.649, 95.954, 85.622, 0.000}, + {17000, -9.913, 20.702, 97.520, 85.816, 0.000}, {20000, -15.129, 15.321, 99.263, 86.770, 0.000}, + {22000, -19.947, 10.382, 98.977, 86.661, 0.000}, {25000, -25.397, 4.731, 98.440, 85.765, 0.000}, + {27000, -30.369, -0.391, 97.279, 85.556, 0.000}, {31000, -36.806, -7.165, 98.674, 86.650, 0.000}, + {35000, -43.628, -14.384, 98.386, 85.747, 0.000}, {39000, -47.286, -18.508, 97.278, 85.545, 0.000}}; + +/// +/// Finds top-row boundary in an array +/// +int finder(double altitude, int index) { + if (altitude < limits[index][0]) { + return index; + } else { + return finder(altitude, index + 1); + } +} + +/// +/// Calculates Bleed Air situation for engine adaptation +/// +double bleedTotal(int type, double altitude, double oat, double cp, double lp, double flexTemp, double ac, double nacelle, double wing) { + double n1Packs = 0; + double n1Nai = 0; + double n1Wai = 0; + double bleed = 0; + + if (flexTemp > lp && type <= 1) { + n1Packs = -0.6; + n1Nai = -0.7; + n1Wai = -0.7; + } else { + switch (type) { + case 0: + if (altitude < 8000) { + if (oat < cp) { + n1Packs = -0.4; + } else { + n1Packs = -0.5; + n1Nai = -0.6; + n1Wai = -0.7; + } + } else { + if (oat < cp) { + n1Packs = -0.6; + } else { + n1Packs = -0.7; + n1Nai = -0.8; + n1Wai = -0.8; + } + } + break; + case 1: + if (altitude < 8000) { + if (oat < cp) { + n1Packs = -0.4; + } else { + n1Packs = -0.4; + n1Nai = -0.6; + n1Wai = -0.6; + } + } else { + if (oat < cp) { + n1Packs = -0.6; + } else { + n1Packs = -0.6; + n1Nai = -0.7; + n1Wai = -0.8; + } + } + break; + case 2: + if (oat < cp) { + n1Packs = -0.2; + } else { + n1Packs = -0.3; + n1Nai = -0.8; + n1Wai = -0.4; + } + break; + case 3: + if (oat < cp) { + n1Packs = -0.6; + } else { + n1Packs = -0.6; + n1Nai = -0.9; + n1Wai = -1.2; + } + break; + } + } + + if (ac == 0) { + n1Packs = 0; + } + if (nacelle == 0) { + n1Nai = 0; + } + if (wing == 0) { + n1Wai = 0; + } + + bleed = n1Packs + n1Nai + n1Wai; + + return bleed; +} + +/// +/// Main N1 Limit Function +/// +/// 0-TO, 1-GA, 2-CLB, 3-MCT +/// +double +limitN1(int type, double altitude, double ambientTemp, double ambientPressure, double flexTemp, double ac, double nacelle, double wing) { + int rowMin = 0; + int rowMax = 0; + int loAltRow = 0; + int hiAltRow = 0; + double mach = 0; + double cp = 0; + double lp = 0; + double cn1 = 0; + double n1 = 0; + double cn1Flat = 0; + double cn1Last = 0; + double cn1Flex = 0; + double m = 0; + double b = 0; + double bleed = 0; + + // Set main variables per Limit Type + switch (type) { + case 0: + rowMin = 0; + rowMax = 20; + mach = 0; + break; + case 1: + rowMin = 21; + rowMax = 41; + mach = 0.225; + break; + case 2: + rowMin = 42; + rowMax = 58; + if (altitude <= 10000) { + mach = cas2mach(250, ambientPressure); + } else { + mach = cas2mach(300, ambientPressure); + if (mach > 0.78) + mach = 0.78; + } + break; + case 3: + rowMin = 59; + rowMax = 71; + mach = cas2mach(230, ambientPressure); + break; + } + + // Check for over/ underflows. Else, find top row value + if (altitude <= limits[rowMin][0]) { + hiAltRow = rowMin; + loAltRow = rowMin; + } else if (altitude >= limits[rowMax][0]) { + hiAltRow = rowMax; + loAltRow = rowMax; + } else { + hiAltRow = finder(altitude, rowMin); + loAltRow = hiAltRow - 1; + } + + // Define key table variables and interpolation + cp = interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][1], limits[hiAltRow][1]); + lp = interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][2], limits[hiAltRow][2]); + cn1Flat = interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][3], limits[hiAltRow][3]); + cn1Last = interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][4], limits[hiAltRow][4]); + cn1Flex = interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][5], limits[hiAltRow][5]); + + if (flexTemp > 0 && type <= 1) { // CN1 for Flex Case + if (flexTemp <= cp) { + cn1 = cn1Flat; + } else if (flexTemp > lp) { + m = (cn1Flex - cn1Last) / (100 - lp); + b = cn1Flex - m * 100; + cn1 = (m * flexTemp) + b; + } else { + m = (cn1Last - cn1Flat) / (lp - cp); + b = cn1Last - m * lp; + cn1 = (m * flexTemp) + b; + } + } + else { // CN1 for All other cases + if (ambientTemp <= cp) { + cn1 = cn1Flat; + } else { + m = (cn1Last - cn1Flat) / (lp - cp); + b = cn1Last - m * lp; + cn1 = (m * ambientTemp) + b; + } + } + + // Define bleed rating/ derating + bleed = bleedTotal(type, altitude, ambientTemp, cp, lp, flexTemp, ac, nacelle, wing); + + // Setting N1 + n1 = (cn1 * sqrt(ratios->theta2(mach, ambientTemp))) + bleed; + /*if (type == 3) { + std::cout << "FADEC: bleed= " << bleed << " cn1= " << cn1 << " theta2= " << sqrt(ratios->theta2(mach, ambientTemp)) + << " n1= " << n1 << std::endl; + }*/ + return n1; +} diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/CMakeLists.txt b/fbw-a32nx/src/wasm/fadec_a32nx/CMakeLists.txt deleted file mode 100644 index adb0d3c83e5..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# flybywire-a32nx-fadec-v2 CMakeLists.txt - -# add additional compiler definitions for the a32nx fadec-v2 build -add_definitions() - -# add the local include directories -include_directories( - ${CMAKE_CURRENT_SOURCE_DIR}/src - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec - ${FBW_COMMON}/cpp-msfs-framework/ - ${FBW_COMMON}/fadec_common/src/ -) - -# define the source files -set(SOURCE_FILES - ${FBW_COMMON}/fadec_common/src/Fadec.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Gauge_Fadec_v2.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/Fadec_A32NX.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/EngineControlA32NX.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/FuelConfiguration_A32NX.cpp -) - -set(INCLUDE_FILES - ${FBW_COMMON}/fadec_common/src/Fadec.h - ${FBW_COMMON}/fadec_common/src/EngineRatios.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/Fadec_A32NX.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/FadecSimData_A32NX.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/EngineControlA32NX.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/FuelConfiguration_A32NX.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/ThrustLimits_A32NX.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/Polynomials_A32NX.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/Tables1502_A32NX.hpp -) - -# create the targets -add_library(fadec-a32nx OBJECT ${SOURCE_FILES} ${INCLUDE_FILES}) -add_wasm_library( - NAME fadec-a32nx - DEPENDENCIES fadec-a32nx cpp-msfs-framework-a32nx -) - diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/README.md b/fbw-a32nx/src/wasm/fadec_a32nx/README.md deleted file mode 100644 index cbb3dadb936..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# A32NX FADEC - -This is a new version of the FADEC system for the A32NX. -It is a migration and cleanup of the original FADEC system, -and is designed to be more modular and easier to maintain. diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/EngineControlA32NX.cpp b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/EngineControlA32NX.cpp deleted file mode 100644 index 97d81ec50e9..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/EngineControlA32NX.cpp +++ /dev/null @@ -1,1180 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - - -#include "logging.h" -#ifdef PROFILING -#include "ScopedTimer.hpp" -#include "SimpleProfiler.hpp" -#endif - -#include "EngineControlA32NX.h" -#include "EngineRatios.hpp" -#include "Polynomials_A32NX.hpp" -#include "Tables1502_A32NX.hpp" -#include "ThrustLimits_A32NX.hpp" - -void EngineControl_A32NX::initialize(MsfsHandler* msfsHandler) { - this->msfsHandlerPtr = msfsHandler; - this->dataManagerPtr = &msfsHandler->getDataManager(); - this->simData.initialize(dataManagerPtr); - LOG_INFO("Fadec::EngineControl_A32NX::initialize() - initialized"); -} - -void EngineControl_A32NX::shutdown() { - LOG_INFO("Fadec::EngineControl_A32NX::shutdown()"); -} - -void EngineControl_A32NX::update(sGaugeDrawData* pData) { -#ifdef PROFILING - profilerUpdate.start(); -#endif - - // Get ATC ID from sim to be able to load and store fuel levels - // If not yet available, request it from sim and return early - // If available initialize the engine control data - if (atcId.empty()) { - simData.atcIdDataPtr->requestUpdateFromSim(msfsHandlerPtr->getTimeStamp(), msfsHandlerPtr->getTickCounter()); - if (simData.atcIdDataPtr->hasChanged()) { - atcId = simData.atcIdDataPtr->data().atcID; - LOG_INFO("Fadec::EngineControl_A32NX::update() - received ATC ID: " + atcId); - initializeEngineControlData(); - } - return; - } - - const double deltaTime = pData->dt; - const double simTime = msfsHandlerPtr->getSimulationTime(); - const double mach = simData.simVarsDataPtr->data().airSpeedMach; - const double pressureAltitude = simData.simVarsDataPtr->data().pressureAltitude; - const double ambientTemperature = simData.simVarsDataPtr->data().ambientTemperature; - const double ambientPressure = simData.simVarsDataPtr->data().ambientPressure; - const double imbalance = simData.engineImbalance->get(); - const double idleN2 = simData.engineIdleN2->get(); - - generateIdleParameters(pressureAltitude, mach, ambientTemperature, ambientPressure); - - double simCN1; - double simN1; - double simN1highest; - double simN2; - double engineTimer; - - for (int engine = 1; engine <= 2; engine++) { - const int engineIdx = engine - 1; - - const int engineIgniter = static_cast(simData.simVarsDataPtr->data().engineIgniter[engineIdx]); // 0: crank, 1:norm, 2: ign - bool engineStarter = static_cast(simData.simVarsDataPtr->data().engineStarter[engineIdx]); - simCN1 = simData.simVarsDataPtr->data().engineCorrectedN1[engineIdx]; - simN1 = simData.simVarsDataPtr->data().simEngineN1[engineIdx]; - simN2 = simData.simVarsDataPtr->data().simEngineN2[engineIdx]; - - const double engineFuelValveOpen = simData.simVarsDataPtr->data().engineFuelValveOpen[engineIdx]; - const double engineStarterPressurized = simData.engineStarterPressurized[engineIdx]->get(); - - // simulates delay to start valve open through fuel valve travel time - const bool engineMasterTurnedOn = (prevEngineMasterPos[engineIdx] < 1 && engineFuelValveOpen >= 1); - const bool engineMasterTurnedOff = (prevEngineMasterPos[engineIdx] == 1 && engineFuelValveOpen < 1); - - engineTimer = simData.engineTimer[engineIdx]->get(); - - // starts engines if Engine Master is turned on and Starter is pressurized - // or the engine is still spinning fast enough - if (!engineStarter && engineFuelValveOpen == 1 && (engineStarterPressurized || simN2 >= 20)) { - simData.setStarterHeldEvent[engineIdx]->trigger(1); - engineStarter = true; - } - // shuts off engines if Engine Master is turned off or starter is depressurized while N2 is below 20% - else if (engineStarter && (engineFuelValveOpen < 1 || (engineFuelValveOpen && !engineStarterPressurized && simN2 < 20))) { - simData.setStarterHeldEvent[engineIdx]->trigger(0); - simData.setStarterEvent[engineIdx]->trigger(0); - engineStarter = false; - } - - const bool engineStarterTurnedOff = prevEngineStarterState[engineIdx] == 1 && !engineStarter; - - // Set & Check Engine Status for this Cycle - EngineState engineState = engineStateMachine(engine, // - engineIgniter, // - engineStarter, // - engineStarterTurnedOff, // - engineMasterTurnedOn, // - engineMasterTurnedOff, // - simN2, // - idleN2, // - ambientTemperature); // - - switch (engineState) { - case STARTING: - case RESTARTING: - if (engineStarter) { - engineStartProcedure(engine, engineState, imbalance, deltaTime, engineTimer, simN2, pressureAltitude, ambientTemperature); - break; - } - case SHUTTING: - engineShutdownProcedure(engine, ambientTemperature, simN1, deltaTime, engineTimer); - updateFF(engine, imbalance, simCN1, mach, pressureAltitude, ambientTemperature, ambientPressure); - break; - default: - updatePrimaryParameters(engine, imbalance, simN1, simN2); - const double correctedFuelFlow = updateFF(engine, imbalance, simCN1, mach, pressureAltitude, ambientTemperature, ambientPressure); - updateEGT(engine, imbalance, deltaTime, msfsHandlerPtr->getSimOnGround(), engineState, simCN1, correctedFuelFlow, mach, - pressureAltitude, ambientTemperature); - // updateOil(engine, imbalance, thrust, simN2, deltaN2, deltaTime, ambientTemp); - } - - // set highest N1 from either engine - simN1highest = (std::max)(simN1highest, simN1); - prevEngineMasterPos[engineIdx] = engineFuelValveOpen; - prevEngineStarterState[engineIdx] = engineStarter; - } - - // update fuel & tank data - updateFuel(deltaTime); - - // Obtain Bleed Variables and update Thrust Limits - const int packs = (simData.packsState[L]->get() > 0.5 || simData.packsState[R]->get() > 0.5) ? 1 : 0; - const int nai = (simData.simVarsDataPtr->data().engineAntiIce[L] > 0.5 || simData.simVarsDataPtr->data().engineAntiIce[R] > 0.5) ? 1 : 0; - const int wai = simData.wingAntiIce->getAsInt64(); - updateThrustLimits(simTime, pressureAltitude, ambientTemperature, ambientPressure, mach, simN1highest, packs, nai, wai); - -#ifdef PROFILING - profilerUpdate.stop(); - if (msfsHandlerPtr->getTickCounter() % 100 == 0) { - profilerUpdate.print(); - } -#endif -} - -// ============================================================================= -// PRIVATE -// ============================================================================= - -/** - * @brief Initializes the engine control data. - * - * This function initializes the engine control data for the aircraft. It is called when the ATC ID - * is received from the simulator. - */ -void EngineControl_A32NX::initializeEngineControlData() { - LOG_INFO("Fadec::EngineControl_A32NX::initializeEngineControlData()"); - -#ifdef PROFILING - ScopedTimer timer("Fadec::EngineControl_A32NX::initializeEngineControlData()"); -#endif - - const FLOAT64 timeStamp = msfsHandlerPtr->getTimeStamp(); - const UINT64 tickCounter = msfsHandlerPtr->getTickCounter(); - - // prepare random number generator for engine imbalance - srand((int)time(0)); - generateEngineImbalance(1); - const double imbalance = simData.engineImbalance->get(); - const double engineImbalanced = imbalanceExtractor(imbalance, 1); - - // Checking engine imbalance - const double paramImbalance = imbalanceExtractor(imbalance, 5) / 10; - - // Setting initial Oil with some randomness and imbalance - const double idleOilL = (rand() % (MAX_OIL - MIN_OIL + 1) + MIN_OIL) / 10; - simData.engineOilTotal[L]->set(idleOilL - ((engineImbalanced == 1) ? paramImbalance : 0)); - const double idleOilR = (rand() % (MAX_OIL - MIN_OIL + 1) + MIN_OIL) / 10; - simData.engineOilTotal[R]->set(idleOilR - ((engineImbalanced == 2) ? paramImbalance : 0)); - - const bool engine1Combustion = static_cast(simData.engineCombustion[L]->updateFromSim(timeStamp, tickCounter)); - const bool engine2Combustion = static_cast(simData.engineCombustion[R]->updateFromSim(timeStamp, tickCounter)); - - double oilTemperaturePre[2]; - if (msfsHandlerPtr->getSimOnGround() && engine1Combustion && engine2Combustion) { - oilTemperaturePre[L] = 75.0; - oilTemperaturePre[R] = 75.0; - } else if (!msfsHandlerPtr->getSimOnGround() && engine1Combustion && engine2Combustion) { - oilTemperaturePre[L] = 85.0; - oilTemperaturePre[R] = 85.0; - } else { - oilTemperaturePre[L] = simData.simVarsDataPtr->data().ambientTemperature; - oilTemperaturePre[R] = simData.simVarsDataPtr->data().ambientTemperature; - } - simData.oilTempDataPtr[L]->data().oilTemp = oilTemperaturePre[L]; - simData.oilTempDataPtr[L]->writeDataToSim(); - simData.oilTempDataPtr[R]->data().oilTemp = oilTemperaturePre[R]; - simData.oilTempDataPtr[R]->writeDataToSim(); - - // Initialize Engine State - simData.engineState[L]->set(OFF); - simData.engineState[R]->set(OFF); - - // Resetting Engine Timers - simData.engineTimer[L]->set(0); - simData.engineTimer[R]->set(0); - - // Initialize Fuel Tanks - const double fuelWeightGallon = simData.simVarsDataPtr->data().fuelWeightPerGallon; // weight of gallon of jet A in lbs - - const double centerQuantity = simData.simVarsDataPtr->data().fuelTankQuantityCenter; // gal - const double leftQuantity = simData.simVarsDataPtr->data().fuelTankQuantityLeft; // gal - const double rightQuantity = simData.simVarsDataPtr->data().fuelTankQuantityRight; // gal - const double leftAuxQuantity = simData.simVarsDataPtr->data().fuelTankQuantityLeftAux; // gal - const double rightAuxQuantity = simData.simVarsDataPtr->data().fuelTankQuantityRightAux; // gal - - // only loads saved fuel quantity on C/D spawn - if (simData.startState->updateFromSim(timeStamp, tickCounter) == 2) { - // Load fuel configuration from file - fuelConfiguration.setConfigFilename(FILENAME_FADEC_CONF_DIRECTORY + atcId + FILENAME_FADEC_CONF_FILE_EXTENSION); - fuelConfiguration.loadConfigurationFromIni(); - - simData.fuelCenterPre->set(fuelConfiguration.getFuelCenter() * fuelWeightGallon); // in Pounds - simData.fuelLeftPre->set(fuelConfiguration.getFuelLeft() * fuelWeightGallon); // in Pounds - simData.fuelRightPre->set(fuelConfiguration.getFuelRight() * fuelWeightGallon); // in Pounds - simData.fuelAuxLeftPre->set(fuelConfiguration.getFuelLeftAux() * fuelWeightGallon); // in Pounds - simData.fuelAuxRightPre->set(fuelConfiguration.getFuelRightAux() * fuelWeightGallon); // in Pounds - - // set fuel levels from configuration to the sim - simData.fuelFeedTankDataPtr->data().fuelLeftMain = fuelConfiguration.getFuelLeft(); - simData.fuelFeedTankDataPtr->data().fuelRightMain = fuelConfiguration.getFuelRight(); - simData.fuelFeedTankDataPtr->writeDataToSim(); - simData.fuelCandAuxDataPtr->data().fuelCenter = fuelConfiguration.getFuelCenter(); - simData.fuelCandAuxDataPtr->data().fuelLeftAux = fuelConfiguration.getFuelLeftAux(); - simData.fuelCandAuxDataPtr->data().fuelRightAux = fuelConfiguration.getFuelRightAux(); - simData.fuelCandAuxDataPtr->writeDataToSim(); - } - // on a non C/D spawn, set fuel levels from the sim - else { - simData.fuelCenterPre->set(centerQuantity * fuelWeightGallon); // in Pounds - simData.fuelLeftPre->set(leftQuantity * fuelWeightGallon); // in Pounds - simData.fuelRightPre->set(rightQuantity * fuelWeightGallon); // in Pounds - simData.fuelAuxLeftPre->set(leftAuxQuantity * fuelWeightGallon); // in Pounds - simData.fuelAuxRightPre->set(rightAuxQuantity * fuelWeightGallon); // in Pounds - } - - // Initialize Pump State - simData.fuelPumpState[L]->set(0); - simData.fuelPumpState[R]->set(0); - - // Initialize Thrust Limits - simData.thrustLimitIdle->set(0); - simData.thrustLimitClimb->set(0); - simData.thrustLimitFlex->set(0); - simData.thrustLimitMct->set(0); - simData.thrustLimitToga->set(0); -} - -void EngineControl_A32NX::generateEngineImbalance(int initial) { - std::string imbalanceCode; - int engine; - - if (initial == 1) { - // Decide Engine with imbalance - if ((rand() % 100) + 1 < 50) { - engine = 1; - } else { - engine = 2; - } - - // Obtain EGT imbalance (Max 20 degree C) - const int egtImbalance = (rand() % 20) + 1; - - // Obtain FF imbalance (Max 36 Kg/h) - const int ffImbalance = (rand() % 36) + 1; - - // Obtain N2 imbalance (Max 0.3%) - const int n2Imbalance = (rand() % 30) + 1; - - // Obtain Oil Qty imbalance (Max 2.0 qt) - const int oilQtyImbalance = (rand() % 20) + 1; - - // Obtain Oil Pressure imbalance (Max 3.0 PSI) - const int oilPressureImbalance = (rand() % 30) + 1; - - // Obtain Oil Pressure Random Idle (-6 to +6 PSI) - const int oilPressureIdle = (rand() % 12) + 1; - - // Obtain Oil Temperature (85 to 95 Celsius) - const int oilTemperatureMax = (rand() % 10) + 86; - - // Zero Padding and Merging - // TODO: this is highly inefficient and should be refactored - maybe use bit operations or even a simple array - imbalanceCode = helper::StringUtils::to_string_with_zero_padding(engine, 2) // - + helper::StringUtils::to_string_with_zero_padding(egtImbalance, 2) // - + helper::StringUtils::to_string_with_zero_padding(ffImbalance, 2) // - + helper::StringUtils::to_string_with_zero_padding(n2Imbalance, 2) // - + helper::StringUtils::to_string_with_zero_padding(oilQtyImbalance, 2) // - + helper::StringUtils::to_string_with_zero_padding(oilPressureImbalance, 2) // - + helper::StringUtils::to_string_with_zero_padding(oilPressureIdle, 2) // - + helper::StringUtils::to_string_with_zero_padding(oilTemperatureMax, 2); - const double value = std::stod(imbalanceCode); - simData.engineImbalance->set(value); - } -} - -double EngineControl_A32NX::imbalanceExtractor(double imbalanceCode, int parameter) { - // Adjust the parameter number to match the position in the imbalance code - parameter = 9 - parameter; - // Shift the decimal point of the imbalance code to the right by the parameter number of places - imbalanceCode = std::floor(imbalanceCode / std::pow(100, parameter)); - // Extract the last two digits of the resulting number - return static_cast(imbalanceCode) % 100; -} - -void EngineControl_A32NX::generateIdleParameters(double pressAltitude, double mach, double ambientTemp, double ambientPressure) { - const double idleCN1 = Tables1502_A32NX::iCN1(pressAltitude, mach, ambientTemp); - const double idleN1 = idleCN1 * sqrt(EngineRatios::theta2(0, ambientTemp)); - const double idleN2 = Tables1502_A32NX::iCN2(pressAltitude, mach) * sqrt(EngineRatios::theta(ambientTemp)); - const double idleCFF = Polynomial_A32NX::correctedFuelFlow(idleCN1, 0, pressAltitude); // lbs/hr - const double idleFF = - idleCFF * Fadec::LBS_TO_KGS * EngineRatios::delta2(0, ambientPressure) * sqrt(EngineRatios::theta2(0, ambientTemp)); // Kg/hr - const double idleEGT = Polynomial_A32NX::correctedEGT(idleCN1, idleCFF, 0, pressAltitude) * EngineRatios::theta2(0, ambientTemp); - - simData.engineIdleN1->set(idleN1); - simData.engineIdleN2->set(idleN2); - simData.engineIdleFF->set(idleFF); - simData.engineIdleEGT->set(idleEGT); -} - -EngineControl_A32NX::EngineState EngineControl_A32NX::engineStateMachine(int engine, // - double engineIgniter, // - bool engineStarter, // - bool engineStarterTurnedOff, // - bool engineMasterTurnedOn, // - bool engineMasterTurnedOff, // - double simN2, // - double idleN2, // - double ambientTemperature) { // -#ifdef PROFILING - profilerEngineStateMachine.start(); -#endif - - const int engineIdx = engine - 1; - - bool resetTimer = false; - - EngineState engineState = static_cast(simData.engineState[engineIdx]->get()); - - // Current State: OFF - if (engineState == OFF) { - if (engineIgniter == 1 && engineStarter && simN2 > 20) { - engineState = ON; - } else if (engineIgniter == 2 && engineMasterTurnedOn) { - engineState = STARTING; - } else { - engineState = OFF; - } - } - // Current State: ON - else if (engineState == ON) { - if (engineStarter) { - engineState = ON; - } else { - engineState = SHUTTING; - } - } - // Current State: Starting. - else if (engineState == STARTING) { - if (engineStarter && simN2 >= (idleN2 - 0.1)) { - engineState = ON; - resetTimer = true; - } else if (engineStarterTurnedOff || engineMasterTurnedOff) { - engineState = SHUTTING; - resetTimer = true; - } else { - engineState = STARTING; - } - } - // Current State: Re-Starting. - else if (engineState == RESTARTING) { - if (engineStarter && simN2 >= (idleN2 - 0.1)) { - engineState = ON; - resetTimer = true; - } else if (engineStarterTurnedOff || engineMasterTurnedOff) { - engineState = SHUTTING; - resetTimer = true; - } else { - engineState = RESTARTING; - } - } - // Current State: Shutting - else if (engineState == SHUTTING) { - if (engineIgniter == 2 && engineMasterTurnedOn) { - engineState = RESTARTING; - resetTimer = true; - } else if (!engineStarter && simN2 < 0.05 && simData.engineEgt[engineIdx]->get() <= ambientTemperature) { - engineState = OFF; - resetTimer = true; - } else if (engineStarter && simN2 > 50) { - engineState = RESTARTING; - resetTimer = true; - } else { - engineState = SHUTTING; - } - } - - simData.engineState[engineIdx]->set(engineState); - if (resetTimer) { - simData.engineTimer[engineIdx]->set(0); - } - - return engineState; - -#ifdef PROFILING - profilerEngineStateMachine.stop(); - if (msfsHandlerPtr->getTickCounter() % 100 == 0) { - profilerEngineStateMachine.print(); - } -#endif -} - -void EngineControl_A32NX::engineStartProcedure(int engine, - EngineState engineState, - double imbalance, - double deltaTime, - [[maybe_unused]] double engineTimer, - double simN2, - [[maybe_unused]] double pressureAltitude, - double ambientTemperature) { -#ifdef PROFILING - profilerEngineStartProcedure.start(); -#endif - - const int engineIdx = engine - 1; - - const double idleN1 = simData.engineIdleN1->get(); - const double idleN2 = simData.engineIdleN2->get(); - const double idleFF = simData.engineIdleFF->get(); - const double idleEGT = simData.engineIdleEGT->get(); - - // Check which engine is imbalanced and set the imbalance parameters - double n2Imbalance = 0; - double ffImbalance = 0; - double egtImbalance = 0; - double engineImbalanced = imbalanceExtractor(imbalance, 1); - if (engineImbalanced == engine) { - n2Imbalance = imbalanceExtractor(imbalance, 4) / 100; - ffImbalance = imbalanceExtractor(imbalance, 3); - egtImbalance = imbalanceExtractor(imbalance, 2); - } - - if (msfsHandlerPtr->getSimOnGround()) { - simData.engineFuelUsed[engineIdx]->set(0); - } - - const double preN2Fbw = simData.engineN2[engineIdx]->get(); - const double preEgtFbw = simData.engineEgt[engineIdx]->get(); - const double newN2Fbw = Polynomial_A32NX::startN2(simN2, preN2Fbw, idleN2 - n2Imbalance); - const double startN1Fbw = Polynomial_A32NX::startN1(newN2Fbw, idleN2 - n2Imbalance, idleN1); - const double startFfFbw = Polynomial_A32NX::startFF(newN2Fbw, idleN2 - n2Imbalance, idleFF - ffImbalance); - const double startEgtFbw = Polynomial_A32NX::startEGT(newN2Fbw, idleN2 - n2Imbalance, ambientTemperature, idleEGT - egtImbalance); - const double shutdownEgtFbw = Polynomial_A32NX::shutdownEGT(preEgtFbw, ambientTemperature, deltaTime); - - simData.engineN2[engineIdx]->set(newN2Fbw); - simData.engineN1[engineIdx]->set(startN1Fbw); - simData.engineFF[engineIdx]->set(startFfFbw); - - if (engineState == RESTARTING) { - if ((std::abs)(startEgtFbw - preEgtFbw) <= 1.5) { - simData.engineEgt[engineIdx]->set(startEgtFbw); - simData.engineState[engineIdx]->set(STARTING); - } else if (startEgtFbw > preEgtFbw) { - simData.engineEgt[engineIdx]->set(preEgtFbw + (0.75 * deltaTime * (idleN2 - newN2Fbw))); - } else { - simData.engineEgt[engineIdx]->set(shutdownEgtFbw); - } - } else { - simData.engineEgt[engineIdx]->set(startEgtFbw); - } - - simData.oilTempDataPtr[engineIdx]->data().oilTemp = Polynomial_A32NX::startOilTemp(newN2Fbw, idleN2, ambientTemperature); - simData.oilTempDataPtr[engineIdx]->writeDataToSim(); - -#ifdef PROFILING - profilerEngineStartProcedure.stop(); - if (msfsHandlerPtr->getTickCounter() % 100 == 0) { - profilerEngineStartProcedure.print(); - } -#endif -} - -void EngineControl_A32NX::engineShutdownProcedure(int engine, // - double ambientTemperature, // - double simN1, // - double deltaTime, // - double engineTimer) { // -#ifdef PROFILING - profilerEngineShutdownProcedure.start(); -#endif - - const int engineIdx = engine - 1; - - if (engineTimer < 1.8) { - simData.engineTimer[engineIdx]->set(engineTimer + deltaTime); - } else { - const double preN1Fbw = simData.engineN1[engineIdx]->get(); - const double preN2Fbw = simData.engineN2[engineIdx]->get(); - const double preEgtFbw = simData.engineEgt[engineIdx]->get(); - - double newN1Fbw = Polynomial_A32NX::shutdownN1(preN1Fbw, deltaTime); - if (simN1 < 5 && simN1 > newN1Fbw) { // Takes care of windmilling - newN1Fbw = simN1; - } - const double newN2Fbw = Polynomial_A32NX::shutdownN2(preN2Fbw, deltaTime); - const double newEgtFbw = Polynomial_A32NX::shutdownEGT(preEgtFbw, ambientTemperature, deltaTime); - - simData.engineN1[engineIdx]->set(newN1Fbw); - simData.engineN2[engineIdx]->set(newN2Fbw); - simData.engineEgt[engineIdx]->set(newEgtFbw); - } - -#ifdef PROFILING - profilerEngineShutdownProcedure.stop(); - if (msfsHandlerPtr->getTickCounter() % 100 == 0) { - profilerEngineShutdownProcedure.print(); - } -#endif -} - -double EngineControl_A32NX::updateFF(int engine, - double imbalance, - double simCN1, - double mach, - double pressureAltitude, - double ambientTemperature, - double ambientPressure) { -#ifdef PROFILING - profilerUpdateFF.start(); -#endif - - const double correctedFuelFlow = Polynomial_A32NX::correctedFuelFlow(simCN1, mach, pressureAltitude); // in lbs/hr. - - // Check which engine is imbalanced and set the imbalance parameter - const double engineImbalanced = imbalanceExtractor(imbalance, 1); - double paramImbalance = 0; - if (engineImbalanced == engine && correctedFuelFlow >= 1) { - paramImbalance = imbalanceExtractor(imbalance, 3); - } - - // Checking Fuel Logic and final Fuel Flow - double outFlow = 0; - if (correctedFuelFlow >= 1) { - outFlow = (std::max)(0.0, // - (correctedFuelFlow * Fadec::LBS_TO_KGS * EngineRatios::delta2(mach, ambientPressure) // - * (std::sqrt)(EngineRatios::theta2(mach, ambientTemperature))) // - - paramImbalance); // - } - simData.engineFF[engine - 1]->set(outFlow); - -#ifdef PROFILING - profilerUpdateFF.stop(); - if (msfsHandlerPtr->getTickCounter() % 100 == 0) { - profilerUpdateFF.print(); - } -#endif - - return correctedFuelFlow; -} - -void EngineControl_A32NX::updatePrimaryParameters(int engine, double imbalance, double simN1, double simN2) { -#ifdef PROFILING - profilerUpdatePrimaryParameters.start(); -#endif - - const int engineIdx = engine - 1; - - // Check which engine is imbalanced and set the imbalance parameter - const double engineImbalanced = imbalanceExtractor(imbalance, 1); - double paramImbalance = 0; - if (engineImbalanced == engine) { - paramImbalance = imbalanceExtractor(imbalance, 4) / 100; - } - simData.engineN1[engineIdx]->set(simN1); - simData.engineN2[engineIdx]->set((std::max)(0.0, simN2 - paramImbalance)); - -#ifdef PROFILING - profilerUpdatePrimaryParameters.stop(); - if (msfsHandlerPtr->getTickCounter() % 100 == 0) { - profilerUpdatePrimaryParameters.print(); - } -#endif -} - -void EngineControl_A32NX::updateEGT(int engine, - double imbalance, - double deltaTime, - double simOnGround, - EngineState engineState, - double simCN1, - double customFuelFlow, - double mach, - double pressureAltitude, - double ambientTemperature) { -#ifdef PROFILING - profilerUpdateEGT.start(); -#endif - - const int engineIdx = engine - 1; - - if (simOnGround == 1 && engineState == OFF) { - simData.engineEgt[engineIdx]->set(ambientTemperature); - } else { - // Check which engine is imbalanced and set the imbalance parameter - const double engineImbalanced = imbalanceExtractor(imbalance, 1); - double paramImbalance = 0; - if (engineImbalanced == engine) { - paramImbalance = imbalanceExtractor(imbalance, 2); - } - const double correctedEGT = Polynomial_A32NX::correctedEGT(simCN1, customFuelFlow, mach, pressureAltitude); - const double egtFbwPreviousEng = simData.engineEgt[engineIdx]->get(); - double egtFbwActualEng = (correctedEGT * EngineRatios::theta2(mach, ambientTemperature)) - paramImbalance; - egtFbwActualEng = egtFbwActualEng + (egtFbwPreviousEng - egtFbwActualEng) * (std::exp)(-0.1 * deltaTime); - simData.engineEgt[engineIdx]->set(egtFbwActualEng); - } - -#ifdef PROFILING - profilerUpdateEGT.stop(); - if (msfsHandlerPtr->getTickCounter() % 100 == 0) { - profilerUpdateEGT.print(); - } -#endif -} - -void EngineControl_A32NX::updateFuel(double deltaTimeSeconds) { -#ifdef PROFILING - profilerUpdateFuel.start(); -#endif - - bool uiFuelTamper = false; - - const double pumpStateLeft = simData.fuelPumpState[L]->get(); - const double pumpStateRight = simData.fuelPumpState[R]->get(); - const bool xfrCenterLeftManual = simData.simVarsDataPtr->data().xfrCenterManual[L] > 1.5; // junction 4 - const bool xfrCenterRightManual = simData.simVarsDataPtr->data().xfrCenterManual[R] > 1.5; // junction 5 - const bool xfrCenterLeftAuto = simData.simVarsDataPtr->data().xfrValveCenterAuto[L] > 1.5 && !xfrCenterLeftManual; // valve 11 - const bool xfrCenterRightAuto = simData.simVarsDataPtr->data().xfrValveCenterAuto[R] > 1.5 && !xfrCenterRightManual; // valve 12 - const bool xfrValveCenterLeftOpen = simData.simVarsDataPtr->data().xfrValveCenterOpen[L] > 1.5 // - && (xfrCenterLeftAuto || xfrCenterLeftManual); // valve 9 - const bool xfrValveCenterRightOpen = simData.simVarsDataPtr->data().xfrValveCenterOpen[R] > 1.5 // - && (xfrCenterRightAuto || xfrCenterRightManual); // valve 10 - const double xfrValveOuterLeft1 = simData.simVarsDataPtr->data().xfrValveOuter1[L]; // valve 6 - const double xfrValveOuterRight1 = simData.simVarsDataPtr->data().xfrValveOuter1[R]; // valve 7 - const double xfrValveOuterLeft2 = simData.simVarsDataPtr->data().xfrValveOuter2[L]; // valve 4 - const double xfrValveOuterRight2 = simData.simVarsDataPtr->data().xfrValveOuter2[R]; // valve 5 - const double lineLeftToCenterFlow = simData.simVarsDataPtr->data().lineToCenterFlow[L]; - const double lineRightToCenterFlow = simData.simVarsDataPtr->data().lineToCenterFlow[R]; - - const double engine1PreFF = simData.enginePreFF[L]->get(); - const double engine2PreFF = simData.enginePreFF[R]->get(); - - const double engine1FF = simData.engineFF[L]->get(); - const double engine2FF = simData.engineFF[R]->get(); - - /// weight of one gallon of fuel in pounds - const double weightLbsPerGallon = simData.simVarsDataPtr->data().fuelWeightPerGallon; - - double fuelLeftPre = simData.fuelLeftPre->get(); - double fuelRightPre = simData.fuelRightPre->get(); - double fuelAuxLeftPre = simData.fuelAuxLeftPre->get(); - double fuelAuxRightPre = simData.fuelAuxRightPre->get(); - double fuelCenterPre = simData.fuelCenterPre->get(); - - const double leftQuantity = simData.simVarsDataPtr->data().fuelTankQuantityLeft * weightLbsPerGallon; // Pounds - const double rightQuantity = simData.simVarsDataPtr->data().fuelTankQuantityRight * weightLbsPerGallon; // Pounds - const double leftAuxQuantity = simData.simVarsDataPtr->data().fuelTankQuantityLeftAux * weightLbsPerGallon; // Pounds - const double rightAuxQuantity = simData.simVarsDataPtr->data().fuelTankQuantityRightAux * weightLbsPerGallon; // Pounds - const double centerQuantity = simData.simVarsDataPtr->data().fuelTankQuantityCenter * weightLbsPerGallon; // Pounds - - const double fuelTotalActual = leftQuantity + rightQuantity + leftAuxQuantity + rightAuxQuantity + centerQuantity; // Pounds - const double fuelTotalPre = fuelLeftPre + fuelRightPre + fuelAuxLeftPre + fuelAuxRightPre + fuelCenterPre; // Pounds - const double deltaFuelRate = (std::abs)(fuelTotalActual - fuelTotalPre) / (weightLbsPerGallon * deltaTimeSeconds); // Pounds/ sec - - const EngineState engine1State = static_cast(simData.engineState[L]->get()); - const EngineState engine2State = static_cast(simData.engineState[R]->get()); - - const double xFeedValve = simData.simVarsDataPtr->data().xFeedValve; - const double leftPump1 = simData.simVarsDataPtr->data().fuelPump1[L]; - const double rightPump1 = simData.simVarsDataPtr->data().fuelPump1[R]; - const double leftPump2 = simData.simVarsDataPtr->data().fuelPump2[L]; - const double rightPump2 = simData.simVarsDataPtr->data().fuelPump2[R]; - const double apuNpercent = simData.apuRpmPercent->get(); - - int isTankClosed = 0; - - /// Delta time for this update in hours - const double deltaTimeHours = deltaTimeSeconds / 3600; - - // Pump State Logic for Left Wing - // TODO: unclear why a timer is used here - const double time = msfsHandlerPtr->getSimulationTime(); - const double elapsedLeft = time - pumpStateLeftTimeStamp; - if (pumpStateLeft == 0 && elapsedLeft >= 1.0) { - if (fuelLeftPre - leftQuantity > 0 && leftQuantity == 0) { - pumpStateLeftTimeStamp = time; - simData.fuelPumpState[L]->set(1); - } else if (fuelLeftPre == 0 && leftQuantity - fuelLeftPre > 0) { - pumpStateLeftTimeStamp = time; - simData.fuelPumpState[L]->set(2); - } else { - simData.fuelPumpState[L]->set(0); - } - } else if (pumpStateLeft == 1 && elapsedLeft >= 2.1) { - pumpStateLeftTimeStamp = time; - simData.fuelPumpState[L]->set(0); - } else if (pumpStateLeft == 2 && elapsedLeft >= 2.7) { - pumpStateLeftTimeStamp = time; - simData.fuelPumpState[L]->set(0); - } - - // Pump State Logic for Right Wing - // TODO: unclear why a timer is used here - const double elapsedRight = time - pumpStateRightTimeStamp; - if (pumpStateRight == 0 && (elapsedRight >= 1.0)) { - if (fuelRightPre - rightQuantity > 0 && rightQuantity == 0) { - pumpStateRightTimeStamp = time; - simData.fuelPumpState[R]->set(1); - } else if (fuelRightPre == 0 && rightQuantity - fuelRightPre > 0) { - pumpStateRightTimeStamp = time; - simData.fuelPumpState[R]->set(2); - } else { - simData.fuelPumpState[R]->set(0); - } - } else if (pumpStateRight == 1 && elapsedRight >= 2.1) { - pumpStateRightTimeStamp = time; - simData.fuelPumpState[R]->set(0); - } else if (pumpStateRight == 2 && elapsedRight >= 2.7) { - pumpStateRightTimeStamp = time; - simData.fuelPumpState[R]->set(0); - } - - // Checking for in-game UI Fuel tampering - const bool isReadyVar = msfsHandlerPtr->getAircraftIsReadyVar(); - const double refuelRate = simData.refuelRate->get(); - const bool refuelStartedByUser = simData.refuelStartedByUser->getAsBool(); - if ((isReadyVar && !refuelStartedByUser && deltaFuelRate > FUEL_RATE_THRESHOLD) || - (isReadyVar && refuelStartedByUser && deltaFuelRate > FUEL_RATE_THRESHOLD && refuelRate < 2)) { - uiFuelTamper = true; - } - - const FLOAT64 aircraftDevelopmentStateVar = msfsHandlerPtr->getAircraftDevelopmentStateVar(); - - if (uiFuelTamper && aircraftDevelopmentStateVar == 0) { - simData.fuelLeftPre->set(fuelLeftPre); // in Pounds - simData.fuelRightPre->set(fuelRightPre); // in Pounds - simData.fuelAuxLeftPre->set(fuelAuxLeftPre); // in Pounds - simData.fuelAuxRightPre->set(fuelAuxRightPre); // in Pounds - simData.fuelCenterPre->set(fuelCenterPre); // in Pounds - - simData.fuelFeedTankDataPtr->data().fuelLeftMain = (fuelLeftPre / weightLbsPerGallon); - simData.fuelFeedTankDataPtr->data().fuelRightMain = (fuelRightPre / weightLbsPerGallon); - simData.fuelFeedTankDataPtr->writeDataToSim(); - - simData.fuelCandAuxDataPtr->data().fuelCenter = (fuelCenterPre / weightLbsPerGallon); - simData.fuelCandAuxDataPtr->data().fuelLeftAux = (fuelAuxLeftPre / weightLbsPerGallon); - simData.fuelCandAuxDataPtr->data().fuelRightAux = (fuelAuxRightPre / weightLbsPerGallon); - simData.fuelCandAuxDataPtr->writeDataToSim(); - - } - // Detects refueling from the EFB - else if (!uiFuelTamper && refuelStartedByUser == 1) { - simData.fuelLeftPre->set(leftQuantity); // in Pounds - simData.fuelRightPre->set(rightQuantity); // in Pounds - simData.fuelAuxLeftPre->set(leftAuxQuantity); // in Pounds - simData.fuelAuxRightPre->set(rightAuxQuantity); // in Pounds - simData.fuelCenterPre->set(centerQuantity); // in Pounds - } else { - if (uiFuelTamper == 1) { - fuelLeftPre = leftQuantity; // Pounds - fuelRightPre = rightQuantity; // Pounds - fuelAuxLeftPre = leftAuxQuantity; // Pounds - fuelAuxRightPre = rightAuxQuantity; // Pounds - fuelCenterPre = centerQuantity; // Pounds - } - //----------------------------------------------------------- - // Cross-feed Logic - // isTankClosed = 0, x-feed valve closed - // isTankClosed = 1, left tank does not supply fuel - // isTankClosed = 2, right tank does not supply fuel - // isTankClosed = 3, left & right tanks do not supply fuel - // isTankClosed = 4, both tanks supply fuel - if (xFeedValve > 0.0) { - if (leftPump1 == 0 && leftPump2 == 0 && rightPump1 == 0 && rightPump2 == 0) - isTankClosed = 3; - else if (leftPump1 == 0 && leftPump2 == 0) - isTankClosed = 1; - else if (rightPump1 == 0 && rightPump2 == 0) - isTankClosed = 2; - else - isTankClosed = 4; - } - - double xfrCenterToLeft = 0; - double xfrCenterToRight = 0; - double xfrAuxLeft = 0; - double xfrAuxRight = 0; - - double fuelFlowRateChange = 0; - double previousFuelFlowRate = 0; - double fuelBurn1 = 0; - double fuelBurn2 = 0; - double apuBurn1 = 0; - double apuBurn2 = 0; - - //-------------------------------------------- - // Left Engine and Wing routine - if (fuelLeftPre > 0) { - // Cycle Fuel Burn for Engine 1 - if (aircraftDevelopmentStateVar != 2) { - fuelFlowRateChange = (engine1FF - engine1PreFF) / deltaTimeHours; - previousFuelFlowRate = engine1PreFF; - fuelBurn1 = (fuelFlowRateChange * pow(deltaTimeHours, 2) / 2) + (previousFuelFlowRate * deltaTimeHours); // KG - } - // Fuel transfer routine for Left Wing - if (xfrValveOuterLeft1 > 0.0 || xfrValveOuterLeft2 > 0.0) { - xfrAuxLeft = fuelAuxLeftPre - leftAuxQuantity; - } - } else { - fuelBurn1 = 0; - fuelLeftPre = 0; - } - - //-------------------------------------------- - // Right Engine and Wing routine - if (fuelRightPre > 0) { - // Cycle Fuel Burn for Engine 2 - if (aircraftDevelopmentStateVar != 2) { - fuelFlowRateChange = (engine2FF - engine2PreFF) / deltaTimeHours; - previousFuelFlowRate = engine2PreFF; - fuelBurn2 = (fuelFlowRateChange * pow(deltaTimeHours, 2) / 2) + (previousFuelFlowRate * deltaTimeHours); // KG - } - // Fuel transfer routine for Right Wing - if (xfrValveOuterRight1 > 0.0 || xfrValveOuterRight2 > 0.0) { - xfrAuxRight = fuelAuxRightPre - rightAuxQuantity; - } - } else { - fuelBurn2 = 0; - fuelRightPre = 0; - } - - /// apu fuel consumption for this frame in pounds - double apuFuelConsumption = simData.simVarsDataPtr->data().apuFuelConsumption * weightLbsPerGallon * deltaTimeHours; - - // check if APU is actually running instead of just the ASU which doesn't consume fuel - if (apuNpercent <= 0.0) { - apuFuelConsumption = 0.0; - } - - apuBurn1 = apuFuelConsumption; - apuBurn2 = 0; - - //-------------------------------------------- - // Fuel used accumulators - double fuelUsedLeft = simData.engineFuelUsed[L]->get() + fuelBurn1; - double fuelUsedRight = simData.engineFuelUsed[R]->get() + fuelBurn2; - - //-------------------------------------------- - // Cross-feed fuel burn routine - // If fuel pumps for a given tank are closed, - // all fuel will be burnt on the other tank - switch (isTankClosed) { - case 1: - fuelBurn2 = fuelBurn1 + fuelBurn2; - fuelBurn1 = 0; - apuBurn1 = 0; - apuBurn2 = apuFuelConsumption; - break; - case 2: - fuelBurn1 = fuelBurn1 + fuelBurn2; - fuelBurn2 = 0; - break; - case 3: - fuelBurn1 = 0; - fuelBurn2 = 0; - apuBurn1 = apuFuelConsumption * 0.5; - apuBurn2 = apuFuelConsumption * 0.5; - break; - case 4: - apuBurn1 = apuFuelConsumption * 0.5; - apuBurn2 = apuFuelConsumption * 0.5; - break; - default: - break; - } - - //-------------------------------------------- - // Center Tank transfer routine - double lineFlowRatio = 0; - if (xfrValveCenterLeftOpen && xfrValveCenterRightOpen) { - if (lineLeftToCenterFlow < 0.1 && lineRightToCenterFlow < 0.1) - lineFlowRatio = 0.5; - else - lineFlowRatio = lineLeftToCenterFlow / (lineLeftToCenterFlow + lineRightToCenterFlow); - - xfrCenterToLeft = (fuelCenterPre - centerQuantity) * lineFlowRatio; - xfrCenterToRight = (fuelCenterPre - centerQuantity) * (1 - lineFlowRatio); - } else if (xfrValveCenterLeftOpen) - xfrCenterToLeft = fuelCenterPre - centerQuantity; - else if (xfrValveCenterRightOpen) - xfrCenterToRight = fuelCenterPre - centerQuantity; - - //-------------------------------------------- - // Final Fuel levels for left and right inner tanks - const double fuelLeft = (fuelLeftPre - (fuelBurn1 * Fadec::KGS_TO_LBS)) + xfrAuxLeft + xfrCenterToLeft - apuBurn1; // Pounds - const double fuelRight = (fuelRightPre - (fuelBurn2 * Fadec::KGS_TO_LBS)) + xfrAuxRight + xfrCenterToRight - apuBurn2; // Pounds - - //-------------------------------------------- - // Setting new pre-cycle conditions - simData.enginePreFF[L]->set(engine1FF); - simData.enginePreFF[R]->set(engine2FF); - - simData.engineFuelUsed[L]->set(fuelUsedLeft); - simData.engineFuelUsed[R]->set(fuelUsedRight); - - simData.fuelAuxLeftPre->set(leftAuxQuantity); - simData.fuelAuxRightPre->set(rightAuxQuantity); - simData.fuelCenterPre->set(centerQuantity); - - simData.fuelLeftPre->set(fuelLeft); // in Pounds - simData.fuelRightPre->set(fuelRight); // in Pounds - - simData.fuelFeedTankDataPtr->data().fuelLeftMain = (fuelLeft / weightLbsPerGallon); - simData.fuelFeedTankDataPtr->data().fuelRightMain = (fuelRight / weightLbsPerGallon); - simData.fuelFeedTankDataPtr->writeDataToSim(); - } - - //-------------------------------------------- - // Will save the current fuel quantities at a certain interval - // if the aircraft is on the ground and the engines are off/shutting down - if (msfsHandlerPtr->getSimOnGround() && (msfsHandlerPtr->getSimulationTime() - lastFuelSaveTime) > FUEL_SAVE_INTERVAL && - (engine1State == OFF || engine1State == SHUTTING || engine2State == OFF || engine2State == SHUTTING)) { - fuelConfiguration.setFuelLeft(simData.fuelLeftPre->get() / weightLbsPerGallon); - fuelConfiguration.setFuelRight(simData.fuelRightPre->get() / weightLbsPerGallon); - fuelConfiguration.setFuelCenter(simData.fuelCenterPre->get() / weightLbsPerGallon); - fuelConfiguration.setFuelLeftAux(simData.fuelAuxLeftPre->get() / weightLbsPerGallon); - fuelConfiguration.setFuelRightAux(simData.fuelAuxRightPre->get() / weightLbsPerGallon); - - fuelConfiguration.saveConfigurationToIni(); - lastFuelSaveTime = msfsHandlerPtr->getSimulationTime(); - } - -#ifdef PROFILING - profilerUpdateFuel.stop(); - if (msfsHandlerPtr->getTickCounter() % 100 == 0) { - profilerUpdateFuel.print(); - } -#endif -} - -void EngineControl_A32NX::updateThrustLimits(double simulationTime, - double pressureAltitude, - double ambientTemperature, - double ambientPressure, - double mach, - [[maybe_unused]] double simN1highest, - int packs, - int nai, - int wai) { -#ifdef PROFILING - profilerUpdateThrustLimits.start(); -#endif - - const double flexTemp = simData.airlinerToFlexTemp->get(); - const double pressAltitude = simData.simVarsDataPtr->data().pressureAltitude; - - double to = 0; - double ga = 0; - double toga = 0; - double clb = 0; - double mct = 0; - double flex_to = 0; - double flex_ga = 0; - double flex = 0; - - // Write all N1 Limits - to = ThrustLimits_A32NX::limitN1(0, (std::min)(16600.0, pressAltitude), ambientTemperature, ambientPressure, 0, packs, nai, wai); - ga = ThrustLimits_A32NX::limitN1(1, (std::min)(16600.0, pressAltitude), ambientTemperature, ambientPressure, 0, packs, nai, wai); - if (flexTemp > 0) { - flex_to = - ThrustLimits_A32NX::limitN1(0, (std::min)(16600.0, pressAltitude), ambientTemperature, ambientPressure, flexTemp, packs, nai, wai); - flex_ga = - ThrustLimits_A32NX::limitN1(1, (std::min)(16600.0, pressAltitude), ambientTemperature, ambientPressure, flexTemp, packs, nai, wai); - } - clb = ThrustLimits_A32NX::limitN1(2, pressAltitude, ambientTemperature, ambientPressure, 0, packs, nai, wai); - mct = ThrustLimits_A32NX::limitN1(3, pressAltitude, ambientTemperature, ambientPressure, 0, packs, nai, wai); - - // transition between TO and GA limit ----------------------------------------------------------------------------- - double machFactorLow = (std::max)(0.0, (std::min)(1.0, (mach - 0.04) / 0.04)); - toga = to + (ga - to) * machFactorLow; - flex = flex_to + (flex_ga - flex_to) * machFactorLow; - - // adaption of CLB due to FLX limit if necessary ------------------------------------------------------------------ - bool isFlexActive = false; - const double thrustLimitType = simData.thrustLimitType->get(); - if ((prevThrustLimitType != 3 && thrustLimitType == 3) || (prevFlexTemperature == 0 && flexTemp > 0)) { - isFlexActive = true; - } else if ((flexTemp == 0) || (thrustLimitType == 4)) { - isFlexActive = false; - } - - double transitionStartTime = 0; - double transitionFactor = 0; - if (isFlexActive && !isTransitionActive && thrustLimitType == 1) { - isTransitionActive = true; - transitionStartTime = simulationTime; - transitionFactor = 0.2; - // transitionFactor = (clb - flex) / transitionTime; - } else if (!isFlexActive) { - isTransitionActive = false; - transitionStartTime = 0; - transitionFactor = 0; - } - - double deltaThrust = 0; - if (isTransitionActive) { - double timeDifference = (std::max)(0.0, (simulationTime - transitionStartTime) - TRANSITION_WAIT_TIME); - if (timeDifference > 0 && clb > flex) { - deltaThrust = (std::min)(clb - flex, timeDifference * transitionFactor); - } - if (flex + deltaThrust >= clb) { - isFlexActive = false; - isTransitionActive = false; - } - } - - if (isFlexActive) { - clb = (std::min)(clb, flex) + deltaThrust; - } - - prevThrustLimitType = thrustLimitType; - prevFlexTemperature = flexTemp; - - // thrust transitions for MCT and TOGA ---------------------------------------------------------------------------- - - // get factors - const double machFactor = (std::max)(0.0, (std::min)(1.0, ((mach - 0.37) / 0.05))); - const double altitudeFactorLow = (std::max)(0.0, (std::min)(1.0, ((pressureAltitude - 16600) / 500))); - const double altitudeFactorHigh = (std::max)(0.0, (std::min)(1.0, ((pressureAltitude - 25000) / 500))); - - // adapt thrust limits - if (pressureAltitude >= 25000) { - mct = (std::max)(clb, mct + (clb - mct) * altitudeFactorHigh); - toga = mct; - } else { - if (mct > toga) { - mct = toga + (mct - toga) * (std::min)(1.0, altitudeFactorLow + machFactor); - toga = mct; - } else { - toga = toga + (mct - toga) * (std::min)(1.0, altitudeFactorLow + machFactor); - } - } - - // write limits --------------------------------------------------------------------------------------------------- - simData.thrustLimitIdle->set(simData.engineIdleN1->get()); - simData.thrustLimitToga->set(toga); - simData.thrustLimitFlex->set(flex); - simData.thrustLimitClimb->set(clb); - simData.thrustLimitMct->set(mct); - -#ifdef PROFILING - profilerUpdateThrustLimits.stop(); - if (msfsHandlerPtr->getTickCounter() % 100 == 0) { - profilerUpdateThrustLimits.print(); - } -#endif -} - -/* - * Previous code - call to it was already commented out and this function was not in use. - * Keeping it to make completing/fixing it easier. - * It is not migrated to the cpp framework yet. - * - * /// -/// FBW Oil Qty, Pressure and Temperature (in Quarts, PSI and degree Celsius) -/// Updates Oil with realistic values visualized in the SD -/// -void updateOil(int engine, double imbalance, double thrust, double simN2, double deltaN2, double deltaTime, double ambientTemp) { - double steadyTemperature; - double thermalEnergy; - double oilTemperaturePre; - double oilQtyActual; - double oilTotalActual; - double oilQtyObjective; - double oilBurn; - double oilIdleRandom; - double oilPressure; - -//-------------------------------------------- -// Engine Reading -//-------------------------------------------- -if (engine == 1) { -steadyTemperature = simVars->getEngine1EGT(); -thermalEnergy = thermalEnergy1; -oilTemperaturePre = oilTemperatureLeftPre; -oilQtyActual = simVars->getEngine1Oil(); -oilTotalActual = simVars->getEngine1OilTotal(); -} else { -steadyTemperature = simVars->getEngine2EGT(); -thermalEnergy = thermalEnergy2; -oilTemperaturePre = oilTemperatureRightPre; -oilQtyActual = simVars->getEngine2Oil(); -oilTotalActual = simVars->getEngine2OilTotal(); -} - -//-------------------------------------------- -// Oil Temperature -//-------------------------------------------- -if (simOnGround == 1 && engineState == 0 && ambientTemp > oilTemperaturePre - 10) { -oilTemperature = ambientTemp; -} else { -if (steadyTemperature > oilTemperatureMax) { - steadyTemperature = oilTemperatureMax; -} -thermalEnergy = (0.995 * thermalEnergy) + (deltaN2 / deltaTime); -oilTemperature = poly->oilTemperature(thermalEnergy, oilTemperaturePre, steadyTemperature, deltaTime); -} - -//-------------------------------------------- -// Oil Quantity -//-------------------------------------------- -// Calculating Oil Qty as a function of thrust -oilQtyObjective = oilTotalActual * (1 - poly->oilGulpPct(thrust)); -oilQtyActual = oilQtyActual - (oilTemperature - oilTemperaturePre); - -// Oil burnt taken into account for tank and total oil -oilBurn = (0.00011111 * deltaTime); -oilQtyActual = oilQtyActual - oilBurn; -oilTotalActual = oilTotalActual - oilBurn; - -//-------------------------------------------- -// Oil Pressure -//-------------------------------------------- -// Engine imbalance -engineImbalanced = imbalanceExtractor(imbalance, 1); -paramImbalance = imbalanceExtractor(imbalance, 6) / 10; -oilIdleRandom = imbalanceExtractor(imbalance, 7) - 6; - -// Checking engine imbalance -if (engineImbalanced != engine) { -paramImbalance = 0; -} - -oilPressure = poly->oilPressure(simN2) - paramImbalance + oilIdleRandom; - -//-------------------------------------------- -// Engine Writing -//-------------------------------------------- -if (engine == 1) { -thermalEnergy1 = thermalEnergy; -oilTemperatureLeftPre = oilTemperature; -simVars->setEngine1Oil(oilQtyActual); -simVars->setEngine1OilTotal(oilTotalActual); -SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempLeft, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), - &oilTemperature); -SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilPsiLeft, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &oilPressure); -} else { -thermalEnergy2 = thermalEnergy; -oilTemperatureRightPre = oilTemperature; -simVars->setEngine2Oil(oilQtyActual); -simVars->setEngine2OilTotal(oilTotalActual); -SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempRight, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), - &oilTemperature); -SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilPsiRight, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &oilPressure); -} -} - - */ diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/EngineControlA32NX.h b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/EngineControlA32NX.h deleted file mode 100644 index e3b37e9558d..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/EngineControlA32NX.h +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_ENGINECONTROL_A32NX_H -#define FLYBYWIRE_AIRCRAFT_ENGINECONTROL_A32NX_H - -#include "MsfsHandler.h" - -#include "FadecSimData_A32NX.hpp" -#include "FuelConfiguration_A32NX.h" - -#define FILENAME_FADEC_CONF_DIRECTORY "\\work\\AircraftStates\\" -#define FILENAME_FADEC_CONF_FILE_EXTENSION ".ini" - -/** - * @class EngineControl_A32NX - * @brief Manages the engine control for the A32NX aircraft, coordinating and synchronising the - * simulator's jet engine simulation with realistic custom values. - * - * Engine control is adding more realistic values and behaviour to the engine simulation of the simulator. - * Esp. for startup, shutdown, fuel consumption, thrust limits, etc. - * - * TODO: The EngineControl_A32NX class is still work in progress and might be extended or replaced in the future. - */ -class EngineControl_A32NX { - private: - // Convenience pointer to the msfs handler - MsfsHandler* msfsHandlerPtr = nullptr; - - // Convenience pointer to the data manager - DataManager* dataManagerPtr = nullptr; - - // FADEC simulation data - FadecSimData_A32NX simData{}; - - // ATC ID for the aircraft used to load and store the fuel levels - std::string atcId{}; - - // Fuel configuration for loading and storing fuel levels - FuelConfiguration_A32NX fuelConfiguration{}; - - // previous time the fuel levels were saved to file - double lastFuelSaveTime = 0.0; - static constexpr double FUEL_SAVE_INTERVAL = 5.0; // seconds - - // some pump timings - unclear why these are needed - double pumpStateLeftTimeStamp = 0.0; - double pumpStateRightTimeStamp = 0.0; - - bool isTransitionActive = false; - // thrust limits transition for flex - static constexpr double TRANSITION_WAIT_TIME = 10; - - // values that need previous state - double prevFlexTemperature = 0.0; - double prevThrustLimitType = 0.0; - double prevEngineMasterPos[2] = {0, 0}; - bool prevEngineStarterState[2] = {false, false}; - - // additional constants - static constexpr int MAX_OIL = 200; - static constexpr int MIN_OIL = 140; - static constexpr double FUEL_RATE_THRESHOLD = 661; // lbs/sec for determining fuel ui tampering - - /** - * @enum EngineState - * @brief Enumerates the possible states for the engine state machine. - * - * @var OFF The engine is turned off. This is the initial state of the engine. - * @var ON The engine is turned on and running. - * @var STARTING The engine is in the process of starting up. - * @var RESTARTING The engine is in the process of restarting. - * @var SHUTTING The engine is in the process of shutting down. - */ - enum EngineState { - OFF = 0, - ON = 1, - STARTING = 2, - RESTARTING = 3, - SHUTTING = 4, - }; - -#ifdef PROFILING - // Profiling for the engine control - can eventually be removed - SimpleProfiler profilerUpdate{"Fadec::EngineControl_A32NX::update()", 100}; - SimpleProfiler profilerGenerateParameters{"Fadec::EngineControl_A32NX::generateIdleParameters()", 100}; - SimpleProfiler profilerEngineStateMachine{"Fadec::EngineControl_A32NX::engineStateMachine()", 100}; - SimpleProfiler profilerEngineStartProcedure{"Fadec::EngineControl_A32NX::engineStartProcedure()", 100}; - SimpleProfiler profilerEngineShutdownProcedure{"Fadec::EngineControl_A32NX::engineShutdownProcedure()", 100}; - SimpleProfiler profilerUpdateFF{"Fadec::EngineControl_A32NX::updateFF()", 100}; - SimpleProfiler profilerUpdatePrimaryParameters{"Fadec::EngineControl_A32NX::updatePrimaryParameters()", 100}; - SimpleProfiler profilerUpdateEGT{"Fadec::EngineControl_A32NX::updateEGT()", 100}; - SimpleProfiler profilerUpdateFuel{"Fadec::EngineControl_A32NX::updateFuel()", 100}; - SimpleProfiler profilerUpdateThrustLimits{"Fadec::EngineControl_A32NX::updateThrustLimits()", 100}; - SimpleProfiler profilerUpdateOil{"Fadec::EngineControl_A32NX::updateOil()", 100}; -#endif - - // =========================================================================== - // Public methods - // =========================================================================== - - public: - /** - * @brief Initializes the EngineControl_A32NX class once during the gauge initialization. - * @param msfsHandler - */ - void initialize(MsfsHandler* msfsHandler); - - /** - * @brief Updates the EngineControl_A32NX class once per frame. - * @param pData - */ - void update(sGaugeDrawData* pData); - - /** - * @brief Shuts down the EngineControl_A32NX class once during the gauge shutdown. - */ - void shutdown(); - - // =========================================================================== - // Private methods - // =========================================================================== - - private: - /** - * @brief Initialize the FADEC and Fuel model - * This is done after we have retrieved the ATC ID so we can load the fuel levels - */ - void initializeEngineControlData(); - - /** - * @brief Generates a random engine imbalance. - * - * This function generates a random engine imbalance for an engine. - * As this imbalance is stored and shared via a LVar the imbalance is encoded as a digital number/word. - * (LVars are limited to FLOAT64) - * - * The coded digital word is structured as follows: - * - The first 2 digits represent the engine number (1 or 2). - * - The next 2 digits represent the EGT imbalance (max 20 degree C). - * - The next 2 digits represent the Fuel Flow imbalance (max 36 Kg/h). - * - The next 2 digits represent the N2 imbalance (max 0.3%). - * - The next 2 digits represent the Oil Quantity imbalance (max 2.0 qt). - * - The next 2 digits represent the Oil Pressure imbalance (max 3.0 PSI). - * - The next 2 digits represent the Oil Pressure Random Idle (-6 to +6 PSI). - * - The last 2 digits represent the Oil Temperature (85 to 95 Celsius). - * - * The function is currently using string operations to generate the coded digital word. - * Future work includes refactoring this to avoid string operations. - * TODO: this is highly inefficient and should be refactored - maybe use bit operations or even a simple array - * - * @param initial A flag to indicate whether this is the initial generation of engine imbalance. If initial is 1, a new imbalance is - * generated. Otherwise, the existing imbalance is used. TODO: is this required? A flag would do instead of a parameter - */ - void generateEngineImbalance(int i); - - /** - * @brief Extracts a specific parameter from the imbalance code. - * - * This function extracts a specific parameter from the imbalance code. The imbalance code is a coded digital word that represents the - * engine imbalance. The coded digital word is structured as follows: - * - The first 2 digits represent the engine number (1 or 2). - * - The next 2 digits represent the EGT imbalance (max 20 degree C). - * - The next 2 digits represent the Fuel Flow imbalance (max 36 Kg/h). - * - The next 2 digits represent the N2 imbalance (max 0.3%). - * - The next 2 digits represent the Oil Quantity imbalance (max 2.0 qt). - * - The next 2 digits represent the Oil Pressure imbalance (max 3.0 PSI). - * - The next 2 digits represent the Oil Pressure Random Idle (-6 to +6 PSI). - * - The last 2 digits represent the Oil Temperature (85 to 95 Celsius). - * - * The function takes the imbalance code number and the parameter number as input. It then calculates the - * position of the parameter in the imbalance code and extracts the corresponding two digits. - * The function returns the extracted parameter as a double. - * TODO: this is highly inefficient and should be refactored - maybe use bit operations or even a simple array - * - * @param imbalanceCode The imbalance code from which to extract the parameter. - * @param parameter The number of the parameter to extract. The parameters are numbered from 1 to 8, - * with 1 being the engine number and 8 being the oil temperature. - * @return The extracted parameter as a double. - */ - double imbalanceExtractor(double imbalance, int parameter); - - /** - * @brief Generate Idle / Initial Engine Parameters (non-imbalanced) - * - * @param pressAltitude The current pressure altitude of the aircraft in feet. - * @param mach The current Mach number of the aircraft. - * @param ambientTemp The current ambient temperature in degrees Celsius. - * @param ambientPressure The current ambient pressure in hPa. - */ - void generateIdleParameters(double pressAltitude, double mach, double ambientTemp, double ambientPressure); - - /** - * @brief Manages the state and state changes of the engine. - * - * @param engine The engine number (1 or 2). - * @param engineIgniter The status of the engine igniter. - * @param engineStarter The status of the engine starter. - * @param engineStarterTurnedOff The status of the engine starter being turned off. - * @param engineMasterTurnedOn The status of the engine master switch being turned on. - * @param engineMasterTurnedOff The status of the engine master switch being turned off. - * @param simN2 The current N2 value from the simulator. - * @param idleN2 The idle N2 value. - * @param ambientTemperature The current ambient temperature. - * @return The current state of the engine as an enum of type EngineState. - * @see EngineState - */ - EngineControl_A32NX::EngineState engineStateMachine(int engine, - double engineIgniter, - bool engineStarter, - bool engineStarterTurnedOff, - bool engineMasterTurnedOn, - bool engineMasterTurnedOff, - double simN2, - double idleN2, - double ambientTemperature); - - /** - * @brief This function manages the engine start procedure. - * - * @param engine The engine number (1 or 2). - * @param engineState The current state of the engine as an enum of type EngineState. - * @param imbalance The current encoded imbalance number of the engine. - * @param deltaTime The time difference since the last update in seconds. - * @param engineTimer A timer used to calculate the elapsed time for various operations. - * @param simN2 The current N2 value from the simulator in percent. - * @param pressureAltitude The current pressure altitude of the aircraft in feet. - * @param ambientTemperature The current ambient temperature in degrees Celsius. - * - * @see EngineState - */ - void engineStartProcedure(int engine, - EngineState engineState, - double imbalance, - double deltaTime, - double engineTimer, - double simN2, - double pressureAltitude, - double ambientTemperature); - - /** - * @brief This function manages the engine shutdown procedure. - * TODO: Temporary solution as per comment in original code - * - * @param engine The engine number (1 or 2). - * @param ambientTemperature The current ambient temperature in degrees Celsius to calculate the engine's operating temperature. - * @param simN1 The current N1 value from the simulator. - * @param deltaTime The time difference since the last update. This is used to calculate the rate of change of various parameters. - * @param engineTimer A timer used to calculate the elapsed time for various operations. - */ - void engineShutdownProcedure(int engine, double ambientTemperature, double simN1, double deltaTime, double engineTimer); - - /** - * @brief Updates the fuel flow of the engine. - * - * @param engine The engine number (1 or 2). - * @param imbalance The current encoded imbalance number of the engine. - * @param simCN1 The current corrected fan speed value from the simulator. - * @param mach The current Mach number of the aircraft. - * @param pressureAltitude The current pressure altitude of the aircraft in feet. - * @param ambientTemperature The current ambient temperature in degrees Celsius to calculate the engine's operating temperature. - * @param ambientPressure The current ambient pressure in hPa. - * @return The updated fuel flow as a double. - */ - double updateFF(int engine, // - double imbalance, // - double simCN1, // - double mach, // - double pressureAltitude, // - double ambientTemperature, // - double ambientPressure); // - - /** - * @brief Updates the primary cusomter parameters (LVars) of the engine when not starting or stopping the engine - * and the sim has control. - * - * @param engine The engine number (1 or 2). - * @param imbalance The current encoded imbalance number of the engine. - * @param simN1 The current N1 value from the simulator in percent. - * @param simN2 The current N2 value from the simulator in percent. - */ - void updatePrimaryParameters(int engine, double imbalance, double simN1, double simN2); - - /** - * @brief FBW Exhaust Gas Temperature (in degree Celsius). Updates EGT with realistic values visualized in the ECAM - * - * @param engine The engine number (1 or 2). - * @param imbalance The current encoded imbalance number of the engine. - * @param deltaTime The time difference since the last update to calculate the rate of change of various parameters. - * @param simOnGround The on ground status of the aircraft (0 or 1). - * @param engineState The current state of the engine. - * @param simCN1 The current corrected fan speed value from the simulator. - * @param customFuelFlow The current custom fuel flow of the engine. - * @param mach The current Mach number of the aircraft. - * @param pressureAltitude The current pressure altitude of the aircraft in feet. - * @param ambientTemperature The current ambient temperature in degrees Celsius. - * - * @see EngineState - */ - void updateEGT(int engine, - double imbalance, - double deltaTime, - double simOnGround, - EngineState engineState, - double simCN1, - double customFuelFlow, - double mach, - double pressureAltitude, - double ambientTemperature); - - /** - * @brief FBW Fuel Consumption and Tanking. Updates Fuel Consumption with realistic values - * - * @param deltaTimeSeconds Frame delta time in seconds - */ - void updateFuel(double deltaTimeSeconds); - - /** - * @brief Updates the thrust limits of the engine. - * - * @param simulationTime The current time in the simulation. - * @param pressureAltitude The current pressure altitude of the aircraft in feet. - * @param ambientTemperature The current ambient temperature in degrees Celsius. - * @param ambientPressure The current ambient pressure in hPa. - * @param mach The current Mach number of the aircraft. - * @param simN1highest The highest N1 value from the simulator. - * @param packs The current state of the packs (0 or 1). - * @param nai The current state of the NAI (0 or 1). - * @param wai The current state of the WAI (0 or 1). - */ - void updateThrustLimits(double simulationTime, - double pressureAltitude, - double ambientTemperature, - double ambientPressure, - double mach, - double simN1highest, - int packs, - int nai, - int wai); -}; - -#endif // FLYBYWIRE_AIRCRAFT_ENGINECONTROL_A32NX_H diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FadecSimData_A32NX.hpp b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FadecSimData_A32NX.hpp deleted file mode 100644 index c368db51c29..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FadecSimData_A32NX.hpp +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_FADECSIMDATA_A32NX_HPP -#define FLYBYWIRE_AIRCRAFT_FADECSIMDATA_A32NX_HPP - -#include - -#include "DataManager.h" - -// Make access to variables more readable -enum EngineAndSide { - L = 0, // - E1 = L, // - ENGINE_1 = L, // - R = 1, // - E2 = R, // - ENGINE_2 = R, // -}; - -/** - * @class FadecSimData_A32NX - * @brief This class manages the simulation data for the FADEC (Full Authority Digital Engine Control) - * simulation for the A32NX aircraft. - */ -class FadecSimData_A32NX { - public: - // Notification groups for events - enum NotificationGroup { NOTIFICATION_GROUP_0 }; - - struct AtcIdData { - char atcID[32]; // MSFS docs say this is max 10 chars - we use 32 for safety - }; - DataDefinitionVector atcIdDataDef = { - {"ATC ID", 0, UNITS.None, SIMCONNECT_DATATYPE_STRING32} // - }; - /** - * @struct AtcID - * @brief This struct represents the ATC ID of the aircraft which we use to create the filename to store and load the fuel configuration. - * @var char atcID[32] The ATC ID of the aircraft. - * @note MSFS docs say that the ATC ID is a string of max 10 characters. We use 32 for safety. - * @see https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Aircraft_SimVars/Aircraft_RadioNavigation_Variables.htm#ATC%20ID - */ - DataDefinitionVariablePtr atcIdDataPtr; - - struct FuelFeedTankData { - FLOAT64 fuelLeftMain; // Gallons - FLOAT64 fuelRightMain; // Gallons - }; - DataDefinitionVector fuelLRDataDef = { - {"FUEL TANK LEFT MAIN QUANTITY", 0, UNITS.Gallons}, // - {"FUEL TANK RIGHT MAIN QUANTITY", 0, UNITS.Gallons} // - }; - DataDefinitionVariablePtr fuelFeedTankDataPtr; - - struct FuelTankData { - FLOAT64 fuelCenter; // Gallons - FLOAT64 fuelLeftAux; // Gallons - FLOAT64 fuelRightAux; // Gallons - }; - DataDefinitionVector fuelCandAuxDataDef = { - {"FUEL TANK CENTER QUANTITY", 0, UNITS.Gallons}, // - {"FUEL TANK LEFT AUX QUANTITY", 0, UNITS.Gallons}, // - {"FUEL TANK RIGHT AUX QUANTITY", 0, UNITS.Gallons} // - }; - DataDefinitionVariablePtr fuelCandAuxDataPtr; - - // Oil Temp Data in separate Data Definitions as they are updated separately - // clang-format off - struct OliTempData { - FLOAT64 oilTemp; // Celsius - }; - DataDefinitionVector oilTempE1DataDef = { {"GENERAL ENG OIL TEMPERATURE", 1, UNITS.Celsius} }; - DataDefinitionVector oilTempE2DataDef = { {"GENERAL ENG OIL TEMPERATURE", 2, UNITS.Celsius} }; - DataDefinitionVariablePtr oilTempDataPtr[2]; - -// Oil Psi Data in separate Data Definitions as they are updated separately - struct OilPsiData { - FLOAT64 oilPsi; // Psi - }; - DataDefinitionVector oilPsiE1DataDef = { {"GENERAL ENG OIL PRESSURE", 1, UNITS.Psi} }; - DataDefinitionVector oilPsiE2DataDef = { {"GENERAL ENG OIL PRESSURE", 2, UNITS.Psi} }; - DataDefinitionVariablePtr oilPsiDataPtr[2]; - // clang-format on - - // Various simvars we require each tick - struct SimVarsData { - FLOAT64 airSpeedMach; // Mach - FLOAT64 ambientPressure; // Millibars - FLOAT64 ambientTemperature; // Celsius - FLOAT64 animationDeltaTime; // Seconds - FLOAT64 apuFuelConsumption; // Gallons per hour - FLOAT64 engineAntiIce[2]; // Bool - FLOAT64 engineCorrectedN1[2]; // Percent - FLOAT64 engineCorrectedN2[2]; // Percent - FLOAT64 engineFuelValveOpen[2]; // Number - FLOAT64 engineIgniter[2]; // Number - FLOAT64 engineStarter[2]; // Bool - FLOAT64 fuelPump1[2]; // Number - FLOAT64 fuelPump2[2]; // Number - FLOAT64 fuelTankQuantityCenter; // Gallons - FLOAT64 fuelTankQuantityLeft; // Gallons - FLOAT64 fuelTankQuantityLeftAux; // Gallons - FLOAT64 fuelTankQuantityRight; // Gallons - FLOAT64 fuelTankQuantityRightAux; // Gallons - FLOAT64 fuelWeightPerGallon; // Pounds - FLOAT64 lineToCenterFlow[2]; // Gallons per hour - FLOAT64 pressureAltitude; // Feet - FLOAT64 simEngineN1[2]; // Percent - FLOAT64 simEngineN2[2]; // Percent - FLOAT64 xFeedValve; // Number - FLOAT64 xfrCenterManual[2]; // Number - FLOAT64 xfrValveCenterAuto[2]; // Number - FLOAT64 xfrValveCenterOpen[2]; // Number - FLOAT64 xfrValveOuter1[2]; // Number - FLOAT64 xfrValveOuter2[2]; // Number - }; - DataDefinitionVector simVarsDataDef = { - {"AIRSPEED MACH", 0, UNITS.Mach }, // - {"AMBIENT PRESSURE", 0, UNITS.Millibars}, // - {"AMBIENT TEMPERATURE", 0, UNITS.Celsius }, // - {"ANIMATION DELTA TIME", 0, UNITS.Seconds }, // - {"FUELSYSTEM LINE FUEL FLOW", 18, UNITS.Gph }, // - {"ENG ANTI ICE", 1, UNITS.Bool }, // - {"ENG ANTI ICE", 2, UNITS.Bool }, // - {"TURB ENG CORRECTED N1", 1, UNITS.Percent }, // - {"TURB ENG CORRECTED N1", 2, UNITS.Percent }, // - {"TURB ENG CORRECTED N2", 1, UNITS.Percent }, // - {"TURB ENG CORRECTED N2", 2, UNITS.Percent }, // - {"FUELSYSTEM VALVE OPEN", 1, UNITS.Number }, // - {"FUELSYSTEM VALVE OPEN", 2, UNITS.Number }, // - {"TURB ENG IGNITION SWITCH EX1", 1, UNITS.Number }, // - {"TURB ENG IGNITION SWITCH EX1", 2, UNITS.Number }, // - {"GENERAL ENG STARTER", 1, UNITS.Bool }, // - {"GENERAL ENG STARTER", 2, UNITS.Bool }, // - {"FUELSYSTEM PUMP ACTIVE", 2, UNITS.Number }, // - {"FUELSYSTEM PUMP ACTIVE", 3, UNITS.Number }, // - {"FUELSYSTEM PUMP ACTIVE", 5, UNITS.Number }, // - {"FUELSYSTEM PUMP ACTIVE", 6, UNITS.Number }, // - {"FUELSYSTEM TANK QUANTITY", 1, UNITS.Gallons }, // - {"FUELSYSTEM TANK QUANTITY", 2, UNITS.Gallons }, // - {"FUELSYSTEM TANK QUANTITY", 4, UNITS.Gallons }, // - {"FUELSYSTEM TANK QUANTITY", 3, UNITS.Gallons }, // - {"FUELSYSTEM TANK QUANTITY", 5, UNITS.Gallons }, // - {"FUEL WEIGHT PER GALLON", 0, UNITS.Pounds }, // - {"FUELSYSTEM LINE FUEL FLOW", 27, UNITS.Gph }, // - {"FUELSYSTEM LINE FUEL FLOW", 28, UNITS.Gph }, // - {"PRESSURE ALTITUDE", 0, UNITS.Feet }, // - {"TURB ENG N1", 1, UNITS.Percent }, // - {"TURB ENG N1", 2, UNITS.Percent }, // - {"TURB ENG N2", 1, UNITS.Percent }, // - {"TURB ENG N2", 2, UNITS.Percent }, // - {"FUELSYSTEM VALVE OPEN", 3, UNITS.Number }, // - {"FUELSYSTEM JUNCTION SETTING", 4, UNITS.Number }, // - {"FUELSYSTEM JUNCTION SETTING", 5, UNITS.Number }, // - {"FUELSYSTEM VALVE OPEN", 11, UNITS.Number }, // - {"FUELSYSTEM VALVE OPEN", 12, UNITS.Number }, // - {"FUELSYSTEM VALVE OPEN", 9, UNITS.Number }, // - {"FUELSYSTEM VALVE OPEN", 10, UNITS.Number }, // - {"FUELSYSTEM VALVE OPEN", 6, UNITS.Number }, // - {"FUELSYSTEM VALVE OPEN", 7, UNITS.Number }, // - {"FUELSYSTEM VALVE OPEN", 4, UNITS.Number }, // - {"FUELSYSTEM VALVE OPEN", 5, UNITS.Number }, // - }; - DataDefinitionVariablePtr simVarsDataPtr; - - // Client events - ClientEventPtr toggleEngineStarter1Event; - ClientEventPtr toggleEngineStarter2Event; - CallbackID toggleEngineStarter1EventCallback{}; - CallbackID toggleEngineStarter2EventCallback{}; - ClientEventPtr setStarterHeldEvent[2]; - ClientEventPtr setStarterEvent[2]; - - // SimVars - AircraftVariablePtr engineCombustion[2]; // Bool - AircraftVariablePtr engineTime[2]; // Seconds - - // LVars - NamedVariablePtr airlinerToFlexTemp; // Celsius - NamedVariablePtr apuRpmPercent; // Percent - NamedVariablePtr engineEgt[2]; - NamedVariablePtr engineFF[2]; - NamedVariablePtr engineFuelUsed[2]; - NamedVariablePtr engineIdleEGT; - NamedVariablePtr engineIdleFF; - NamedVariablePtr engineIdleN1; - NamedVariablePtr engineIdleN2; - NamedVariablePtr engineImbalance; - NamedVariablePtr engineN1[2]; - NamedVariablePtr engineN2[2]; - NamedVariablePtr engineOilTotal[2]; - NamedVariablePtr engineOil[2]; - NamedVariablePtr enginePreFF[2]; - NamedVariablePtr engineStarterPressurized[2]; - NamedVariablePtr engineState[2]; - NamedVariablePtr engineTimer[2]; - NamedVariablePtr fuelAuxLeftPre; // Pounds - NamedVariablePtr fuelAuxRightPre; // Pounds - NamedVariablePtr fuelCenterPre; // Pounds - NamedVariablePtr fuelLeftPre; // Pounds - NamedVariablePtr fuelPumpState[2]; - NamedVariablePtr fuelRightPre; - NamedVariablePtr packsState[2]; - NamedVariablePtr refuelRate; - NamedVariablePtr refuelStartedByUser; - NamedVariablePtr startState; - NamedVariablePtr thrustLimitClimb; - NamedVariablePtr thrustLimitFlex; - NamedVariablePtr thrustLimitIdle; - NamedVariablePtr thrustLimitMct; - NamedVariablePtr thrustLimitToga; - NamedVariablePtr thrustLimitType; - NamedVariablePtr wingAntiIce; - - // =============================================================================================== - - /** - * @brief Initializes the FadecSimData_A32NX object. - * @param dm Pointer to the DataManager object. This object is used to create the data definition - * variable for the ATC ID data. - */ - void initialize(DataManager* dm) { - initDataDefinitions(dm); - initEvents(dm); - initSimvars(dm); - initLvars(dm); - LOG_INFO("Fadec::FadecSimData_A32NX initialized"); - } - - void initDataDefinitions(DataManager* dm) { - atcIdDataPtr = dm->make_datadefinition_var("ATC ID DATA", atcIdDataDef, NO_AUTO_UPDATE); - fuelFeedTankDataPtr = dm->make_datadefinition_var("FUEL LR DATA", fuelLRDataDef, NO_AUTO_UPDATE); - fuelCandAuxDataPtr = dm->make_datadefinition_var("FUEL CAND AUX DATA", fuelCandAuxDataDef, NO_AUTO_UPDATE); - oilTempDataPtr[L] = dm->make_datadefinition_var("OIL TEMP LEFT DATA", oilTempE1DataDef, NO_AUTO_UPDATE); - oilTempDataPtr[R] = dm->make_datadefinition_var("OIL TEMP RIGHT DATA", oilTempE2DataDef, NO_AUTO_UPDATE); - oilPsiDataPtr[L] = dm->make_datadefinition_var("OIL PSI LEFT DATA", oilPsiE1DataDef, NO_AUTO_UPDATE); - oilPsiDataPtr[R] = dm->make_datadefinition_var("OIL PSI RIGHT DATA", oilPsiE2DataDef, NO_AUTO_UPDATE); - - simVarsDataPtr = dm->make_datadefinition_var("SIMVARS DATA", simVarsDataDef); - simVarsDataPtr->requestPeriodicDataFromSim(SIMCONNECT_PERIOD_VISUAL_FRAME); - } - - void initEvents(DataManager* dm) { - // Create the client events for the engine starter toggles - // we just want to mask the events, not do anything with them - toggleEngineStarter1Event = dm->make_client_event("TOGGLE_STARTER1", true); - toggleEngineStarter1Event->addClientEventToNotificationGroup(NOTIFICATION_GROUP_0, true); - toggleEngineStarter2Event = dm->make_client_event("TOGGLE_STARTER2", true); - toggleEngineStarter2Event->addClientEventToNotificationGroup(NOTIFICATION_GROUP_0, true); - - setStarterHeldEvent[L] = dm->make_client_event("SET_STARTER1_HELD", true, NOTIFICATION_GROUP_0); - setStarterHeldEvent[R] = dm->make_client_event("SET_STARTER2_HELD", true, NOTIFICATION_GROUP_0); - - setStarterEvent[L] = dm->make_client_event("STARTER1_SET", true, NOTIFICATION_GROUP_0); - setStarterEvent[R] = dm->make_client_event("STARTER2_SET", true, NOTIFICATION_GROUP_0); - } - - void initSimvars(DataManager* dm) { - // not read each tick (mainly only once in initialization) - will be updated in code - engineCombustion[L] = dm->make_aircraft_var("GENERAL ENG COMBUSTION", 1, "", nullptr, UNITS.Bool, NO_AUTO_UPDATE); - engineCombustion[R] = dm->make_aircraft_var("GENERAL ENG COMBUSTION", 2, "", nullptr, UNITS.Bool, NO_AUTO_UPDATE); - - engineTime[L] = dm->make_aircraft_var("GENERAL ENG ELAPSED TIME", 1, "", nullptr, UNITS.Seconds, NO_AUTO_UPDATE); - engineTime[R] = dm->make_aircraft_var("GENERAL ENG ELAPSED TIME", 2, "", nullptr, UNITS.Seconds, NO_AUTO_UPDATE); - } - - void initLvars(DataManager* dm) { - // TODO: consider DataDefinition for the groups tha are read/write each tick - startState = dm->make_named_var("A32NX_START_STATE", UNITS.Number, NO_AUTO_UPDATE); - - engineEgt[L] = dm->make_named_var("A32NX_ENGINE_EGT:1", UNITS.Number, AUTO_READ_WRITE); - engineEgt[R] = dm->make_named_var("A32NX_ENGINE_EGT:2", UNITS.Number, AUTO_READ_WRITE); - - engineFF[L] = dm->make_named_var("A32NX_ENGINE_FF:1", UNITS.Number, AUTO_READ_WRITE); - engineFF[R] = dm->make_named_var("A32NX_ENGINE_FF:2", UNITS.Number, AUTO_READ_WRITE); - - engineFuelUsed[L] = dm->make_named_var("A32NX_FUEL_USED:1", UNITS.Number, AUTO_READ_WRITE); - engineFuelUsed[R] = dm->make_named_var("A32NX_FUEL_USED:2", UNITS.Number, AUTO_READ_WRITE); - - engineIdleEGT = dm->make_named_var("A32NX_ENGINE_IDLE_EGT", UNITS.Number, AUTO_READ_WRITE); - engineIdleFF = dm->make_named_var("A32NX_ENGINE_IDLE_FF", UNITS.Number, AUTO_READ_WRITE); - - engineIdleN1 = dm->make_named_var("A32NX_ENGINE_IDLE_N1", UNITS.Number, AUTO_READ_WRITE); - engineIdleN2 = dm->make_named_var("A32NX_ENGINE_IDLE_N2", UNITS.Number, AUTO_READ_WRITE); - - engineImbalance = dm->make_named_var("A32NX_ENGINE_IMBALANCE", UNITS.Number, AUTO_READ_WRITE); - - engineN1[L] = dm->make_named_var("A32NX_ENGINE_N1:1", UNITS.Number, AUTO_READ_WRITE); - engineN1[R] = dm->make_named_var("A32NX_ENGINE_N1:2", UNITS.Number, AUTO_READ_WRITE); - - engineN2[L] = dm->make_named_var("A32NX_ENGINE_N2:1", UNITS.Number, AUTO_READ_WRITE); - engineN2[R] = dm->make_named_var("A32NX_ENGINE_N2:2", UNITS.Number, AUTO_READ_WRITE); - - engineOil[L] = dm->make_named_var("A32NX_ENGINE_OIL_QTY:1", UNITS.Number, AUTO_READ_WRITE); - engineOil[R] = dm->make_named_var("A32NX_ENGINE_OIL_QTY:2", UNITS.Number, AUTO_READ_WRITE); - - engineOilTotal[L] = dm->make_named_var("A32NX_ENGINE_OIL_TOTAL:1", UNITS.Number, AUTO_READ_WRITE); - engineOilTotal[R] = dm->make_named_var("A32NX_ENGINE_OIL_TOTAL:2", UNITS.Number, AUTO_READ_WRITE); - - enginePreFF[L] = dm->make_named_var("A32NX_ENGINE_PRE_FF:1", UNITS.Number, AUTO_READ_WRITE); - enginePreFF[R] = dm->make_named_var("A32NX_ENGINE_PRE_FF:2", UNITS.Number, AUTO_READ_WRITE); - - engineState[L] = dm->make_named_var("A32NX_ENGINE_STATE:1", UNITS.Number, AUTO_READ_WRITE); - engineState[R] = dm->make_named_var("A32NX_ENGINE_STATE:2", UNITS.Number, AUTO_READ_WRITE); - - engineTimer[L] = dm->make_named_var("A32NX_ENGINE_TIMER:1", UNITS.Number, AUTO_READ_WRITE); - engineTimer[R] = dm->make_named_var("A32NX_ENGINE_TIMER:2", UNITS.Number, AUTO_READ_WRITE); - - engineStarterPressurized[L] = dm->make_named_var("A32NX_PNEU_ENG_1_STARTER_PRESSURIZED", UNITS.Number, AUTO_READ); - engineStarterPressurized[R] = dm->make_named_var("A32NX_PNEU_ENG_2_STARTER_PRESSURIZED", UNITS.Number, AUTO_READ); - - fuelAuxLeftPre = dm->make_named_var("A32NX_FUEL_AUX_LEFT_PRE", UNITS.Number, AUTO_READ_WRITE); - fuelAuxRightPre = dm->make_named_var("A32NX_FUEL_AUX_RIGHT_PRE", UNITS.Number, AUTO_READ_WRITE); - fuelCenterPre = dm->make_named_var("A32NX_FUEL_CENTER_PRE", UNITS.Number, AUTO_READ_WRITE); - fuelLeftPre = dm->make_named_var("A32NX_FUEL_LEFT_PRE", UNITS.Number, AUTO_READ_WRITE); - fuelPumpState[L] = dm->make_named_var("A32NX_PUMP_STATE:1", UNITS.Number, AUTO_READ_WRITE); - fuelPumpState[R] = dm->make_named_var("A32NX_PUMP_STATE:2", UNITS.Number, AUTO_READ_WRITE); - fuelRightPre = dm->make_named_var("A32NX_FUEL_RIGHT_PRE", UNITS.Number, AUTO_READ_WRITE); - - thrustLimitType = dm->make_named_var("A32NX_AUTOTHRUST_THRUST_LIMIT_TYPE", UNITS.Number, AUTO_READ); - thrustLimitIdle = dm->make_named_var("A32NX_AUTOTHRUST_THRUST_LIMIT_IDLE", UNITS.Number, AUTO_WRITE); - thrustLimitClimb = dm->make_named_var("A32NX_AUTOTHRUST_THRUST_LIMIT_CLB", UNITS.Number, AUTO_WRITE); - thrustLimitFlex = dm->make_named_var("A32NX_AUTOTHRUST_THRUST_LIMIT_FLX", UNITS.Number, AUTO_WRITE); - thrustLimitMct = dm->make_named_var("A32NX_AUTOTHRUST_THRUST_LIMIT_MCT", UNITS.Number, AUTO_WRITE); - thrustLimitToga = dm->make_named_var("A32NX_AUTOTHRUST_THRUST_LIMIT_TOGA", UNITS.Number, AUTO_WRITE); - - packsState[L] = dm->make_named_var("A32NX_COND_PACK_FLOW_VALVE_1_IS_OPEN", UNITS.Number, AUTO_READ); - packsState[R] = dm->make_named_var("A32NX_COND_PACK_FLOW_VALVE_2_IS_OPEN", UNITS.Number, AUTO_READ); - wingAntiIce = dm->make_named_var("A32NX_PNEU_WING_ANTI_ICE_SYSTEM_ON", UNITS.Number, AUTO_READ); - refuelRate = dm->make_named_var("A32NX_EFB_REFUEL_RATE_SETTING", UNITS.Number, AUTO_READ); - refuelStartedByUser = dm->make_named_var("A32NX_REFUEL_STARTED_BY_USR", UNITS.Number, AUTO_READ); - airlinerToFlexTemp = dm->make_named_var("AIRLINER_TO_FLEX_TEMP", UNITS.Number, AUTO_READ); - apuRpmPercent = dm->make_named_var("A32NX_APU_N_RAW", UNITS.Number, AUTO_READ); - - // reset LVars to 0 - engineEgt[L]->setAndWriteToSim(0); - engineEgt[R]->setAndWriteToSim(0); - engineFF[L]->setAndWriteToSim(0); - engineFF[R]->setAndWriteToSim(0); - engineFuelUsed[L]->setAndWriteToSim(0); - engineFuelUsed[R]->setAndWriteToSim(0); - engineIdleEGT->setAndWriteToSim(0); - engineIdleFF->setAndWriteToSim(0); - engineIdleN1->setAndWriteToSim(0); - engineIdleN2->setAndWriteToSim(0); - engineImbalance->setAndWriteToSim(0); - engineN1[L]->setAndWriteToSim(0); - engineN1[R]->setAndWriteToSim(0); - engineN2[L]->setAndWriteToSim(0); - engineN2[R]->setAndWriteToSim(0); - engineOilTotal[L]->setAndWriteToSim(0); - engineOilTotal[R]->setAndWriteToSim(0); - engineOil[L]->setAndWriteToSim(0); - engineOil[R]->setAndWriteToSim(0); - enginePreFF[L]->setAndWriteToSim(0); - enginePreFF[R]->setAndWriteToSim(0); - engineState[L]->setAndWriteToSim(0); - engineState[R]->setAndWriteToSim(0); - engineTimer[L]->setAndWriteToSim(0); - engineTimer[R]->setAndWriteToSim(0); - fuelAuxLeftPre->setAndWriteToSim(0); - fuelAuxRightPre->setAndWriteToSim(0); - fuelCenterPre->setAndWriteToSim(0); - fuelLeftPre->setAndWriteToSim(0); - fuelPumpState[L]->setAndWriteToSim(0); - fuelPumpState[R]->setAndWriteToSim(0); - fuelRightPre->setAndWriteToSim(0); - thrustLimitClimb->setAndWriteToSim(0); - thrustLimitFlex->setAndWriteToSim(0); - thrustLimitIdle->setAndWriteToSim(0); - thrustLimitMct->setAndWriteToSim(0); - thrustLimitToga->setAndWriteToSim(0); - } -}; - -#endif // FLYBYWIRE_AIRCRAFT_FADECSIMDATA_A32NX_HPP diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Fadec_A32NX.cpp b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Fadec_A32NX.cpp deleted file mode 100644 index ce75b37778d..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Fadec_A32NX.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#include "Fadec_A32NX.h" - -bool Fadec_A32NX::initialize() { - engineControl.initialize(&msfsHandler); - - _isInitialized = true; - LOG_INFO("Fadec_A32NX initialized"); - return true; -} - -bool Fadec_A32NX::update(sGaugeDrawData* pData) { - if (!_isInitialized) { - std::cerr << "Fadec_A32NX::update() - not initialized" << std::endl; - return false; - } - - engineControl.update(pData); - - return true; -} - -bool Fadec_A32NX::shutdown() { - _isInitialized = false; - LOG_INFO("Fadec_A32NX::shutdown()"); - return true; -} diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Fadec_A32NX.h b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Fadec_A32NX.h deleted file mode 100644 index 4add0f4921d..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Fadec_A32NX.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_FADEC_A32NX_H -#define FLYBYWIRE_AIRCRAFT_FADEC_A32NX_H - -#include "EngineControlA32NX.h" -#include "Fadec.h" - -/** - * @brief: The Fadec_A32NX class is responsible for managing the FADEC system for the A32NX aircraft. - * - * In this current implementation is only holding the EngineControl_A32NX instance and is - * responsible for calling its initialize, update and shutdown methods. - * The actual fadec logic is implemented in the EngineControl_A32NX class. - */ -class Fadec_A32NX : public Fadec { - private: - // Engine control instance - EngineControl_A32NX engineControl{}; - - public: - /** - * Creates a new Fadec_A32NX instance and takes a reference to the MsfsHandler instance. - * @param msfsHandler The MsfsHandler instance that is used to communicate with the simulator. - */ - explicit Fadec_A32NX(MsfsHandler& msfsHandler) : Fadec(msfsHandler) {} - - bool initialize() override; - bool preUpdate(sGaugeDrawData*) override { return true; } - bool update(sGaugeDrawData* pData) override; - bool postUpdate(sGaugeDrawData*) override { return true; } - bool shutdown() override; -}; - -#endif // FLYBYWIRE_AIRCRAFT_FADEC_A32NX_H diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FuelConfiguration_A32NX.cpp b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FuelConfiguration_A32NX.cpp deleted file mode 100644 index e30877cc31d..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FuelConfiguration_A32NX.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#include -#include - -#include "inih/ini.h" -#include "inih/ini_type_conversion.h" - -#include "logging.h" - -#include "FuelConfiguration_A32NX.h" - -void FuelConfiguration_A32NX::loadConfigurationFromIni() { - if (configFilename.empty()) { - LOG_ERROR("Fadec::FuelConfiguration_A32NX: no configuration file specified -> using default fuel" - " quantities. Use setConfigFilename() to set the configuration file)"); - return; - } - - LOG_INFO("Fadec::FuelConfiguration_A32NX: loading configuration file " + configFilename); - - mINI::INIStructure ini; - mINI::INIFile iniFile(configFilename); - - if (!iniFile.read(ini)) { - LOG_ERROR("Fadec::FuelConfiguration_A32NX: failed to read configuration file \"" + configFilename + "\" due to error \"" + - strerror(errno) + "\" -> using default fuel quantities."); - return; - } - - fuelCenter = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_CENTER_QUANTITY, fuelCenterDefault); - fuelLeft = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_LEFT_QUANTITY, fuelLeftDefault); - fuelRight = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_RIGHT_QUANTITY, fuelRightDefault); - fuelLeftAux = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_LEFT_AUX_QUANTITY, fuelLeftAuxDefault); - fuelRightAux = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_RIGHT_AUX_QUANTITY, fuelRightAuxDefault); - - LOG_DEBUG("Fadec::FuelConfiguration_A32NX: loaded fuel configuration from " + configFilename + " with the following values:"); - LOG_DEBUG("Fadec::FuelConfiguration_A32NX: " + this->toString()); -} - -void FuelConfiguration_A32NX::saveConfigurationToIni() { - LOG_DEBUG("Fadec::FuelConfiguration_A32NX: saving configuration file " + configFilename); - - mINI::INIStructure ini; - mINI::INIFile iniFile(configFilename); - - // Do not check a possible error since the file may not exist yet - iniFile.read(ini); - - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_CENTER_QUANTITY] = std::to_string(this->fuelCenter); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_LEFT_QUANTITY] = std::to_string(this->fuelLeft); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_RIGHT_QUANTITY] = std::to_string(this->fuelRight); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_LEFT_AUX_QUANTITY] = std::to_string(this->fuelLeftAux); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_RIGHT_AUX_QUANTITY] = std::to_string(this->fuelRightAux); - - if (!iniFile.write(ini, true)) { - LOG_ERROR("Fadec::FuelConfiguration_A32NX: failed to write engine conf " + configFilename + " due to error \"" + strerror(errno) + - "\""); - return; - } - - LOG_DEBUG("Fadec::FuelConfiguration_A32NX: saved fuel configuration to " + configFilename + " with the following values:"); - LOG_DEBUG("Fadec::FuelConfiguration_A32NX: " + this->toString()); -} - -std::string FuelConfiguration_A32NX::toString() const { - std::ostringstream oss; - oss << "FuelConfiguration_A32NX: { " - << "fuelCenter: " << fuelCenter // - << ", fuelLeft: " << fuelLeft // - << ", fuelRight: " << fuelRight // - << ", fuelLeftAux: " << fuelLeftAux // - << ", fuelRightAux: " << fuelRightAux // - << " }"; - return oss.str(); -} diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FuelConfiguration_A32NX.h b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FuelConfiguration_A32NX.h deleted file mode 100644 index 4911463168c..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FuelConfiguration_A32NX.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_FUELCONFIGURATION_A32NX_H -#define FLYBYWIRE_AIRCRAFT_FUELCONFIGURATION_A32NX_H - -#include - -// Define constants for the INI file sections and keys -#define INI_SECTION_FUEL "FUEL" -#define INI_SECTION_FUEL_CENTER_QUANTITY "FUEL_CENTER_QUANTITY" -#define INI_SECTION_FUEL_LEFT_QUANTITY "FUEL_LEFT_QUANTITY" -#define INI_SECTION_FUEL_RIGHT_QUANTITY "FUEL_RIGHT_QUANTITY" -#define INI_SECTION_FUEL_LEFT_AUX_QUANTITY "FUEL_LEFT_AUX_QUANTITY" -#define INI_SECTION_FUEL_RIGHT_AUX_QUANTITY "FUEL_RIGHT_AUX_QUANTITY" - -/** - * @class FuelConfiguration_A32NX - * @brief Class to manage the fuel configuration for the A32NX aircraft. - * - * This class provides methods to load and save the fuel configuration from/to an INI file. - * It also provides getter and setter methods for each fuel tank quantity. - */ -class FuelConfiguration_A32NX { - private: - // Fuel tank default quantities in gallons - static constexpr double fuelCenterDefault = 0; - static constexpr double fuelLeftDefault = 411.34; - static constexpr double fuelRightDefault = fuelLeftDefault; - static constexpr double fuelLeftAuxDefault = 0; - static constexpr double fuelRightAuxDefault = fuelLeftAuxDefault; - - // Actual fuel tank quantities in gallons - double fuelCenter = fuelCenterDefault; - double fuelLeft = fuelLeftDefault; - double fuelRight = fuelRightDefault; - double fuelLeftAux = fuelLeftAuxDefault; - double fuelRightAux = fuelRightAuxDefault; - - std::string configFilename{"A32NX-default-fuel-config.ini"}; - - public: - /** - * @brief Returns the filename of the INI file to use for loading and saving the fuel configuration. - */ - std::string getConfigFilename() const { return configFilename; } - - /** - * @brief Sets the filename of the INI file to use for loading and saving the fuel configuration. - * - * This must be called before calling loadConfigurationFromIni or saveConfigurationToIni otherwise the default - * filename will be used. - * - * @param configFilename The filename of the INI file to use for loading and saving the fuel configuration. - */ - void setConfigFilename(const std::string& configFilename) { this->configFilename = configFilename; } - - /** - * @brief Loads the fuel configuration from an INI file. - * - * This method reads the INI file specified in the configFilename member variable and updates the fuel quantities accordingly. - * If the INI file cannot be read, an error message is logged and the method returns without making any changes. - */ - void loadConfigurationFromIni(); - - /** - * @brief Saves the current fuel configuration to an INI file. - * - * This method writes the current fuel quantities to the INI file specified in the configFilename member variable. - * If the INI file cannot be written, an error message is logged. - */ - void saveConfigurationToIni(); - - /** - * @brief Converts the current fuel configuration to a string. - * - * This method is used to convert the current state of the fuel configuration into a string format. - * The string includes the quantities of fuel in each tank. - * - * @return A string representation of the current fuel configuration. - */ - std::string toString() const; - - // === Getters and setters === - - double getFuelCenter() const { return fuelCenter; } - double getFuelLeft() const { return fuelLeft; } - double getFuelRight() const { return fuelRight; } - double getFuelLeftAux() const { return fuelLeftAux; } - double getFuelRightAux() const { return fuelRightAux; } - - void setFuelCenter(double fuelCenter) { this->fuelCenter = fuelCenter; } - void setFuelLeft(double fuelLeft) { this->fuelLeft = fuelLeft; } - void setFuelRight(double fuelRight) { this->fuelRight = fuelRight; } - void setFuelLeftAux(double fuelLeftAux) { this->fuelLeftAux = fuelLeftAux; } - void setFuelRightAux(double fuelRightAux) { this->fuelRightAux = fuelRightAux; } -}; - -#endif // FLYBYWIRE_AIRCRAFT_FUELCONFIGURATION_A32NX_H diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Polynomials_A32NX.hpp b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Polynomials_A32NX.hpp deleted file mode 100644 index 9552594172b..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Polynomials_A32NX.hpp +++ /dev/null @@ -1,428 +0,0 @@ -#ifndef FLYBYWIRE_AIRCRAFT_POLYNOMIAL_A32NX_HPP -#define FLYBYWIRE_AIRCRAFT_POLYNOMIAL_A32NX_HPP - -#include -#include -#include - -/** - * @brief Class representing a collection of multi-variate regression polynomials for engine parameters. - * - * This class contains static methods for calculating various engine parameters based on multi-variate - * regression polynomials. These parameters include N2, N1, EGT, Fuel Flow, Oil Temperature, - * and Oil Pressure during different engine states such as shutdown and startup. The class also - * includes methods for calculating corrected EGT and Fuel Flow, as well as Oil Gulping percentage. - */ -class Polynomial_A32NX { - public: - /** - * @brief Calculates the N2 percentage during engine start-up using real-life modeled polynomials. - * - * @param n2 The current N2 percentage. - * @param preN2 The previous N2 percentage. - * @param idleN2 The idle N2 percentage. - * @return The calculated N2 percentage. - */ - static double startN2(double n2, double preN2, double idleN2) { - // Normalize the current N2 percentage by scaling it with the idle N2 - // percentage and a constant factor. - // The constant factor 68.2 is likely derived from empirical data or a mathematical model of the - // engine's behavior. - double normalN2 = n2 * 68.2 / idleN2; - - // Coefficients for the polynomial used to calculate the N2 percentage. - constexpr double c_N2[16] = { - 4.03649879e+00, // coefficient for x^0 - -9.41981960e-01, // coefficient for x^1 - 1.98426614e-01, // coefficient for x^2 - -2.11907840e-02, // coefficient for x^3 - 1.00777507e-03, // coefficient for x^4 - -1.57319166e-06, // coefficient for x^5 - -2.15034888e-06, // coefficient for x^6 - 1.08288379e-07, // coefficient for x^7 - -2.48504632e-09, // coefficient for x^8 - 2.52307089e-11, // coefficient for x^9 - -2.06869243e-14, // coefficient for x^10 - 8.99045761e-16, // coefficient for x^11 - -9.94853959e-17, // coefficient for x^12 - 1.85366499e-18, // coefficient for x^13 - -1.44869928e-20, // coefficient for x^14 - 4.31033031e-23 // coefficient for x^15 - }; - - // Calculate the N2 percentage using the polynomial equation. - double outN2 = 0.0; - for (int i = 0; i < 16; ++i) { - outN2 += c_N2[i] * (std::pow)(normalN2, i); - } - - outN2 *= n2; - outN2 = (std::max)(outN2, preN2 + 0.002); - return (std::min)(outN2, idleN2 + 0.1); - } - - /** - * @brief Calculates the N1 percentage during engine start-up. - * - * @param fbwN2 The current custom N2 percentage. - * @param idleN2 The idle N2 percentage. - * @param idleN1 The idle N1 percentage. - * @return The calculated N1 percentage. - */ - static double startN1(double fbwN2, double idleN2, double idleN1) { - // Normalize the current N2 percentage by dividing it with the idle N2 percentage. - const double normalN2 = fbwN2 / idleN2; - - // Coefficients for the polynomial used to calculate the N1 percentage. - constexpr double c_N1[9] = { - -2.2812156e-12, // coefficient for x^0 - -5.9830374e+01, // coefficient for x^1 - 7.0629094e+02, // coefficient for x^2 - -3.4580361e+03, // coefficient for x^3 - 9.1428923e+03, // coefficient for x^4 - -1.4097740e+04, // coefficient for x^5 - 1.2704110e+04, // coefficient for x^6 - -6.2099935e+03, // coefficient for x^7 - 1.2733071e+03 // coefficient for x^8 - }; - - // Calculate the N1 percentage using the polynomial equation. - const double normalN1pre = (-2.4698087 * (std::pow)(normalN2, 3)) // - + (0.9662026 * (std::pow)(normalN2, 2)) // - + (0.0701367 * normalN2); // - - // Calculate the N2 percentage using the polynomial equation. - double normalN1post = 0.0; - for (int i = 0; i < 9; ++i) { - normalN1post += c_N1[i] * (std::pow)(normalN2, i); - } - - // Return the calculated N1 percentage, ensuring it is within the range [normalN1pre, normalN1post] - // and then multiplied by idleN1. - return (normalN1post >= normalN1pre ? normalN1post : normalN1pre) * idleN1; - } - - /** - * @brief Calculates the Fuel Flow (FF) during engine start-up using real-life modeled polynomials. - * - * @param fbwN2 The current N2 percentage. - * @param idleN2 The idle N2 percentage. - * @param idleFF The idle FF. - * @return The calculated FF. - */ - static double startFF(double fbwN2, double idleN2, double idleFF) { - const double normalN2 = fbwN2 / idleN2; - double normalFF = 0; - - // If the normalized N2 percentage is less than or equal to 0.37, the FF is 0. - if (normalN2 <= 0.37) { - normalFF = 0; - } else { - // Coefficients for the polynomial used to calculate the FF. - constexpr double c_FF[9] = { - 3.1110282e-12, // coefficient for x^0 - 1.0804331e+02, // coefficient for x^1 - -1.3972629e+03, // coefficient for x^2 - 7.4874131e+03, // coefficient for x^3 - -2.1511983e+04, // coefficient for x^4 - 3.5957757e+04, // coefficient for x^5 - -3.5093994e+04, // coefficient for x^6 - 1.8573033e+04, // coefficient for x^7 - -4.1220062e+03 // coefficient for x^8 - }; - // Calculate the FF using the polynomial equation. - for (int i = 0; i < 9; ++i) { - normalFF += c_FF[i] * (std::pow)(normalN2, i); - } - } - - // Return the calculated FF, ensuring it is not less than 0.0 and then multiplied by idleFF. - return (std::max)(normalFF, 0.0) * idleFF; - } - - /** - * @brief Calculates the Exhaust Gas Temperature (EGT) during engine start-up using real-life modeled polynomials. - * - * @param fbwN2 The current N2 percentage. - * @param idleN2 The idle N2 percentage. - * @param ambientTemp The ambient temperature. - * @param idleEGT The idle EGT. - * @return The calculated EGT. - */ - static double startEGT(double fbwN2, double idleN2, double ambientTemp, double idleEGT) { - // Normalize the current N2 percentage by dividing it with the idle N2 percentage. - const double normalizedN2 = fbwN2 / idleN2; - - - // Calculate the normalized EGT value based on the normalized N2 value - double normalizedEGT; - if (normalizedN2 < 0.17) { - normalizedEGT = 0; - } - // If the normalized N2 percentage is less than or equal to 0.4, the EGT is calculated using a linear equation. - else if (normalizedN2 <= 0.4) { - normalizedEGT = (0.04783 * normalizedN2) - 0.00813; - } - // If the normalized N2 percentage is greater than 0.4, the EGT is calculated using a polynomial equation. - else { - // Coefficients for the polynomial used to calculate the EGT. - constexpr double c_EGT[9] = { - -6.8725167e+02, // coefficient for x^0 - 7.7548864e+03, // coefficient for x^1 - -3.7507098e+04, // coefficient for x^2 - 1.0147016e+05, // coefficient for x^3 - -1.6779273e+05, // coefficient for x^4 - 1.7357157e+05, // coefficient for x^5 - -1.0960924e+05, // coefficient for x^6 - 3.8591956e+04, // coefficient for x^7 - -5.7912600e+03 // coefficient for x^8 - }; - - // Calculate the EGT using the polynomial equation. - normalizedEGT = 0.0; - for (int i = 0; i < 9; ++i) { - normalizedEGT += c_EGT[i] * (std::pow)(normalizedN2, i); - } - } - - // Return the calculated EGT, ensuring it is within the range [ambientTemp, idleEGT]. - return (normalizedEGT * (idleEGT - (ambientTemp))) + (ambientTemp); - } - - /** - * @brief Calculates the Oil Temperature during engine start-up. - * - * @param fbwN2 The current custom N2 percentage. - * @param idleN2 The custom idle N2 percentage. - * @param ambientTemperature The ambient temperature. - * @return The calculated Oil Temperature. - */ - static double startOilTemp(double fbwN2, double idleN2, double ambientTemperature) { - if (fbwN2 < 0.79 * idleN2) { - return ambientTemperature; - } - if (fbwN2 < 0.98 * idleN2) { - return ambientTemperature + 5; - } - return ambientTemperature + 10; - } - - /** - * @brief Calculates the N2 percentage during engine shutdown. - * - * @param previousN2 The previous N2 percentage. - * @param deltaTime The elapsed time since the last calculation in seconds. - * @return The calculated N2 percentage. - */ - static double shutdownN2(double previousN2, double deltaTime) { - // The decayRate is used to model the rate at which the N2 percentage decreases during the engine - // shutdown process. - // The specific values -0.0515 and -0.08183 are likely derived from empirical - // data or a mathematical model of the engine's behavior. - // The choice to use a different decay rate for 'previousN2' values below 30 suggests that the - // engine's shutdown behavior changes at this threshold. - double decayRate = previousN2 < 30 ? -0.0515 : -0.08183; - return previousN2 * (std::exp)(decayRate * deltaTime); - } - - /** - * @brief Calculates the N1 percentage during engine shutdown. - * - * @param previousN1 The previous N1 percentage. - * @param deltaTime The elapsed time since the last calculation. - * @return The calculated N1 percentage. - */ - static double shutdownN1(double previousN1, double deltaTime) { - // The decayRate is used to model the rate at which the N1 percentage decreases during the engine - // shutdown process. - // The specific values -0.08 and -0.164 are likely derived from empirical data or a mathematical - // model of the engine's behavior. The choice to use a different decay rate for 'previousN1' values - // below 4 suggests that the engine's shutdown behavior changes at this threshold. - double decayRate = previousN1 < 4 ? -0.08 : -0.164; - return previousN1 * exp(decayRate * deltaTime); - } - - /** - * @brief Calculates the Exhaust Gas Temperature (EGT) during engine shutdown. - * - * @param previousEGT The previous EGT value in degrees Celsius. - * @param ambientTemp The ambient temperature in degrees Celsius. - * @param deltaTime The elapsed time since the last update in seconds. - * @return The calculated EGT value in degrees Celsius. - */ - static double shutdownEGT(double previousEGT, double ambientTemp, double deltaTime) { - // The specific values used (140, 0.0257743, 135, 0.00072756, and 30) are likely derived from empirical - // data or a mathematical model of the engine's behavior. - // The choice to use different decay rates and steady state temperatures based on the previous - // EGT suggests that the engine's shutdown behavior changes at this threshold. - double threshold = ambientTemp + 140; - double decayRate = previousEGT > threshold ? 0.0257743 : 0.00072756; - double steadyStateTemp = previousEGT > threshold ? 135 + ambientTemp : 30 + ambientTemp; - return steadyStateTemp + (previousEGT - steadyStateTemp) * exp(-decayRate * deltaTime); - } - - /** - * @brief Calculates the corrected Exhaust Gas Temperature (EGT) based on corrected fan speed, - * corrected fuel flow, Mach number, and altitude. Real-life modeled polynomials. - * - * @param cn1 The corrected fan speed in percent. - * @param cff The corrected fuel flow in pounds per hour. - * @param mach The Mach number. - * @param alt The altitude in feet. - * @return The calculated corrected EGT in Celsius. - */ - static double correctedEGT(double cn1, double cff, double mach, double alt) { - constexpr double c_EGT[16] = { - 3.2636e+02, // coefficient for x^0 - 0.0000e+00, // coefficient for x^1 - 9.2893e-01, // coefficient for x^2 - 3.9505e-02, // coefficient for x^3 - 3.9070e+02, // coefficient for x^4 - -4.7911e-04, // coefficient for x^5 - 7.7679e-03, // coefficient for x^6 - 5.8361e-05, // coefficient for x^7 - -2.5566e+00, // coefficient for x^8 - 5.1227e-06, // coefficient for x^9 - 1.0178e-07, // coefficient for x^10 - -7.4602e-03, // coefficient for x^11 - 1.2106e-07, // coefficient for x^12 - -5.1639e+01, // coefficient for x^13 - -2.7356e-03, // coefficient for x^14 - 1.9312e-08 // coefficient for x^15 - }; - - return c_EGT[0] // - + c_EGT[1] // - + (c_EGT[2] * cn1) // - + (c_EGT[3] * cff) // - + (c_EGT[4] * mach) // - + (c_EGT[5] * alt) // - + (c_EGT[6] * (std::pow)(cn1, 2)) // - + (c_EGT[7] * cn1 * cff) // - + (c_EGT[8] * cn1 * mach) // - + (c_EGT[9] * cn1 * alt) // - + (c_EGT[10] * (std::pow)(cff, 2)) // - + (c_EGT[11] * mach * cff) // - + (c_EGT[12] * cff * alt) // - + (c_EGT[13] * (std::pow)(mach, 2)) // - + (c_EGT[14] * mach * alt) // - + (c_EGT[15] * (std::pow)(alt, 2)); - } - - /** - * @brief Calculates the customer corrected fuel flow based on cn1, mach, and altitude based on - * real-life modeled polynomials. - * - * @param cn1 The corrected fan speed. - * @param mach The Mach number. - * @param alt The altitude. - * @return The calculated corrected fuel flow in pounds per hour. - */ - static double correctedFuelFlow(double cn1, double mach, double alt) { - constexpr double c_Flow[21] = { - -1.7630e+02, // coefficient for x^0 - -2.1542e-01, // coefficient for x^1 - 4.7119e+01, // coefficient for x^2 - 6.1519e+02, // coefficient for x^3 - 1.8047e-03, // coefficient for x^4 - -4.4554e-01, // coefficient for x^5 - -4.3940e+01, // coefficient for x^6 - 4.0459e-05, // coefficient for x^7 - -3.2912e+01, // coefficient for x^8 - -6.2894e-03, // coefficient for x^9 - -1.2544e-07, // coefficient for x^10 - 1.0938e-02, // coefficient for x^11 - 4.0936e-01, // coefficient for x^12 - -5.5841e-06, // coefficient for x^13 - -2.3829e+01, // coefficient for x^14 - 9.3269e-04, // coefficient for x^15 - 2.0273e-11, // coefficient for x^16 - -2.4100e+02, // coefficient for x^17 - 1.4171e-02, // coefficient for x^18 - -9.5581e-07, // coefficient for x^19 - 1.2728e-11 // coefficient for x^20 - }; - - return c_Flow[0] // - + c_Flow[1] // - + (c_Flow[2] * cn1) // - + (c_Flow[3] * mach) // - + (c_Flow[4] * alt) // - + (c_Flow[5] * (std::pow)(cn1, 2)) // - + (c_Flow[6] * cn1 * mach) // - + (c_Flow[7] * cn1 * alt) // - + (c_Flow[8] * (std::pow)(mach, 2)) // - + (c_Flow[9] * mach * alt) // - + (c_Flow[10] * (std::pow)(alt, 2)) // - + (c_Flow[11] * (std::pow)(cn1, 3)) // - + (c_Flow[12] * (std::pow)(cn1, 2) * mach) // - + (c_Flow[13] * (std::pow)(cn1, 2) * alt) // - + (c_Flow[14] * cn1 * (std::pow)(mach, 2)) // - + (c_Flow[15] * cn1 * mach * alt) // - + (c_Flow[16] * cn1 * (std::pow)(alt, 2)) // - + (c_Flow[17] * (std::pow)(mach, 3)) // - + (c_Flow[18] * (std::pow)(mach, 2) * alt) // - + (c_Flow[19] * mach * (std::pow)(alt, 2)) // - + (c_Flow[20] * (std::pow)(alt, 3)); - } - - /** - * @brief Calculates the oil temperature based on energy, previous oil temperature, maximum oil temperature, and time interval. - * - * @param thermalEnergy The thermal energy in Joules. - * @param previousOilTemp The previous oil temperature in Celsius. - * @param maxOilTemperature The maximum oil temperature in Celsius. - * @param deltaTime The time interval in seconds. - * @return The calculated oil temperature in Celsius. - * - * TODO: Currently not used in the code. - */ - static double oilTemperature(double thermalEnergy, double previousOilTemp, double maxOilTemperature, double deltaTime) { - // these constants are likely derived from empirical data or a mathematical model of the engine's behavior - // they were not documented in the original code, and their names here are inferred from their usage - const double heatTransferCoefficient = 0.001; - const double energyScalingFactor = 0.002; - const double temperatureThreshold = 10; - const double temperatureScalingFactor = 0.999997; - - const double changeInThermalEnergy = thermalEnergy * deltaTime * energyScalingFactor; - const double steadyStateTemp = - ((maxOilTemperature * heatTransferCoefficient * deltaTime) + previousOilTemp) / (1 + (heatTransferCoefficient * deltaTime)); - - const double newTemp = steadyStateTemp - changeInThermalEnergy; - if (newTemp >= maxOilTemperature) { - return maxOilTemperature; - } else if (newTemp >= maxOilTemperature - temperatureThreshold) { - return newTemp * temperatureScalingFactor; - } else { - return newTemp; - } - } - - /** - * @brief Calculates the Oil Gulping percentage based on thrust. - * Real-life modeled polynomials - Oil Gulping (%) - * - * @param thrust The thrust in Newton. - * @return The calculated Oil Gulping percentage. - */ - static double oilGulpPct(double thrust) { - const double oilGulpCoefficients[3] = {20.1968848, -1.2270302e-4, 1.78442e-8}; - const double outOilGulpPct = - oilGulpCoefficients[0] + (oilGulpCoefficients[1] * thrust) + (oilGulpCoefficients[2] * (std::pow)(thrust, 2)); - return outOilGulpPct / 100; - } - - /** - * @brief Calculates the Oil Pressure (PSI) based on simulated N2 value. - * Real-life modeled polynomials - Oil Pressure (PSI) - * @param simN2 The simulated N2 value in percent. - * @return The calculated Oil Pressure value in PSI. - */ - static double oilPressure(double simN2) { - const double oilPressureCoefficients[3] = {-0.88921, 0.23711, 0.00682}; - return oilPressureCoefficients[0] + (oilPressureCoefficients[1] * simN2) + (oilPressureCoefficients[2] * (std::pow)(simN2, 2)); - } -}; - -#endif // FLYBYWIRE_AIRCRAFT_POLYNOMIAL_A32NX_HPP diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Tables1502_A32NX.hpp b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Tables1502_A32NX.hpp deleted file mode 100644 index 5bfd8691629..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Tables1502_A32NX.hpp +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_TABLES1502_A32NX_HPP -#define FLYBYWIRE_AIRCRAFT_TABLES1502_A32NX_HPP - -#include - -#include "Fadec.h" - -/** - * @class Table1502_A32NX - * - * This class contains methods and data used in the calculation of the corrected fan speed (CN1 and CN2). - * The class has a 2D array `table` that contains values used in the calculation of the corrected fan speed. - * Each row in the `table` represents a set of values. The columns represent different parameters used in the calculation. - * The class also has two static methods `iCN3` and `iCN1` that calculate the corrected fan speed (CN2 and CN1) respectively. - * - * TODO: extract the reusable code to a common library - */ -class Tables1502_A32NX { - /** - * @brief Table 1502 (CN2 vs correctedN1) representations with FSX nomenclature. - * - * This table represents the relationship between CN2 and correctedN1 in the FSX nomenclature. - * Each row in the table represents a different state of the engine, with the first column being the CN2 value, - * the second and third columns being the lower and upper bounds of the correctedN1 value at Mach 0.2, - * and the fourth column being the correctedN1 value at Mach 0.9. - * - * @return A 2D array representing the CN2 - correctedN1 pairs. - */ - static constexpr double table1502[13][4] = { - {18.20, 0.00, 0.00, 17.00}, // CN2 = 18.20, correctedN1 = [0.00, 0.00] at Mach 0.2, correctedN1 = 17.00 at Mach 0.9 - {22.00, 1.90, 1.90, 17.40}, // CN2 = 22.00, correctedN1 = [1.90, 1.90] at Mach 0.2, correctedN1 = 17.40 at Mach 0.9 - {26.00, 2.50, 2.50, 18.20}, // CN2 = 26.00, correctedN1 = [2.50, 2.50] at Mach 0.2, correctedN1 = 18.20 at Mach 0.9 - {57.00, 12.80, 12.80, 27.00}, // CN2 = 57.00, correctedN1 = [12.80, 12.80] at Mach 0.2, correctedN1 = 27.00 at Mach 0.9 - {68.20, 19.60, 19.60, 34.83}, // CN2 = 68.20, correctedN1 = [19.60, 19.60] at Mach 0.2, correctedN1 = 34.83 at Mach 0.9 - {77.00, 26.00, 26.00, 40.84}, // CN2 = 77.00, correctedN1 = [26.00, 26.00] at Mach 0.2, correctedN1 = 40.84 at Mach 0.9 - {83.00, 31.42, 31.42, 44.77}, // CN2 = 83.00, correctedN1 = [31.42, 31.42] at Mach 0.2, correctedN1 = 44.77 at Mach 0.9 - {89.00, 40.97, 40.97, 50.09}, // CN2 = 89.00, correctedN1 = [40.97, 40.97] at Mach 0.2, correctedN1 = 50.09 at Mach 0.9 - {92.80, 51.00, 51.00, 55.04}, // CN2 = 92.80, correctedN1 = [51.00, 51.00] at Mach 0.2, correctedN1 = 55.04 at Mach 0.9 - {97.00, 65.00, 65.00, 65.00}, // CN2 = 97.00, correctedN1 = [65.00, 65.00] at Mach 0.2, correctedN1 = 65.00 at Mach 0.9 - {100.00, 77.00, 77.00, 77.00}, // CN2 = 100.00, correctedN1 = [77.00, 77.00] at Mach 0.2, correctedN1 = 77.00 at Mach 0.9 - {104.00, 85.00, 85.00, 85.50}, // CN2 = 104.00, correctedN1 = [85.00, 85.00] at Mach 0.2, correctedN1 = 85.50 at Mach 0.9 - {116.50, 101.00, 101.00, 101.00} // CN2 = 116.50, correctedN1 = [101.00, 101.00] at Mach 0.2, correctedN1 = 101.00 at Mach 0.9 - }; - - public: - - /** - * @brief Calculates the expected CN2 at idle. - * - * @param pressureAltitude The pressure altitude in feet. - * @param mach The Mach number. - * @return The expected CN2 value at idle in percent. - */ - static double iCN2(double pressureAltitude, double mach) { - // The specific values are likely derived from empirical data or a mathematical model of the engine's behavior. - // The original source code does not provide any information on the origin of these values. - return 68.2 / ((std::sqrt)((288.15 - (1.98 * pressureAltitude / 1000)) / 288.15) * (std::sqrt)(1 + (0.2 * (std::pow)(mach, 2)))); - } - - /** - * @brief Calculates the expected CN1 at idle. - * - * @param pressureAltitude The pressure altitude in feet. - * @param mach The Mach number. - * @param ambientTemp The ambient temperature in Kelvin. - * @return The expected CN1 value at idle. - */ - static double iCN1(double pressureAltitude, double mach, [[maybe_unused]] double ambientTemp) { - // Calculate the expected CN2 value - const double cn2 = iCN2(pressureAltitude, mach); - - // Find the row in the table that contains the CN2 value and store the index in i - int i = 0; - while (table1502[i][0] <= cn2 && i < 13) { - i++; - } - - // Retrieve the lower and upper bounds of the CN2 value and the correctedN1 value at Mach 0.2 and Mach 0.9 - const double cn2lo = table1502[i - 1][0]; - const double cn2hi = table1502[i][0]; - const double cn1lolo = table1502[i - 1][1]; - const double cn1hilo = table1502[i][1]; - const double cn1lohi = table1502[i - 1][3]; - const double cn1hihi = table1502[i][3]; - - // Interpolate the correctedN1 value based on the CN2 value and the Mach number - const double cn1_lo = Fadec::interpolate(cn2, cn2lo, cn2hi, cn1lolo, cn1hilo); - const double cn1_hi = Fadec::interpolate(cn2, cn2lo, cn2hi, cn1lohi, cn1hihi); - - return Fadec::interpolate(mach, 0.2, 0.9, cn1_lo, cn1_hi); - } -}; - -#endif // FLYBYWIRE_AIRCRAFT_TABLES1502_A32NX_HPP diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/ThrustLimits_A32NX.hpp b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/ThrustLimits_A32NX.hpp deleted file mode 100644 index 392ddbdb94d..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/ThrustLimits_A32NX.hpp +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_THRUSTLIMITS_A32NX_HPP -#define FLYBYWIRE_AIRCRAFT_THRUSTLIMITS_A32NX_HPP - -#include -#include - -#include "EngineRatios.hpp" -#include "Fadec.h" - -/** - * @class ThrustLimits_A32NX - * @brief A static class that provides methods for calculating various engine thrust limits for the A33NX aircraft. - * - * TODO: extract the reusable code to a common library - */ -class ThrustLimits_A32NX { - /** - * @brief A 2D array representing various engine thrust limits. - * - * This 2D array contains 72 rows, each with 6 columns. Each row represents a different altitude - * level and the corresponding engine thrust - * limits. The columns in each row represent the following parameters: - * 1. Altitude (in feet) - * 2. Corner Point (CP) - the temperature below which the engine can operate at full thrust without any restrictions. - * 3. Limit Point (LP) - the temperature above which the engine thrust starts to be limited. - * 4. CN1 Flat - the engine's N1 fan speed limit at the CP temperature. - * 5. CN1 Last - the engine's N1 fan speed limit at the LP temperature. - * 6. CN1 Flex - the engine's N1 fan speed limit at a temperature of 100 degrees Celsius. - * - * The array is divided into sections for different flight phases: Takeoff (TO), Go-Around (GA), - * Climb (CLB), and Maximum Continuous Thrust (MCT). - */ - static constexpr double limits[72][6] = { - // TO - {-2000, 48.000, 55.000, 81.351, 79.370, 61.535}, // row 0 - {-1000, 46.000, 55.000, 82.605, 80.120, 62.105}, // - {0, 44.000, 55.000, 83.832, 80.776, 62.655}, // - {500, 42.000, 52.000, 84.210, 81.618, 62.655}, // - {1000, 42.000, 52.000, 84.579, 81.712, 62.655}, // - {2000, 40.000, 50.000, 85.594, 82.720, 62.655}, // - {3000, 36.000, 48.000, 86.657, 83.167, 61.960}, // - {4000, 32.000, 46.000, 87.452, 83.332, 61.206}, // - {5000, 29.000, 44.000, 88.833, 84.166, 61.206}, // - {6000, 25.000, 42.000, 90.232, 84.815, 61.206}, // - {7000, 21.000, 40.000, 91.711, 85.565, 61.258}, // - {8000, 17.000, 38.000, 93.247, 86.225, 61.777}, // - {9000, 15.000, 36.000, 94.031, 86.889, 60.968}, // - {10000, 13.000, 34.000, 94.957, 88.044, 60.935}, // - {11000, 12.000, 32.000, 95.295, 88.526, 59.955}, // - {12000, 11.000, 30.000, 95.568, 88.818, 58.677}, // - {13000, 10.000, 28.000, 95.355, 88.819, 59.323}, // - {14000, 10.000, 26.000, 95.372, 89.311, 59.965}, // - {15000, 8.000, 24.000, 95.686, 89.907, 58.723}, // - {16000, 5.000, 22.000, 96.160, 89.816, 57.189}, // - {16600, 5.000, 22.000, 96.560, 89.816, 57.189}, // row 20 - // GA - {-2000, 47.751, 54.681, 84.117, 81.901, 63.498}, // row 21 - {-1000, 45.771, 54.681, 85.255, 82.461, 63.920}, // - {0, 43.791, 54.681, 86.411, 83.021, 64.397}, // - {500, 42.801, 52.701, 86.978, 83.740, 64.401}, // - {1000, 41.811, 52.701, 87.568, 83.928, 64.525}, // - {2000, 38.841, 50.721, 88.753, 84.935, 64.489}, // - {3000, 36.861, 48.741, 89.930, 85.290, 63.364}, // - {4000, 32.901, 46.761, 91.004, 85.836, 62.875}, // - {5000, 28.941, 44.781, 92.198, 86.293, 62.614}, // - {6000, 24.981, 42.801, 93.253, 86.563, 62.290}, // - {7000, 21.022, 40.821, 94.273, 86.835, 61.952}, // - {8000, 17.062, 38.841, 94.919, 87.301, 62.714}, // - {9000, 15.082, 36.861, 95.365, 87.676, 61.692}, // - {10000, 13.102, 34.881, 95.914, 88.150, 60.906}, // - {11000, 12.112, 32.901, 96.392, 88.627, 59.770}, // - {12000, 11.122, 30.921, 96.640, 89.206, 58.933}, // - {13000, 10.132, 28.941, 96.516, 89.789, 60.503}, // - {14000, 9.142, 26.961, 96.516, 90.475, 62.072}, // - {15000, 9.142, 24.981, 96.623, 90.677, 59.333}, // - {16000, 7.162, 23.001, 96.845, 90.783, 58.045}, // - {16600, 5.182, 21.022, 97.366, 91.384, 58.642}, // row 41 - // CLB - {-2000, 30.800, 56.870, 80.280, 72.000, 0.000}, // row 42 - {2000, 20.990, 48.157, 82.580, 74.159, 0.000}, // - {5000, 16.139, 43.216, 84.642, 75.737, 0.000}, // - {8000, 7.342, 38.170, 86.835, 77.338, 0.000}, // - {10000, 4.051, 34.518, 88.183, 77.999, 0.000}, // - {10000.1, 4.051, 34.518, 87.453, 77.353, 0.000}, // - {12000, 0.760, 30.865, 88.303, 78.660, 0.000}, // - {15000, -4.859, 25.039, 89.748, 79.816, 0.000}, // - {17000, -9.934, 19.813, 90.668, 80.895, 0.000}, // - {20000, -15.822, 13.676, 92.106, 81.894, 0.000}, // - {24000, -22.750, 6.371, 93.651, 82.716, 0.000}, // - {27000, -29.105, -0.304, 93.838, 83.260, 0.000}, // - {29314, -32.049, -3.377, 93.502, 82.962, 0.000}, // - {31000, -34.980, -6.452, 95.392, 84.110, 0.000}, // - {35000, -45.679, -17.150, 96.104, 85.248, 0.000}, // - {39000, -45.679, -17.150, 96.205, 84.346, 0.000}, // - {41500, -45.679, -17.150, 95.676, 83.745, 0.000}, // row 58 - // MCT - {-1000, 26.995, 54.356, 82.465, 74.086, 0.000}, // row 59 - {3000, 18.170, 45.437, 86.271, 77.802, 0.000}, // - {7000, 9.230, 40.266, 89.128, 79.604, 0.000}, // - {11000, 4.019, 31.046, 92.194, 82.712, 0.000}, // - {15000, -5.226, 21.649, 95.954, 85.622, 0.000}, // - {17000, -9.913, 20.702, 97.520, 85.816, 0.000}, // - {20000, -15.129, 15.321, 99.263, 86.770, 0.000}, // - {22000, -19.947, 10.382, 98.977, 86.661, 0.000}, // - {25000, -25.397, 4.731, 98.440, 85.765, 0.000}, // - {27000, -30.369, -0.391, 97.279, 85.556, 0.000}, // - {31000, -36.806, -7.165, 98.674, 86.650, 0.000}, // - {35000, -43.628, -14.384, 98.386, 85.747, 0.000}, // - {39000, -47.286, -18.508, 97.278, 85.545, 0.000} // row 71 - }; - - public: - /** - * @brief Finds the top-row boundary in the limits array. - * - * @param altitude The altitude to find the top-row boundary for. - * @param index The index to start the search from. - * @return The index of the top-row boundary in the limits array. - */ - static int finder(double altitude, int index) { - while (altitude >= limits[index][0]) { - index++; - } - return index; - } - - /** - * @brief Structure to store the bleed values for different types of limits. - * - * This structure contains three members, each representing a specific bleed value: - * - `n1Packs`: A double representing the bleed value for the packs. - * - `n1Nai`: A double representing the bleed value for the nacelle anti-ice. - * - `n1Wai`: A double representing the bleed value for the wing anti-ice. - */ - struct BleedValues { - double n1Packs; - double n1Nai; - double n1Wai; - }; - - /** - * @brief Lookup table for bleed values based on limit type. - * - * This map stores the bleed values for different types of limits. The key is an integer - * representing the type of limit (0-TO, 1-GA, 2-CLB, 3-MCT), - * and the value is a `BleedValues` structure containing the values for `n1Packs`, `n1Nai`, and `n1Wai`. - */ - static std::map bleedValuesLookup; // see below for initialization - - /** - * @brief Calculates the total bleed for the engine. - * - * @param type The type of operation (0-TO, 1-GA, 2-CLB, 3-MCT). - * @param altitude The current altitude of the aircraft in feet. - * @param oat The outside air temperature in degrees Celsius. - * @param cp The corner point - the temperature below which the engine can operate at full thrust without any restrictions (in degrees Celsius). - * @param lp The limit point - the temperature above which the engine thrust starts to be limited (in degrees Celsius). - * @param flexTemp The flex temperature in degrees Celsius. - * @param packs The status of the air conditioning (0 for off, 1 for on). - * @param nacelle The status of the nacelle anti-ice (0 for off, 1 for on). - * @param wing The status of the wing anti-ice (0 for off, 1 for on). - * @return The total bleed for the engine - */ - static double bleedTotal(int type, // - double altitude, // - double oat, // - double cp, // - double lp, // - double flexTemp, // - int packs, // - int nacelle, // - int wing // - ) { - if (flexTemp > lp && type <= 1) { - return packs * -0.6 + nacelle * -0.7 + wing * -0.7; - } - - // Define a map to store the bleed values for different conditions - // Keys: - // int - Represents the type of operation (0-TO, 1-GA, 2-CLB, 3-MCT). - // bool - Represents whether the altitude is less than 8000. - // bool - Represents whether the outside air temperature is less than the corner point. - // Values: - // double - Represents the bleed value for the packs. - // double - Represents the bleed value for the nacelle anti-ice. - // double - Represents the bleed value for the wing anti-ice. - std::map, std::tuple> bleedValues = { - {{0, true, true}, {-0.4, -0.6, -0.7}}, // - {{0, true, false}, {-0.5, -0.6, -0.7}}, // - {{0, false, true}, {-0.6, -0.8, -0.8}}, // - {{0, false, false}, {-0.7, -0.8, -0.8}}, // - {{1, true, true}, {-0.4, -0.6, -0.6}}, // - {{1, true, false}, {-0.4, -0.6, -0.6}}, // - {{1, false, true}, {-0.6, -0.7, -0.8}}, // - {{1, false, false}, {-0.6, -0.7, -0.8}}, // - {{2, true, false}, {-0.2, -0.8, -0.4}}, // - {{2, false, false}, {-0.3, -0.8, -0.4}}, // - {{3, true, false}, {-0.6, -0.9, -1.2}}, // - {{3, false, false}, {-0.6, -0.9, -1.2}} // - }; - - double n1Packs = 0; - double n1Nai = 0; - double n1Wai = 0; - - // Use the map to get the bleed values - std::tie(n1Packs, n1Nai, n1Wai) = bleedValues[{type, altitude < 8000, oat < cp}]; - - return packs * n1Packs + nacelle * n1Nai + wing * n1Wai; - } - - /** - * @brief Calculates the N1 limit for the engine. - * - * This function calculates the N1 limit for the engine based on various parameters such as the - * type of operation, altitude, ambient temperature, ambient pressure, flex temperature, and the - * status of the air conditioning (AC), nacelle anti-ice (nacelle), and wing anti-ice (wing). - * - * @param type The type of operation (0-TO, 1-GA, 2-CLB, 3-MCT). - * @param altitude The current altitude of the aircraft. - * @param ambientTemp The ambient temperature. - * @param ambientPressure The ambient pressure. - * @param flexTemp The flex temperature. - * @param packs The status of the air conditioning (0 for off, 1 for on). - * @param nacelle The status of the nacelle anti-ice (0 for off, 1 for on). - * @param wing The status of the wing anti-ice (0 for off, 1 for on). - * @return The N1 limit for the engine. - */ - static double limitN1(int type, // - double altitude, // - double ambientTemp, // - double ambientPressure, // - double flexTemp, // - int packs, // - int nacelle, // - int wing // - ) { - int rowMin = 0; - int rowMax = 0; - int loAltRow = 0; - int hiAltRow = 0; - double mach = 0; - - // Set main variables per Limit Type - switch (type) { - case 0: // TO - rowMin = 0; - rowMax = 20; - mach = 0; - break; - case 1: // GA - rowMin = 21; - rowMax = 41; - mach = 0.225; - break; - case 2: // CLB - rowMin = 42; - rowMax = 58; - if (altitude <= 10000) { - mach = Fadec::cas2mach(250, ambientPressure); - } else { - mach = Fadec::cas2mach(300, ambientPressure); - if (mach > 0.78) - mach = 0.78; - } - break; - case 3: // MCT - rowMin = 59; - rowMax = 71; - mach = Fadec::cas2mach(230, ambientPressure); - break; - } - - // Check for over/under flows. Else, find top row value - if (altitude <= limits[rowMin][0]) { - hiAltRow = rowMin; - loAltRow = rowMin; - } else if (altitude >= limits[rowMax][0]) { - hiAltRow = rowMax; - loAltRow = rowMax; - } else { - hiAltRow = finder(altitude, rowMin); - loAltRow = hiAltRow - 1; - } - - // Define key table variables and interpolation - const double cp = Fadec::interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][1], limits[hiAltRow][1]); - const double lp = Fadec::interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][2], limits[hiAltRow][2]); - const double cn1Flat = Fadec::interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][3], limits[hiAltRow][3]); - const double cn1Last = Fadec::interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][4], limits[hiAltRow][4]); - const double cn1Flex = Fadec::interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][5], limits[hiAltRow][5]); - - double cn1 = 0; - double m = 0; - double b = 0; - if (flexTemp > 0 && type <= 1) { // CN1 for Flex Case - if (flexTemp <= cp) { - cn1 = cn1Flat; - } else if (flexTemp > lp) { - m = (cn1Flex - cn1Last) / (100 - lp); - b = cn1Flex - m * 100; - cn1 = (m * flexTemp) + b; - } else { - m = (cn1Last - cn1Flat) / (lp - cp); - b = cn1Last - m * lp; - cn1 = (m * flexTemp) + b; - } - } - else { // CN1 for All other cases - if (ambientTemp <= cp) { - cn1 = cn1Flat; - } else { - m = (cn1Last - cn1Flat) / (lp - cp); - b = cn1Last - m * lp; - cn1 = (m * ambientTemp) + b; - } - } - - // Define bleed rating/ de-rating - const double bleed = bleedTotal(type, altitude, ambientTemp, cp, lp, flexTemp, packs, nacelle, wing); - - return (cn1 * (std::sqrt)(EngineRatios::theta2(mach, ambientTemp))) + bleed; - } -}; - -#endif // FLYBYWIRE_AIRCRAFT_THRUSTLIMITS_A32NX_HPP diff --git a/fbw-a32nx/src/wasm/fadec_a32nx/src/Gauge_Fadec_v2.cpp b/fbw-a32nx/src/wasm/fadec_a32nx/src/Gauge_Fadec_v2.cpp deleted file mode 100644 index 018f44fdda9..00000000000 --- a/fbw-a32nx/src/wasm/fadec_a32nx/src/Gauge_Fadec_v2.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2023 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef __INTELLISENSE__ -#define MODULE_EXPORT __attribute__((visibility("default"))) -#define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod))) -#else -#define MODULE_EXPORT -#define MODULE_WASM_MODNAME(mod) -#define __attribute__(x) -#define __restrict__ -#endif - -#include -#include - -#include "Fadec/Fadec_A32NX.h" -#include "MsfsHandler.h" - -// Create an instance of the MsfsHandler -// We do not use a prefix and use the full LVar name in the code. -// Prefixes in framework code are not ideal and also prevent the creation -// of an LVar without a prefix -MsfsHandler msfsHandler("Gauge_Fadec_A32NX", ""); - -// ADD ADDITIONAL MODULES HERE -// This is the only place these have to be added - everything else is handled automatically -Fadec_A32NX fadec(msfsHandler); - -/** - * Gauge Callback - * There can by multiple gauges in a single wasm module. Just add another gauge callback function - * and register it in the panel.cfg file. - * - * Avoid putting any logic in the gauge callback function. Instead, create a new class and put - * the logic there. - * - * @see - * https://docs.flightsimulator.com/html/Content_Configuration/SimObjects/Aircraft_SimO/Instruments/C_C++_Gauges.htm?rhhlterm=_gauge_callback&rhsearch=_gauge_callback - */ -extern "C" { -[[maybe_unused]] MSFS_CALLBACK bool Gauge_Fadec_gauge_callback([[maybe_unused]] FsContext ctx, int svcId, void* pData) { - switch (svcId) { - case PANEL_SERVICE_PRE_INSTALL: { - return msfsHandler.initialize(); - } - case PANEL_SERVICE_PRE_DRAW: { - return msfsHandler.update(static_cast(pData)); - } - case PANEL_SERVICE_PRE_KILL: { - return msfsHandler.shutdown(); - } - default: - break; - } - return false; -} -} diff --git a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg index 8edba55a73a..0a1953fa006 100644 --- a/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg +++ b/fbw-a380x/src/base/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/panel.cfg @@ -151,16 +151,16 @@ background_color=0,0,0 htmlgauge00=WasmInstrument/WasmInstrument.html?wasm_module=systems.wasm&wasm_gauge=systems, 0,0,1,1 htmlgauge01=WasmInstrument/WasmInstrument.html?wasm_module=fbw.wasm&wasm_gauge=fbw, 0,0,1,1 -htmlgauge02=WasmInstrument/WasmInstrument.html?wasm_module=fadec-a380x.wasm&wasm_gauge=Gauge_Fadec,0,0,1,1 +htmlgauge02=WasmInstrument/WasmInstrument.html?wasm_module=fadec.wasm&wasm_gauge=FadecGauge, 0,0,1,1 htmlgauge03=WasmInstrument/WasmInstrument.html?wasm_module=extra-backend-a380x.wasm&wasm_gauge=Gauge_Extra_Backend,0,0,1,1 [VCockpit20] -size_mm=0,0 -pixel_size=0,0 -texture=NO_TEXTURE -background_color=0,0,0 +size_mm = 0,0 +pixel_size = 0,0 +texture = NO_TEXTURE +background_color = 0,0,0 -htmlgauge00=A380X/SystemsHost/systems-host.html,0,0,1,1 +htmlgauge00 = A380X/SystemsHost/systems-host.html,0,0,1,1 [VCockpit21] size_mm=0,0 diff --git a/fbw-a380x/src/systems/instruments/src/EWD/elements/IgnitionBorder.tsx b/fbw-a380x/src/systems/instruments/src/EWD/elements/IgnitionBorder.tsx index d8c904990f5..a0c146971e9 100644 --- a/fbw-a380x/src/systems/instruments/src/EWD/elements/IgnitionBorder.tsx +++ b/fbw-a380x/src/systems/instruments/src/EWD/elements/IgnitionBorder.tsx @@ -3,7 +3,7 @@ import { Position, EngineNumber, FadecActive } from '@instruments/common/types'; import React from 'react'; const IgnitionBorder: React.FC = ({ x, y, engine, active }) => { - const [engineState] = useSimVar(`L:A32NX_ENGINE_STATE:${engine}`, 'enum', 500); + const [engineState] = useSimVar(`L:A32NX_ENGINE_STATE:${engine}`, 'bool', 500); const [N1Percent] = useSimVar(`L:A32NX_ENGINE_N1:${engine}`, 'percent', 100); const [N1Idle] = useSimVar('L:A32NX_ENGINE_IDLE_N1', 'percent', 1000); const showBorder = !!((N1Percent < Math.floor(N1Idle) - 1) && (engineState === 2)); diff --git a/fbw-a380x/src/wasm/CMakeLists.txt b/fbw-a380x/src/wasm/CMakeLists.txt index a7894cbd909..b68e233e5c0 100644 --- a/fbw-a380x/src/wasm/CMakeLists.txt +++ b/fbw-a380x/src/wasm/CMakeLists.txt @@ -5,10 +5,9 @@ set(OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../out/flybywire-aircraft-a3 add_definitions(-DA380X) add_subdirectory(extra-backend-a380x) -add_subdirectory(fadec_a380x) -# these are only here to allow CMake compatible IDEs (JetBrains' Clion for example) to show systax -# highlighting and allow code navigation +# FIXME: remove the if-clause as soon as all components are using CMake if (WIN32) + add_subdirectory(fadec_a380) add_subdirectory(fbw_a380) endif () diff --git a/fbw-a380x/src/wasm/fadec_a380/CMakeLists.txt b/fbw-a380x/src/wasm/fadec_a380/CMakeLists.txt new file mode 100644 index 00000000000..0d2e084ca1e --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380/CMakeLists.txt @@ -0,0 +1,25 @@ +# This CMakeLists.txt file is used only for syntax highlighting and navigating +# through the code in an IDE. It is not used for building the project. + +cmake_minimum_required(VERSION 3.19) + +project(flybywire-a380x-fadec) + +set(CMAKE_CXX_STANDARD 20) + +set(CMAKE_CXX_FLAGS "-c -g -DDEBUG -Wno-unused-command-line-argument -Wno-ignored-attributes -Wno-macro-redefined --sysroot \"${MSFS_SDK}/WASM/wasi-sysroot\" -target wasm32-unknown-wasi -flto -D_MSFS_WASM=1 -D__wasi__ -D_LIBCPP_HAS_NO_THREADS -D_WINDLL -D_MBCS -mthread-model single -fno-exceptions -fms-extensions") + +include_directories("${MSFS_SDK}/WASM/include") +include_directories("${MSFS_SDK}/WASM/wasi-sysroot/include") +include_directories("${MSFS_SDK}/SimConnect SDK/include") + +include_directories( + ./src + ${FBW_ROOT}/fbw-common/src/wasm/fadec_common/src + ${FBW_ROOT}/fbw-common/src/wasm/fbw_common/src + ${FBW_ROOT}/fbw-common/src/wasm/fbw_common/src/inih +) + +add_executable(flybywire-a380x-fadec + ./src/FadecGauge.cpp + ) diff --git a/fbw-a380x/src/wasm/fadec_a380/build.sh b/fbw-a380x/src/wasm/fadec_a380/build.sh new file mode 100755 index 00000000000..c06fdb76fd4 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380/build.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# get directory of this script relative to root +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +COMMON_DIR="${DIR}/../../../../fbw-common/src/wasm" +OUTPUT="${DIR}/../../../out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/fadec.wasm" + +echo "DIR = $DIR" +echo "COMMON_DIR = $COMMON_DIR" +echo "OUTPUT = $OUTPUT" + +if [ "$1" == "--debug" ]; then + WASMLD_ARGS="" + CLANG_ARGS="-g" +else + WASMLD_ARGS="-O2 --lto-O2 --strip-debug" + CLANG_ARGS="-flto -O2 -DNDEBUG" +fi + +set -e + +# create temporary folder for o files +mkdir -p "${DIR}/obj" +pushd "${DIR}/obj" + +# compile c++ code for the A32NX +clang++ \ + -c \ + ${CLANG_ARGS} \ + -std=c++20 \ + -Wno-unused-command-line-argument \ + -Wno-ignored-attributes \ + -Wno-macro-redefined \ + --sysroot "${MSFS_SDK}/WASM/wasi-sysroot" \ + -target wasm32-unknown-wasi \ + -D_MSFS_WASM=1 \ + -D__wasi__ \ + -D_LIBCPP_HAS_NO_THREADS \ + -D_WINDLL \ + -D_MBCS \ + -mthread-model single \ + -fno-exceptions \ + -fms-extensions \ + -fvisibility=hidden \ + -I "${MSFS_SDK}/WASM/include" \ + -I "${MSFS_SDK}/SimConnect SDK/include" \ + -I "${COMMON_DIR}/fadec_common/src" \ + -I "${COMMON_DIR}/fbw_common/src/inih" \ + -I "${DIR}/common" \ + "${DIR}/src/FadecGauge.cpp" + +# restore directory +popd + +wasm-ld \ + --no-entry \ + --allow-undefined \ + -L "${MSFS_SDK}/WASM/wasi-sysroot/lib/wasm32-wasi" \ + -lc "${MSFS_SDK}/WASM/wasi-sysroot/lib/wasm32-wasi/libclang_rt.builtins-wasm32.a" \ + --export __wasm_call_ctors \ + ${WASMLD_ARGS} \ + --export-dynamic \ + --export malloc \ + --export free \ + --export __wasm_call_ctors \ + --export mallinfo \ + --export mchunkit_begin \ + --export mchunkit_next \ + --export get_pages_state \ + --export mark_decommit_pages \ + --export-table \ + --gc-sections \ + -lc++ -lc++abi \ + ${DIR}/obj/*.o \ + -o $OUTPUT diff --git a/fbw-a380x/src/wasm/fadec_a380/src/EngineControl.h b/fbw-a380x/src/wasm/fadec_a380/src/EngineControl.h new file mode 100644 index 00000000000..d58d0e8bcc0 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380/src/EngineControl.h @@ -0,0 +1,1536 @@ +#pragma once + +#include +#include + +#include +#include + +#include "RegPolynomials.h" +#include "SimVars.h" +#include "Tables.h" +#include "ThrustLimits.h" +#include "common.h" + +#include "ini_type_conversion.h" + +#define FILENAME_FADEC_CONF_DIRECTORY "\\work\\AircraftStates\\" +#define FILENAME_FADEC_CONF_FILE_EXTENSION ".ini" +#define CONFIGURATION_SECTION_FUEL "FUEL" + +#define CONFIGURATION_SECTION_FUEL_LEFT_OUTER_QTY "FUEL_LEFT_OUTER_QTY" +#define CONFIGURATION_SECTION_FUEL_FEED_ONE_QTY "FUEL_FEED_ONE_QTY" +#define CONFIGURATION_SECTION_FUEL_LEFT_MID_QTY "FUEL_LEFT_MID_QTY" +#define CONFIGURATION_SECTION_FUEL_LEFT_INNER_QTY "FUEL_LEFT_INNER_QTY" +#define CONFIGURATION_SECTION_FUEL_FEED_TWO_QTY "FUEL_FEED_TWO_QTY" +#define CONFIGURATION_SECTION_FUEL_FEED_THREE_QTY "FUEL_FEED_THREE_QTY" +#define CONFIGURATION_SECTION_FUEL_RIGHT_INNER_QTY "FUEL_RIGHT_INNER_QTY" +#define CONFIGURATION_SECTION_FUEL_RIGHT_MID_QTY "FUEL_RIGHT_MID_QTY" +#define CONFIGURATION_SECTION_FUEL_FEED_FOUR_QTY "FUEL_FEED_FOUR_QTY" +#define CONFIGURATION_SECTION_FUEL_RIGHT_OUTER_QTY "FUEL_RIGHT_OUTER_QTY" +#define CONFIGURATION_SECTION_FUEL_TRIM_QTY "FUEL_TRIM_QTY" + +/* Values in gallons */ +struct Configuration { + double fuelLeftOuter = 2731.0; + double fuelFeedOne = 1082.0; + double fuelLeftMid = 9630.0; + double fuelLeftInner = 12187.0; + double fuelFeedTwo = fuelFeedOne; + double fuelFeedThree = fuelFeedOne; + double fuelRightInner = fuelLeftInner; + double fuelRightMid = fuelLeftMid; + double fuelFeedFour = fuelFeedOne; + double fuelRightOuter = fuelLeftOuter; + double fuelTrim = 6259.0; +}; + +class EngineControl { + private: + SimVars* simVars; + EngineRatios* ratios; + Polynomial* poly; + Timer timerEngine1; + Timer timerEngine2; + Timer timerEngine3; + Timer timerEngine4; + Timer timerFuel; + + std::string confFilename = FILENAME_FADEC_CONF_DIRECTORY; + + bool simPaused; + double animationDeltaTime; + double timer; + double ambientTemp; + double ambientPressure; + double simOnGround; + double devState; + double isReady; + + int engine; + double engineState; + double engineStarter; + double engineIgniter; + + double packs; + double nai; + double wai; + + double simCN1; + double simN1; + double simN3; + double thrust; + double simN3Engine1Pre; + double simN3Engine2Pre; + double simN3Engine3Pre; + double simN3Engine4Pre; + double deltaN3; + double thermalEnergy1; + double thermalEnergy2; + double thermalEnergy3; + double thermalEnergy4; + double oilTemperature; + double oilTemperatureEngine1Pre; + double oilTemperatureEngine2Pre; + double oilTemperatureEngine3Pre; + double oilTemperatureEngine4Pre; + double oilTemperatureMax; + double idleN1; + double idleN3; + double idleFF; + double idleEGT; + double idleOil; + double mach; + double pressAltitude; + double correctedEGT; + double correctedFuelFlow; + double cFbwFF; + + const double LBS_TO_KGS = 0.4535934; + const double KGS_TO_LBS = 1 / 0.4535934; + const double FUEL_THRESHOLD = 661; // lbs/sec + + bool isFlexActive = false; + double prevThrustLimitType = 0; + double prevFlexTemperature = 0; + + const double waitTime = 10; + const double transitionTime = 30; + + bool isTransitionActive = false; + double transitionFactor = 0; + double transitionStartTime = 0; + + /// + /// Generate Idle/ Initial Engine Parameters (non-imbalanced) + /// + void generateIdleParameters(double pressAltitude, double mach, double ambientTemp, double ambientPressure) { + double idleCN1; + double idleCFF; + + idleCN1 = iCN1(pressAltitude, mach, ambientTemp); + idleN1 = idleCN1 * sqrt(ratios->theta2(0, ambientTemp)); + idleN3 = iCN3(pressAltitude, mach) * sqrt(ratios->theta(ambientTemp)); + idleCFF = poly->correctedFuelFlow(idleCN1, 0, pressAltitude); // lbs/hr + idleFF = idleCFF * LBS_TO_KGS * ratios->delta2(0, ambientPressure) * sqrt(ratios->theta2(0, ambientTemp)); // Kg/hr + idleEGT = poly->correctedEGT(idleCN1, idleCFF, 0, pressAltitude) * ratios->theta2(0, ambientTemp); + + simVars->setEngineIdleN1(idleN1); + simVars->setEngineIdleN3(idleN3); + simVars->setEngineIdleFF(idleFF); + simVars->setEngineIdleEGT(idleEGT); + } + + double initOil(int minOil, int maxOil) { + double idleOil = (rand() % (maxOil - minOil + 1) + minOil) / 10; + return idleOil; + } + + /// + /// Engine State Machine + /// 0 - Engine OFF, 1 - Engine ON, 2 - Engine Starting, 3 - Engine Re-starting & 4 - Engine Shutting + /// + void engineStateMachine(int engine, + double engineIgniter, + double engineStarter, + double simN3, + double idleN3, + double pressAltitude, + double ambientTemp, + double deltaTimeDiff) { + int resetTimer = 0; + double egtFbw = 0; + + switch (engine) { + case 1: + engineState = simVars->getEngine1State(); + egtFbw = simVars->getEngine1EGT(); + break; + case 2: + engineState = simVars->getEngine2State(); + egtFbw = simVars->getEngine2EGT(); + break; + case 3: + engineState = simVars->getEngine3State(); + egtFbw = simVars->getEngine3EGT(); + break; + case 4: + engineState = simVars->getEngine4State(); + egtFbw = simVars->getEngine4EGT(); + break; + } + + // Present State PAUSED + if (deltaTimeDiff == 0 && engineState < 10) { + engineState = engineState + 10; + simPaused = true; + } else if (deltaTimeDiff == 0 && engineState >= 10) { + simPaused = true; + } else { + simPaused = false; + + // Present State OFF + if (engineState == 0 || engineState == 10) { + if (engineIgniter == 1 && engineStarter == 1 && simN3 > 20) { + engineState = 1; + } else if (engineIgniter == 2 && engineStarter == 1) { + engineState = 2; + } else { + engineState = 0; + } + } + + // Present State ON + if (engineState == 1 || engineState == 11) { + if (engineStarter == 1) { + engineState = 1; + } else { + engineState = 4; + } + } + + // Present State Starting. + if (engineState == 2 || engineState == 12) { + if (engineStarter == 1 && simN3 >= (idleN3 - 0.1)) { + engineState = 1; + resetTimer = 1; + } else if (engineStarter == 0) { + engineState = 4; + resetTimer = 1; + } else { + engineState = 2; + } + } + + // Present State Re-Starting. + if (engineState == 3 || engineState == 13) { + if (engineStarter == 1 && simN3 >= (idleN3 - 0.1)) { + engineState = 1; + resetTimer = 1; + } else if (engineStarter == 0) { + engineState = 4; + resetTimer = 1; + } else { + engineState = 3; + } + } + + // Present State Shutting + if (engineState == 4 || engineState == 14) { + if (engineIgniter == 2 && engineStarter == 1) { + engineState = 3; + resetTimer = 1; + } else if (engineStarter == 0 && simN3 < 0.05 && egtFbw <= ambientTemp) { + engineState = 0; + resetTimer = 1; + } else if (engineStarter == 1 && simN3 > 50) { + engineState = 3; + resetTimer = 1; + } else { + engineState = 4; + } + } + } + + switch (engine) { + case 1: + simVars->setEngine1State(engineState); + if (resetTimer == 1) { + simVars->setEngine1Timer(0); + } + break; + case 2: + simVars->setEngine2State(engineState); + if (resetTimer == 1) { + simVars->setEngine2Timer(0); + } + break; + case 3: + simVars->setEngine3State(engineState); + if (resetTimer == 1) { + simVars->setEngine3Timer(0); + } + break; + case 4: + simVars->setEngine4State(engineState); + if (resetTimer == 1) { + simVars->setEngine4Timer(0); + } + break; + } + } + + /// + /// Engine Start Procedure + /// TIT + void engineStartProcedure(int engine, + double engineState, + double deltaTime, + double timer, + double simN3, + double pressAltitude, + double ambientTemp) { + double startCN3Engine1; + double startCN3Engine2; + double startCN3Engine3; + double startCN3Engine4; + double preN3Fbw; + double newN3Fbw; + double preEgtFbw; + double startEgtFbw; + double shutdownEgtFbw; + + idleN3 = simVars->getEngineIdleN3(); + idleN1 = simVars->getEngineIdleN1(); + idleFF = simVars->getEngineIdleFF(); + idleEGT = simVars->getEngineIdleEGT(); + + if (engine == 1) { + // Delay between Engine Master ON and Start Valve Open + if (timer < 1.7) { + if (simOnGround == 1) { + simVars->setFuelUsedEngine1(0); + } + simVars->setEngine1Timer(timer + deltaTime); + startCN3Engine1 = 0; + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::StartCN3Engine1, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &startCN3Engine1); + } else { + preN3Fbw = simVars->getEngine1N3(); + preEgtFbw = simVars->getEngine1EGT(); + newN3Fbw = poly->startN3(simN3, preN3Fbw, idleN3); + startEgtFbw = poly->startEGT(newN3Fbw, idleN3, ambientTemp, idleEGT); + shutdownEgtFbw = poly->shutdownEGT(preEgtFbw, ambientTemp, deltaTime); + + simVars->setEngine1N3(newN3Fbw); + simVars->setEngine1N2(newN3Fbw + 0.7); + simVars->setEngine1N1(poly->startN1(newN3Fbw, idleN3, idleN1)); + simVars->setEngine1FF(poly->startFF(newN3Fbw, idleN3, idleFF)); + + if (engineState == 3) { + if (abs(startEgtFbw - preEgtFbw) <= 1.5) { + simVars->setEngine1EGT(startEgtFbw); + simVars->setEngine1State(2); + } else if (startEgtFbw > preEgtFbw) { + simVars->setEngine1EGT(preEgtFbw + (0.75 * deltaTime * (idleN3 - newN3Fbw))); + } else { + simVars->setEngine1EGT(shutdownEgtFbw); + } + } else { + simVars->setEngine1EGT(startEgtFbw); + } + + oilTemperature = poly->startOilTemp(newN3Fbw, idleN3, ambientTemp); + oilTemperatureEngine1Pre = oilTemperature; + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine1, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperature); + } + } else if (engine == 2) { + if (timer < 1.7) { + if (simOnGround == 1) { + simVars->setFuelUsedEngine2(0); + } + simVars->setEngine2Timer(timer + deltaTime); + startCN3Engine2 = 0; + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::StartCN3Engine2, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &startCN3Engine2); + } else { + preN3Fbw = simVars->getEngine2N3(); + preEgtFbw = simVars->getEngine2EGT(); + newN3Fbw = poly->startN3(simN3, preN3Fbw, idleN3); + startEgtFbw = poly->startEGT(newN3Fbw, idleN3, ambientTemp, idleEGT); + shutdownEgtFbw = poly->shutdownEGT(preEgtFbw, ambientTemp, deltaTime); + + simVars->setEngine2N3(newN3Fbw); + simVars->setEngine2N2(newN3Fbw + 0.7); + simVars->setEngine2N1(poly->startN1(newN3Fbw, idleN3, idleN1)); + simVars->setEngine2FF(poly->startFF(newN3Fbw, idleN3, idleFF)); + + if (engineState == 3) { + if (abs(startEgtFbw - preEgtFbw) <= 1.5) { + simVars->setEngine2EGT(startEgtFbw); + simVars->setEngine2State(2); + } else if (startEgtFbw > preEgtFbw) { + simVars->setEngine2EGT(preEgtFbw + (0.75 * deltaTime * (idleN3 - newN3Fbw))); + } else { + simVars->setEngine2EGT(shutdownEgtFbw); + } + } else { + simVars->setEngine2EGT(startEgtFbw); + } + + oilTemperature = poly->startOilTemp(newN3Fbw, idleN3, ambientTemp); + oilTemperatureEngine2Pre = oilTemperature; + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine2, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperature); + } + } else if (engine == 3) { + if (timer < 1.7) { + if (simOnGround == 1) { + simVars->setFuelUsedEngine3(0); + } + simVars->setEngine3Timer(timer + deltaTime); + startCN3Engine3 = 0; + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::StartCN3Engine3, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &startCN3Engine3); + } else { + preN3Fbw = simVars->getEngine3N3(); + preEgtFbw = simVars->getEngine3EGT(); + newN3Fbw = poly->startN3(simN3, preN3Fbw, idleN3); + startEgtFbw = poly->startEGT(newN3Fbw, idleN3, ambientTemp, idleEGT); + shutdownEgtFbw = poly->shutdownEGT(preEgtFbw, ambientTemp, deltaTime); + + simVars->setEngine3N3(newN3Fbw); + simVars->setEngine3N2(newN3Fbw + 0.7); + simVars->setEngine3N1(poly->startN1(newN3Fbw, idleN3, idleN1)); + simVars->setEngine3FF(poly->startFF(newN3Fbw, idleN3, idleFF)); + + if (engineState == 3) { + if (abs(startEgtFbw - preEgtFbw) <= 1.5) { + simVars->setEngine3EGT(startEgtFbw); + simVars->setEngine3State(2); + } else if (startEgtFbw > preEgtFbw) { + simVars->setEngine3EGT(preEgtFbw + (0.75 * deltaTime * (idleN3 - newN3Fbw))); + } else { + simVars->setEngine3EGT(shutdownEgtFbw); + } + } else { + simVars->setEngine3EGT(startEgtFbw); + } + + oilTemperature = poly->startOilTemp(newN3Fbw, idleN3, ambientTemp); + oilTemperatureEngine3Pre = oilTemperature; + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine3, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperature); + } + } else { + if (timer < 1.7) { + if (simOnGround == 1) { + simVars->setFuelUsedEngine4(0); + } + simVars->setEngine4Timer(timer + deltaTime); + startCN3Engine4 = 0; + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::StartCN3Engine4, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &startCN3Engine4); + } else { + preN3Fbw = simVars->getEngine4N3(); + preEgtFbw = simVars->getEngine4EGT(); + newN3Fbw = poly->startN3(simN3, preN3Fbw, idleN3); + startEgtFbw = poly->startEGT(newN3Fbw, idleN3, ambientTemp, idleEGT); + shutdownEgtFbw = poly->shutdownEGT(preEgtFbw, ambientTemp, deltaTime); + + simVars->setEngine4N3(newN3Fbw); + simVars->setEngine4N2(newN3Fbw + 0.7); + simVars->setEngine4N1(poly->startN1(newN3Fbw, idleN3, idleN1)); + simVars->setEngine4FF(poly->startFF(newN3Fbw, idleN3, idleFF)); + + if (engineState == 3) { + if (abs(startEgtFbw - preEgtFbw) <= 1.5) { + simVars->setEngine4EGT(startEgtFbw); + simVars->setEngine4State(2); + } else if (startEgtFbw > preEgtFbw) { + simVars->setEngine4EGT(preEgtFbw + (0.75 * deltaTime * (idleN3 - newN3Fbw))); + } else { + simVars->setEngine4EGT(shutdownEgtFbw); + } + } else { + simVars->setEngine4EGT(startEgtFbw); + } + + oilTemperature = poly->startOilTemp(newN3Fbw, idleN3, ambientTemp); + oilTemperatureEngine4Pre = oilTemperature; + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine4, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperature); + } + } + } + + /// + /// Engine Shutdown Procedure - TEMPORAL SOLUTION + /// + void engineShutdownProcedure(int engine, double ambientTemp, double simN1, double deltaTime, double timer) { + double preN1Fbw; + double preN3Fbw; + double preEgtFbw; + double newN1Fbw; + double newN3Fbw; + double newEgtFbw; + + if (engine == 1) { + if (timer < 1.8) { + simVars->setEngine1Timer(timer + deltaTime); + } else { + preN1Fbw = simVars->getEngine1N1(); + preN3Fbw = simVars->getEngine1N3(); + preEgtFbw = simVars->getEngine1EGT(); + newN1Fbw = poly->shutdownN1(preN1Fbw, deltaTime); + if (simN1 < 5 && simN1 > newN1Fbw) { // Takes care of windmilling + newN1Fbw = simN1; + } + newN3Fbw = poly->shutdownN3(preN3Fbw, deltaTime); + newEgtFbw = poly->shutdownEGT(preEgtFbw, ambientTemp, deltaTime); + simVars->setEngine1N1(newN1Fbw); + simVars->setEngine1N2(newN3Fbw + 0.7); + simVars->setEngine1N3(newN3Fbw); + simVars->setEngine1EGT(newEgtFbw); + } + } else if (engine == 2) { + if (timer < 1.8) { + simVars->setEngine2Timer(timer + deltaTime); + } else { + preN1Fbw = simVars->getEngine2N1(); + preN3Fbw = simVars->getEngine2N3(); + preEgtFbw = simVars->getEngine2EGT(); + newN1Fbw = poly->shutdownN1(preN1Fbw, deltaTime); + if (simN1 < 5 && simN1 > newN1Fbw) { // Takes care of windmilling + newN1Fbw = simN1; + } + newN3Fbw = poly->shutdownN3(preN3Fbw, deltaTime); + newEgtFbw = poly->shutdownEGT(preEgtFbw, ambientTemp, deltaTime); + simVars->setEngine2N1(newN1Fbw); + simVars->setEngine2N2(newN3Fbw + 0.7); + simVars->setEngine2N3(newN3Fbw); + simVars->setEngine2EGT(newEgtFbw); + } + } else if (engine == 3) { + if (timer < 1.8) { + simVars->setEngine3Timer(timer + deltaTime); + } else { + preN1Fbw = simVars->getEngine3N1(); + preN3Fbw = simVars->getEngine3N3(); + preEgtFbw = simVars->getEngine3EGT(); + newN1Fbw = poly->shutdownN1(preN1Fbw, deltaTime); + if (simN1 < 5 && simN1 > newN1Fbw) { // Takes care of windmilling + newN1Fbw = simN1; + } + newN3Fbw = poly->shutdownN3(preN3Fbw, deltaTime); + newEgtFbw = poly->shutdownEGT(preEgtFbw, ambientTemp, deltaTime); + simVars->setEngine3N1(newN1Fbw); + simVars->setEngine3N2(newN3Fbw + 0.7); + simVars->setEngine3N3(newN3Fbw); + simVars->setEngine3EGT(newEgtFbw); + } + } else { + if (timer < 1.8) { + simVars->setEngine4Timer(timer + deltaTime); + } else { + preN1Fbw = simVars->getEngine4N1(); + preN3Fbw = simVars->getEngine4N3(); + preEgtFbw = simVars->getEngine4EGT(); + newN1Fbw = poly->shutdownN1(preN1Fbw, deltaTime); + if (simN1 < 5 && simN1 > newN1Fbw) { // Takes care of windmilling + newN1Fbw = simN1; + } + newN3Fbw = poly->shutdownN3(preN3Fbw, deltaTime); + newEgtFbw = poly->shutdownEGT(preEgtFbw, ambientTemp, deltaTime); + simVars->setEngine4N1(newN1Fbw); + simVars->setEngine4N2(newN3Fbw + 0.7); + simVars->setEngine4N3(newN3Fbw); + simVars->setEngine4EGT(newEgtFbw); + } + } + } + /// + /// FBW Engine RPM (N1, N2 and N3) + /// Updates Engine N1, N2 and N3 with our own algorithm for start-up and shutdown + /// + void updatePrimaryParameters(int engine, double simN1, double simN3) { + if (engine == 1) { + simVars->setEngine1N1(simN1); + simVars->setEngine1N2(simN3 + 0.7); + simVars->setEngine1N3(simN3); + } else if (engine == 2) { + simVars->setEngine2N1(simN1); + simVars->setEngine2N2(simN3 + 0.7); + simVars->setEngine2N3(simN3); + } else if (engine == 3) { + simVars->setEngine3N1(simN1); + simVars->setEngine3N2(simN3 + 0.7); + simVars->setEngine3N3(simN3); + } else { + simVars->setEngine4N1(simN1); + simVars->setEngine4N2(simN3 + 0.7); + simVars->setEngine4N3(simN3); + } + } + + /// + /// FBW Exhaust Gas Temperature (in degree Celsius) + /// Updates EGT with realistic values visualized in the ECAM + /// + void updateEGT(int engine, + double deltaTime, + double simOnGround, + double engineState, + double simCN1, + double cFbwFF, + double mach, + double pressAltitude, + double ambientTemp) { + double egtFbwPreviousEng1; + double egtFbwActualEng1; + double egtFbwPreviousEng2; + double egtFbwActualEng2; + double egtFbwPreviousEng3; + double egtFbwActualEng3; + double egtFbwPreviousEng4; + double egtFbwActualEng4; + + correctedEGT = poly->correctedEGT(simCN1, cFbwFF, mach, pressAltitude); + + if (engine == 1) { + if (simOnGround == 1 && engineState == 0) { + simVars->setEngine1EGT(ambientTemp); + } else { + egtFbwPreviousEng1 = simVars->getEngine1EGT(); + egtFbwActualEng1 = (correctedEGT * ratios->theta2(mach, ambientTemp)); + egtFbwActualEng1 = egtFbwActualEng1 + (egtFbwPreviousEng1 - egtFbwActualEng1) * expFBW(-0.1 * deltaTime); + simVars->setEngine1EGT(egtFbwActualEng1); + } + } else if (engine == 2) { + if (simOnGround == 1 && engineState == 0) { + simVars->setEngine2EGT(ambientTemp); + } else { + egtFbwPreviousEng2 = simVars->getEngine2EGT(); + egtFbwActualEng2 = (correctedEGT * ratios->theta2(mach, ambientTemp)); + egtFbwActualEng2 = egtFbwActualEng2 + (egtFbwPreviousEng2 - egtFbwActualEng2) * expFBW(-0.1 * deltaTime); + simVars->setEngine2EGT(egtFbwActualEng2); + } + } else if (engine == 3) { + if (simOnGround == 1 && engineState == 0) { + simVars->setEngine3EGT(ambientTemp); + } else { + egtFbwPreviousEng3 = simVars->getEngine3EGT(); + egtFbwActualEng3 = (correctedEGT * ratios->theta2(mach, ambientTemp)); + egtFbwActualEng3 = egtFbwActualEng3 + (egtFbwPreviousEng3 - egtFbwActualEng3) * expFBW(-0.1 * deltaTime); + simVars->setEngine3EGT(egtFbwActualEng3); + } + } else { + if (simOnGround == 1 && engineState == 0) { + simVars->setEngine4EGT(ambientTemp); + } else { + egtFbwPreviousEng4 = simVars->getEngine3EGT(); + egtFbwActualEng4 = (correctedEGT * ratios->theta2(mach, ambientTemp)); + egtFbwActualEng4 = egtFbwActualEng4 + (egtFbwPreviousEng4 - egtFbwActualEng4) * expFBW(-0.1 * deltaTime); + simVars->setEngine4EGT(egtFbwActualEng4); + } + } + } + + /// + /// FBW Fuel FLow (in Kg/h) + /// Updates Fuel Flow with realistic values + /// + double updateFF(int engine, double simCN1, double mach, double pressAltitude, double ambientTemp, double ambientPressure) { + double outFlow = 0; + + correctedFuelFlow = poly->correctedFuelFlow(simCN1, mach, pressAltitude); // in lbs/hr. + + // Checking Fuel Logic and final Fuel Flow + if (correctedFuelFlow < 1) { + outFlow = 0; + } else { + outFlow = (correctedFuelFlow * LBS_TO_KGS * ratios->delta2(mach, ambientPressure) * sqrt(ratios->theta2(mach, ambientTemp))); + } + + if (engine == 1) { + simVars->setEngine1FF(outFlow); + } else if (engine == 2) { + simVars->setEngine2FF(outFlow); + } else if (engine == 3) { + simVars->setEngine3FF(outFlow); + } else { + simVars->setEngine4FF(outFlow); + } + + return correctedFuelFlow; + } + + /// + /// FBW Oil Qty, Pressure and Temperature (in Quarts, PSI and degree Celsius) + /// Updates Oil with realistic values visualized in the SD + /// + void updateOil(int engine, double thrust, double simN3, double deltaN3, double deltaTime, double ambientTemp) { + double steadyTemperature; + double thermalEnergy; + double oilTemperaturePre; + double oilQtyActual; + double oilTotalActual; + double oilQtyObjective; + double oilBurn; + double oilPressureIdle; + double oilPressure; + + //-------------------------------------------- + // Engine Reading + //-------------------------------------------- + if (engine == 1) { + steadyTemperature = simVars->getEngine1EGT(); + thermalEnergy = thermalEnergy1; + oilTemperaturePre = oilTemperatureEngine1Pre; + oilQtyActual = simVars->getEngine1Oil(); + oilTotalActual = simVars->getEngine1TotalOil(); + } else if (engine == 2) { + steadyTemperature = simVars->getEngine2EGT(); + thermalEnergy = thermalEnergy2; + oilTemperaturePre = oilTemperatureEngine2Pre; + oilQtyActual = simVars->getEngine2Oil(); + oilTotalActual = simVars->getEngine2TotalOil(); + } else if (engine == 3) { + steadyTemperature = simVars->getEngine3EGT(); + thermalEnergy = thermalEnergy3; + oilTemperaturePre = oilTemperatureEngine3Pre; + oilQtyActual = simVars->getEngine3Oil(); + oilTotalActual = simVars->getEngine3TotalOil(); + } else { + steadyTemperature = simVars->getEngine4EGT(); + thermalEnergy = thermalEnergy4; + oilTemperaturePre = oilTemperatureEngine4Pre; + oilQtyActual = simVars->getEngine4Oil(); + oilTotalActual = simVars->getEngine4TotalOil(); + } + + //-------------------------------------------- + // Oil Temperature + //-------------------------------------------- + if (simOnGround == 1 && engineState == 0 && ambientTemp > oilTemperaturePre - 10) { + oilTemperature = ambientTemp; + } else { + if (steadyTemperature > oilTemperatureMax) { + steadyTemperature = oilTemperatureMax; + } + thermalEnergy = (0.995 * thermalEnergy) + (deltaN3 / deltaTime); + oilTemperature = poly->oilTemperature(thermalEnergy, oilTemperaturePre, steadyTemperature, deltaTime); + } + + //-------------------------------------------- + // Oil Quantity + //-------------------------------------------- + // Calculating Oil Qty as a function of thrust + oilQtyObjective = oilTotalActual * (1 - poly->oilGulpPct(thrust)); + oilQtyActual = oilQtyActual - (oilTemperature - oilTemperaturePre); + + // Oil burnt taken into account for tank and total oil + oilBurn = (0.00011111 * deltaTime); + oilQtyActual = oilQtyActual - oilBurn; + oilTotalActual = oilTotalActual - oilBurn; + + //-------------------------------------------- + // Oil Pressure + //-------------------------------------------- + oilPressureIdle = 0; + + oilPressure = poly->oilPressure(simN3) + oilPressureIdle; + + //-------------------------------------------- + // Engine Writing + //-------------------------------------------- + if (engine == 1) { + thermalEnergy1 = thermalEnergy; + oilTemperatureEngine1Pre = oilTemperature; + simVars->setEngine1Oil(oilQtyActual); + simVars->setEngine1TotalOil(oilTotalActual); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine1, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperature); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilPsiEngine1, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &oilPressure); + } else if (engine == 2) { + thermalEnergy2 = thermalEnergy; + oilTemperatureEngine2Pre = oilTemperature; + simVars->setEngine2Oil(oilQtyActual); + simVars->setEngine2TotalOil(oilTotalActual); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine2, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperature); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilPsiEngine2, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &oilPressure); + } else if (engine == 3) { + thermalEnergy3 = thermalEnergy; + oilTemperatureEngine3Pre = oilTemperature; + simVars->setEngine3Oil(oilQtyActual); + simVars->setEngine3TotalOil(oilTotalActual); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine3, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperature); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilPsiEngine3, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &oilPressure); + } else { + thermalEnergy4 = thermalEnergy; + oilTemperatureEngine4Pre = oilTemperature; + simVars->setEngine4Oil(oilQtyActual); + simVars->setEngine4TotalOil(oilTotalActual); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine4, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperature); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilPsiEngine4, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &oilPressure); + } + } + + /// + /// FBW Fuel Consumption and Tankering + /// Updates Fuel Consumption with realistic values + /// + void updateFuel(double deltaTime) { + + double m = 0; + double b = 0; + double fuelBurn1 = 0; + double fuelBurn2 = 0; + double fuelBurn3 = 0; + double fuelBurn4 = 0; + + double refuelRate = simVars->getRefuelRate(); + double refuelStartedByUser = simVars->getRefuelStartedByUser(); + bool uiFuelTamper = false; + + double pumpStateEngine1 = simVars->getPumpStateEngine1(); + double pumpStateEngine2 = simVars->getPumpStateEngine2(); + double pumpStateEngine3 = simVars->getPumpStateEngine3(); + double pumpStateEngine4 = simVars->getPumpStateEngine4(); + + double engine1PreFF = simVars->getEngine1PreFF(); // KG/H + double engine2PreFF = simVars->getEngine2PreFF(); // KG/H + double engine3PreFF = simVars->getEngine3PreFF(); // KG/H + double engine4PreFF = simVars->getEngine4PreFF(); // KG/H + double engine1FF = simVars->getEngine1FF(); // KG/H + double engine2FF = simVars->getEngine2FF(); // KG/H + double engine3FF = simVars->getEngine3FF(); // KG/H + double engine4FF = simVars->getEngine4FF(); // KG/H + + double fuelWeightGallon = simVars->getFuelWeightGallon(); + double fuelUsedEngine1 = simVars->getFuelUsedEngine1(); // Kg + double fuelUsedEngine2 = simVars->getFuelUsedEngine2(); // Kg + double fuelUsedEngine3 = simVars->getFuelUsedEngine3(); // Kg + double fuelUsedEngine4 = simVars->getFuelUsedEngine4(); // Kg + + double fuelLeftOuterPre = simVars->getFuelLeftOuterPre(); // LBS + double fuelFeedOnePre = simVars->getFuelFeedOnePre(); // LBS + double fuelLeftMidPre = simVars->getFuelLeftMidPre(); // LBS + double fuelLeftInnerPre = simVars->getFuelLeftInnerPre(); // LBS + double fuelFeedTwoPre = simVars->getFuelFeedTwoPre(); // LBS + double fuelFeedThreePre = simVars->getFuelFeedThreePre(); // LBS + double fuelRightInnerPre = simVars->getFuelRightInnerPre(); // LBS + double fuelRightMidPre = simVars->getFuelRightMidPre(); // LBS + double fuelFeedFourPre = simVars->getFuelFeedFourPre(); // LBS + double fuelRightOuterPre = simVars->getFuelRightOuterPre(); // LBS + double fuelTrimPre = simVars->getFuelTrimPre(); // LBS + + double leftOuterQty = simVars->getTankFuelQuantity(1) * fuelWeightGallon; // LBS + double feedOneQty = simVars->getTankFuelQuantity(2) * fuelWeightGallon; // LBS + double leftMidQty = simVars->getTankFuelQuantity(3) * fuelWeightGallon; // LBS + double leftInnerQty = simVars->getTankFuelQuantity(4) * fuelWeightGallon; // LBS + double feedTwoQty = simVars->getTankFuelQuantity(5) * fuelWeightGallon; // LBS + double feedThreeQty = simVars->getTankFuelQuantity(6) * fuelWeightGallon; // LBS + double rightInnerQty = simVars->getTankFuelQuantity(7) * fuelWeightGallon; // LBS + double rightMidQty = simVars->getTankFuelQuantity(8) * fuelWeightGallon; // LBS + double feedFourQty = simVars->getTankFuelQuantity(9) * fuelWeightGallon; // LBS + double rightOuterQty = simVars->getTankFuelQuantity(10) * fuelWeightGallon; // LBS + double trimQty = simVars->getTankFuelQuantity(11) * fuelWeightGallon; // LBS + + double fuelLeftOuter = 0; + double fuelFeedOne = 0; + double fuelLeftMid = 0; + double fuelLeftInner = 0; + double fuelFeedTwo = 0; + double fuelFeedThree = 0; + double fuelRightInner = 0; + double fuelRightMid = 0; + double fuelFeedFour = 0; + double fuelRightOuter = 0; + double fuelTrim = 0; + + double fuelTotalActual = leftOuterQty + feedOneQty + leftMidQty + leftInnerQty + feedTwoQty + feedThreeQty + rightInnerQty + + rightMidQty + feedFourQty + rightOuterQty + trimQty; // LBS + double fuelTotalPre = fuelLeftOuterPre + fuelFeedOnePre + fuelLeftMidPre + fuelLeftInnerPre + fuelFeedTwoPre + fuelFeedThreePre + + fuelRightInnerPre + fuelRightMidPre + fuelFeedFourPre + fuelRightOuterPre + fuelTrimPre; // LBS + double deltaFuelRate = abs(fuelTotalActual - fuelTotalPre) / (fuelWeightGallon * deltaTime); // LBS/ sec + + double engine1State = simVars->getEngine1State(); + double engine2State = simVars->getEngine2State(); + double engine3State = simVars->getEngine3State(); + double engine4State = simVars->getEngine4State(); + + // Check Development State for UI + isReady = simVars->getIsReady(); + devState = simVars->getDeveloperState(); + + deltaTime = deltaTime / 3600; + + /*-------------------------------------------- + // Pump Logic - TO BE IMPLEMENTED + + // Pump State Logic for Engine 1 + if (pumpStateEngine1 == 0 && (timerEngine1.elapsed() == 0 || timerEngine1.elapsed() >= 1000)) { + if (fuelLeftPre - leftQuantity > 0 && leftQuantity == 0) { + timerEngine1.reset(); + simVars->setPumpStateEngine1(1); + } else if (fuelLeftPre == 0 && leftQuantity - fuelLeftPre > 0) { + timerEngine1.reset(); + simVars->setPumpStateEngine1(2); + } else { + simVars->setPumpStateEngine1(0); + } + } else if (pumpStateEngine1 == 1 && timerEngine1.elapsed() >= 2100) { + simVars->setPumpStateEngine1(0); + fuelLeftPre = 0; + timerEngine1.reset(); + } else if (pumpStateEngine1 == 2 && timerEngine1.elapsed() >= 2700) { + simVars->setPumpStateEngine1(0); + timerEngine1.reset(); + } + + // Pump State Logic for Engine 2 + if (pumpStateEngine2 == 0 && (timerEngine2.elapsed() == 0 || timerEngine2.elapsed() >= 1000)) { + if (fuelLeftPre - leftQuantity > 0 && leftQuantity == 0) { + timerEngine2.reset(); + simVars->setPumpStateEngine2(1); + } else if (fuelLeftPre == 0 && leftQuantity - fuelLeftPre > 0) { + timerEngine2.reset(); + simVars->setPumpStateEngine2(2); + } else { + simVars->setPumpStateEngine2(0); + } + } else if (pumpStateEngine2 == 1 && timerEngine2.elapsed() >= 2100) { + simVars->setPumpStateEngine2(0); + fuelLeftPre = 0; + timerEngine2.reset(); + } else if (pumpStateEngine2 == 2 && timerEngine2.elapsed() >= 2700) { + simVars->setPumpStateEngine2(0); + timerEngine2.reset(); + } + + // Pump State Logic for Engine 3 + if (pumpStateEngine3 == 0 && (timerEngine3.elapsed() == 0 || timerEngine3.elapsed() >= 1000)) { + if (fuelRightPre - rightQuantity > 0 && rightQuantity == 0) { + timerEngine3.reset(); + simVars->setPumpStateEngine3(1); + } else if (fuelRightPre == 0 && rightQuantity - fuelRightPre > 0) { + timerEngine3.reset(); + simVars->setPumpStateEngine3(2); + } else { + simVars->setPumpStateEngine3(0); + } + } else if (pumpStateEngine3 == 1 && timerEngine3.elapsed() >= 2100) { + simVars->setPumpStateEngine3(0); + fuelRightPre = 0; + timerEngine3.reset(); + } else if (pumpStateEngine3 == 2 && timerEngine3.elapsed() >= 2700) { + simVars->setPumpStateEngine3(0); + timerEngine3.reset(); + } + + // Pump State Logic for Engine 4 + if (pumpStateEngine4 == 0 && (timerEngine4.elapsed() == 0 || timerEngine4.elapsed() >= 1000)) { + if (fuelRightPre - rightQuantity > 0 && rightQuantity == 0) { + timerEngine4.reset(); + simVars->setPumpStateEngine4(1); + } else if (fuelRightPre == 0 && rightQuantity - fuelRightPre > 0) { + timerEngine4.reset(); + simVars->setPumpStateEngine4(2); + } else { + simVars->setPumpStateEngine4(0); + } + } else if (pumpStateEngine4 == 1 && timerEngine4.elapsed() >= 2100) { + simVars->setPumpStateEngine4(0); + fuelRightPre = 0; + timerEngine4.reset(); + } else if (pumpStateEngine4 == 2 && timerEngine4.elapsed() >= 2700) { + simVars->setPumpStateEngine4(0); + timerEngine4.reset(); + } + --------------------------------------------*/ + + // Checking for in-game UI Fuel tampering + if ((isReady == 1 && refuelStartedByUser == 0 && deltaFuelRate > FUEL_THRESHOLD) || + (isReady == 1 && refuelStartedByUser == 1 && deltaFuelRate > FUEL_THRESHOLD && refuelRate < 2)) { + uiFuelTamper = true; + } + + //-------------------------------------------- + // Main Fuel Burn Logic + //-------------------------------------------- + if (simPaused || uiFuelTamper && devState == 0) { // Detects whether the Sim is paused or the Fuel UI is being tampered with + simVars->setFuelLeftOuterPre(fuelLeftOuterPre); // in LBS + simVars->setFuelFeedOnePre(fuelFeedOnePre); // in LBS + simVars->setFuelLeftMidPre(fuelLeftMidPre); // in LBS + simVars->setFuelLeftInnerPre(fuelLeftInnerPre); // in LBS + simVars->setFuelFeedTwoPre(fuelFeedTwoPre); // in LBS + simVars->setFuelFeedThreePre(fuelFeedThreePre); // in LBS + simVars->setFuelRightInnerPre(fuelRightInnerPre); // in LBS + simVars->setFuelRightMidPre(fuelRightMidPre); // in LBS + simVars->setFuelFeedFourPre(fuelFeedFourPre); // in LBS + simVars->setFuelRightOuterPre(fuelRightOuterPre); // in LBS + simVars->setFuelTrimPre(fuelTrimPre); // in LBS + + fuelLeftOuter = (fuelLeftOuterPre / fuelWeightGallon); // USG + fuelFeedOne = (fuelFeedOnePre / fuelWeightGallon); // USG + fuelLeftMid = (fuelLeftMidPre / fuelWeightGallon); // USG + fuelLeftInner = (fuelLeftInnerPre / fuelWeightGallon); // USG + fuelFeedTwo = (fuelFeedTwoPre / fuelWeightGallon); // USG + fuelFeedThree = (fuelFeedThreePre / fuelWeightGallon); // USG + fuelRightInner = (fuelRightInnerPre / fuelWeightGallon); // USG + fuelRightMid = (fuelRightMidPre / fuelWeightGallon); // USG + fuelFeedFour = (fuelFeedFourPre / fuelWeightGallon); // USG + fuelRightOuter = (fuelRightOuterPre / fuelWeightGallon); // USG + fuelTrim = (fuelTrimPre / fuelWeightGallon); // USG + + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemLeftOuter, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelLeftOuter); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemFeedOne, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelFeedOne); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemLeftMid, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelLeftMid); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemLeftInner, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelLeftInner); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemFeedTwo, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelFeedTwo); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemFeedThree, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelFeedThree); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemRightInner, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelRightInner); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemRightMid, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelRightMid); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemFeedFour, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelFeedFour); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemRightOuter, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelRightOuter); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemTrim, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &fuelTrim); + } else if (!uiFuelTamper && refuelStartedByUser == 1) { // Detects refueling from the EFB + simVars->setFuelLeftOuterPre(leftOuterQty); // in LBS + simVars->setFuelFeedOnePre(feedOneQty); // in LBS + simVars->setFuelLeftMidPre(leftMidQty); // in LBS + simVars->setFuelLeftInnerPre(leftInnerQty); // in LBS + simVars->setFuelFeedTwoPre(feedTwoQty); // in LBS + simVars->setFuelFeedThreePre(feedThreeQty); // in LBS + simVars->setFuelRightInnerPre(rightInnerQty); // in LBS + simVars->setFuelRightMidPre(rightMidQty); // in LBS + simVars->setFuelFeedFourPre(feedFourQty); // in LBS + simVars->setFuelRightOuterPre(rightOuterQty); // in LBS + simVars->setFuelTrimPre(trimQty); // in LBS + } else { + if (uiFuelTamper == 1) { + fuelLeftOuterPre = leftOuterQty; // in LBS + fuelFeedOnePre = feedOneQty; // in LBS + fuelLeftMidPre = leftMidQty; // in LBS + fuelLeftInnerPre = leftInnerQty; // in LBS + fuelFeedTwoPre = feedTwoQty; // in LBS + fuelFeedThreePre = feedThreeQty; // in LBS + fuelRightInnerPre = rightInnerQty; // in LBS + fuelRightMidPre = rightMidQty; // in LBS + fuelFeedFourPre = feedFourQty; // in LBS + fuelRightOuterPre = rightOuterQty; // in LBS + fuelTrimPre = trimQty; // in LBS + } + //-------------------------------------------- + // Engine 1 Fuel Burn routine + if (fuelFeedOnePre > 0) { + // Cycle Fuel Burn for Engine 1 + if (devState != 2) { + m = (engine1FF - engine1PreFF) / deltaTime; + b = engine1PreFF; + fuelBurn1 = (m * pow(deltaTime, 2) / 2) + (b * deltaTime); // KG + } + // Fuel Used Accumulators - Engine 1 + fuelUsedEngine1 += fuelBurn1; + } else { + fuelBurn1 = 0; + fuelFeedOnePre = 0; + } + //-------------------------------------------- + // Engine 2 Fuel Burn routine + if (fuelFeedTwoPre > 0) { + // Cycle Fuel Burn for Engine 2 + if (devState != 2) { + m = (engine2FF - engine2PreFF) / deltaTime; + b = engine2PreFF; + fuelBurn2 = (m * pow(deltaTime, 2) / 2) + (b * deltaTime); // KG + } + // Fuel Used Accumulators - Engine 2 + fuelUsedEngine2 += fuelBurn2; + } else { + fuelBurn2 = 0; + fuelFeedTwoPre = 0; + } + //-------------------------------------------- + // Engine 3 Fuel Burn routine + if (fuelFeedThreePre > 0) { + // Cycle Fuel Burn for Engine 3 + if (devState != 2) { + m = (engine3FF - engine3PreFF) / deltaTime; + b = engine3PreFF; + fuelBurn3 = (m * pow(deltaTime, 2) / 2) + (b * deltaTime); // KG + } + // Fuel Used Accumulators - Engine 3 + fuelUsedEngine3 += fuelBurn3; + } else { + fuelBurn3 = 0; + fuelFeedThreePre = 0; + } + //-------------------------------------------- + // Engine 4 Fuel Burn routine + if (fuelFeedFourPre > 0) { + // Cycle Fuel Burn for Engine 4 + if (devState != 2) { + m = (engine4FF - engine4PreFF) / deltaTime; + b = engine4PreFF; + fuelBurn4 = (m * pow(deltaTime, 2) / 2) + (b * deltaTime); // KG + } + // Fuel Used Accumulators - Engine 4 + fuelUsedEngine4 += fuelBurn4; + } else { + fuelBurn4 = 0; + fuelFeedFourPre = 0; + } + + fuelFeedOne = fuelFeedOnePre - (fuelBurn1 * KGS_TO_LBS); // LBS + fuelFeedTwo = fuelFeedTwoPre - (fuelBurn2 * KGS_TO_LBS); // LBS + fuelFeedThree = fuelFeedThreePre - (fuelBurn3 * KGS_TO_LBS); // LBS + fuelFeedFour = fuelFeedFourPre - (fuelBurn4 * KGS_TO_LBS); // LBS + + // Setting new pre-cycle conditions + simVars->setEngine1PreFF(engine1FF); + simVars->setEngine2PreFF(engine2FF); + simVars->setEngine3PreFF(engine3FF); + simVars->setEngine4PreFF(engine4FF); + simVars->setFuelUsedEngine1(fuelUsedEngine1); // in KG + simVars->setFuelUsedEngine2(fuelUsedEngine2); // in KG + simVars->setFuelUsedEngine3(fuelUsedEngine3); // in KG + simVars->setFuelUsedEngine4(fuelUsedEngine4); // in KG + + simVars->setFuelFeedOnePre(fuelFeedOne); // in LBS + simVars->setFuelFeedTwoPre(fuelFeedTwo); // in LBS + simVars->setFuelFeedThreePre(fuelFeedThree); // in LBS + simVars->setFuelFeedFourPre(fuelFeedFour); // in LBS + + fuelFeedOne = (fuelFeedOne / fuelWeightGallon); // USG + fuelFeedTwo = (fuelFeedTwo / fuelWeightGallon); // USG + fuelFeedThree = (fuelFeedThree / fuelWeightGallon); // USG + fuelFeedFour = (fuelFeedFour / fuelWeightGallon); // USG + + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemFeedOne, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelFeedOne); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemFeedTwo, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelFeedTwo); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemFeedThree, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelFeedThree); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::FuelSystemFeedFour, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &fuelFeedFour); + } + + // Will save the current fuel quantities if on the ground AND engines being shutdown + if (timerFuel.elapsed() >= 1000 && simVars->getSimOnGround() && + (engine1State == 0 || engine1State == 10 || engine1State == 4 || engine1State == 14 || engine2State == 0 || engine2State == 10 || + engine2State == 4 || engine2State == 14 || engine3State == 0 || engine3State == 10 || engine3State == 4 || engine3State == 14 || + engine4State == 0 || engine4State == 10 || engine4State == 4 || engine4State == 14)) { + Configuration configuration; + + configuration.fuelLeftOuter = simVars->getFuelLeftOuterPre() / simVars->getFuelWeightGallon(); + configuration.fuelFeedOne = simVars->getFuelFeedOnePre() / simVars->getFuelWeightGallon(); + configuration.fuelLeftMid = simVars->getFuelLeftMidPre() / simVars->getFuelWeightGallon(); + configuration.fuelLeftInner = simVars->getFuelLeftInnerPre() / simVars->getFuelWeightGallon(); + configuration.fuelFeedTwo = simVars->getFuelFeedTwoPre() / simVars->getFuelWeightGallon(); + configuration.fuelFeedThree = simVars->getFuelFeedThreePre() / simVars->getFuelWeightGallon(); + configuration.fuelRightInner = simVars->getFuelRightInnerPre() / simVars->getFuelWeightGallon(); + configuration.fuelRightMid = simVars->getFuelRightMidPre() / simVars->getFuelWeightGallon(); + configuration.fuelFeedFour = simVars->getFuelFeedFourPre() / simVars->getFuelWeightGallon(); + configuration.fuelRightOuter = simVars->getFuelRightOuterPre() / simVars->getFuelWeightGallon(); + configuration.fuelTrim = simVars->getFuelTrimPre() / simVars->getFuelWeightGallon(); + + saveFuelInConfiguration(configuration); + timerFuel.reset(); + } + } + + void updateThrustLimits(double simulationTime, + double altitude, + double ambientTemp, + double ambientPressure, + double mach, + double simN1highest, + double packs, + double nai, + double wai) { + double idle = simVars->getEngineIdleN1(); + double flexTemp = simVars->getFlexTemp(); + double thrustLimitType = simVars->getThrustLimitType(); + double to = 0; + double ga = 0; + double toga = 0; + double clb = 0; + double mct = 0; + double flex_to = 0; + double flex_ga = 0; + double flex = 0; + + using std::min; + using std::max; + + // Write all N1 Limits + to = limitN1(0, min(16600.0, pressAltitude), ambientTemp, ambientPressure, 0, packs, nai, wai); + ga = limitN1(1, min(16600.0, pressAltitude), ambientTemp, ambientPressure, 0, packs, nai, wai); + if (flexTemp > 0) { + flex_to = limitN1(0, min(16600.0, pressAltitude), ambientTemp, ambientPressure, flexTemp, packs, nai, wai); + flex_ga = limitN1(1, min(16600.0, pressAltitude), ambientTemp, ambientPressure, flexTemp, packs, nai, wai); + } + clb = limitN1(2, pressAltitude, ambientTemp, ambientPressure, 0, packs, nai, wai); + mct = limitN1(3, pressAltitude, ambientTemp, ambientPressure, 0, packs, nai, wai); + + // transition between TO and GA limit ----------------------------------------------------------------------------- + double machFactorLow = max(0.0, min(1.0, (mach - 0.04) / 0.04)); + toga = to + (ga - to) * machFactorLow; + flex = flex_to + (flex_ga - flex_to) * machFactorLow; + + // adaption of CLB due to FLX limit if necessary ------------------------------------------------------------------ + + if ((prevThrustLimitType != 3 && thrustLimitType == 3) || (prevFlexTemperature == 0 && flexTemp > 0)) { + isFlexActive = true; + } else if ((flexTemp == 0) || (thrustLimitType == 4)) { + isFlexActive = false; + } + + if (isFlexActive && !isTransitionActive && thrustLimitType == 1) { + isTransitionActive = true; + transitionStartTime = simulationTime; + transitionFactor = 0.2; + // transitionFactor = (clb - flex) / transitionTime; + } else if (!isFlexActive) { + isTransitionActive = false; + transitionStartTime = 0; + transitionFactor = 0; + } + + double deltaThrust = 0; + + if (isTransitionActive) { + double timeDifference = max(0.0, (simulationTime - transitionStartTime) - waitTime); + + if (timeDifference > 0 && clb > flex) { + deltaThrust = min(clb - flex, timeDifference * transitionFactor); + } + + if (flex + deltaThrust >= clb) { + isFlexActive = false; + isTransitionActive = false; + } + } + + if (isFlexActive) { + clb = min(clb, flex) + deltaThrust; + } + + prevThrustLimitType = thrustLimitType; + prevFlexTemperature = flexTemp; + + // thrust transitions for MCT and TOGA ---------------------------------------------------------------------------- + + // get factors + double machFactor = max(0.0, min(1.0, ((mach - 0.37) / 0.05))); + double altitudeFactorLow = max(0.0, min(1.0, ((altitude - 16600) / 500))); + double altitudeFactorHigh = max(0.0, min(1.0, ((altitude - 25000) / 500))); + + // adapt thrust limits + if (altitude >= 25000) { + mct = max(clb, mct + (clb - mct) * altitudeFactorHigh); + toga = mct; + } else { + if (mct > toga) { + mct = toga + (mct - toga) * min(1.0, altitudeFactorLow + machFactor); + toga = mct; + } else { + toga = toga + (mct - toga) * min(1.0, altitudeFactorLow + machFactor); + } + } + + // write limits --------------------------------------------------------------------------------------------------- + simVars->setThrustLimitIdle(idle); + simVars->setThrustLimitToga(toga); + simVars->setThrustLimitFlex(flex); + simVars->setThrustLimitClimb(clb); + simVars->setThrustLimitMct(mct); + } + + public: + /// + /// Initialize the FADEC and Fuel model + /// + void initialize(const char* acftRegistration) { + srand((int)time(0)); + + std::cout << "FADEC: Initializing EngineControl" << std::endl; + + simVars = new SimVars(); + double engTime = 0; + ambientTemp = simVars->getAmbientTemperature(); + simN3Engine1Pre = simVars->getN2(1); + simN3Engine2Pre = simVars->getN2(2); + simN3Engine3Pre = simVars->getN2(3); + simN3Engine4Pre = simVars->getN2(4); + + confFilename += acftRegistration; + confFilename += FILENAME_FADEC_CONF_FILE_EXTENSION; + + Configuration configuration = getConfigurationFromFile(); + + for (engine = 1; engine <= 4; engine++) { + // Obtain Engine Time + engTime = simVars->getEngineTime(engine) + engTime; + + // Engine Idle Oil Qty + idleOil = initOil(140, 200); + + // Setting initial Oil + if (engine == 1) { + simVars->setEngine1TotalOil(idleOil); + } else if (engine == 2) { + simVars->setEngine2TotalOil(idleOil); + } else if (engine == 3) { + simVars->setEngine3TotalOil(idleOil); + } else { + simVars->setEngine4TotalOil(idleOil); + } + } + + // Setting initial Oil Temperature + thermalEnergy1 = 0; + thermalEnergy2 = 0; + thermalEnergy3 = 0; + thermalEnergy4 = 0; + oilTemperatureMax = 85; + simOnGround = simVars->getSimOnGround(); + double engine1Combustion = simVars->getEngineCombustion(1); + double engine2Combustion = simVars->getEngineCombustion(2); + double engine3Combustion = simVars->getEngineCombustion(3); + double engine4Combustion = simVars->getEngineCombustion(4); + + if (simOnGround == 1 && engine1Combustion == 1 && engine2Combustion == 1 && engine3Combustion == 1 && engine4Combustion == 1) { + oilTemperatureEngine1Pre = 75; + oilTemperatureEngine2Pre = 75; + oilTemperatureEngine3Pre = 75; + oilTemperatureEngine4Pre = 75; + } else if (simOnGround == 0 && engine1Combustion == 1 && engine2Combustion == 1 && engine3Combustion == 1 && engine4Combustion == 1) { + oilTemperatureEngine1Pre = 85; + oilTemperatureEngine2Pre = 85; + oilTemperatureEngine3Pre = 85; + oilTemperatureEngine4Pre = 85; + } else { + oilTemperatureEngine1Pre = ambientTemp; + oilTemperatureEngine2Pre = ambientTemp; + oilTemperatureEngine3Pre = ambientTemp; + oilTemperatureEngine4Pre = ambientTemp; + } + + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine1, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperatureEngine1Pre); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine2, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperatureEngine2Pre); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine3, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperatureEngine3Pre); + SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine4, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), + &oilTemperatureEngine4Pre); + + // Initialize Engine State + simVars->setEngine1State(10); + simVars->setEngine2State(10); + simVars->setEngine3State(10); + simVars->setEngine4State(10); + + // Resetting Engine Timers + simVars->setEngine1Timer(0); + simVars->setEngine2Timer(0); + simVars->setEngine3Timer(0); + simVars->setEngine4Timer(0); + + // Initialize Fuel Tanks + simVars->setFuelLeftOuterPre(configuration.fuelLeftOuter * simVars->getFuelWeightGallon()); + simVars->setFuelFeedOnePre(configuration.fuelFeedOne * simVars->getFuelWeightGallon()); + simVars->setFuelLeftMidPre(configuration.fuelLeftMid * simVars->getFuelWeightGallon()); + simVars->setFuelLeftInnerPre(configuration.fuelLeftInner * simVars->getFuelWeightGallon()); + simVars->setFuelFeedTwoPre(configuration.fuelFeedTwo * simVars->getFuelWeightGallon()); + simVars->setFuelFeedThreePre(configuration.fuelFeedThree * simVars->getFuelWeightGallon()); + simVars->setFuelRightInnerPre(configuration.fuelRightInner * simVars->getFuelWeightGallon()); + simVars->setFuelRightMidPre(configuration.fuelRightMid * simVars->getFuelWeightGallon()); + simVars->setFuelFeedFourPre(configuration.fuelFeedFour * simVars->getFuelWeightGallon()); + simVars->setFuelRightOuterPre(configuration.fuelRightOuter * simVars->getFuelWeightGallon()); + simVars->setFuelTrimPre(configuration.fuelTrim * simVars->getFuelWeightGallon()); + + // Initialize Pump State + simVars->setPumpStateEngine1(0); + simVars->setPumpStateEngine2(0); + simVars->setPumpStateEngine3(0); + simVars->setPumpStateEngine4(0); + + // Initialize Thrust Limits + simVars->setThrustLimitIdle(0); + simVars->setThrustLimitToga(0); + simVars->setThrustLimitFlex(0); + simVars->setThrustLimitClimb(0); + simVars->setThrustLimitMct(0); + } + + /// + /// Update cycle at deltaTime + /// + void update(double deltaTime, double simulationTime) { + double animationDeltaTime; + double prevAnimationDeltaTime; + double simN1highest = 0; + + // animationDeltaTimes being used to detect a Paused situation + prevAnimationDeltaTime = animationDeltaTime; + animationDeltaTime = simVars->getAnimDeltaTime(); + + mach = simVars->getMach(); + pressAltitude = simVars->getPressureAltitude(); + ambientTemp = simVars->getAmbientTemperature(); + ambientPressure = simVars->getAmbientPressure(); + simOnGround = simVars->getSimOnGround(); + packs = 0; + nai = 0; + wai = 0; + + // Obtain Bleed Variables + if (simVars->getPacksState1() > 0.5 || simVars->getPacksState2() > 0.5) { + packs = 1; + } + if (simVars->getNAI(1) > 0.5 || simVars->getNAI(2) > 0.5) { + nai = 1; + } + wai = simVars->getWAI(); + + generateIdleParameters(pressAltitude, mach, ambientTemp, ambientPressure); + + // Timer timer; + for (engine = 1; engine <= 4; engine++) { + engineStarter = simVars->getEngineStarter(engine); + engineIgniter = simVars->getEngineIgniter(engine); + simCN1 = simVars->getCN1(engine); + simN1 = simVars->getN1(engine); + simN3 = simVars->getN2(engine); + thrust = simVars->getThrust(engine); + + // Set & Check Engine Status for this Cycle + engineStateMachine(engine, engineIgniter, engineStarter, simN3, idleN3, pressAltitude, ambientTemp, + animationDeltaTime - prevAnimationDeltaTime); + if (engine == 1) { + engineState = simVars->getEngine1State(); + deltaN3 = simN3 - simN3Engine1Pre; + simN3Engine1Pre = simN3; + timer = simVars->getEngine1Timer(); + } else if (engine == 2) { + engineState = simVars->getEngine2State(); + deltaN3 = simN3 - simN3Engine2Pre; + simN3Engine2Pre = simN3; + timer = simVars->getEngine2Timer(); + } else if (engine == 3) { + engineState = simVars->getEngine3State(); + deltaN3 = simN3 - simN3Engine3Pre; + simN3Engine3Pre = simN3; + timer = simVars->getEngine3Timer(); + } else { + engineState = simVars->getEngine4State(); + deltaN3 = simN3 - simN3Engine4Pre; + simN3Engine4Pre = simN3; + timer = simVars->getEngine4Timer(); + } + + switch (int(engineState)) { + case 2: + case 3: + engineStartProcedure(engine, engineState, deltaTime, timer, simN3, pressAltitude, ambientTemp); + break; + case 4: + engineShutdownProcedure(engine, ambientTemp, simN1, deltaTime, timer); + cFbwFF = updateFF(engine, simCN1, mach, pressAltitude, ambientTemp, ambientPressure); + break; + default: + updatePrimaryParameters(engine, simN1, simN3); + cFbwFF = updateFF(engine, simCN1, mach, pressAltitude, ambientTemp, ambientPressure); + updateEGT(engine, deltaTime, simOnGround, engineState, simCN1, cFbwFF, mach, pressAltitude, ambientTemp); + // updateOil(engine, imbalance, thrust, simN3, deltaN3, deltaTime, ambientTemp); + } + + // set highest N1 from either engine + simN1highest = (std::max)(simN1highest, simN1); + } + + updateFuel(deltaTime); + + updateThrustLimits(simulationTime, pressAltitude, ambientTemp, ambientPressure, mach, simN1highest, packs, nai, wai); + // timer.elapsed(); + } + + void terminate() {} + + Configuration getConfigurationFromFile() { + Configuration configuration; + mINI::INIStructure stInitStructure; + + mINI::INIFile iniFile(confFilename); + + if (!iniFile.read(stInitStructure)) { + std::cout << "EngineControl: failed to read configuration file " << confFilename << " due to error \"" << strerror(errno) + << "\" -> using default fuel quantities." << std::endl; + } else { + configuration = loadConfiguration(stInitStructure); + } + + return configuration; + } + + Configuration loadConfiguration(const mINI::INIStructure& structure) { + return { + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_LEFT_OUTER_QTY, 2731.0), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_FEED_ONE_QTY, 1082.0), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_LEFT_MID_QTY, 9630.0), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_LEFT_INNER_QTY, 12187.0), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_FEED_TWO_QTY, 1082.0), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_FEED_THREE_QTY, 1082.0), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_RIGHT_INNER_QTY, 12187.0), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_RIGHT_MID_QTY, 9630.0), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_FEED_FOUR_QTY, 1082.0), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_RIGHT_OUTER_QTY, 2731.0), + mINI::INITypeConversion::getDouble(structure, CONFIGURATION_SECTION_FUEL, CONFIGURATION_SECTION_FUEL_TRIM_QTY, 6259.0), + }; + } + + void saveFuelInConfiguration(Configuration configuration) { + mINI::INIStructure stInitStructure; + mINI::INIFile iniFile(confFilename); + + // Do not check a possible error since the file may not exist yet + iniFile.read(stInitStructure); + + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_LEFT_OUTER_QTY] = std::to_string(configuration.fuelLeftOuter); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_FEED_ONE_QTY] = std::to_string(configuration.fuelFeedOne); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_LEFT_MID_QTY] = std::to_string(configuration.fuelLeftMid); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_LEFT_INNER_QTY] = std::to_string(configuration.fuelLeftInner); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_FEED_TWO_QTY] = std::to_string(configuration.fuelFeedTwo); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_FEED_THREE_QTY] = std::to_string(configuration.fuelFeedThree); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_RIGHT_INNER_QTY] = std::to_string(configuration.fuelRightInner); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_RIGHT_MID_QTY] = std::to_string(configuration.fuelRightMid); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_FEED_FOUR_QTY] = std::to_string(configuration.fuelFeedFour); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_RIGHT_OUTER_QTY] = std::to_string(configuration.fuelRightOuter); + stInitStructure[CONFIGURATION_SECTION_FUEL][CONFIGURATION_SECTION_FUEL_TRIM_QTY] = std::to_string(configuration.fuelTrim); + + if (!iniFile.write(stInitStructure, true)) { + std::cout << "EngineControl: failed to write engine conf " << confFilename << " due to error \"" << strerror(errno) << "\"" + << std::endl; + } + } +}; + +EngineControl EngineControlInstance; diff --git a/fbw-a380x/src/wasm/fadec_a380/src/FadecGauge.cpp b/fbw-a380x/src/wasm/fadec_a380/src/FadecGauge.cpp new file mode 100644 index 00000000000..f502997cecc --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380/src/FadecGauge.cpp @@ -0,0 +1,32 @@ +#include "FadecGauge.h" + +FadecGauge FADEC_GAUGE; + +__attribute__((export_name("FadecGauge_gauge_callback"))) extern "C" bool FadecGauge_gauge_callback(FsContext ctx, + int service_id, + void* pData) { + switch (service_id) { + case PANEL_SERVICE_PRE_INSTALL: { + return true; + } break; + case PANEL_SERVICE_POST_INSTALL: { + return FADEC_GAUGE.initializeFADEC(); + } break; + case PANEL_SERVICE_PRE_DRAW: { + /* Start updating the engines only if all needed data was fetched */ + /* Due to the data fetching feature from the sim being available at this stage only (from what I have observed) */ + /* Event SIMCONNECT_RECV_ID_OPEN received after the first PANEL_SERVICE_POST_INSTALL */ + if (FADEC_GAUGE.isReady()) { + sGaugeDrawData* drawData = static_cast(pData); + return FADEC_GAUGE.onUpdate(drawData->dt); + } else { + return FADEC_GAUGE.fetchNeededData(); + } + } break; + case PANEL_SERVICE_PRE_KILL: { + FADEC_GAUGE.killFADEC(); + return true; + } break; + } + return false; +} diff --git a/fbw-a380x/src/wasm/fadec_a380/src/FadecGauge.h b/fbw-a380x/src/wasm/fadec_a380/src/FadecGauge.h new file mode 100644 index 00000000000..d5f35196d18 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380/src/FadecGauge.h @@ -0,0 +1,409 @@ +#pragma once + +#ifndef __INTELLISENSE__ +#define MODULE_EXPORT __attribute__((visibility("default"))) +#define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod))) +#else +#define MODULE_EXPORT +#define MODULE_WASM_MODNAME(mod) +#define __attribute__(x) +#define __restrict__ +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "EngineControl.h" +// #include "ThrustLimits.h" +#include "RegPolynomials.h" +#include "SimVars.h" +#include "Tables.h" +#include "common.h" + +#define DEFAULT_AIRCRAFT_REGISTRATION "ASX380" + +class FadecGauge { + private: + bool isConnected = false; + bool _isReady = false; + double previousSimulationTime = 0; + SimulationData simulationData = {}; + SimulationDataLivery simulationDataLivery = {}; + + /// + /// Initializes the connection to SimConnect + /// + /// True if successful, false otherwise. + bool initializeSimConnect() { + std::cout << "FADEC: Connecting to SimConnect..." << std::endl; + if (SUCCEEDED(SimConnect_Open(&hSimConnect, "FadecGauge", nullptr, 0, 0, 0))) { + std::cout << "FADEC: SimConnect connected." << std::endl; + + // SimConnect Payload Definitions + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::PayloadStation1, "PAYLOAD STATION WEIGHT:1", "Pounds"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::PayloadStation2, "PAYLOAD STATION WEIGHT:2", "Pounds"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::PayloadStation3, "PAYLOAD STATION WEIGHT:3", "Pounds"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::PayloadStation4, "PAYLOAD STATION WEIGHT:4", "Pounds"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::PayloadStation5, "PAYLOAD STATION WEIGHT:5", "Pounds"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::PayloadStation6, "PAYLOAD STATION WEIGHT:6", "Pounds"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::PayloadStation7, "PAYLOAD STATION WEIGHT:7", "Pounds"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::PayloadStation8, "PAYLOAD STATION WEIGHT:8", "Pounds"); + + // SimConnect Tanker Definitions + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelSystemLeftOuter, "FUELSYSTEM TANK QUANTITY:1", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelSystemFeedOne, "FUELSYSTEM TANK QUANTITY:2", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelSystemLeftMid, "FUELSYSTEM TANK QUANTITY:3", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelSystemLeftInner, "FUELSYSTEM TANK QUANTITY:4", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelSystemFeedTwo, "FUELSYSTEM TANK QUANTITY:5", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelSystemFeedThree, "FUELSYSTEM TANK QUANTITY:6", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelSystemRightInner, "FUELSYSTEM TANK QUANTITY:7", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelSystemRightMid, "FUELSYSTEM TANK QUANTITY:8", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelSystemFeedFour, "FUELSYSTEM TANK QUANTITY:9", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelSystemRightOuter, "FUELSYSTEM TANK QUANTITY:10", "Gallons"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::FuelSystemTrim, "FUELSYSTEM TANK QUANTITY:11", "Gallons"); + + // SimConnect Oil Temperature Definitions + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::OilTempEngine1, "GENERAL ENG OIL TEMPERATURE:1", "Celsius"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::OilTempEngine2, "GENERAL ENG OIL TEMPERATURE:2", "Celsius"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::OilTempEngine3, "GENERAL ENG OIL TEMPERATURE:3", "Celsius"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::OilTempEngine4, "GENERAL ENG OIL TEMPERATURE:4", "Celsius"); + + // SimConnect Oil Pressure Definitions + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::OilPsiEngine1, "GENERAL ENG OIL PRESSURE:1", "Psi"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::OilPsiEngine2, "GENERAL ENG OIL PRESSURE:2", "Psi"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::OilPsiEngine3, "GENERAL ENG OIL PRESSURE:3", "Psi"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::OilPsiEngine4, "GENERAL ENG OIL PRESSURE:4", "Psi"); + + // SimConnect Engine Start Definitions + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::StartCN3Engine1, "TURB ENG CORRECTED N2:1", "Percent"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::StartCN3Engine2, "TURB ENG CORRECTED N2:2", "Percent"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::StartCN3Engine3, "TURB ENG CORRECTED N2:3", "Percent"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::StartCN3Engine4, "TURB ENG CORRECTED N2:4", "Percent"); + // Simulation Data + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::SimulationDataTypeId, "SIMULATION TIME", "NUMBER"); + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::SimulationDataTypeId, "SIMULATION RATE", "NUMBER"); + + SimConnect_AddToDataDefinition(hSimConnect, DataTypesID::AcftInfo, "ATC ID", NULL, SIMCONNECT_DATATYPE_STRING32); + + std::cout << "FADEC: SimConnect registrations complete." << std::endl; + return true; + } + + std::cout << "FADEC: SimConnect failed." << std::endl; + + return false; + } + + bool isRegistrationFound() { return simulationDataLivery.atc_id[0] != 0; } + + public: + /// + /// Initializes the FADEC control + /// + /// True if successful, false otherwise. + bool initializeFADEC() { + if (!this->initializeSimConnect()) { + std::cout << "FADEC: Init SimConnect failed." << std::endl; + return false; + } + + isConnected = true; + simConnectRequestData(); + + return true; + } + + /// + /// Callback used to update the FADEC at each tick (dt) + /// + /// True if successful, false otherwise. + bool onUpdate(double deltaTime) { + if (isConnected == true) { + // read simulation data from simconnect + simConnectReadData(); + // detect pause + if ((simulationData.simulationTime == previousSimulationTime) || (simulationData.simulationTime < 0.2)) { + // pause detected -> return + return true; + } + // calculate delta time + double calculatedSampleTime = max(0.002, simulationData.simulationTime - previousSimulationTime); + // store previous simulation time + previousSimulationTime = simulationData.simulationTime; + // update engines + EngineControlInstance.update(calculatedSampleTime, simulationData.simulationTime); + } + + return true; + } + + bool simConnectRequestDataAcftInfo() { + // check if we are connected + if (!isConnected) { + return false; + } + + // request data + return S_OK == + SimConnect_RequestDataOnSimObject(hSimConnect, 8, DataTypesID::AcftInfo, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_ONCE); + } + + bool simConnectRequestData() { + // check if we are connected + if (!isConnected) { + return false; + } + + // request data + HRESULT result = SimConnect_RequestDataOnSimObject(hSimConnect, 0, DataTypesID::SimulationDataTypeId, SIMCONNECT_OBJECT_ID_USER, + SIMCONNECT_PERIOD_VISUAL_FRAME); + + // check result of data request + if (result != S_OK) { + // request failed + return false; + } + + // success + return true; + } + + bool simConnectReadData() { + // check if we are connected + if (!isConnected) { + return false; + } + + // get next dispatch message(s) and process them + DWORD cbData; + SIMCONNECT_RECV* pData; + while (SUCCEEDED(SimConnect_GetNextDispatch(hSimConnect, &pData, &cbData))) { + simConnectProcessDispatchMessage(pData, &cbData); + } + + // success + return true; + } + + void simConnectProcessDispatchMessage(SIMCONNECT_RECV* pData, DWORD* cbData) { + switch (pData->dwID) { + case SIMCONNECT_RECV_ID_OPEN: + // connection established + std::cout << "FADEC: SimConnect connection established" << std::endl; + break; + + case SIMCONNECT_RECV_ID_QUIT: + // connection lost + std::cout << "FADEC: Received SimConnect connection quit message" << std::endl; + break; + + case SIMCONNECT_RECV_ID_SIMOBJECT_DATA: + // process data + simConnectProcessSimObjectData(static_cast(pData)); + break; + + case SIMCONNECT_RECV_ID_EXCEPTION: + // exception + std::cout << "FADEC: Exception in SimConnect connection: "; + std::cout << getSimConnectExceptionString( + static_cast(static_cast(pData)->dwException)); + std::cout << std::endl; + break; + + default: + break; + } + } + + void simConnectProcessSimObjectData(const SIMCONNECT_RECV_SIMOBJECT_DATA* data) { + // process depending on request id + switch (data->dwRequestID) { + case 0: + // store aircraft data + simulationData = *((SimulationData*)&data->dwData); + return; + case 8: + simulationDataLivery = *((SimulationDataLivery*)&data->dwData); + if (simulationDataLivery.atc_id[0] == '\0') { + std::cout << "FADEC: Use default aircraft registration " << DEFAULT_AIRCRAFT_REGISTRATION << std::endl; + strncpy(simulationDataLivery.atc_id, DEFAULT_AIRCRAFT_REGISTRATION, sizeof(simulationDataLivery.atc_id)); + } + return; + + default: + // print unknown request id + std::cout << "FADEC: Unknown request id in SimConnect connection: "; + std::cout << data->dwRequestID << std::endl; + return; + } + } + + /// + /// Kills the FADEC and unregisters all LVars + /// + /// True if successful, false otherwise. + bool killFADEC() { + std::cout << "FADEC: Disconnecting ..." << std::endl; + EngineControlInstance.terminate(); + + isConnected = false; + unregister_all_named_vars(); + + std::cout << "FADEC: Disconnected." << std::endl; + return SUCCEEDED(SimConnect_Close(hSimConnect)); + } + + std::string getSimConnectExceptionString(SIMCONNECT_EXCEPTION exception) { + switch (exception) { + case SIMCONNECT_EXCEPTION_NONE: + return "NONE"; + + case SIMCONNECT_EXCEPTION_ERROR: + return "ERROR"; + + case SIMCONNECT_EXCEPTION_SIZE_MISMATCH: + return "SIZE_MISMATCH"; + + case SIMCONNECT_EXCEPTION_UNRECOGNIZED_ID: + return "UNRECOGNIZED_ID"; + + case SIMCONNECT_EXCEPTION_UNOPENED: + return "UNOPENED"; + + case SIMCONNECT_EXCEPTION_VERSION_MISMATCH: + return "VERSION_MISMATCH"; + + case SIMCONNECT_EXCEPTION_TOO_MANY_GROUPS: + return "TOO_MANY_GROUPS"; + + case SIMCONNECT_EXCEPTION_NAME_UNRECOGNIZED: + return "NAME_UNRECOGNIZED"; + + case SIMCONNECT_EXCEPTION_TOO_MANY_EVENT_NAMES: + return "TOO_MANY_EVENT_NAMES"; + + case SIMCONNECT_EXCEPTION_EVENT_ID_DUPLICATE: + return "EVENT_ID_DUPLICATE"; + + case SIMCONNECT_EXCEPTION_TOO_MANY_MAPS: + return "TOO_MANY_MAPS"; + + case SIMCONNECT_EXCEPTION_TOO_MANY_OBJECTS: + return "TOO_MANY_OBJECTS"; + + case SIMCONNECT_EXCEPTION_TOO_MANY_REQUESTS: + return "TOO_MANY_REQUESTS"; + + case SIMCONNECT_EXCEPTION_WEATHER_INVALID_PORT: + return "WEATHER_INVALID_PORT"; + + case SIMCONNECT_EXCEPTION_WEATHER_INVALID_METAR: + return "WEATHER_INVALID_METAR"; + + case SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_GET_OBSERVATION: + return "WEATHER_UNABLE_TO_GET_OBSERVATION"; + + case SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_CREATE_STATION: + return "WEATHER_UNABLE_TO_CREATE_STATION"; + + case SIMCONNECT_EXCEPTION_WEATHER_UNABLE_TO_REMOVE_STATION: + return "WEATHER_UNABLE_TO_REMOVE_STATION"; + + case SIMCONNECT_EXCEPTION_INVALID_DATA_TYPE: + return "INVALID_DATA_TYPE"; + + case SIMCONNECT_EXCEPTION_INVALID_DATA_SIZE: + return "INVALID_DATA_SIZE"; + + case SIMCONNECT_EXCEPTION_DATA_ERROR: + return "DATA_ERROR"; + + case SIMCONNECT_EXCEPTION_INVALID_ARRAY: + return "INVALID_ARRAY"; + + case SIMCONNECT_EXCEPTION_CREATE_OBJECT_FAILED: + return "CREATE_OBJECT_FAILED"; + + case SIMCONNECT_EXCEPTION_LOAD_FLIGHTPLAN_FAILED: + return "LOAD_FLIGHTPLAN_FAILED"; + + case SIMCONNECT_EXCEPTION_OPERATION_INVALID_FOR_OBJECT_TYPE: + return "OPERATION_INVALID_FOR_OBJECT_TYPE"; + + case SIMCONNECT_EXCEPTION_ILLEGAL_OPERATION: + return "ILLEGAL_OPERATION"; + + case SIMCONNECT_EXCEPTION_ALREADY_SUBSCRIBED: + return "ALREADY_SUBSCRIBED"; + + case SIMCONNECT_EXCEPTION_INVALID_ENUM: + return "INVALID_ENUM"; + + case SIMCONNECT_EXCEPTION_DEFINITION_ERROR: + return "DEFINITION_ERROR"; + + case SIMCONNECT_EXCEPTION_DUPLICATE_ID: + return "DUPLICATE_ID"; + + case SIMCONNECT_EXCEPTION_DATUM_ID: + return "DATUM_ID"; + + case SIMCONNECT_EXCEPTION_OUT_OF_BOUNDS: + return "OUT_OF_BOUNDS"; + + case SIMCONNECT_EXCEPTION_ALREADY_CREATED: + return "ALREADY_CREATED"; + + case SIMCONNECT_EXCEPTION_OBJECT_OUTSIDE_REALITY_BUBBLE: + return "OBJECT_OUTSIDE_REALITY_BUBBLE"; + + case SIMCONNECT_EXCEPTION_OBJECT_CONTAINER: + return "OBJECT_CONTAINER"; + + case SIMCONNECT_EXCEPTION_OBJECT_AI: + return "OBJECT_AI"; + + case SIMCONNECT_EXCEPTION_OBJECT_ATC: + return "OBJECT_ATC"; + + case SIMCONNECT_EXCEPTION_OBJECT_SCHEDULE: + return "OBJECT_SCHEDULE"; + + default: + return "UNKNOWN"; + } + } + + /* + * This function is to call if some data are needed before initialization of the engine (such as aircraft registration). + * This has to be done here due to data fetch feature being available after the first PANEL_SERVICE_PRE_DRAW (from what I have observed) + * This problem was observed when engine configuration file was under development + * Reach Julian Sebline on Discord if information needed + * + * Modify this function as needed + * + * @return true if all the requirements to initialize the engine are fulfilled + */ + bool fetchNeededData() { + // This two lines request aircraft registration + simConnectRequestDataAcftInfo(); + simConnectReadData(); + + _isReady = isRegistrationFound(); + + if (_isReady) { + EngineControlInstance.initialize(simulationDataLivery.atc_id); + } + + return _isReady; + } + + bool isReady() { return _isReady; } +}; diff --git a/fbw-a380x/src/wasm/fadec_a380/src/RegPolynomials.h b/fbw-a380x/src/wasm/fadec_a380/src/RegPolynomials.h new file mode 100644 index 00000000000..70786d438b4 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380/src/RegPolynomials.h @@ -0,0 +1,272 @@ +#pragma once + +#include "common.h" + +/// +/// A collection of multi-variate regression polynomials for engine parameters +/// +/// True if successful, false otherwise. +class Polynomial { + public: + /// + /// Shutdown polynomials - N3 (%) + /// + double shutdownN3(double preN3, double deltaTime) { + double outN3 = 0; + double k = -0.08183; + + if (preN3 < 30) + k = -0.0515; + + outN3 = preN3 * expFBW(k * deltaTime); + + return outN3; + } + + /// + /// Shutdown polynomials - N1 (%) + /// + double shutdownN1(double preN1, double deltaTime) { + double outN1 = 0; + double k = -0.164; + + if (preN1 < 4) + k = -0.08; + + outN1 = preN1 * expFBW(k * deltaTime); + + return outN1; + } + + /// + /// Shutdown polynomials - EGT (degrees C) + /// + double shutdownEGT(double preEGT, double ambientTemp, double deltaTime) { + double outEGT = 0; + double threshold = ambientTemp + 140; + double k = 0; + double ts = 0; + + if (preEGT > threshold) { + k = 0.0257743; + ts = 135 + ambientTemp; + } else { + k = 0.00072756; + ts = 30 + ambientTemp; + } + + outEGT = ts + (preEGT - ts) * expFBW(-k * deltaTime); + + return outEGT; + } + + /// + /// Start-up polynomials - N3 (%) + /// + double startN3(double n3, double preN3, double idleN3) { + double outN3 = 0; + double normalN3 = 0; + + normalN3 = n3 * 60.0 / idleN3; + + double c_N3[16] = {4.03649879e+00, -9.41981960e-01, 1.98426614e-01, -2.11907840e-02, 1.00777507e-03, -1.57319166e-06, + -2.15034888e-06, 1.08288379e-07, -2.48504632e-09, 2.52307089e-11, -2.06869243e-14, 8.99045761e-16, + -9.94853959e-17, 1.85366499e-18, -1.44869928e-20, 4.31033031e-23}; + + outN3 = c_N3[0] + (c_N3[1] * normalN3) + (c_N3[2] * powFBW(normalN3, 2)) + (c_N3[3] * powFBW(normalN3, 3)) + + (c_N3[4] * powFBW(normalN3, 4)) + (c_N3[5] * powFBW(normalN3, 5)) + (c_N3[6] * powFBW(normalN3, 6)) + + (c_N3[7] * powFBW(normalN3, 7)) + (c_N3[8] * powFBW(normalN3, 8)) + (c_N3[9] * powFBW(normalN3, 9)) + + (c_N3[10] * powFBW(normalN3, 10)) + (c_N3[11] * powFBW(normalN3, 11)) + (c_N3[12] * powFBW(normalN3, 12)) + + (c_N3[13] * powFBW(normalN3, 13)) + (c_N3[14] * powFBW(normalN3, 14)) + (c_N3[15] * powFBW(normalN3, 15)); + + outN3 = outN3 * n3; + + if (outN3 < preN3) { + outN3 = preN3 + 0.002; + } + if (outN3 >= idleN3 + 0.1) { + outN3 = idleN3 + 0.05; + } + + return outN3; + } + + /// + /// Start-up polynomials - N1 (%) + /// + double startN1(double fbwN3, double idleN3, double idleN1) { + double normalN1pre = 0; + double normalN1post = 0; + double normalN3 = fbwN3 / idleN3; + double c_N1[9] = {-2.2812156e-12, -5.9830374e+01, 7.0629094e+02, -3.4580361e+03, 9.1428923e+03, + -1.4097740e+04, 1.2704110e+04, -6.2099935e+03, 1.2733071e+03}; + + normalN1pre = (-2.4698087 * powFBW(normalN3, 3)) + (0.9662026 * powFBW(normalN3, 2)) + (0.0701367 * normalN3); + + normalN1post = c_N1[0] + (c_N1[1] * normalN3) + (c_N1[2] * powFBW(normalN3, 2)) + (c_N1[3] * powFBW(normalN3, 3)) + + (c_N1[4] * powFBW(normalN3, 4)) + (c_N1[5] * powFBW(normalN3, 5)) + (c_N1[6] * powFBW(normalN3, 6)) + + (c_N1[7] * powFBW(normalN3, 7)) + (c_N1[8] * powFBW(normalN3, 8)); + + if (normalN1post >= normalN1pre) + return normalN1post * idleN1; + else + return normalN1pre * idleN1; + } + + /// + /// Start-up polynomials - Fuel Flow (Kg/hr) + /// + double startFF(double fbwN3, double idleN3, double idleFF) { + double normalFF = 0; + double outFF = 0; + double normalN3 = fbwN3 / idleN3; + + if (normalN3 <= 0.37) { + normalFF = 0; + } else { + double c_FF[9] = {3.1110282e-12, 1.0804331e+02, -1.3972629e+03, 7.4874131e+03, -2.1511983e+04, + 3.5957757e+04, -3.5093994e+04, 1.8573033e+04, -4.1220062e+03}; + + normalFF = c_FF[0] + (c_FF[1] * normalN3) + (c_FF[2] * powFBW(normalN3, 2)) + (c_FF[3] * powFBW(normalN3, 3)) + + (c_FF[4] * powFBW(normalN3, 4)) + (c_FF[5] * powFBW(normalN3, 5)) + (c_FF[6] * powFBW(normalN3, 6)) + + (c_FF[7] * powFBW(normalN3, 7)) + (c_FF[8] * powFBW(normalN3, 8)); + } + + if (normalFF < 0) { + normalFF = 0; + } + + return normalFF * idleFF; + } + + /// + /// Start-up polynomials - EGT (Celsius) + /// + double startEGT(double fbwN3, double idleN3, double ambientTemp, double idleEGT) { + double normalEGT = 0; + double outEGT = 0; + double normalN3 = fbwN3 / idleN3; + + if (normalN3 < 0.17) { + normalEGT = 0; + } else if (normalN3 <= 0.4) { + normalEGT = (0.04783 * normalN3) - 0.00813; + } else { + double c_EGT[9] = {-6.8725167e+02, 7.7548864e+03, -3.7507098e+04, 1.0147016e+05, -1.6779273e+05, + 1.7357157e+05, -1.0960924e+05, 3.8591956e+04, -5.7912600e+03}; + + normalEGT = c_EGT[0] + (c_EGT[1] * normalN3) + (c_EGT[2] * powFBW(normalN3, 2)) + (c_EGT[3] * powFBW(normalN3, 3)) + + (c_EGT[4] * powFBW(normalN3, 4)) + (c_EGT[5] * powFBW(normalN3, 5)) + (c_EGT[6] * powFBW(normalN3, 6)) + + (c_EGT[7] * powFBW(normalN3, 7)) + (c_EGT[8] * powFBW(normalN3, 8)); + } + + outEGT = (normalEGT * (idleEGT - (ambientTemp))) + (ambientTemp); + + return outEGT; + } + + /// + /// Start-up polynomials - Oil Temperature (Celsius) + /// + double startOilTemp(double fbwN3, double idleN3, double ambientTemp) { + double outOilTemp = 0; + + if (fbwN3 < 0.79 * idleN3) { + outOilTemp = ambientTemp; + } else if (fbwN3 < 0.98 * idleN3) { + outOilTemp = ambientTemp + 5; + } else { + outOilTemp = ambientTemp + 10; + } + + return outOilTemp; + } + + /// + /// Real-life modeled polynomials - Corrected EGT (Celsius) + /// + double correctedEGT(double cn1, double cff, double mach, double alt) { + double outCEGT = 0; + cff = cff / 2; // to account for the A380 double fuel flow. Will have to be taken care of + + double c_EGT[16] = {3.2636e+02, 0.0000e+00, 9.2893e-01, 3.9505e-02, 3.9070e+02, -4.7911e-04, 7.7679e-03, 5.8361e-05, + -2.5566e+00, 5.1227e-06, 1.0178e-07, -7.4602e-03, 1.2106e-07, -5.1639e+01, -2.7356e-03, 1.9312e-08}; + + outCEGT = c_EGT[0] + c_EGT[1] + (c_EGT[2] * cn1) + (c_EGT[3] * cff) + (c_EGT[4] * mach) + (c_EGT[5] * alt) + + (c_EGT[6] * powFBW(cn1, 2)) + (c_EGT[7] * cn1 * cff) + (c_EGT[8] * cn1 * mach) + (c_EGT[9] * cn1 * alt) + + (c_EGT[10] * powFBW(cff, 2)) + (c_EGT[11] * mach * cff) + (c_EGT[12] * cff * alt) + (c_EGT[13] * powFBW(mach, 2)) + + (c_EGT[14] * mach * alt) + (c_EGT[15] * powFBW(alt, 2)); + + return outCEGT; + } + + /// + /// Real-life modeled polynomials - Corrected Fuel Flow (lbs/ hr) + /// + double correctedFuelFlow(double cn1, double mach, double alt) { + double outCFF = 0; + + double c_Flow[21] = {-1.7630e+02, -2.1542e-01, 4.7119e+01, 6.1519e+02, 1.8047e-03, -4.4554e-01, -4.3940e+01, + 4.0459e-05, -3.2912e+01, -6.2894e-03, -1.2544e-07, 1.0938e-02, 4.0936e-01, -5.5841e-06, + -2.3829e+01, 9.3269e-04, 2.0273e-11, -2.4100e+02, 1.4171e-02, -9.5581e-07, 1.2728e-11}; + + outCFF = c_Flow[0] + c_Flow[1] + (c_Flow[2] * cn1) + (c_Flow[3] * mach) + (c_Flow[4] * alt) + (c_Flow[5] * powFBW(cn1, 2)) + + (c_Flow[6] * cn1 * mach) + (c_Flow[7] * cn1 * alt) + (c_Flow[8] * powFBW(mach, 2)) + (c_Flow[9] * mach * alt) + + (c_Flow[10] * powFBW(alt, 2)) + (c_Flow[11] * powFBW(cn1, 3)) + (c_Flow[12] * powFBW(cn1, 2) * mach) + + (c_Flow[13] * powFBW(cn1, 2) * alt) + (c_Flow[14] * cn1 * powFBW(mach, 2)) + (c_Flow[15] * cn1 * mach * alt) + + (c_Flow[16] * cn1 * powFBW(alt, 2)) + (c_Flow[17] * powFBW(mach, 3)) + (c_Flow[18] * powFBW(mach, 2) * alt) + + (c_Flow[19] * mach * powFBW(alt, 2)) + (c_Flow[20] * powFBW(alt, 3)); + + return 2*outCFF; + } + + double oilTemperature(double energy, double preOilTemp, double maxOilTemp, double deltaTime) { + double t_steady = 0; + double k = 0.001; + double dt = 0; + double oilTemp_out; + + dt = energy * deltaTime * 0.002; + + t_steady = ((maxOilTemp * k * deltaTime) + preOilTemp) / (1 + (k * deltaTime)); + + if (t_steady - dt >= maxOilTemp) { + oilTemp_out = maxOilTemp; + } else if (t_steady - dt >= maxOilTemp - 10) { + oilTemp_out = (t_steady - dt) * 0.999997; + } else { + oilTemp_out = (t_steady - dt); + } + + // std::cout << "FADEC: Max= " << maxOilTemp << " Energy = " << energy << " dt = " << dt << " preT= " << preOilTemp + // << " Tss = " << t_steady << " To = " << oilTemp_out << std::flush; + + return oilTemp_out; + } + + /// + /// Real-life modeled polynomials - Oil Gulping (%) + /// + double oilGulpPct(double thrust) { + double outOilGulpPct = 0; + + double c_OilGulp[3] = {20.1968848, -1.2270302e-4, 1.78442e-8}; + + outOilGulpPct = c_OilGulp[0] + (c_OilGulp[1] * thrust) + (c_OilGulp[2] * powFBW(thrust, 2)); + + return outOilGulpPct / 100; + } + + /// + /// Real-life modeled polynomials - Oil Pressure (PSI) + /// + double oilPressure(double simN3) { + double outOilPressure = 0; + + double c_OilPress[3] = {-0.88921, 0.23711, 0.00682}; + + outOilPressure = c_OilPress[0] + (c_OilPress[1] * simN3) + (c_OilPress[2] * powFBW(simN3, 2)); + + return outOilPressure; + } +}; diff --git a/fbw-a380x/src/wasm/fadec_a380/src/SimVars.h b/fbw-a380x/src/wasm/fadec_a380/src/SimVars.h new file mode 100644 index 00000000000..531d1877929 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380/src/SimVars.h @@ -0,0 +1,547 @@ +#pragma once + +#include + +/// +/// SimConnect data types to send to Sim Updated +/// +enum DataTypesID { + PayloadStation1, + PayloadStation2, + PayloadStation3, + PayloadStation4, + PayloadStation5, + PayloadStation6, + PayloadStation7, + PayloadStation8, + FuelSystemLeftOuter, + FuelSystemFeedOne, + FuelSystemLeftMid, + FuelSystemLeftInner, + FuelSystemFeedTwo, + FuelSystemFeedThree, + FuelSystemRightInner, + FuelSystemRightMid, + FuelSystemFeedFour, + FuelSystemRightOuter, + FuelSystemTrim, + OilTempEngine1, + OilTempEngine2, + OilTempEngine3, + OilTempEngine4, + OilPsiEngine1, + OilPsiEngine2, + OilPsiEngine3, + OilPsiEngine4, + StartCN3Engine1, + StartCN3Engine2, + StartCN3Engine3, + StartCN3Engine4, + SimulationDataTypeId, + AcftInfo, +}; + +struct SimulationData { + double simulationTime; + double simulationRate; +}; + +struct SimulationDataLivery { + char atc_id[32] = ""; +}; + +/// +/// A collection of SimVar unit enums. +/// +class Units { + public: + ENUM Percent = get_units_enum("Percent"); + ENUM Pounds = get_units_enum("Pounds"); + ENUM Psi = get_units_enum("Psi"); + ENUM Pph = get_units_enum("Pounds per hour"); + ENUM Gallons = get_units_enum("Gallons"); + ENUM Feet = get_units_enum("Feet"); + ENUM FootPounds = get_units_enum("Foot pounds"); + ENUM FeetMin = get_units_enum("Feet per minute"); + ENUM Number = get_units_enum("Number"); + ENUM Mach = get_units_enum("Mach"); + ENUM Millibars = get_units_enum("Millibars"); + ENUM SluggerSlugs = get_units_enum("Slug per cubic feet"); + ENUM Celsius = get_units_enum("Celsius"); + ENUM Bool = get_units_enum("Bool"); + ENUM Hours = get_units_enum("Hours"); + ENUM Seconds = get_units_enum("Seconds"); +}; + +/// +/// A collection of SimVars and LVars for the A32NX +/// +class SimVars { + public: + Units* m_Units; + + /// + /// Collection of SimVars for the A32NX + /// + ENUM CorrectedN1 = get_aircraft_var_enum("TURB ENG CORRECTED N1"); + ENUM CorrectedN2 = get_aircraft_var_enum("TURB ENG CORRECTED N2"); + ENUM N1 = get_aircraft_var_enum("TURB ENG N1"); + ENUM N2 = get_aircraft_var_enum("TURB ENG N2"); + ENUM OilPSI = get_aircraft_var_enum("GENERAL ENG OIL PRESSURE"); + ENUM OilTemp = get_aircraft_var_enum("GENERAL ENG OIL TEMPERATURE"); + ENUM Thrust = get_aircraft_var_enum("TURB ENG JET THRUST"); + ENUM correctedFF = get_aircraft_var_enum("TURB ENG CORRECTED FF"); + ENUM PlaneAltitude = get_aircraft_var_enum("PLANE ALTITUDE"); + ENUM PlaneAltitudeAGL = get_aircraft_var_enum("PLANE ALT ABOVE GROUND"); + ENUM PressureAltitude = get_aircraft_var_enum("PRESSURE ALTITUDE"); + ENUM AirSpeedMach = get_aircraft_var_enum("AIRSPEED MACH"); + ENUM AmbientTemp = get_aircraft_var_enum("AMBIENT TEMPERATURE"); + ENUM AmbientPressure = get_aircraft_var_enum("AMBIENT PRESSURE"); + ENUM VerticalSpeed = get_aircraft_var_enum("VERTICAL SPEED"); + ENUM StdTemp = get_aircraft_var_enum("STANDARD ATM TEMPERATURE"); + ENUM SimOnGround = get_aircraft_var_enum("SIM ON GROUND"); + ENUM EngineTime = get_aircraft_var_enum("GENERAL ENG ELAPSED TIME"); + ENUM EngineStarter = get_aircraft_var_enum("GENERAL ENG STARTER"); + ENUM EngineIgniter = get_aircraft_var_enum("TURB ENG IGNITION SWITCH EX1"); + ENUM EngineCombustion = get_aircraft_var_enum("GENERAL ENG COMBUSTION"); + ENUM animDeltaTime = get_aircraft_var_enum("ANIMATION DELTA TIME"); + + ENUM TankFuelQuantity = get_aircraft_var_enum("FUELSYSTEM TANK QUANTITY"); + ENUM FuelTotalQuantity = get_aircraft_var_enum("FUEL TOTAL QUANTITY"); + + ENUM EmptyWeight = get_aircraft_var_enum("EMPTY WEIGHT"); + ENUM TotalWeight = get_aircraft_var_enum("TOTAL WEIGHT"); + ENUM FuelWeightGallon = get_aircraft_var_enum("FUEL WEIGHT PER GALLON"); + + ENUM NacelleAntiIce = get_aircraft_var_enum("ENG ANTI ICE"); + + ENUM PayloadStationWeights = get_aircraft_var_enum("PAYLOAD STATION WEIGHT"); + + /// + /// Collection of LVars for the A32NX + /// + ID DevVar; + ID IsReady; + ID FlexTemp; + ID Engine1N3; + ID Engine2N3; + ID Engine3N3; + ID Engine4N3; + ID Engine1N2; + ID Engine2N2; + ID Engine3N2; + ID Engine4N2; + ID Engine1N1; + ID Engine2N1; + ID Engine3N1; + ID Engine4N1; + ID EngineIdleN1; + ID EngineIdleN3; + ID EngineIdleFF; + ID EngineIdleEGT; + ID Engine1EGT; + ID Engine2EGT; + ID Engine3EGT; + ID Engine4EGT; + ID Engine1Oil; + ID Engine2Oil; + ID Engine3Oil; + ID Engine4Oil; + ID Engine1TotalOil; + ID Engine2TotalOil; + ID Engine3TotalOil; + ID Engine4TotalOil; + ID Engine1FF; + ID Engine2FF; + ID Engine3FF; + ID Engine4FF; + ID Engine1PreFF; + ID Engine2PreFF; + ID Engine3PreFF; + ID Engine4PreFF; + ID EngineCycleTime; + ID EngineImbalance; + ID WingAntiIce; + ID FuelUsedEngine1; + ID FuelUsedEngine2; + ID FuelUsedEngine3; + ID FuelUsedEngine4; + + ID FuelLeftOuterPre; + ID FuelFeedOnePre; + ID FuelLeftMidPre; + ID FuelLeftInnerPre; + ID FuelFeedTwoPre; + ID FuelFeedThreePre; + ID FuelRightInnerPre; + ID FuelRightMidPre; + ID FuelFeedFourPre; + ID FuelRightOuterPre; + ID FuelTrimPre; + + ID RefuelRate; + ID RefuelStartedByUser; + ID FuelOverflowLeft; + ID FuelOverflowRight; + ID Engine1State; + ID Engine2State; + ID Engine3State; + ID Engine4State; + ID Engine1Timer; + ID Engine2Timer; + ID Engine3Timer; + ID Engine4Timer; + ID PumpStateEngine1; + ID PumpStateEngine2; + ID PumpStateEngine3; + ID PumpStateEngine4; + ID ThrustLimitType; + ID ThrustLimitIdle; + ID ThrustLimitToga; + ID ThrustLimitFlex; + ID ThrustLimitClimb; + ID ThrustLimitMct; + ID PacksState1; + ID PacksState2; + + SimVars() { this->initializeVars(); } + + void initializeVars() { + DevVar = register_named_variable("A32NX_DEVELOPER_STATE"); + IsReady = register_named_variable("A32NX_IS_READY"); + FlexTemp = register_named_variable("AIRLINER_TO_FLEX_TEMP"); + Engine1N3 = register_named_variable("A32NX_ENGINE_N3:1"); + Engine2N3 = register_named_variable("A32NX_ENGINE_N3:2"); + Engine3N3 = register_named_variable("A32NX_ENGINE_N3:3"); + Engine4N3 = register_named_variable("A32NX_ENGINE_N3:4"); + Engine1N2 = register_named_variable("A32NX_ENGINE_N2:1"); + Engine2N2 = register_named_variable("A32NX_ENGINE_N2:2"); + Engine3N2 = register_named_variable("A32NX_ENGINE_N2:3"); + Engine4N2 = register_named_variable("A32NX_ENGINE_N2:4"); + Engine1N1 = register_named_variable("A32NX_ENGINE_N1:1"); + Engine2N1 = register_named_variable("A32NX_ENGINE_N1:2"); + Engine3N1 = register_named_variable("A32NX_ENGINE_N1:3"); + Engine4N1 = register_named_variable("A32NX_ENGINE_N1:4"); + EngineIdleN1 = register_named_variable("A32NX_ENGINE_IDLE_N1"); + EngineIdleN3 = register_named_variable("A32NX_ENGINE_IDLE_N3"); + EngineIdleFF = register_named_variable("A32NX_ENGINE_IDLE_FF"); + EngineIdleEGT = register_named_variable("A32NX_ENGINE_IDLE_EGT"); + Engine1EGT = register_named_variable("A32NX_ENGINE_EGT:1"); + Engine2EGT = register_named_variable("A32NX_ENGINE_EGT:2"); + Engine3EGT = register_named_variable("A32NX_ENGINE_EGT:3"); + Engine4EGT = register_named_variable("A32NX_ENGINE_EGT:4"); + Engine1Oil = register_named_variable("A32NX_ENGINE_OIL_QTY:1"); + Engine2Oil = register_named_variable("A32NX_ENGINE_OIL_QTY:2"); + Engine3Oil = register_named_variable("A32NX_ENGINE_OIL_QTY:3"); + Engine4Oil = register_named_variable("A32NX_ENGINE_OIL_QTY:4"); + Engine1TotalOil = register_named_variable("A32NX_ENGINE_OIL_TOTAL:1"); + Engine2TotalOil = register_named_variable("A32NX_ENGINE_OIL_TOTAL:2"); + Engine3TotalOil = register_named_variable("A32NX_ENGINE_OIL_TOTAL:3"); + Engine4TotalOil = register_named_variable("A32NX_ENGINE_OIL_TOTAL:4"); + Engine1FF = register_named_variable("A32NX_ENGINE_FF:1"); + Engine2FF = register_named_variable("A32NX_ENGINE_FF:2"); + Engine3FF = register_named_variable("A32NX_ENGINE_FF:3"); + Engine4FF = register_named_variable("A32NX_ENGINE_FF:4"); + Engine1PreFF = register_named_variable("A32NX_ENGINE_PRE_FF:1"); + Engine2PreFF = register_named_variable("A32NX_ENGINE_PRE_FF:2"); + Engine3PreFF = register_named_variable("A32NX_ENGINE_PRE_FF:3"); + Engine4PreFF = register_named_variable("A32NX_ENGINE_PRE_FF:4"); + EngineImbalance = register_named_variable("A32NX_ENGINE_IMBALANCE"); + WingAntiIce = register_named_variable("A32NX_PNEU_WING_ANTI_ICE_SYSTEM_ON"); + FuelUsedEngine1 = register_named_variable("A32NX_FUEL_USED:1"); + FuelUsedEngine2 = register_named_variable("A32NX_FUEL_USED:2"); + FuelUsedEngine3 = register_named_variable("A32NX_FUEL_USED:3"); + FuelUsedEngine4 = register_named_variable("A32NX_FUEL_USED:4"); + + FuelLeftOuterPre = register_named_variable("A32NX_FUEL_LEFTOUTER_PRE"); + FuelFeedOnePre = register_named_variable("A32NX_FUEL_FEED1_PRE"); + FuelLeftMidPre = register_named_variable("A32NX_FUEL_LEFTMID_PRE"); + FuelLeftInnerPre = register_named_variable("A32NX_FUEL_LEFTINNER_PRE"); + FuelFeedTwoPre = register_named_variable("A32NX_FUEL_FEED2_PRE"); + FuelFeedThreePre = register_named_variable("A32NX_FUEL_FEED3_PRE"); + FuelRightInnerPre = register_named_variable("A32NX_FUEL_RIGHTINNER_PRE"); + FuelRightMidPre = register_named_variable("A32NX_FUEL_RIGHTMID_PRE"); + FuelFeedFourPre = register_named_variable("A32NX_FUEL_FEED4_PRE"); + FuelRightOuterPre = register_named_variable("A32NX_FUEL_RIGHTOUTER_PRE"); + FuelTrimPre = register_named_variable("A32NX_FUEL_TRIM_PRE"); + + RefuelRate = register_named_variable("A32NX_EFB_REFUEL_RATE_SETTING"); + RefuelStartedByUser = register_named_variable("A32NX_REFUEL_STARTED_BY_USR"); + Engine1State = register_named_variable("A32NX_ENGINE_STATE:1"); + Engine2State = register_named_variable("A32NX_ENGINE_STATE:2"); + Engine3State = register_named_variable("A32NX_ENGINE_STATE:3"); + Engine4State = register_named_variable("A32NX_ENGINE_STATE:4"); + Engine1Timer = register_named_variable("A32NX_ENGINE_TIMER:1"); + Engine2Timer = register_named_variable("A32NX_ENGINE_TIMER:2"); + Engine3Timer = register_named_variable("A32NX_ENGINE_TIMER:3"); + Engine4Timer = register_named_variable("A32NX_ENGINE_TIMER:4"); + PumpStateEngine1 = register_named_variable("A32NX_PUMP_STATE:1"); + PumpStateEngine2 = register_named_variable("A32NX_PUMP_STATE:2"); + PumpStateEngine3 = register_named_variable("A32NX_PUMP_STATE:3"); + PumpStateEngine4 = register_named_variable("A32NX_PUMP_STATE:4"); + + ThrustLimitType = register_named_variable("A32NX_AUTOTHRUST_THRUST_LIMIT_TYPE"); + ThrustLimitIdle = register_named_variable("A32NX_AUTOTHRUST_THRUST_LIMIT_IDLE"); + ThrustLimitToga = register_named_variable("A32NX_AUTOTHRUST_THRUST_LIMIT_TOGA"); + ThrustLimitFlex = register_named_variable("A32NX_AUTOTHRUST_THRUST_LIMIT_FLX"); + ThrustLimitClimb = register_named_variable("A32NX_AUTOTHRUST_THRUST_LIMIT_CLB"); + ThrustLimitMct = register_named_variable("A32NX_AUTOTHRUST_THRUST_LIMIT_MCT"); + + PacksState1 = register_named_variable("A32NX_COND_PACK_FLOW_VALVE_1_IS_OPEN"); + PacksState2 = register_named_variable("A32NX_COND_PACK_FLOW_VALVE_2_IS_OPEN"); + + this->setDeveloperState(0); + this->setEngine1N3(0); + this->setEngine2N3(0); + this->setEngine3N3(0); + this->setEngine4N3(0); + this->setEngine1N2(0); + this->setEngine2N2(0); + this->setEngine3N2(0); + this->setEngine4N2(0); + this->setEngine1N1(0); + this->setEngine2N1(0); + this->setEngine3N1(0); + this->setEngine4N1(0); + this->setEngineIdleN1(0); + this->setEngineIdleN3(0); + this->setEngineIdleFF(0); + this->setEngineIdleEGT(0); + this->setEngine1EGT(0); + this->setEngine2EGT(0); + this->setEngine3EGT(0); + this->setEngine4EGT(0); + this->setEngine1Oil(0); + this->setEngine2Oil(0); + this->setEngine3Oil(0); + this->setEngine4Oil(0); + this->setEngine1TotalOil(0); + this->setEngine2TotalOil(0); + this->setEngine3TotalOil(0); + this->setEngine4TotalOil(0); + this->setEngine1FF(0); + this->setEngine2FF(0); + this->setEngine3FF(0); + this->setEngine4FF(0); + this->setEngine1PreFF(0); + this->setEngine2PreFF(0); + this->setEngine3PreFF(0); + this->setEngine4PreFF(0); + this->setEngineImbalance(0); + this->setFuelUsedEngine1(0); + this->setFuelUsedEngine2(0); + this->setFuelUsedEngine3(0); + this->setFuelUsedEngine4(0); + this->setFuelLeftOuterPre(0); + this->setFuelFeedOnePre(0); + this->setFuelLeftMidPre(0); + this->setFuelLeftInnerPre(0); + this->setFuelFeedTwoPre(0); + this->setFuelFeedThreePre(0); + this->setFuelRightInnerPre(0); + this->setFuelRightMidPre(0); + this->setFuelFeedFourPre(0); + this->setFuelRightOuterPre(0); + this->setFuelTrimPre(0); + this->setEngine1State(0); + this->setEngine2State(0); + this->setEngine3State(0); + this->setEngine4State(0); + this->setEngine1Timer(0); + this->setEngine2Timer(0); + this->setEngine3Timer(0); + this->setEngine4Timer(0); + this->setPumpStateEngine1(0); + this->setPumpStateEngine2(0); + this->setPumpStateEngine3(0); + this->setPumpStateEngine4(0); + this->setThrustLimitIdle(0); + this->setThrustLimitToga(0); + this->setThrustLimitFlex(0); + this->setThrustLimitClimb(0); + this->setThrustLimitMct(0); + + m_Units = new Units(); + } + + // Collection of LVar 'set' Functions + void setDeveloperState(FLOAT64 value) { set_named_variable_value(DevVar, value); } + void setEngine1N3(FLOAT64 value) { set_named_variable_value(Engine1N3, value); } + void setEngine2N3(FLOAT64 value) { set_named_variable_value(Engine2N3, value); } + void setEngine3N3(FLOAT64 value) { set_named_variable_value(Engine3N3, value); } + void setEngine4N3(FLOAT64 value) { set_named_variable_value(Engine4N3, value); } + void setEngine1N2(FLOAT64 value) { set_named_variable_value(Engine1N2, value); } + void setEngine2N2(FLOAT64 value) { set_named_variable_value(Engine2N2, value); } + void setEngine3N2(FLOAT64 value) { set_named_variable_value(Engine3N2, value); } + void setEngine4N2(FLOAT64 value) { set_named_variable_value(Engine4N2, value); } + void setEngine1N1(FLOAT64 value) { set_named_variable_value(Engine1N1, value); } + void setEngine2N1(FLOAT64 value) { set_named_variable_value(Engine2N1, value); } + void setEngine3N1(FLOAT64 value) { set_named_variable_value(Engine3N1, value); } + void setEngine4N1(FLOAT64 value) { set_named_variable_value(Engine4N1, value); } + void setEngineIdleN1(FLOAT64 value) { set_named_variable_value(EngineIdleN1, value); } + void setEngineIdleN3(FLOAT64 value) { set_named_variable_value(EngineIdleN3, value); } + void setEngineIdleFF(FLOAT64 value) { set_named_variable_value(EngineIdleFF, value); } + void setEngineIdleEGT(FLOAT64 value) { set_named_variable_value(EngineIdleEGT, value); } + void setEngine1EGT(FLOAT64 value) { set_named_variable_value(Engine1EGT, value); } + void setEngine2EGT(FLOAT64 value) { set_named_variable_value(Engine2EGT, value); } + void setEngine3EGT(FLOAT64 value) { set_named_variable_value(Engine3EGT, value); } + void setEngine4EGT(FLOAT64 value) { set_named_variable_value(Engine4EGT, value); } + void setEngine1Oil(FLOAT64 value) { set_named_variable_value(Engine1Oil, value); } + void setEngine2Oil(FLOAT64 value) { set_named_variable_value(Engine2Oil, value); } + void setEngine3Oil(FLOAT64 value) { set_named_variable_value(Engine3Oil, value); } + void setEngine4Oil(FLOAT64 value) { set_named_variable_value(Engine4Oil, value); } + void setEngine1TotalOil(FLOAT64 value) { set_named_variable_value(Engine1TotalOil, value); } + void setEngine2TotalOil(FLOAT64 value) { set_named_variable_value(Engine2TotalOil, value); } + void setEngine3TotalOil(FLOAT64 value) { set_named_variable_value(Engine3TotalOil, value); } + void setEngine4TotalOil(FLOAT64 value) { set_named_variable_value(Engine4TotalOil, value); } + void setEngine1FF(FLOAT64 value) { set_named_variable_value(Engine1FF, value); } + void setEngine2FF(FLOAT64 value) { set_named_variable_value(Engine2FF, value); } + void setEngine3FF(FLOAT64 value) { set_named_variable_value(Engine3FF, value); } + void setEngine4FF(FLOAT64 value) { set_named_variable_value(Engine4FF, value); } + void setEngine1PreFF(FLOAT64 value) { set_named_variable_value(Engine1PreFF, value); } + void setEngine2PreFF(FLOAT64 value) { set_named_variable_value(Engine2PreFF, value); } + void setEngine3PreFF(FLOAT64 value) { set_named_variable_value(Engine3PreFF, value); } + void setEngine4PreFF(FLOAT64 value) { set_named_variable_value(Engine4PreFF, value); } + void setEngineImbalance(FLOAT64 value) { set_named_variable_value(EngineImbalance, value); } + void setFuelUsedEngine1(FLOAT64 value) { set_named_variable_value(FuelUsedEngine1, value); } + void setFuelUsedEngine2(FLOAT64 value) { set_named_variable_value(FuelUsedEngine2, value); } + void setFuelUsedEngine3(FLOAT64 value) { set_named_variable_value(FuelUsedEngine3, value); } + void setFuelUsedEngine4(FLOAT64 value) { set_named_variable_value(FuelUsedEngine4, value); } + + void setFuelLeftOuterPre(FLOAT64 value) { set_named_variable_value(FuelLeftOuterPre, value); }; + void setFuelFeedOnePre(FLOAT64 value) { set_named_variable_value(FuelFeedOnePre, value); }; + void setFuelLeftMidPre(FLOAT64 value) { set_named_variable_value(FuelLeftMidPre, value); }; + void setFuelLeftInnerPre(FLOAT64 value) { set_named_variable_value(FuelLeftInnerPre, value); }; + void setFuelFeedTwoPre(FLOAT64 value) { set_named_variable_value(FuelFeedTwoPre, value); }; + void setFuelFeedThreePre(FLOAT64 value) { set_named_variable_value(FuelFeedThreePre, value); }; + void setFuelRightInnerPre(FLOAT64 value) { set_named_variable_value(FuelRightInnerPre, value); }; + void setFuelRightMidPre(FLOAT64 value) { set_named_variable_value(FuelRightMidPre, value); }; + void setFuelFeedFourPre(FLOAT64 value) { set_named_variable_value(FuelFeedFourPre, value); }; + void setFuelRightOuterPre(FLOAT64 value) { set_named_variable_value(FuelRightOuterPre, value); }; + void setFuelTrimPre(FLOAT64 value) { set_named_variable_value(FuelTrimPre, value); }; + + void setEngine1State(FLOAT64 value) { set_named_variable_value(Engine1State, value); } + void setEngine2State(FLOAT64 value) { set_named_variable_value(Engine2State, value); } + void setEngine3State(FLOAT64 value) { set_named_variable_value(Engine3State, value); } + void setEngine4State(FLOAT64 value) { set_named_variable_value(Engine4State, value); } + void setEngine1Timer(FLOAT64 value) { set_named_variable_value(Engine1Timer, value); } + void setEngine2Timer(FLOAT64 value) { set_named_variable_value(Engine2Timer, value); } + void setEngine3Timer(FLOAT64 value) { set_named_variable_value(Engine3Timer, value); } + void setEngine4Timer(FLOAT64 value) { set_named_variable_value(Engine4Timer, value); } + void setPumpStateEngine1(FLOAT64 value) { set_named_variable_value(PumpStateEngine1, value); } + void setPumpStateEngine2(FLOAT64 value) { set_named_variable_value(PumpStateEngine2, value); } + void setPumpStateEngine3(FLOAT64 value) { set_named_variable_value(PumpStateEngine3, value); } + void setPumpStateEngine4(FLOAT64 value) { set_named_variable_value(PumpStateEngine4, value); } + void setThrustLimitIdle(FLOAT64 value) { set_named_variable_value(ThrustLimitIdle, value); } + void setThrustLimitToga(FLOAT64 value) { set_named_variable_value(ThrustLimitToga, value); } + void setThrustLimitFlex(FLOAT64 value) { set_named_variable_value(ThrustLimitFlex, value); } + void setThrustLimitClimb(FLOAT64 value) { set_named_variable_value(ThrustLimitClimb, value); } + void setThrustLimitMct(FLOAT64 value) { set_named_variable_value(ThrustLimitMct, value); } + + // Collection of SimVar/LVar 'get' Functions + FLOAT64 getDeveloperState() { return get_named_variable_value(DevVar); } + FLOAT64 getIsReady() { return get_named_variable_value(IsReady); } + FLOAT64 getFlexTemp() { return get_named_variable_value(FlexTemp); } + FLOAT64 getEngine1N3() { return get_named_variable_value(Engine1N3); } + FLOAT64 getEngine2N3() { return get_named_variable_value(Engine2N3); } + FLOAT64 getEngine3N3() { return get_named_variable_value(Engine3N3); } + FLOAT64 getEngine4N3() { return get_named_variable_value(Engine4N3); } + FLOAT64 getEngine1N2() { return get_named_variable_value(Engine1N2); } + FLOAT64 getEngine2N2() { return get_named_variable_value(Engine2N2); } + FLOAT64 getEngine3N2() { return get_named_variable_value(Engine3N2); } + FLOAT64 getEngine4N2() { return get_named_variable_value(Engine4N2); } + FLOAT64 getEngine1N1() { return get_named_variable_value(Engine1N1); } + FLOAT64 getEngine2N1() { return get_named_variable_value(Engine2N1); } + FLOAT64 getEngine3N1() { return get_named_variable_value(Engine3N1); } + FLOAT64 getEngine4N1() { return get_named_variable_value(Engine4N1); } + FLOAT64 getEngineIdleN1() { return get_named_variable_value(EngineIdleN1); } + FLOAT64 getEngineIdleN3() { return get_named_variable_value(EngineIdleN3); } + FLOAT64 getEngineIdleFF() { return get_named_variable_value(EngineIdleFF); } + FLOAT64 getEngineIdleEGT() { return get_named_variable_value(EngineIdleEGT); } + FLOAT64 getEngine1FF() { return get_named_variable_value(Engine1FF); } + FLOAT64 getEngine2FF() { return get_named_variable_value(Engine2FF); } + FLOAT64 getEngine3FF() { return get_named_variable_value(Engine3FF); } + FLOAT64 getEngine4FF() { return get_named_variable_value(Engine4FF); } + FLOAT64 getEngine1EGT() { return get_named_variable_value(Engine1EGT); } + FLOAT64 getEngine2EGT() { return get_named_variable_value(Engine2EGT); } + FLOAT64 getEngine3EGT() { return get_named_variable_value(Engine3EGT); } + FLOAT64 getEngine4EGT() { return get_named_variable_value(Engine4EGT); } + FLOAT64 getEngine1Oil() { return get_named_variable_value(Engine1Oil); } + FLOAT64 getEngine2Oil() { return get_named_variable_value(Engine2Oil); } + FLOAT64 getEngine3Oil() { return get_named_variable_value(Engine3Oil); } + FLOAT64 getEngine4Oil() { return get_named_variable_value(Engine4Oil); } + FLOAT64 getEngine1TotalOil() { return get_named_variable_value(Engine1TotalOil); } + FLOAT64 getEngine2TotalOil() { return get_named_variable_value(Engine2TotalOil); } + FLOAT64 getEngine3TotalOil() { return get_named_variable_value(Engine3TotalOil); } + FLOAT64 getEngine4TotalOil() { return get_named_variable_value(Engine4TotalOil); } + FLOAT64 getEngine1PreFF() { return get_named_variable_value(Engine1PreFF); } + FLOAT64 getEngine2PreFF() { return get_named_variable_value(Engine2PreFF); } + FLOAT64 getEngine3PreFF() { return get_named_variable_value(Engine3PreFF); } + FLOAT64 getEngine4PreFF() { return get_named_variable_value(Engine4PreFF); } + FLOAT64 getEngineImbalance() { return get_named_variable_value(EngineImbalance); } + FLOAT64 getWAI() { return get_named_variable_value(WingAntiIce); } + FLOAT64 getFuelUsedEngine1() { return get_named_variable_value(FuelUsedEngine1); } + FLOAT64 getFuelUsedEngine2() { return get_named_variable_value(FuelUsedEngine2); } + FLOAT64 getFuelUsedEngine3() { return get_named_variable_value(FuelUsedEngine3); } + FLOAT64 getFuelUsedEngine4() { return get_named_variable_value(FuelUsedEngine4); } + + FLOAT64 getFuelLeftOuterPre() { return get_named_variable_value(FuelLeftOuterPre); } + FLOAT64 getFuelFeedOnePre() { return get_named_variable_value(FuelFeedOnePre); } + FLOAT64 getFuelLeftMidPre() { return get_named_variable_value(FuelLeftMidPre); } + FLOAT64 getFuelLeftInnerPre() { return get_named_variable_value(FuelLeftInnerPre); } + FLOAT64 getFuelFeedTwoPre() { return get_named_variable_value(FuelFeedTwoPre); } + FLOAT64 getFuelFeedThreePre() { return get_named_variable_value(FuelFeedThreePre); } + FLOAT64 getFuelRightInnerPre() { return get_named_variable_value(FuelRightInnerPre); } + FLOAT64 getFuelRightMidPre() { return get_named_variable_value(FuelRightMidPre); } + FLOAT64 getFuelFeedFourPre() { return get_named_variable_value(FuelFeedFourPre); } + FLOAT64 getFuelRightOuterPre() { return get_named_variable_value(FuelRightOuterPre); } + FLOAT64 getFuelTrimPre() { return get_named_variable_value(FuelTrimPre); } + + FLOAT64 getRefuelRate() { return get_named_variable_value(RefuelRate); } + FLOAT64 getRefuelStartedByUser() { return get_named_variable_value(RefuelStartedByUser); } + FLOAT64 getPumpStateEngine1() { return get_named_variable_value(PumpStateEngine1); } + FLOAT64 getPumpStateEngine2() { return get_named_variable_value(PumpStateEngine2); } + FLOAT64 getPumpStateEngine3() { return get_named_variable_value(PumpStateEngine3); } + FLOAT64 getPumpStateEngine4() { return get_named_variable_value(PumpStateEngine4); } + FLOAT64 getPacksState1() { return get_named_variable_value(PacksState1); } + FLOAT64 getPacksState2() { return get_named_variable_value(PacksState2); } + FLOAT64 getThrustLimitType() { return get_named_variable_value(ThrustLimitType); } + + FLOAT64 getCN1(int index) { return aircraft_varget(CorrectedN1, m_Units->Percent, index); } + FLOAT64 getCN2(int index) { return aircraft_varget(CorrectedN2, m_Units->Percent, index); } + FLOAT64 getN1(int index) { return aircraft_varget(N1, m_Units->Percent, index); } + FLOAT64 getN2(int index) { return aircraft_varget(N2, m_Units->Percent, index); } + FLOAT64 getOilPsi(int index) { return aircraft_varget(OilPSI, m_Units->Psi, index); } + FLOAT64 getOilTemp(int index) { return aircraft_varget(OilTemp, m_Units->Celsius, index); } + FLOAT64 getThrust(int index) { return aircraft_varget(Thrust, m_Units->Pounds, index); } + FLOAT64 getEngine1State() { return get_named_variable_value(Engine1State); } + FLOAT64 getEngine2State() { return get_named_variable_value(Engine2State); } + FLOAT64 getEngine3State() { return get_named_variable_value(Engine3State); } + FLOAT64 getEngine4State() { return get_named_variable_value(Engine4State); } + FLOAT64 getEngine1Timer() { return get_named_variable_value(Engine1Timer); } + FLOAT64 getEngine2Timer() { return get_named_variable_value(Engine2Timer); } + FLOAT64 getEngine3Timer() { return get_named_variable_value(Engine3Timer); } + FLOAT64 getEngine4Timer() { return get_named_variable_value(Engine4Timer); } + FLOAT64 getFF(int index) { return aircraft_varget(correctedFF, m_Units->Pph, index); } + FLOAT64 getMach() { return aircraft_varget(AirSpeedMach, m_Units->Mach, 0); } + FLOAT64 getPlaneAltitude() { return aircraft_varget(PlaneAltitude, m_Units->Feet, 0); } + FLOAT64 getPlaneAltitudeAGL() { return aircraft_varget(PlaneAltitudeAGL, m_Units->Feet, 0); } + FLOAT64 getPressureAltitude() { return aircraft_varget(PressureAltitude, m_Units->Feet, 0); } + FLOAT64 getVerticalSpeed() { return aircraft_varget(VerticalSpeed, m_Units->FeetMin, 0); } + FLOAT64 getAmbientTemperature() { return aircraft_varget(AmbientTemp, m_Units->Celsius, 0); } + FLOAT64 getAmbientPressure() { return aircraft_varget(AmbientPressure, m_Units->Millibars, 0); } + FLOAT64 getStdTemperature() { return aircraft_varget(StdTemp, m_Units->Celsius, 0); } + FLOAT64 getSimOnGround() { return aircraft_varget(SimOnGround, m_Units->Bool, 0); } + FLOAT64 getFuelTotalQuantity() { return aircraft_varget(FuelTotalQuantity, m_Units->Gallons, 0); } + FLOAT64 getEmptyWeight() { return aircraft_varget(EmptyWeight, m_Units->Pounds, 0); } + FLOAT64 getTotalWeight() { return aircraft_varget(TotalWeight, m_Units->Pounds, 0); } + FLOAT64 getFuelWeightGallon() { return aircraft_varget(FuelWeightGallon, m_Units->Pounds, 0); } + FLOAT64 getTankFuelQuantity(int index) { return aircraft_varget(TankFuelQuantity, m_Units->Gallons, index); } + FLOAT64 getEngineTime(int index) { return aircraft_varget(EngineTime, m_Units->Seconds, index); } + FLOAT64 getEngineStarter(int index) { return aircraft_varget(EngineStarter, m_Units->Bool, index); } + FLOAT64 getEngineIgniter(int index) { return aircraft_varget(EngineIgniter, m_Units->Number, index); } + FLOAT64 getEngineCombustion(int index) { return aircraft_varget(EngineCombustion, m_Units->Bool, index); } + FLOAT64 getAnimDeltaTime() { return aircraft_varget(animDeltaTime, m_Units->Seconds, 0); } + FLOAT64 getNAI(int index) { return aircraft_varget(NacelleAntiIce, m_Units->Bool, index); } + FLOAT64 getPayloadStationWeight(int index) { return aircraft_varget(PayloadStationWeights, m_Units->Pounds, index); } +}; diff --git a/fbw-a380x/src/wasm/fadec_a380/src/Tables.h b/fbw-a380x/src/wasm/fadec_a380/src/Tables.h new file mode 100644 index 00000000000..eaef2b201ad --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380/src/Tables.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +#include "SimVars.h" +#include "common.h" + +EngineRatios* ratios; + +/// +/// Table 1502 (CN3 vs correctedN1) representations with FSX nomenclature +/// +/// Returns CN3 - correctedN1 pair. +double table1502(int i, int j) { + double t[13][4] = {{16.012, 0.000, 0.000, 17.000}, {19.355, 1.845, 1.845, 17.345}, {22.874, 2.427, 2.427, 18.127}, + {50.147, 12.427, 12.427, 26.627}, {60.000, 18.500, 18.500, 33.728}, {67.742, 25.243, 25.243, 40.082}, + {73.021, 30.505, 30.505, 43.854}, {78.299, 39.779, 39.779, 48.899}, {81.642, 49.515, 49.515, 53.557}, + {85.337, 63.107, 63.107, 63.107}, {87.977, 74.757, 74.757, 74.757}, {97.800, 97.200, 97.200, 97.200}, + {118.000, 115.347, 115.347, 115.347}}; + + return t[i][j]; +} + +/// +/// Calculate expected CN3 at Idle +/// +double iCN3(double pressAltitude, double mach) { + double cn3 = 0; + + cn3 = 60.0 / (std::sqrt((288.15 - (1.98 * pressAltitude / 1000)) / 288.15) * sqrt(1 + (0.2 * powFBW(mach, 2)))); + + return cn3; +} + +/// +/// Calculate expected correctedN1 at Idle +/// +double iCN1(double pressAltitude, double mach, double ambientTemp) { + int i; + double cn1_lo = 0, cn1_hi = 0, cn1 = 0; + double cn3 = iCN3(pressAltitude, mach); + double cell = 0; + double cn3lo = 0, cn3hi = 0; + double cn1lolo = 0, cn1hilo = 0, cn1lohi = 0, cn1hihi = 0; + + for (i = 0; i < 13; i++) { + cell = table1502(i, 0); + if (cell > cn3) { + break; + } + } + + cn3lo = table1502(i - 1, 0); + cn3hi = table1502(i, 0); + + cn1lolo = table1502(i - 1, 1); + cn1hilo = table1502(i, 1); + + if (mach <= 0.2) { + cn1 = interpolate(cn3, cn3lo, cn3hi, cn1lolo, cn1hilo); + } else { + cn1lohi = table1502(i - 1, 3); + cn1hihi = table1502(i, 3); + + cn1_lo = interpolate(cn3, cn3lo, cn3hi, cn1lolo, cn1hilo); + cn1_hi = interpolate(cn3, cn3lo, cn3hi, cn1lohi, cn1hihi); + cn1 = interpolate(mach, 0.2, 0.9, cn1_lo, cn1_hi); + } + + return cn1; +} diff --git a/fbw-a380x/src/wasm/fadec_a380/src/ThrustLimits.h b/fbw-a380x/src/wasm/fadec_a380/src/ThrustLimits.h new file mode 100644 index 00000000000..33008e867f2 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380/src/ThrustLimits.h @@ -0,0 +1,259 @@ +#pragma once + +#include + +#include "SimVars.h" +#include "common.h" +#include "Tables.h" + +double cas2mach(double cas, double ambientPressure) { + double k = 2188648.141; + double delta = ambientPressure / 1013; + double mach = sqrt((5 * pow(((pow(((pow(cas, 2.0) / k) + 1), 3.5) * (1 / delta)) - (1 / delta) + 1), 0.285714286)) - 5); + return mach; +} + +static constexpr double limits[72][6] = { + {-2000, 48.000, 55.000, 81.351, 79.370, 61.535}, {-1000, 46.000, 55.000, 82.605, 80.120, 62.105}, + {0, 44.000, 55.000, 83.832, 80.776, 62.655}, {500, 42.000, 52.000, 84.210, 81.618, 62.655}, + {1000, 42.000, 52.000, 84.579, 81.712, 62.655}, {2000, 40.000, 50.000, 85.594, 82.720, 62.655}, + {3000, 36.000, 48.000, 86.657, 83.167, 61.960}, {4000, 32.000, 46.000, 87.452, 83.332, 61.206}, + {5000, 29.000, 44.000, 88.833, 84.166, 61.206}, {6000, 25.000, 42.000, 90.232, 84.815, 61.206}, + {7000, 21.000, 40.000, 91.711, 85.565, 61.258}, {8000, 17.000, 38.000, 93.247, 86.225, 61.777}, + {9000, 15.000, 36.000, 94.031, 86.889, 60.968}, {10000, 13.000, 34.000, 94.957, 88.044, 60.935}, + {11000, 12.000, 32.000, 95.295, 88.526, 59.955}, {12000, 11.000, 30.000, 95.568, 88.818, 58.677}, + {13000, 10.000, 28.000, 95.355, 88.819, 59.323}, {14000, 10.000, 26.000, 95.372, 89.311, 59.965}, + {15000, 8.000, 24.000, 95.686, 89.907, 58.723}, {16000, 5.000, 22.000, 96.160, 89.816, 57.189}, + {16600, 5.000, 22.000, 96.560, 89.816, 57.189}, {-2000, 47.751, 54.681, 84.117, 81.901, 63.498}, + {-1000, 45.771, 54.681, 85.255, 82.461, 63.920}, {0, 43.791, 54.681, 86.411, 83.021, 64.397}, + {500, 42.801, 52.701, 86.978, 83.740, 64.401}, {1000, 41.811, 52.701, 87.568, 83.928, 64.525}, + {2000, 38.841, 50.721, 88.753, 84.935, 64.489}, {3000, 36.861, 48.741, 89.930, 85.290, 63.364}, + {4000, 32.901, 46.761, 91.004, 85.836, 62.875}, {5000, 28.941, 44.781, 92.198, 86.293, 62.614}, + {6000, 24.981, 42.801, 93.253, 86.563, 62.290}, {7000, 21.022, 40.821, 94.273, 86.835, 61.952}, + {8000, 17.062, 38.841, 94.919, 87.301, 62.714}, {9000, 15.082, 36.861, 95.365, 87.676, 61.692}, + {10000, 13.102, 34.881, 95.914, 88.150, 60.906}, {11000, 12.112, 32.901, 96.392, 88.627, 59.770}, + {12000, 11.122, 30.921, 96.640, 89.206, 58.933}, {13000, 10.132, 28.941, 96.516, 89.789, 60.503}, + {14000, 9.142, 26.961, 96.516, 90.475, 62.072}, {15000, 9.142, 24.981, 96.623, 90.677, 59.333}, + {16000, 7.162, 23.001, 96.845, 90.783, 58.045}, {16600, 5.182, 21.022, 97.366, 91.384, 58.642}, + {-2000, 30.800, 56.870, 80.280, 72.000, 0.000}, {2000, 20.990, 48.157, 82.580, 74.159, 0.000}, + {5000, 16.139, 43.216, 84.642, 75.737, 0.000}, {8000, 7.342, 38.170, 86.835, 77.338, 0.000}, + {10000, 4.051, 34.518, 88.183, 77.999, 0.000}, {10000.1, 4.051, 34.518, 87.453, 77.353, 0.000}, + {12000, 0.760, 30.865, 88.303, 78.660, 0.000}, {15000, -4.859, 25.039, 89.748, 79.816, 0.000}, + {17000, -9.934, 19.813, 90.668, 80.895, 0.000}, {20000, -15.822, 13.676, 92.106, 81.894, 0.000}, + {24000, -22.750, 6.371, 93.651, 82.716, 0.000}, {27000, -29.105, -0.304, 93.838, 83.260, 0.000}, + {29314, -32.049, -3.377, 93.502, 82.962, 0.000}, {31000, -34.980, -6.452, 95.392, 84.110, 0.000}, + {35000, -45.679, -17.150, 96.104, 85.248, 0.000}, {39000, -45.679, -17.150, 96.205, 84.346, 0.000}, + {41500, -45.679, -17.150, 95.676, 83.745, 0.000}, {-1000, 26.995, 54.356, 82.465, 74.086, 0.000}, + {3000, 18.170, 45.437, 86.271, 77.802, 0.000}, {7000, 9.230, 40.266, 89.128, 79.604, 0.000}, + {11000, 4.019, 31.046, 92.194, 82.712, 0.000}, {15000, -5.226, 21.649, 95.954, 85.622, 0.000}, + {17000, -9.913, 20.702, 97.520, 85.816, 0.000}, {20000, -15.129, 15.321, 99.263, 86.770, 0.000}, + {22000, -19.947, 10.382, 98.977, 86.661, 0.000}, {25000, -25.397, 4.731, 98.440, 85.765, 0.000}, + {27000, -30.369, -0.391, 97.279, 85.556, 0.000}, {31000, -36.806, -7.165, 98.674, 86.650, 0.000}, + {35000, -43.628, -14.384, 98.386, 85.747, 0.000}, {39000, -47.286, -18.508, 97.278, 85.545, 0.000}}; + +/// +/// Finds top-row boundary in an array +/// +int finder(double altitude, int index) { + if (altitude < limits[index][0]) { + return index; + } else { + return finder(altitude, index + 1); + } +} + +/// +/// Calculates Bleed Air situation for engine adaptation +/// +double bleedTotal(int type, double altitude, double oat, double cp, double lp, double flexTemp, double ac, double nacelle, double wing) { + double n1Packs = 0; + double n1Nai = 0; + double n1Wai = 0; + double bleed = 0; + + if (flexTemp > lp && type <= 1) { + n1Packs = -0.6; + n1Nai = -0.7; + n1Wai = -0.7; + } else { + switch (type) { + case 0: + if (altitude < 8000) { + if (oat < cp) { + n1Packs = -0.4; + } else { + n1Packs = -0.5; + n1Nai = -0.6; + n1Wai = -0.7; + } + } else { + if (oat < cp) { + n1Packs = -0.6; + } else { + n1Packs = -0.7; + n1Nai = -0.8; + n1Wai = -0.8; + } + } + break; + case 1: + if (altitude < 8000) { + if (oat < cp) { + n1Packs = -0.4; + } else { + n1Packs = -0.4; + n1Nai = -0.6; + n1Wai = -0.6; + } + } else { + if (oat < cp) { + n1Packs = -0.6; + } else { + n1Packs = -0.6; + n1Nai = -0.7; + n1Wai = -0.8; + } + } + break; + case 2: + if (oat < cp) { + n1Packs = -0.2; + } else { + n1Packs = -0.3; + n1Nai = -0.8; + n1Wai = -0.4; + } + break; + case 3: + if (oat < cp) { + n1Packs = -0.6; + } else { + n1Packs = -0.6; + n1Nai = -0.9; + n1Wai = -1.2; + } + break; + } + } + + if (ac == 0) { + n1Packs = 0; + } + if (nacelle == 0) { + n1Nai = 0; + } + if (wing == 0) { + n1Wai = 0; + } + + bleed = n1Packs + n1Nai + n1Wai; + + return bleed; +} + +/// +/// Main N1 Limit Function +/// +/// 0-TO, 1-GA, 2-CLB, 3-MCT +/// +double +limitN1(int type, double altitude, double ambientTemp, double ambientPressure, double flexTemp, double ac, double nacelle, double wing) { + int rowMin = 0; + int rowMax = 0; + int loAltRow = 0; + int hiAltRow = 0; + double mach = 0; + double cp = 0; + double lp = 0; + double cn1 = 0; + double n1 = 0; + double cn1Flat = 0; + double cn1Last = 0; + double cn1Flex = 0; + double m = 0; + double b = 0; + double bleed = 0; + + // Set main variables per Limit Type + switch (type) { + case 0: + rowMin = 0; + rowMax = 20; + mach = 0; + break; + case 1: + rowMin = 21; + rowMax = 41; + mach = 0.225; + break; + case 2: + rowMin = 42; + rowMax = 58; + if (altitude <= 10000) { + mach = cas2mach(250, ambientPressure); + } else { + mach = cas2mach(300, ambientPressure); + if (mach > 0.78) + mach = 0.78; + } + break; + case 3: + rowMin = 59; + rowMax = 71; + mach = cas2mach(230, ambientPressure); + break; + } + + // Check for over/ underflows. Else, find top row value + if (altitude <= limits[rowMin][0]) { + hiAltRow = rowMin; + loAltRow = rowMin; + } else if (altitude >= limits[rowMax][0]) { + hiAltRow = rowMax; + loAltRow = rowMax; + } else { + hiAltRow = finder(altitude, rowMin); + loAltRow = hiAltRow - 1; + } + + // Define key table variables and interpolation + cp = interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][1], limits[hiAltRow][1]); + lp = interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][2], limits[hiAltRow][2]); + cn1Flat = interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][3], limits[hiAltRow][3]); + cn1Last = interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][4], limits[hiAltRow][4]); + cn1Flex = interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][5], limits[hiAltRow][5]); + + if (flexTemp > 0 && type <= 1) { // CN1 for Flex Case + if (flexTemp <= cp) { + cn1 = cn1Flat; + } else if (flexTemp > lp) { + m = (cn1Flex - cn1Last) / (100 - lp); + b = cn1Flex - m * 100; + cn1 = (m * flexTemp) + b; + } else { + m = (cn1Last - cn1Flat) / (lp - cp); + b = cn1Last - m * lp; + cn1 = (m * flexTemp) + b; + } + } + else { // CN1 for All other cases + if (ambientTemp <= cp) { + cn1 = cn1Flat; + } else { + m = (cn1Last - cn1Flat) / (lp - cp); + b = cn1Last - m * lp; + cn1 = (m * ambientTemp) + b; + } + } + + // Define bleed rating/ derating + bleed = bleedTotal(type, altitude, ambientTemp, cp, lp, flexTemp, ac, nacelle, wing); + + // Setting N1 + n1 = (cn1 * sqrt(ratios->theta2(mach, ambientTemp))) + bleed; + /*if (type == 3) { + std::cout << "FADEC: bleed= " << bleed << " cn1= " << cn1 << " theta2= " << sqrt(ratios->theta2(mach, ambientTemp)) + << " n1= " << n1 << std::endl; + }*/ + return n1; +} diff --git a/fbw-a380x/src/wasm/fadec_a380x/CMakeLists.txt b/fbw-a380x/src/wasm/fadec_a380x/CMakeLists.txt deleted file mode 100644 index a6f0c0d2e5a..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -# flybywire-a380x-fadec-v2 CMakeLists.txt - -# add additional compiler definitions for the a380x fadec-v2 build -#add_definitions( ) - -# add the local include directories -include_directories( - ${CMAKE_CURRENT_SOURCE_DIR}/src - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec - ${FBW_COMMON}/cpp-msfs-framework/ - ${FBW_COMMON}/fadec_common/src/ -) - -# define the source files -set(SOURCE_FILES - ${FBW_COMMON}/fadec_common/src/Fadec.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Gauge_Fadec_v2.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/Fadec_A380X.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/EngineControl_A380X.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/FuelConfiguration_A380X.cpp -) - -set(INCLUDE_FILES - ${FBW_COMMON}/fadec_common/src/Fadec.h - ${FBW_COMMON}/fadec_common/src/EngineRatios.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/Fadec_A380X.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/EngineControl_A380X.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/FuelConfiguration_A380X.h - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/FadecSimData_A380X.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/ThrustLimits_A380X.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/Polynomials_A380X.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Fadec/Table1502_A380X.hpp -) - -# create the targets -add_library(fadec-a380x OBJECT ${SOURCE_FILES} ${INCLUDE_FILES}) -add_wasm_library( - NAME fadec-a380x - DEPENDENCIES fadec-a380x cpp-msfs-framework-a380x -) - diff --git a/fbw-a380x/src/wasm/fadec_a380x/README.md b/fbw-a380x/src/wasm/fadec_a380x/README.md deleted file mode 100644 index b3d2525a9f8..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# A380X FADEC - -This is a new version of the FADEC system for the A380X. -It is a migration and cleanup of the original FADEC system, -and is designed to be more modular and easier to maintain. diff --git a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/EngineControl_A380X.cpp b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/EngineControl_A380X.cpp deleted file mode 100644 index 87de1e3a55d..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/EngineControl_A380X.cpp +++ /dev/null @@ -1,1090 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#include "logging.h" -#ifdef PROFILING -#include "ScopedTimer.hpp" -#include "SimpleProfiler.hpp" -#endif - -#include "EngineControl_A380X.h" -#include "EngineRatios.hpp" -#include "Polynomials_A380X.hpp" -#include "Table1502_A380X.hpp" -#include "ThrustLimits_A380X.hpp" - -void EngineControl_A380X::initialize(MsfsHandler* msfsHandler) { - this->msfsHandlerPtr = msfsHandler; - this->dataManagerPtr = &msfsHandler->getDataManager(); - this->simData.initialize(dataManagerPtr); - LOG_INFO("Fadec::EngineControl_A380X::initialize() - initialized"); -} - -void EngineControl_A380X::shutdown() { - LOG_INFO("Fadec::EngineControl_A380X::shutdown()"); -} - -void EngineControl_A380X::update(sGaugeDrawData* pData) { -#ifdef PROFILING - profilerUpdate.start(); -#endif - - // Get ATC ID from sim to be able to load and store fuel levels - // If not yet available, request it from sim and return early - // If available initialize the engine control data - if (atcId.empty()) { - simData.atcIdDataPtr->requestUpdateFromSim(msfsHandlerPtr->getTimeStamp(), msfsHandlerPtr->getTickCounter()); - if (simData.atcIdDataPtr->hasChanged()) { - atcId = simData.atcIdDataPtr->data().atcID; - LOG_INFO("Fadec::EngineControl_A380X::update() - received ATC ID: " + atcId); - initializeEngineControlData(); - } - return; - } - - const double deltaTime = pData->dt; - const double mach = simData.simVarsDataPtr->data().airSpeedMach; - const double pressureAltitude = simData.simVarsDataPtr->data().pressureAltitude; - const double ambientTemperature = simData.simVarsDataPtr->data().ambientTemperature; - const double ambientPressure = simData.simVarsDataPtr->data().ambientPressure; - const double idleN3 = simData.engineIdleN3->get(); - - generateIdleParameters(pressureAltitude, mach, ambientTemperature, ambientPressure); - - // Update engine states - for (int engine = 1; engine <= 4; engine++) { - const int engineIdx = engine - 1; - - const bool engineStarter = static_cast(simData.simVarsDataPtr->data().engineStarter[engineIdx]); - const int engineIgniter = static_cast(simData.simVarsDataPtr->data().engineIgniter[engineIdx]); - - // determine the current engine state based on the previous state and the current ignition, starter and other parameters - // also resets the engine timer if the engine is starting or restarting - EngineState engineState = engineStateMachine(engine, // - engineIgniter, // - engineStarter, // - prevSimEngineN3[engineIdx], // - idleN3, // - ambientTemperature); // - - const bool simOnGround = msfsHandlerPtr->getSimOnGround(); - const double engineTimer = simData.engineTimer[engineIdx]->get(); - const double simCN1 = simData.simVarsDataPtr->data().simEngineCorrectedN1[engineIdx]; - const double simN1 = simData.simVarsDataPtr->data().simEngineN1[engineIdx]; - const double simN3 = simData.simVarsDataPtr->data().simEngineN2[engineIdx]; // as the sim does not have N3, we use N2 - prevSimEngineN3[engineIdx] = simN3; - - // Update various engine values based on the current engine state - switch (static_cast(engineState)) { - case STARTING: - case RESTARTING: - engineStartProcedure(engine, engineState, deltaTime, engineTimer, simN3, ambientTemperature); - break; - case SHUTTING: - engineShutdownProcedure(engine, deltaTime, engineTimer, simN1, ambientTemperature); - updateFF(engine, simCN1, mach, pressureAltitude, ambientTemperature, ambientPressure); - break; - default: - updatePrimaryParameters(engine, simN1, simN3); - double correctedFuelFlow = updateFF(engine, simCN1, mach, pressureAltitude, ambientTemperature, ambientPressure); - updateEGT(engine, engineState, deltaTime, simCN1, correctedFuelFlow, mach, pressureAltitude, ambientTemperature, simOnGround); - // TODO: Oil to be implemented - // The following call is commented out because it was not yet implemented/working in the original code - // The function is at the end of this files in its original form - // updateOil(engine, imbalance, thrust, simN2, deltaN2, deltaTime, ambientTemperature); - break; - } - } - - // Update fuel & tank data - updateFuel(deltaTime); - - // Update thrust limits while considering the current bleed air settings (packs, nai, wai) - const int packs = (simData.packsState[1]->get() > 0.5 || simData.packsState[2]->get() > 0.5) ? 1 : 0; - const int nai = (simData.simVarsDataPtr->data().engineAntiIce[E1] > 0.5 // - || simData.simVarsDataPtr->data().engineAntiIce[E2] > 0.5 // - || simData.simVarsDataPtr->data().engineAntiIce[E3] > 0.5 // - || simData.simVarsDataPtr->data().engineAntiIce[E4] > 0.5) - ? 1 - : 0; - const int wai = simData.wingAntiIce->getAsInt64(); - updateThrustLimits(msfsHandlerPtr->getSimulationTime(), pressureAltitude, ambientTemperature, ambientPressure, mach, packs, nai, wai); - -#ifdef PROFILING - profilerUpdate.stop(); - if (msfsHandlerPtr->getTickCounter() % 100 == 0) { - profilerUpdateThrustLimits.print(); - profilerUpdateFuel.print(); - profilerUpdateEGT.print(); - profilerUpdateFF.print(); - profilerEngineShutdownProcedure.print(); - profilerEngineStartProcedure.print(); - profilerUpdatePrimaryParameters.print(); - profilerEngineStateMachine.print(); - profilerUpdate.print(); - } -#endif -} - -// ===================================================================================================================== -// Private methods -// ===================================================================================================================== - -void EngineControl_A380X::initializeEngineControlData() { - LOG_INFO("Fadec::EngineControl_A380X::initializeEngineControlData()"); - -#ifdef PROFILING - ScopedTimer timer("Fadec::EngineControl_A380X::initializeEngineControlData()"); -#endif - - const FLOAT64 timeStamp = msfsHandlerPtr->getTimeStamp(); - const UINT64 tickCounter = msfsHandlerPtr->getTickCounter(); - - // Getting and saving initial N2 into pre (= previous) variables - prevSimEngineN3[0] = simData.simVarsDataPtr->data().simEngineN2[0]; - prevSimEngineN3[1] = simData.simVarsDataPtr->data().simEngineN2[1]; - prevSimEngineN3[2] = simData.simVarsDataPtr->data().simEngineN2[2]; - prevSimEngineN3[3] = simData.simVarsDataPtr->data().simEngineN2[3]; - - // Setting initial Oil Quantity and adding some randomness to it - std::srand(std::time(0)); - simData.engineOilTotal[E1]->set((std::rand() % (MAX_OIL - MIN_OIL + 1) + MIN_OIL) / 10); - simData.engineOilTotal[E2]->set((std::rand() % (MAX_OIL - MIN_OIL + 1) + MIN_OIL) / 10); - simData.engineOilTotal[E3]->set((std::rand() % (MAX_OIL - MIN_OIL + 1) + MIN_OIL) / 10); - simData.engineOilTotal[E4]->set((std::rand() % (MAX_OIL - MIN_OIL + 1) + MIN_OIL) / 10); - - // Setting initial Oil Temperature - const bool simOnGround = msfsHandlerPtr->getSimOnGround(); - - const bool engine1Combustion = static_cast(simData.engineCombustion[E1]->updateFromSim(timeStamp, tickCounter)); - const bool engine2Combustion = static_cast(simData.engineCombustion[E2]->updateFromSim(timeStamp, tickCounter)); - const bool engine3Combustion = static_cast(simData.engineCombustion[E3]->updateFromSim(timeStamp, tickCounter)); - const bool engine4Combustion = static_cast(simData.engineCombustion[E4]->updateFromSim(timeStamp, tickCounter)); - - double oilTemperaturePre[4]; - if (simOnGround == 1 && engine1Combustion == 1 && engine2Combustion == 1 && engine3Combustion == 1 && engine4Combustion == 1) { - oilTemperaturePre[E1] = 75; - oilTemperaturePre[E2] = 75; - oilTemperaturePre[E3] = 75; - oilTemperaturePre[E4] = 75; - } else if (simOnGround == 0 && engine1Combustion == 1 && engine2Combustion == 1 && engine3Combustion == 1 && engine4Combustion == 1) { - oilTemperaturePre[E1] = 85; - oilTemperaturePre[E2] = 85; - oilTemperaturePre[E3] = 85; - oilTemperaturePre[E4] = 85; - } else { - const double ambientTemperature = simData.simVarsDataPtr->data().ambientTemperature; - oilTemperaturePre[E1] = ambientTemperature; - oilTemperaturePre[E2] = ambientTemperature; - oilTemperaturePre[E3] = ambientTemperature; - oilTemperaturePre[E4] = ambientTemperature; - } - simData.oilTempDataPtr[E1]->data().oilTemp = oilTemperaturePre[E1]; - simData.oilTempDataPtr[E1]->writeDataToSim(); - simData.oilTempDataPtr[E2]->data().oilTemp = oilTemperaturePre[E2]; - simData.oilTempDataPtr[E2]->writeDataToSim(); - simData.oilTempDataPtr[E3]->data().oilTemp = oilTemperaturePre[E3]; - simData.oilTempDataPtr[E3]->writeDataToSim(); - simData.oilTempDataPtr[E4]->data().oilTemp = oilTemperaturePre[E4]; - simData.oilTempDataPtr[E4]->writeDataToSim(); - - // Setting initial Engine State - simData.engineState[E1]->set(OFF); - simData.engineState[E2]->set(OFF); - simData.engineState[E3]->set(OFF); - simData.engineState[E4]->set(OFF); - - // Setting initial Engine Timer - simData.engineTimer[E1]->set(0); - simData.engineTimer[E2]->set(0); - simData.engineTimer[E3]->set(0); - simData.engineTimer[E4]->set(0); - - // Setting initial Fuel Levels - const double weightLbsPerGallon = simData.simVarsDataPtr->data().fuelWeightLbsPerGallon; - - // only loads saved fuel quantity on C/D spawn - if (simData.startState->updateFromSim(timeStamp, tickCounter) == 2) { - // Load fuel configuration from file - fuelConfiguration.setConfigFilename(FILENAME_FADEC_CONF_DIRECTORY + atcId + FILENAME_FADEC_CONF_FILE_EXTENSION); - fuelConfiguration.loadConfigurationFromIni(); - - simData.fuelLeftOuterPre->set(fuelConfiguration.getFuelLeftOuterGallons() * weightLbsPerGallon); - simData.fuelFeedOnePre->set(fuelConfiguration.getFuelFeedOneGallons() * weightLbsPerGallon); - simData.fuelLeftMidPre->set(fuelConfiguration.getFuelLeftMidGallons() * weightLbsPerGallon); - simData.fuelLeftInnerPre->set(fuelConfiguration.getFuelLeftInnerGallons() * weightLbsPerGallon); - simData.fuelFeedTwoPre->set(fuelConfiguration.getFuelFeedTwoGallons() * weightLbsPerGallon); - simData.fuelFeedThreePre->set(fuelConfiguration.getFuelFeedThreeGallons() * weightLbsPerGallon); - simData.fuelRightInnerPre->set(fuelConfiguration.getFuelRightInnerGallons() * weightLbsPerGallon); - simData.fuelRightMidPre->set(fuelConfiguration.getFuelRightMidGallons() * weightLbsPerGallon); - simData.fuelFeedFourPre->set(fuelConfiguration.getFuelFeedFourGallons() * weightLbsPerGallon); - simData.fuelRightOuterPre->set(fuelConfiguration.getFuelRightOuterGallons() * weightLbsPerGallon); - simData.fuelTrimPre->set(fuelConfiguration.getFuelTrimGallons() * weightLbsPerGallon); - - // set fuel levels from configuration to the sim - simData.fuelFeedTankDataPtr->data().fuelSystemFeedOne = fuelConfiguration.getFuelFeedOneGallons(); - simData.fuelFeedTankDataPtr->data().fuelSystemFeedTwo = fuelConfiguration.getFuelFeedTwoGallons(); - simData.fuelFeedTankDataPtr->data().fuelSystemFeedThree = fuelConfiguration.getFuelFeedThreeGallons(); - simData.fuelFeedTankDataPtr->data().fuelSystemFeedFour = fuelConfiguration.getFuelFeedFourGallons(); - simData.fuelFeedTankDataPtr->writeDataToSim(); - simData.fuelTankDataPtr->data().fuelSystemLeftOuter = fuelConfiguration.getFuelLeftOuterGallons(); - simData.fuelTankDataPtr->data().fuelSystemLeftMid = fuelConfiguration.getFuelLeftMidGallons(); - simData.fuelTankDataPtr->data().fuelSystemLeftInner = fuelConfiguration.getFuelLeftInnerGallons(); - simData.fuelTankDataPtr->data().fuelSystemRightInner = fuelConfiguration.getFuelRightInnerGallons(); - simData.fuelTankDataPtr->data().fuelSystemRightMid = fuelConfiguration.getFuelRightMidGallons(); - simData.fuelTankDataPtr->data().fuelSystemRightOuter = fuelConfiguration.getFuelRightOuterGallons(); - simData.fuelTankDataPtr->data().fuelSystemTrim = fuelConfiguration.getFuelTrimGallons(); - simData.fuelTankDataPtr->writeDataToSim(); - } - // on a non C/D spawn, set fuel levels from the sim - else { - simData.fuelLeftOuterPre->set(simData.fuelTankDataPtr->data().fuelSystemLeftOuter * weightLbsPerGallon); - simData.fuelFeedOnePre->set(simData.fuelFeedTankDataPtr->data().fuelSystemFeedOne * weightLbsPerGallon); - simData.fuelLeftMidPre->set(simData.fuelTankDataPtr->data().fuelSystemLeftMid * weightLbsPerGallon); - simData.fuelLeftInnerPre->set(simData.fuelTankDataPtr->data().fuelSystemLeftInner * weightLbsPerGallon); - simData.fuelFeedTwoPre->set(simData.fuelFeedTankDataPtr->data().fuelSystemFeedTwo * weightLbsPerGallon); - simData.fuelFeedThreePre->set(simData.fuelFeedTankDataPtr->data().fuelSystemFeedThree * weightLbsPerGallon); - simData.fuelRightInnerPre->set(simData.fuelTankDataPtr->data().fuelSystemRightInner * weightLbsPerGallon); - simData.fuelRightMidPre->set(simData.fuelTankDataPtr->data().fuelSystemRightMid * weightLbsPerGallon); - simData.fuelFeedFourPre->set(simData.fuelFeedTankDataPtr->data().fuelSystemFeedFour * weightLbsPerGallon); - simData.fuelRightOuterPre->set(simData.fuelTankDataPtr->data().fuelSystemRightOuter * weightLbsPerGallon); - simData.fuelTrimPre->set(simData.fuelTankDataPtr->data().fuelSystemTrim * weightLbsPerGallon); - } - - // Setting initial Fuel Flow - simData.fuelPumpState[E1]->set(0); - simData.fuelPumpState[E2]->set(0); - simData.fuelPumpState[E3]->set(0); - simData.fuelPumpState[E4]->set(0); - - // Setting initial Thrust Limits - simData.thrustLimitIdle->set(0); - simData.thrustLimitClimb->set(0); - simData.thrustLimitFlex->set(0); - simData.thrustLimitMct->set(0); - simData.thrustLimitToga->set(0); -} - -void EngineControl_A380X::generateIdleParameters(double pressAltitude, double mach, double ambientTemperature, double ambientPressure) { - const double idleCN1 = Table1502_A380X::iCN1(pressAltitude, mach, ambientTemperature); - const double idleN1 = idleCN1 * sqrt(EngineRatios::theta2(0, ambientTemperature)); - const double idleN3 = Table1502_A380X::iCN3(pressAltitude, mach) * sqrt(EngineRatios::theta(ambientTemperature)); - const double idleCFF = Polynomial_A380X::correctedFuelFlow(idleCN1, 0, pressAltitude); - const double idleFF = - idleCFF * Fadec::LBS_TO_KGS * EngineRatios::delta2(0, ambientPressure) * sqrt(EngineRatios::theta2(0, ambientTemperature)); - const double idleEGT = Polynomial_A380X::correctedEGT(idleCN1, idleCFF, 0, pressAltitude) * EngineRatios::theta2(0, ambientTemperature); - - simData.engineIdleN1->set(idleN1); - simData.engineIdleN3->set(idleN3); - simData.engineIdleFF->set(idleFF); - simData.engineIdleEGT->set(idleEGT); -} - -EngineControl_A380X::EngineState EngineControl_A380X::engineStateMachine(int engine, - int engineIgniter, - bool engineStarter, - double simN3, - double idleN3, - double ambientTemperature) { -#ifdef PROFILING - profilerEngineStateMachine.start(); -#endif - - const int engineIdx = engine - 1; - - bool resetTimer = false; - - EngineState engineState = static_cast(simData.engineState[engineIdx]->get()); - - // Current State: OFF - if (engineState == OFF) { - if (engineIgniter == 1 && engineStarter && simN3 > 20) { - engineState = ON; - } else if (engineIgniter == 2 && engineStarter) { - engineState = STARTING; - } else { - engineState = OFF; - } - } - // Current State: ON - else if (engineState == ON) { - if (engineStarter) { - engineState = ON; - } else { - engineState = SHUTTING; - } - } - // Current State: Starting. - else if (engineState == STARTING) { - if (engineStarter && simN3 >= (idleN3 - 0.1)) { - engineState = ON; - resetTimer = true; - } else if (!engineStarter) { - engineState = SHUTTING; - resetTimer = true; - } else { - engineState = STARTING; - } - } - // Current State: Re-Starting. - else if (engineState == RESTARTING) { - if (engineStarter && simN3 >= (idleN3 - 0.1)) { - engineState = ON; - resetTimer = true; - } else if (!engineStarter) { - engineState = SHUTTING; - resetTimer = true; - } else { - engineState = RESTARTING; - } - } - // Current State: Shutting - else if (engineState == SHUTTING) { - if (engineIgniter == 2 && engineStarter) { - engineState = RESTARTING; - resetTimer = true; - } else if (!engineStarter && simN3 < 0.05 && simData.engineEgt[engineIdx]->get() <= ambientTemperature) { - engineState = OFF; - resetTimer = true; - } else if (engineStarter == 1 && simN3 > 50) { - engineState = RESTARTING; - resetTimer = true; - } else { - engineState = SHUTTING; - } - } - - simData.engineState[engineIdx]->set(static_cast(engineState)); - if (resetTimer) { - simData.engineTimer[engineIdx]->set(0); - } - -#ifdef PROFILING - profilerEngineStateMachine.stop(); -#endif - - return static_cast(engineState); -} - -void EngineControl_A380X::engineStartProcedure(int engine, - EngineState engineState, - double deltaTime, - double engineTimer, - double simN3, - double ambientTemperature) { -#ifdef PROFILING - profilerEngineStartProcedure.start(); -#endif - - const int engineIdx = engine - 1; - - const double idleN1 = simData.engineIdleN1->get(); - const double idleN3 = simData.engineIdleN3->get(); - const double idleFF = simData.engineIdleFF->get(); - const double idleEGT = simData.engineIdleEGT->get(); - - // delay to simulate the delay between master-switch setting and actual engine start - if (engineTimer < 1.7) { - if (msfsHandlerPtr->getSimOnGround()) { - simData.engineFuelUsed[engineIdx]->set(0); - } - simData.engineTimer[engineIdx]->set(engineTimer + deltaTime); - simData.engineCorrectedN3DataPtr[engineIdx]->data().correctedN3 = 0; - simData.engineCorrectedN3DataPtr[engineIdx]->writeDataToSim(); - } - // engine start procedure after the delay - else { - const double preN3Fbw = simData.engineN3[engineIdx]->get(); - const double preEgtFbw = simData.engineEgt[engineIdx]->get(); - const double newN3Fbw = Polynomial_A380X::startN3(simN3, preN3Fbw, idleN3); - - const double startN1Fbw = Polynomial_A380X::startN1(newN3Fbw, idleN3, idleN1); - const double startFfFbw = Polynomial_A380X::startFF(newN3Fbw, idleN3, idleFF); - const double startEgtFbw = Polynomial_A380X::startEGT(newN3Fbw, idleN3, ambientTemperature, idleEGT); - - const double shutdownEgtFbw = Polynomial_A380X::shutdownEGT(preEgtFbw, ambientTemperature, deltaTime); - - simData.engineN3[engineIdx]->set(newN3Fbw); - simData.engineN2[engineIdx]->set(newN3Fbw + 0.7); // 0.7 seems to be an arbitrary offset to get N2 from N3 - simData.engineN1[engineIdx]->set(startN1Fbw); - simData.engineFF[engineIdx]->set(startFfFbw); - - if (engineState == RESTARTING) { - if ((std::abs)(startEgtFbw - preEgtFbw) <= 1.5) { - simData.engineEgt[engineIdx]->set(startEgtFbw); - simData.engineState[engineIdx]->set(STARTING); - } else if (startEgtFbw > preEgtFbw) { - // calculation and constant values unclear in original code - simData.engineEgt[engineIdx]->set(preEgtFbw + (0.75 * deltaTime * (idleN3 - newN3Fbw))); - } else { - simData.engineEgt[engineIdx]->set(shutdownEgtFbw); - } - } else { - simData.engineEgt[engineIdx]->set(startEgtFbw); - } - - simData.oilTempDataPtr[engineIdx]->data().oilTemp = Polynomial_A380X::startOilTemp(newN3Fbw, idleN3, ambientTemperature); - simData.oilTempDataPtr[engineIdx]->writeDataToSim(); - } - -#ifdef PROFILING - profilerEngineStartProcedure.stop(); -#endif -} - -// Original comment: Engine Shutdown Procedure - TEMPORARY SOLUTION -void EngineControl_A380X::engineShutdownProcedure(int engine, - double deltaTime, - double engineTimer, - double simN1, - double ambientTemperature) { -#ifdef PROFILING - profilerEngineShutdownProcedure.start(); -#endif - - const int engineIdx = engine - 1; - - // delay to simulate the delay between master-switch setting and actual engine shutdown - if (engineTimer < 1.8) { - simData.engineTimer[engineIdx]->set(engineTimer + deltaTime); - } else { - const double preN1Fbw = simData.engineN1[engineIdx]->get(); - const double preN3Fbw = simData.engineN3[engineIdx]->get(); - const double preEgtFbw = simData.engineEgt[engineIdx]->get(); - - double newN1Fbw = Polynomial_A380X::shutdownN1(preN1Fbw, deltaTime); - if (simN1 < 5 && simN1 > newN1Fbw) { // Takes care of windmilling - newN1Fbw = simN1; - } - const double newN3Fbw = Polynomial_A380X::shutdownN3(preN3Fbw, deltaTime); - const double newEgtFbw = Polynomial_A380X::shutdownEGT(preEgtFbw, ambientTemperature, deltaTime); - - simData.engineN1[engineIdx]->set(newN1Fbw); - simData.engineN2[engineIdx]->set(newN3Fbw + 0.7); - simData.engineN3[engineIdx]->set(newN3Fbw); - simData.engineEgt[engineIdx]->set(newEgtFbw); - } - -#ifdef PROFILING - profilerEngineShutdownProcedure.stop(); -#endif -} - -int EngineControl_A380X::updateFF(int engine, - double simCN1, - double mach, - double pressureAltitude, - double ambientTemperature, - double ambientPressure) { -#ifdef PROFILING - profilerUpdateFF.start(); -#endif - - const double correctedFuelFlow = Polynomial_A380X::correctedFuelFlow(simCN1, mach, pressureAltitude); // in lbs/hr. - - // Checking Fuel Logic and final Fuel Flow - double outFlow = 0; // kg/hour - if (correctedFuelFlow >= 1) { - outFlow = (std::max)(0.0, // - (correctedFuelFlow * Fadec::LBS_TO_KGS * EngineRatios::delta2(mach, ambientPressure) // - * (std::sqrt)(EngineRatios::theta2(mach, ambientTemperature)))); - } - simData.engineFF[engine - 1]->set(outFlow); - -#ifdef PROFILING - profilerUpdateFF.stop(); -#endif - - return correctedFuelFlow; -} - -void EngineControl_A380X::updatePrimaryParameters(int engine, double simN1, double simN3) { -#ifdef PROFILING - profilerUpdatePrimaryParameters.start(); -#endif - - const int engineIdx = engine - 1; - - simData.engineN1[engineIdx]->set(simN1); - simData.engineN2[engineIdx]->set(simN3 > 0 ? simN3 + 0.7 : simN3); - simData.engineN3[engineIdx]->set(simN3); - -#ifdef PROFILING - profilerUpdatePrimaryParameters.stop(); -#endif -} - -void EngineControl_A380X::updateEGT(int engine, - double engineState, - double deltaTime, - double simCN1, - int correctedFuelFlow, - const double mach, - const double pressureAltitude, - const double ambientTemperature, - bool simOnGround) { -#ifdef PROFILING - profilerUpdateEGT.start(); -#endif - - const int engineIdx = engine - 1; - - if (simOnGround && engineState == 0) { - simData.engineEgt[engineIdx]->set(ambientTemperature); - } else { - const double correctedEGT = Polynomial_A380X::correctedEGT(simCN1, correctedFuelFlow, mach, pressureAltitude); - const double egtFbwPrevious = simData.engineEgt[engineIdx]->get(); - double egtFbwActualEng = (correctedEGT * EngineRatios::theta2(mach, ambientTemperature)); - egtFbwActualEng = egtFbwActualEng + (egtFbwPrevious - egtFbwActualEng) * (std::exp)(-0.1 * deltaTime); - simData.engineEgt[engineIdx]->set(egtFbwActualEng); - } - -#ifdef PROFILING - profilerUpdateEGT.stop(); -#endif -} - -void EngineControl_A380X::updateFuel(double deltaTimeSeconds) { -#ifdef PROFILING - profilerUpdateFuel.start(); -#endif - - bool uiFuelTamper = false; - - const double engine1PreFF = simData.enginePreFF[E1]->get(); - const double engine2PreFF = simData.enginePreFF[E2]->get(); - const double engine3PreFF = simData.enginePreFF[E3]->get(); - const double engine4PreFF = simData.enginePreFF[E4]->get(); - - const double engine1FF = simData.engineFF[E1]->get(); // kg/hour - const double engine2FF = simData.engineFF[E2]->get(); // kg/hour - const double engine3FF = simData.engineFF[E3]->get(); // kg/hour - const double engine4FF = simData.engineFF[E4]->get(); // kg/hour - - /// weight of one gallon of fuel in pounds - const double weightLbsPerGallon = simData.simVarsDataPtr->data().fuelWeightLbsPerGallon; - - double fuelLeftOuterPre = simData.fuelLeftOuterPre->get(); // Pounds - double fuelFeedOnePre = simData.fuelFeedOnePre->get(); // Pounds - double fuelLeftMidPre = simData.fuelLeftMidPre->get(); // Pounds - double fuelLeftInnerPre = simData.fuelLeftInnerPre->get(); // Pounds - double fuelFeedTwoPre = simData.fuelFeedTwoPre->get(); // Pounds - double fuelFeedThreePre = simData.fuelFeedThreePre->get(); // Pounds - double fuelRightInnerPre = simData.fuelRightInnerPre->get(); // Pounds - double fuelRightMidPre = simData.fuelRightMidPre->get(); // Pounds - double fuelFeedFourPre = simData.fuelFeedFourPre->get(); // Pounds - double fuelRightOuterPre = simData.fuelRightOuterPre->get(); // Pounds - double fuelTrimPre = simData.fuelTrimPre->get(); // Pounds - - const double leftOuterQty = simData.fuelTankDataPtr->data().fuelSystemLeftOuter * weightLbsPerGallon; // Pounds - const double feedOneQty = simData.fuelFeedTankDataPtr->data().fuelSystemFeedOne * weightLbsPerGallon; // Pounds - const double leftMidQty = simData.fuelTankDataPtr->data().fuelSystemLeftMid * weightLbsPerGallon; // Pounds - const double leftInnerQty = simData.fuelTankDataPtr->data().fuelSystemLeftInner * weightLbsPerGallon; // Pounds - const double feedTwoQty = simData.fuelFeedTankDataPtr->data().fuelSystemFeedTwo * weightLbsPerGallon; // Pounds - const double feedThreeQty = simData.fuelFeedTankDataPtr->data().fuelSystemFeedThree * weightLbsPerGallon; // Pounds - const double rightInnerQty = simData.fuelTankDataPtr->data().fuelSystemRightInner * weightLbsPerGallon; // Pounds - const double rightMidQty = simData.fuelTankDataPtr->data().fuelSystemRightMid * weightLbsPerGallon; // Pounds - const double feedFourQty = simData.fuelFeedTankDataPtr->data().fuelSystemFeedFour * weightLbsPerGallon; // Pounds - const double rightOuterQty = simData.fuelTankDataPtr->data().fuelSystemRightOuter * weightLbsPerGallon; // Pounds - const double trimQty = simData.fuelTankDataPtr->data().fuelSystemTrim * weightLbsPerGallon; // Pounds - - const double fuelTotalActual = leftOuterQty + feedOneQty + leftMidQty + leftInnerQty + feedTwoQty + feedThreeQty + rightInnerQty + - rightMidQty + feedFourQty + rightOuterQty + trimQty; // Pounds - const double fuelTotalPre = fuelLeftOuterPre + fuelFeedOnePre + fuelLeftMidPre + fuelLeftInnerPre + fuelFeedTwoPre + fuelFeedThreePre + - fuelRightInnerPre + fuelRightMidPre + fuelFeedFourPre + fuelRightOuterPre + fuelTrimPre; // Pounds - const double deltaFuelRate = (std::abs)(fuelTotalActual - fuelTotalPre) / (weightLbsPerGallon * deltaTimeSeconds); // Pounds/ sec - - const EngineState engine1State = static_cast(simData.engineState[E1]->get()); - const EngineState engine2State = static_cast(simData.engineState[E2]->get()); - const EngineState engine3State = static_cast(simData.engineState[E3]->get()); - const EngineState engine4State = static_cast(simData.engineState[E4]->get()); - - /// Delta time for this update in hours - const double deltaTimeHours = deltaTimeSeconds / 3600; - - // TODO: Pump Logic - TO BE IMPLEMENTED - /* - - const double pumpStateEngine1 = simData.pumpStateDataPtr->data().pumpStateEngine1; - const double pumpStateEngine2 = simData.pumpStateDataPtr->data().pumpStateEngine2; - const double pumpStateEngine3 = simData.pumpStateDataPtr->data().pumpStateEngine3; - const double pumpStateEngine4 = simData.pumpStateDataPtr->data().pumpStateEngine4; - - // Pump State Logic for Engine 1 - if (pumpStateEngine1 == 0 && (timerEngine1.elapsed() == 0 || timerEngine1.elapsed() >= 1000)) { - if (fuelLeftPre - leftQuantity > 0 && leftQuantity == 0) { - timerEngine1.reset(); - simVars->setPumpStateEngine1(1); - } else if (fuelLeftPre == 0 && leftQuantity - fuelLeftPre > 0) { - timerEngine1.reset(); - simVars->setPumpStateEngine1(2); - } else { - simVars->setPumpStateEngine1(0); - } - } else if (pumpStateEngine1 == 1 && timerEngine1.elapsed() >= 2100) { - simVars->setPumpStateEngine1(0); - fuelLeftPre = 0; - timerEngine1.reset(); - } else if (pumpStateEngine1 == 2 && timerEngine1.elapsed() >= 2700) { - simVars->setPumpStateEngine1(0); - timerEngine1.reset(); - } - - // Pump State Logic for Engine 2 - if (pumpStateEngine2 == 0 && (timerEngine2.elapsed() == 0 || timerEngine2.elapsed() >= 1000)) { - if (fuelLeftPre - leftQuantity > 0 && leftQuantity == 0) { - timerEngine2.reset(); - simVars->setPumpStateEngine2(1); - } else if (fuelLeftPre == 0 && leftQuantity - fuelLeftPre > 0) { - timerEngine2.reset(); - simVars->setPumpStateEngine2(2); - } else { - simVars->setPumpStateEngine2(0); - } - } else if (pumpStateEngine2 == 1 && timerEngine2.elapsed() >= 2100) { - simVars->setPumpStateEngine2(0); - fuelLeftPre = 0; - timerEngine2.reset(); - } else if (pumpStateEngine2 == 2 && timerEngine2.elapsed() >= 2700) { - simVars->setPumpStateEngine2(0); - timerEngine2.reset(); - } - - // Pump State Logic for Engine 3 - if (pumpStateEngine3 == 0 && (timerEngine3.elapsed() == 0 || timerEngine3.elapsed() >= 1000)) { - if (fuelRightPre - rightQuantity > 0 && rightQuantity == 0) { - timerEngine3.reset(); - simVars->setPumpStateEngine3(1); - } else if (fuelRightPre == 0 && rightQuantity - fuelRightPre > 0) { - timerEngine3.reset(); - simVars->setPumpStateEngine3(2); - } else { - simVars->setPumpStateEngine3(0); - } - } else if (pumpStateEngine3 == 1 && timerEngine3.elapsed() >= 2100) { - simVars->setPumpStateEngine3(0); - fuelRightPre = 0; - timerEngine3.reset(); - } else if (pumpStateEngine3 == 2 && timerEngine3.elapsed() >= 2700) { - simVars->setPumpStateEngine3(0); - timerEngine3.reset(); - } - - // Pump State Logic for Engine 4 - if (pumpStateEngine4 == 0 && (timerEngine4.elapsed() == 0 || timerEngine4.elapsed() >= 1000)) { - if (fuelRightPre - rightQuantity > 0 && rightQuantity == 0) { - timerEngine4.reset(); - simVars->setPumpStateEngine4(1); - } else if (fuelRightPre == 0 && rightQuantity - fuelRightPre > 0) { - timerEngine4.reset(); - simVars->setPumpStateEngine4(2); - } else { - simVars->setPumpStateEngine4(0); - } - } else if (pumpStateEngine4 == 1 && timerEngine4.elapsed() >= 2100) { - simVars->setPumpStateEngine4(0); - fuelRightPre = 0; - timerEngine4.reset(); - } else if (pumpStateEngine4 == 2 && timerEngine4.elapsed() >= 2700) { - simVars->setPumpStateEngine4(0); - timerEngine4.reset(); - } - --------------------------------------------*/ - - // Checking for in-game UI Fuel tampering - const bool isReadyVar = msfsHandlerPtr->getAircraftIsReadyVar(); - const double refuelRate = simData.refuelRate->get(); - const double refuelStartedByUser = simData.refuelStartedByUser->get(); - if ((isReadyVar && !refuelStartedByUser && deltaFuelRate > FUEL_RATE_THRESHOLD) || - (isReadyVar && refuelStartedByUser && deltaFuelRate > FUEL_RATE_THRESHOLD && refuelRate < 2)) { - uiFuelTamper = true; - } - - //-------------------------------------------- - // Main Fuel Burn Logic - //-------------------------------------------- - const FLOAT64 aircraftDevelopmentStateVar = msfsHandlerPtr->getAircraftDevelopmentStateVar(); - - if (uiFuelTamper && aircraftDevelopmentStateVar == 0) { - simData.fuelLeftOuterPre->set(fuelLeftOuterPre); // Pounds - simData.fuelFeedOnePre->set(fuelFeedOnePre); // Pounds - simData.fuelLeftMidPre->set(fuelLeftMidPre); // Pounds - simData.fuelLeftInnerPre->set(fuelLeftInnerPre); // Pounds - simData.fuelFeedTwoPre->set(fuelFeedTwoPre); // Pounds - simData.fuelFeedThreePre->set(fuelFeedThreePre); // Pounds - simData.fuelRightInnerPre->set(fuelRightInnerPre); // Pounds - simData.fuelRightMidPre->set(fuelRightMidPre); // Pounds - simData.fuelFeedFourPre->set(fuelFeedFourPre); // Pounds - simData.fuelRightOuterPre->set(fuelRightOuterPre); // Pounds - simData.fuelTrimPre->set(fuelTrimPre); // Pounds - - simData.fuelFeedTankDataPtr->data().fuelSystemFeedOne = fuelFeedOnePre / weightLbsPerGallon; - simData.fuelFeedTankDataPtr->data().fuelSystemFeedTwo = fuelFeedTwoPre / weightLbsPerGallon; - simData.fuelFeedTankDataPtr->data().fuelSystemFeedThree = fuelFeedThreePre / weightLbsPerGallon; - simData.fuelFeedTankDataPtr->data().fuelSystemFeedFour = fuelFeedFourPre / weightLbsPerGallon; - simData.fuelFeedTankDataPtr->writeDataToSim(); - - simData.fuelTankDataPtr->data().fuelSystemLeftOuter = fuelLeftOuterPre / weightLbsPerGallon; - simData.fuelTankDataPtr->data().fuelSystemLeftMid = fuelLeftMidPre / weightLbsPerGallon; - simData.fuelTankDataPtr->data().fuelSystemLeftInner = fuelLeftInnerPre / weightLbsPerGallon; - simData.fuelTankDataPtr->data().fuelSystemRightInner = fuelRightInnerPre / weightLbsPerGallon; - simData.fuelTankDataPtr->data().fuelSystemRightMid = fuelRightMidPre / weightLbsPerGallon; - simData.fuelTankDataPtr->data().fuelSystemRightOuter = fuelRightOuterPre / weightLbsPerGallon; - simData.fuelTankDataPtr->data().fuelSystemTrim = fuelTrimPre / weightLbsPerGallon; - simData.fuelTankDataPtr->writeDataToSim(); - } - // Detects refueling from the EFB - else if (!uiFuelTamper && refuelStartedByUser == 1) { - simData.fuelLeftOuterPre->set(leftOuterQty); - simData.fuelFeedOnePre->set(feedOneQty); - simData.fuelLeftMidPre->set(leftMidQty); - simData.fuelLeftInnerPre->set(leftInnerQty); - simData.fuelFeedTwoPre->set(feedTwoQty); - simData.fuelFeedThreePre->set(feedThreeQty); - simData.fuelRightInnerPre->set(rightInnerQty); - simData.fuelRightMidPre->set(rightMidQty); - simData.fuelFeedFourPre->set(feedFourQty); - simData.fuelRightOuterPre->set(rightOuterQty); - simData.fuelTrimPre->set(trimQty); - } else { - if (uiFuelTamper) { - fuelLeftOuterPre = leftOuterQty; // in Pounds - fuelFeedOnePre = feedOneQty; // in Pounds - fuelLeftMidPre = leftMidQty; // in Pounds - fuelLeftInnerPre = leftInnerQty; // in Pounds - fuelFeedTwoPre = feedTwoQty; // in Pounds - fuelFeedThreePre = feedThreeQty; // in Pounds - fuelRightInnerPre = rightInnerQty; // in Pounds - fuelRightMidPre = rightMidQty; // in Pounds - fuelFeedFourPre = feedFourQty; // in Pounds - fuelRightOuterPre = rightOuterQty; // in Pounds - fuelTrimPre = trimQty; // in Pounds - } - - double fuelFlowRateChange = 0; // was m in the original code - double previousFuelFlowRate = 0; // was b in the original code - double fuelBurn1 = 0; // in kg - double fuelBurn2 = 0; // in kg - double fuelBurn3 = 0; // in kg - double fuelBurn4 = 0; // in kg - - double fuelUsedEngine1 = simData.engineFuelUsed[E1]->get(); - double fuelUsedEngine2 = simData.engineFuelUsed[E2]->get(); - double fuelUsedEngine3 = simData.engineFuelUsed[E3]->get(); - double fuelUsedEngine4 = simData.engineFuelUsed[E4]->get(); - - // Initialize arrays to avoid code duplication when looping over engines - const double* engineFF[4] = {&engine1FF, &engine2FF, &engine3FF, &engine4FF}; - const double* enginePreFF[4] = {&engine1PreFF, &engine2PreFF, &engine3PreFF, &engine4PreFF}; - double* fuelFeedPre[4] = {&fuelFeedOnePre, &fuelFeedTwoPre, &fuelFeedThreePre, &fuelFeedFourPre}; - double* fuelBurn[4] = {&fuelBurn1, &fuelBurn2, &fuelBurn3, &fuelBurn4}; - double* fuelUsedEngine[4] = {&fuelUsedEngine1, &fuelUsedEngine2, &fuelUsedEngine3, &fuelUsedEngine4}; - - // Loop over engines - for (int i = 0; i < 4; i++) { - // Engines fuel burn routine - if (*fuelFeedPre[i] > 0) { - // Cycle Fuel Burn - if (aircraftDevelopmentStateVar != 2) { - fuelFlowRateChange = (*engineFF[i] - *enginePreFF[i]) / deltaTimeHours; - previousFuelFlowRate = *enginePreFF[i]; - *fuelBurn[i] = (fuelFlowRateChange * (std::pow)(deltaTimeHours, 2) / 2) + (previousFuelFlowRate * deltaTimeHours); // KG - } - // Fuel Used Accumulators - *fuelUsedEngine[i] += *fuelBurn[i]; - } else { - fuelBurn[i] = 0; - fuelFeedPre[i] = 0; - } - } - - const double fuelFeedOne = fuelFeedOnePre - (fuelBurn1 * Fadec::KGS_TO_LBS); // Pounds - const double fuelFeedTwo = fuelFeedTwoPre - (fuelBurn2 * Fadec::KGS_TO_LBS); // Pounds - const double fuelFeedThree = fuelFeedThreePre - (fuelBurn3 * Fadec::KGS_TO_LBS); // Pounds - const double fuelFeedFour = fuelFeedFourPre - (fuelBurn4 * Fadec::KGS_TO_LBS); // Pounds - - // Setting new pre-cycle conditions - simData.enginePreFF[E1]->set(engine1FF); - simData.enginePreFF[E2]->set(engine2FF); - simData.enginePreFF[E3]->set(engine3FF); - simData.enginePreFF[E4]->set(engine4FF); - - simData.engineFuelUsed[E1]->set(fuelUsedEngine1); - simData.engineFuelUsed[E2]->set(fuelUsedEngine2); - simData.engineFuelUsed[E3]->set(fuelUsedEngine3); - simData.engineFuelUsed[E4]->set(fuelUsedEngine4); - - simData.fuelFeedOnePre->set(fuelFeedOne); - simData.fuelFeedTwoPre->set(fuelFeedTwo); - simData.fuelFeedThreePre->set(fuelFeedThree); - simData.fuelFeedFourPre->set(fuelFeedFour); - - simData.fuelLeftOuterPre->set(leftOuterQty); - simData.fuelLeftMidPre->set(leftMidQty); - simData.fuelLeftInnerPre->set(leftInnerQty); - simData.fuelRightInnerPre->set(rightInnerQty); - simData.fuelRightMidPre->set(rightMidQty); - simData.fuelRightOuterPre->set(rightOuterQty); - simData.fuelTrimPre->set(trimQty); - - simData.fuelFeedTankDataPtr->data().fuelSystemFeedOne = (fuelFeedOne / weightLbsPerGallon); - simData.fuelFeedTankDataPtr->data().fuelSystemFeedTwo = (fuelFeedTwo / weightLbsPerGallon); - simData.fuelFeedTankDataPtr->data().fuelSystemFeedThree = (fuelFeedThree / weightLbsPerGallon); - simData.fuelFeedTankDataPtr->data().fuelSystemFeedFour = (fuelFeedFour / weightLbsPerGallon); - simData.fuelFeedTankDataPtr->writeDataToSim(); - } - - // Will save the current fuel quantities if on the ground AND engines being shutdown - // AND 5 seconds have passed since the last save - if (msfsHandlerPtr->getSimOnGround() && (msfsHandlerPtr->getSimulationTime() - lastFuelSaveTime) > 5.0 && - (engine1State == OFF || engine1State == SHUTTING || // 1 - engine2State == OFF || engine2State == SHUTTING || // 2 - engine3State == OFF || engine3State == SHUTTING || // 3 - engine4State == OFF || engine4State == SHUTTING) // 4 - ) { - fuelConfiguration.setFuelLeftOuterGallons(simData.fuelTankDataPtr->data().fuelSystemLeftOuter); - fuelConfiguration.setFuelFeedOneGallons(simData.fuelFeedTankDataPtr->data().fuelSystemFeedOne); - fuelConfiguration.setFuelLeftMidGallons(simData.fuelTankDataPtr->data().fuelSystemLeftMid); - fuelConfiguration.setFuelLeftInnerGallons(simData.fuelTankDataPtr->data().fuelSystemLeftInner); - fuelConfiguration.setFuelFeedTwoGallons(simData.fuelFeedTankDataPtr->data().fuelSystemFeedTwo); - fuelConfiguration.setFuelFeedThreeGallons(simData.fuelFeedTankDataPtr->data().fuelSystemFeedThree); - fuelConfiguration.setFuelRightInnerGallons(simData.fuelTankDataPtr->data().fuelSystemRightInner); - fuelConfiguration.setFuelRightMidGallons(simData.fuelTankDataPtr->data().fuelSystemRightMid); - fuelConfiguration.setFuelFeedFourGallons(simData.fuelFeedTankDataPtr->data().fuelSystemFeedFour); - fuelConfiguration.setFuelRightOuterGallons(simData.fuelTankDataPtr->data().fuelSystemRightOuter); - fuelConfiguration.setFuelTrimGallons(simData.fuelTankDataPtr->data().fuelSystemTrim); - fuelConfiguration.saveConfigurationToIni(); - lastFuelSaveTime = msfsHandlerPtr->getSimulationTime(); - } - -#ifdef PROFILING - profilerUpdateFuel.stop(); -#endif -} - -void EngineControl_A380X::updateThrustLimits(double simulationTime, - double pressureAltitude, - double ambientTemperature, - double ambientPressure, - double mach, - int packs, - int nai, - int wai) { -#ifdef PROFILING - profilerUpdateThrustLimits.start(); -#endif - - const double flexTemp = simData.airlinerToFlexTemp->get(); - const double pressAltitude = simData.simVarsDataPtr->data().pressureAltitude; - - // Write all N1 Limits - const double altitude = (std::min)(16600.0, pressAltitude); - const double to = ThrustLimits_A380X::limitN1(0, altitude, ambientTemperature, ambientPressure, 0, packs, nai, wai); - const double ga = ThrustLimits_A380X::limitN1(1, altitude, ambientTemperature, ambientPressure, 0, packs, nai, wai); - double flex_to = 0; - double flex_ga = 0; - if (flexTemp > 0) { - flex_to = ThrustLimits_A380X::limitN1(0, altitude, ambientTemperature, ambientPressure, flexTemp, packs, nai, wai); - flex_ga = ThrustLimits_A380X::limitN1(1, altitude, ambientTemperature, ambientPressure, flexTemp, packs, nai, wai); - } - double clb = ThrustLimits_A380X::limitN1(2, pressAltitude, ambientTemperature, ambientPressure, 0, packs, nai, wai); - double mct = ThrustLimits_A380X::limitN1(3, pressAltitude, ambientTemperature, ambientPressure, 0, packs, nai, wai); - - // transition between TO and GA limit ----------------------------------------------------------------------------- - const double machFactorLow = (std::max)(0.0, (std::min)(1.0, (mach - 0.04) / 0.04)); - const double flex = flex_to + (flex_ga - flex_to) * machFactorLow; - double toga = to + (ga - to) * machFactorLow; - - // adaption of CLB due to FLX limit if necessary ------------------------------------------------------------------ - bool isFlexActive = false; - const double thrustLimitType = simData.thrustLimitType->get(); - if ((prevThrustLimitType != 3 && thrustLimitType == 3) || (prevFlexTemperature == 0 && flexTemp > 0)) { - isFlexActive = true; - } else if ((flexTemp == 0) || (thrustLimitType == 4)) { - isFlexActive = false; - } - - double transitionStartTime = 0; - double transitionFactor = 0; - if (isFlexActive && !isTransitionActive && thrustLimitType == 1) { - isTransitionActive = true; - transitionStartTime = simulationTime; - transitionFactor = 0.2; - // transitionFactor = (clb - flex) / transitionTime; - } else if (!isFlexActive) { - isTransitionActive = false; - transitionStartTime = 0; - transitionFactor = 0; - } - - double deltaThrust = 0; - if (isTransitionActive) { - double timeDifference = (std::max)(0.0, (simulationTime - transitionStartTime) - TRANSITION_WAIT_TIME); - if (timeDifference > 0 && clb > flex) { - deltaThrust = (std::min)(clb - flex, timeDifference * transitionFactor); - } - if (flex + deltaThrust >= clb) { - isFlexActive = false; - isTransitionActive = false; - } - } - - if (isFlexActive) { - clb = (std::min)(clb, flex) + deltaThrust; - } - - prevThrustLimitType = thrustLimitType; - prevFlexTemperature = flexTemp; - - // thrust transitions for MCT and TOGA ---------------------------------------------------------------------------- - - // get factors - const double machFactor = (std::max)(0.0, (std::min)(1.0, ((mach - 0.37) / 0.05))); - const double altitudeFactorLow = (std::max)(0.0, (std::min)(1.0, ((pressureAltitude - 16600) / 500))); - const double altitudeFactorHigh = (std::max)(0.0, (std::min)(1.0, ((pressureAltitude - 25000) / 500))); - - // adapt thrust limits - if (pressureAltitude >= 25000) { - mct = (std::max)(clb, mct + (clb - mct) * altitudeFactorHigh); - toga = mct; - } else { - if (mct > toga) { - mct = toga + (mct - toga) * (std::min)(1.0, altitudeFactorLow + machFactor); - toga = mct; - } else { - toga = toga + (mct - toga) * (std::min)(1.0, altitudeFactorLow + machFactor); - } - } - - // write limits --------------------------------------------------------------------------------------------------- - simData.thrustLimitIdle->set(simData.engineIdleN1->get()); - simData.thrustLimitToga->set(toga); - simData.thrustLimitFlex->set(flex); - simData.thrustLimitClimb->set(clb); - simData.thrustLimitMct->set(mct); - -#ifdef PROFILING - profilerUpdateThrustLimits.stop(); -#endif -} - -/* - * Previous code - call to it was already commented out and this function was not in use. - * Keeping it to make completing/fixing it easier. - * It is not migrated to the cpp framework yet. - * -/// -/// FBW Oil Qty, Pressure and Temperature (in Quarts, PSI and degree Celsius) -/// Updates Oil with realistic values visualized in the SD -/// -void updateOil(int engine, double thrust, double simN3, double deltaN3, double deltaTime, double ambientTemp) { - double steadyTemperature; - double thermalEnergy; - double oilTemperaturePre; - double oilQtyActual; - double oilTotalActual; - double oilQtyObjective; - double oilBurn; - double oilPressureIdle; - double oilPressure; - - //-------------------------------------------- - // Engine Reading - //-------------------------------------------- - if (engine == 1) { - steadyTemperature = simVars->getEngine1EGT(); - thermalEnergy = thermalEnergy1; - oilTemperaturePre = oilTemperatureEngine1Pre; - oilQtyActual = simVars->getEngine1Oil(); - oilTotalActual = simVars->getEngine1TotalOil(); - } else if (engine == 2) { - steadyTemperature = simVars->getEngine2EGT(); - thermalEnergy = thermalEnergy2; - oilTemperaturePre = oilTemperatureEngine2Pre; - oilQtyActual = simVars->getEngine2Oil(); - oilTotalActual = simVars->getEngine2TotalOil(); - } else if (engine == 3) { - steadyTemperature = simVars->getEngine3EGT(); - thermalEnergy = thermalEnergy3; - oilTemperaturePre = oilTemperatureEngine3Pre; - oilQtyActual = simVars->getEngine3Oil(); - oilTotalActual = simVars->getEngine3TotalOil(); - } else { - steadyTemperature = simVars->getEngine4EGT(); - thermalEnergy = thermalEnergy4; - oilTemperaturePre = oilTemperatureEngine4Pre; - oilQtyActual = simVars->getEngine4Oil(); - oilTotalActual = simVars->getEngine4TotalOil(); - } - - //-------------------------------------------- - // Oil Temperature - //-------------------------------------------- - if (simOnGround == 1 && engineState == 0 && ambientTemp > oilTemperaturePre - 10) { - oilTemperature = ambientTemp; - } else { - if (steadyTemperature > oilTemperatureMax) { - steadyTemperature = oilTemperatureMax; - } - thermalEnergy = (0.995 * thermalEnergy) + (deltaN3 / deltaTime); - oilTemperature = poly->oilTemperature(thermalEnergy, oilTemperaturePre, steadyTemperature, deltaTime); - } - - //-------------------------------------------- - // Oil Quantity - //-------------------------------------------- - // Calculating Oil Qty as a function of thrust - oilQtyObjective = oilTotalActual * (1 - poly->oilGulpPct(thrust)); - oilQtyActual = oilQtyActual - (oilTemperature - oilTemperaturePre); - - // Oil burnt taken into account for tank and total oil - oilBurn = (0.00011111 * deltaTime); - oilQtyActual = oilQtyActual - oilBurn; - oilTotalActual = oilTotalActual - oilBurn; - - //-------------------------------------------- - // Oil Pressure - //-------------------------------------------- - oilPressureIdle = 0; - - oilPressure = poly->oilPressure(simN3) + oilPressureIdle; - - //-------------------------------------------- - // Engine Writing - //-------------------------------------------- - if (engine == 1) { - thermalEnergy1 = thermalEnergy; - oilTemperatureEngine1Pre = oilTemperature; - simVars->setEngine1Oil(oilQtyActual); - simVars->setEngine1TotalOil(oilTotalActual); - SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine1, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), - &oilTemperature); - SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilPsiEngine1, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &oilPressure); - } else if (engine == 2) { - thermalEnergy2 = thermalEnergy; - oilTemperatureEngine2Pre = oilTemperature; - simVars->setEngine2Oil(oilQtyActual); - simVars->setEngine2TotalOil(oilTotalActual); - SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine2, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), - &oilTemperature); - SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilPsiEngine2, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &oilPressure); - } else if (engine == 3) { - thermalEnergy3 = thermalEnergy; - oilTemperatureEngine3Pre = oilTemperature; - simVars->setEngine3Oil(oilQtyActual); - simVars->setEngine3TotalOil(oilTotalActual); - SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine3, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), - &oilTemperature); - SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilPsiEngine3, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &oilPressure); - } else { - thermalEnergy4 = thermalEnergy; - oilTemperatureEngine4Pre = oilTemperature; - simVars->setEngine4Oil(oilQtyActual); - simVars->setEngine4TotalOil(oilTotalActual); - SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilTempEngine4, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), - &oilTemperature); - SimConnect_SetDataOnSimObject(hSimConnect, DataTypesID::OilPsiEngine4, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(double), &oilPressure); - } -} - - */ diff --git a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/EngineControl_A380X.h b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/EngineControl_A380X.h deleted file mode 100644 index 7e1d99c5d15..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/EngineControl_A380X.h +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_ENGINECONTROL_A380X_H -#define FLYBYWIRE_AIRCRAFT_ENGINECONTROL_A380X_H - -#include "MsfsHandler.h" - -#include "FadecSimData_A380X.hpp" -#include "FuelConfiguration_A380X.h" - -#define FILENAME_FADEC_CONF_DIRECTORY "\\work\\AircraftStates\\" -#define FILENAME_FADEC_CONF_FILE_EXTENSION ".ini" - -/** - * @class EngineControl_A380X - * @brief Manages the engine control for the A380XX aircraft, coordinating and synchronising the - * simulator's jet engine simulation with realistic custom values. - * - * Engine control is adding more realistic values and behaviour to the engine simulation of the simulator. - * Esp. for startup, shutdown, fuel consumption, thrust limits, etc. - * - * TODO: The EngineControl_A380X class is still work in progress and might be extended or replaced in the future. - */ -class EngineControl_A380X { - private: - // Convenience pointer to the msfs handler - MsfsHandler* msfsHandlerPtr = nullptr; - - // Convenience pointer to the data manager - DataManager* dataManagerPtr = nullptr; - - // FADEC simulation data - FadecSimData_A380X simData{}; - - // ATC ID for the aircraft used to load and store the fuel levels - std::string atcId{}; - - // Fuel configuration for loading and storing fuel levels - FuelConfiguration_A380X fuelConfiguration{}; - - // Remember last fuel save time to allow saving fuel only every 5 seconds - FLOAT64 lastFuelSaveTime = 0; - static constexpr double FUEL_SAVE_INTERVAL = 5.0; // seconds - - // thrust limits transition for flex - bool isTransitionActive = false; - static constexpr double TRANSITION_WAIT_TIME = 10; - - // values that need previous state - double prevFlexTemperature = 0.0; - double prevThrustLimitType = 0.0; - - // TODO - might not be required - feeds into stateMachine but really relevant - double prevSimEngineN3[4] = {0.0, 0.0, 0.0, 0.0}; - - // additional constants - static constexpr int MAX_OIL = 200; - static constexpr int MIN_OIL = 140; - static constexpr double FUEL_RATE_THRESHOLD = 661; // lbs/sec for determining fuel ui tampering - - /** - * @enum EngineState - * @brief Enumerates the possible states for the engine state machine. - * - * @var OFF The engine is turned off. This is the initial state of the engine. - * @var ON The engine is turned on and running. - * @var STARTING The engine is in the process of starting up. - * @var RESTARTING The engine is in the process of restarting. - * @var SHUTTING The engine is in the process of shutting down. - */ - enum EngineState { - OFF = 0, - ON = 1, - STARTING = 2, - RESTARTING = 3, - SHUTTING = 4, - }; - -#ifdef PROFILING - // Profiling for the engine control - can eventually be removed - SimpleProfiler profilerUpdate{"Fadec::EngineControl_A380X::update()", 100}; - SimpleProfiler profilerEngineStateMachine{"Fadec::EngineControl_A380X::engineStateMachine()", 100}; - SimpleProfiler profilerEngineStartProcedure{"Fadec::EngineControl_A380X::engineStartProcedure()", 100}; - SimpleProfiler profilerEngineShutdownProcedure{"Fadec::EngineControl_A380X::engineShutdownProcedure()", 100}; - SimpleProfiler profilerUpdateFF{"Fadec::EngineControl_A380X::updateFF()", 100}; - SimpleProfiler profilerUpdatePrimaryParameters{"Fadec::EngineControl_A380X::updatePrimaryParameters()", 100}; - SimpleProfiler profilerUpdateEGT{"Fadec::EngineControl_A380X::updateEGT()", 100}; - SimpleProfiler profilerUpdateFuel{"Fadec::EngineControl_A380X::updateFuel()", 100}; - SimpleProfiler profilerUpdateThrustLimits{"Fadec::EngineControl_A380X::updateThrustLimits()", 100}; -#endif - - // =========================================================================== - // Public methods - // =========================================================================== - - public: - /** - * @brief Initializes the EngineControl_A32NX class once during the gauge initialization. - * @param msfsHandler - */ - void initialize(MsfsHandler* msfsHandler); - - /** - * @brief Updates the EngineControl_A32NX class once per frame. - * @param pData - */ - void update(sGaugeDrawData* pData); - - /** - * @brief Shuts down the EngineControl_A32NX class once during the gauge shutdown. - */ - void shutdown(); - - // =========================================================================== - // Private methods - // =========================================================================== - - private: - /** - * @brief Initialize the FADEC and Fuel model - * This is done after we have retrieved the ATC ID so we can load the fuel levels - */ - void initializeEngineControlData(); - - /** - * @brief Generate Idle / Initial Engine Parameters (non-imbalanced) - * - * @param pressureAltitude The current pressure altitude of the aircraft in feet. - * @param mach The current Mach number of the aircraft. - * @param ambientTemperature The current ambient temperature in degrees Celsius. - * @param ambientPressure The current ambient pressure in hPa. - */ - void generateIdleParameters(FLOAT64 pressureAltitude, FLOAT64 mach, FLOAT64 ambientTemperature, FLOAT64 ambientPressure); - - /** - * @brief Manages the state and state changes of the engine. - * - * @param engine The engine number (1 or 2). - * @param engineIgniter The status of the engine igniter (enum 0=Crank, 1=Norm, 2=Ign). - * @param engineStarter The status of the engine starter as bool. - * @param simN3 The current N2 value from the simulator used as N3 for the A380X in percent. - * @param idleN3 The idle N3 value in percent. - * @param ambientTemperature The current ambient temperature in degrees Celsius. - * @return The current state of the engine as an enum of type EngineState (OFF, ON, STARTING, RESTARTING, SHUTTING). - * @see EngineState - */ - EngineControl_A380X::EngineState engineStateMachine(int engine, - int engineIgniter, - bool engineStarter, - double simN3, - double idleN3, - double ambientTemperature); - - /** - * @brief This function manages the engine start procedure. - * - * @param engine The engine number (1 or 2). - * @param engineState The current state of the engine as an enum of type EngineState. - * @param deltaTime The time difference since the last update in seconds. - * @param engineTimer A timer used to calculate the elapsed time for various operations. - * @param simN3 The current N3 value from the simulator in percent (actually reading the sim's N2 as the sim does not have an N3. - * @param ambientTemperature The current ambient temperature in degrees Celsius. - * - * @see EngineState - */ - void engineStartProcedure(int engine, - EngineState engineState, - double deltaTime, - double engineTimer, - double simN3, - double ambientTemperature); - - /** - * @brief This function manages the engine shutdown procedure. - * TODO: Temporary solution as per comment in original code - * - * @param engine The engine number (1 or 2). - * @param ambientTemperature The current ambient temperature in degrees Celsius to calculate the engine's operating temperature. - * @param simN1 The current N1 value from the simulator. - * @param deltaTime The time difference since the last update. This is used to calculate the rate of change of various parameters. - * @param engineTimer A timer used to calculate the elapsed time for various operations. - */ - void engineShutdownProcedure(int engine, double deltaTime, double engineTimer, double simN1, double ambientTemperature); - - /** - * @brief Updates the fuel flow of the engine. - * - * @param engine The engine number (1 or 2). - * @param simCN1 The current corrected fan speed value from the simulator. - * @param mach The current Mach number of the aircraft. - * @param pressureAltitude The current pressure altitude of the aircraft in feet. - * @param ambientTemperature The current ambient temperature in degrees Celsius to calculate the engine's operating temperature. - * @param ambientPressure The current ambient pressure in hPa. - * @return The updated fuel flow as a double in kg/hour. - */ - int updateFF(int engine, FLOAT64 simCN1, FLOAT64 mach, FLOAT64 altitude, FLOAT64 temperature, FLOAT64 pressure); - - /** - * @brief Updates the primary custom parameters (LVars) of the engine when not starting or stopping the engine - * and the sim has control. - * - * @param engine The engine number (1 or 2). - * @param imbalance The current encoded imbalance number of the engine. - * @param simN1 The current N1 value from the simulator in percent. - * @param simN3 The current N2 value from the simulator in percent used as N3 input in the A380X. - */ - void updatePrimaryParameters(int engine, FLOAT64 simN1, FLOAT64 simN3); - - /** - * @brief FBW Exhaust Gas Temperature (in degree Celsius). Updates EGT with realistic values visualized in the ECAM - * - * @param engine The engine number (1 or 2). - * @param deltaTime The time difference since the last update to calculate the rate of change of various parameters. - * @param simOnGround The on ground status of the aircraft (0 or 1). - * @param engineState The current state of the engine. - * @param simCN1 The current corrected fan speed value from the simulator. - * @param correctedFuelFlow The current custom fuel flow of the engine (FBW corrected). - * @param mach The current Mach number of the aircraft. - * @param pressureAltitude The current pressure altitude of the aircraft in feet. - * @param ambientTemperature The current ambient pressure in hPa. - * - * @see EngineState - */ - void updateEGT(int engine, - double engineState, - double deltaTime, - double simCN1, - int customFuelFlow, - const double mach, - const double pressureAltitude, - const double ambientPressure, - bool simOnGround); - - /** - * @brief FBW Fuel Consumption and Tanking. Updates Fuel Consumption with realistic values - * - * @param deltaTimeSeconds Frame delta time in seconds - */ - void updateFuel(FLOAT64 deltaTimeSeconds); - - /** - * @brief Updates the thrust limits of the engine. - * - * @param simulationTime The current time in the simulation. - * @param pressureAltitude The current pressure altitude of the aircraft in feet. - * @param ambientTemperature The current ambient temperature in degrees Celsius. - * @param ambientPressure The current ambient pressure in hPa. - * @param mach The current Mach number of the aircraft. - * @param packs The current state of the packs (0 or 1). - * @param nai The current state of the NAI (0 or 1). - * @param wai The current state of the WAI (0 or 1). - */ - void updateThrustLimits(double simulationTime, - double pressureAltitude, - double ambientTemperature, - double ambientPressure, - double mach, - int packs, - int nai, - int wai); -}; - -#endif // FLYBYWIRE_AIRCRAFT_ENGINECONTROL_A380X_H diff --git a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/FadecSimData_A380X.hpp b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/FadecSimData_A380X.hpp deleted file mode 100644 index 8207e687052..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/FadecSimData_A380X.hpp +++ /dev/null @@ -1,462 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_FADECSIMDATA_A380X_HPP -#define FLYBYWIRE_AIRCRAFT_FADECSIMDATA_A380X_HPP - -#include - -#include "DataManager.h" - -// Make access to variables more readable -enum EngineAndSide { - OL = 0, // outer left - E1 = OL, // - ENGINE_1 = OL, // - IL = 1, // inner left - E2 = IL, // - ENGINE_2 = IL, // - IR = 2, // inner right - E3 = IR, // - ENGINE_3 = IR, // - OR = 3, // outer right - E4 = OR, // - ENGINE_4 = OR, // -}; - -/** - * @class FadecSimData_A380X - * @brief This class manages the simulation data for the FADEC (Full Authority Digital Engine Control) - * simulation for the A380X aircraft. - */ -class FadecSimData_A380X { - public: - // Notification groups for events - enum NotificationGroup { NOTIFICATION_GROUP_0 }; - - struct AtcIdData { - char atcID[32]; - }; - DataDefinitionVector atcIdDataDef = { - // MSFS docs say this is max 10 chars - we use 32 for safety - {"ATC ID", 0, UNITS.None, SIMCONNECT_DATATYPE_STRING32} // - }; - /** - * @var atcIdDataPtr - * @brief This struct represents the ATC ID of the aircraft which we use to create the filename to store and load the fuel configuration. - * @data char atcID[32] The ATC ID of the aircraft. - * @note MSFS docs say that the ATC ID is a string of max 10 characters. We use 32 for safety. - * @see https://docs.flightsimulator.com/html/Programming_Tools/SimVars/Aircraft_SimVars/Aircraft_RadioNavigation_Variables.htm#ATC%20ID - */ - DataDefinitionVariablePtr atcIdDataPtr; - - // Fuel Feed Tank Data in one Data Definition as they are read and updated together - struct FuelFeedTankData { - FLOAT64 fuelSystemFeedOne; // in Gallons - FLOAT64 fuelSystemFeedTwo; // in Gallons - FLOAT64 fuelSystemFeedThree; // in Gallons - FLOAT64 fuelSystemFeedFour; // in Gallons - }; - DataDefinitionVector fuelFeedTankDataDef = { - {"FUELSYSTEM TANK QUANTITY", 2, UNITS.Gallons}, // - {"FUELSYSTEM TANK QUANTITY", 5, UNITS.Gallons}, // - {"FUELSYSTEM TANK QUANTITY", 6, UNITS.Gallons}, // - {"FUELSYSTEM TANK QUANTITY", 9, UNITS.Gallons}, // - }; - DataDefinitionVariablePtr fuelFeedTankDataPtr; // in Gallons - - // Fuel Tank Data in one Data Definition as they are read and updated together - struct FuelTankData { - FLOAT64 fuelSystemLeftOuter; // in Gallons - FLOAT64 fuelSystemLeftMid; // in Gallons - FLOAT64 fuelSystemLeftInner; // in Gallons - FLOAT64 fuelSystemRightInner; // in Gallons - FLOAT64 fuelSystemRightMid; // in Gallons - FLOAT64 fuelSystemRightOuter; // in Gallons - FLOAT64 fuelSystemTrim; // in Gallons - }; - DataDefinitionVector fuelTankDataDef = { - {"FUELSYSTEM TANK QUANTITY", 1, UNITS.Gallons}, // - {"FUELSYSTEM TANK QUANTITY", 3, UNITS.Gallons}, // - {"FUELSYSTEM TANK QUANTITY", 4, UNITS.Gallons}, // - {"FUELSYSTEM TANK QUANTITY", 7, UNITS.Gallons}, // - {"FUELSYSTEM TANK QUANTITY", 8, UNITS.Gallons}, // - {"FUELSYSTEM TANK QUANTITY", 10, UNITS.Gallons}, // - {"FUELSYSTEM TANK QUANTITY", 11, UNITS.Gallons}, // - }; - DataDefinitionVariablePtr fuelTankDataPtr; // in Gallons - - // Oil Temp Data in separate Data Definitions as they are updated separately - // clang-format off - struct OilTempData { - FLOAT64 oilTemp; // in Celsius - }; - DataDefinitionVector oilTempE1DataDef = { {"GENERAL ENG OIL TEMPERATURE", 1, UNITS.Celsius} }; - DataDefinitionVector oilTempE2DataDef = { {"GENERAL ENG OIL TEMPERATURE", 2, UNITS.Celsius} }; - DataDefinitionVector oilTempE3DataDef = { {"GENERAL ENG OIL TEMPERATURE", 3, UNITS.Celsius} }; - DataDefinitionVector oilTempE4DataDef = { {"GENERAL ENG OIL TEMPERATURE", 4, UNITS.Celsius} }; - DataDefinitionVariablePtr oilTempDataPtr[4]; - // clang-format on - - // Oil Psi Data in separate Data Definitions as they are updated separately - // clang-format off - struct OilPsiData { - FLOAT64 oilPsi; // in Psi - }; - DataDefinitionVector oilPsiE1DataDef = { {"GENERAL ENG OIL PRESSURE", 1, UNITS.Psi} }; - DataDefinitionVector oilPsiE2DataDef = { {"GENERAL ENG OIL PRESSURE", 2, UNITS.Psi} }; - DataDefinitionVector oilPsiE3DataDef = { {"GENERAL ENG OIL PRESSURE", 3, UNITS.Psi} }; - DataDefinitionVector oilPsiE4DataDef = { {"GENERAL ENG OIL PRESSURE", 4, UNITS.Psi} }; - DataDefinitionVariablePtr oilPsiDataPtr[4]; - // clang-format on - - // Corrected N3 Data in separate Data Definitions as they are updated separately - // Note: the sim does not have a direct N3 value, so we use N2 as a proxy - // clang-format off - struct CorrectedN3Data { - FLOAT64 correctedN3; // in Percent - }; - DataDefinitionVector engine1CN3DataDef = { {"TURB ENG CORRECTED N2", 1, UNITS.Percent} }; - DataDefinitionVector engine2CN3DataDef = { {"TURB ENG CORRECTED N2", 2, UNITS.Percent} }; - DataDefinitionVector engine3CN3DataDef = { {"TURB ENG CORRECTED N2", 3, UNITS.Percent} }; - DataDefinitionVector engine4CN3DataDef = { {"TURB ENG CORRECTED N2", 4, UNITS.Percent} }; - DataDefinitionVariablePtr engineCorrectedN3DataPtr[4]; - // clang-format on - - // SimVars Data in one Data Definition as they are read together and never updated - struct SimVarsData { - FLOAT64 animationDeltaTime; // in Seconds - FLOAT64 airSpeedMach; // in Mach - FLOAT64 ambientPressure; // in Millibars - FLOAT64 ambientTemperature; // in Celsius - FLOAT64 pressureAltitude; // in Feet - FLOAT64 fuelWeightLbsPerGallon; // in Pounds - FLOAT64 engineAntiIce[4]; // 0 or 1 - FLOAT64 engineIgniter[4]; // 0 or 1 - FLOAT64 engineStarter[4]; // 0 or 1 - FLOAT64 simEngineCorrectedN1[4]; // in Percent - FLOAT64 simEngineN1[4]; // in Percent - FLOAT64 simEngineN2[4]; // in Percent - }; - DataDefinitionVector simVarsDataDef = { - {"ANIMATION DELTA TIME", 0, UNITS.Seconds }, // - {"AIRSPEED MACH", 0, UNITS.Mach }, // - {"AMBIENT PRESSURE", 0, UNITS.Millibars}, // - {"AMBIENT TEMPERATURE", 0, UNITS.Celsius }, // - {"PRESSURE ALTITUDE", 0, UNITS.Feet }, // - {"FUEL WEIGHT PER GALLON", 0, UNITS.Pounds }, // - {"ENG ANTI ICE", 1, UNITS.Bool }, // - {"ENG ANTI ICE", 2, UNITS.Bool }, // - {"ENG ANTI ICE", 3, UNITS.Bool }, // - {"ENG ANTI ICE", 4, UNITS.Bool }, // - {"TURB ENG IGNITION SWITCH EX1", 1, UNITS.Enum }, // - {"TURB ENG IGNITION SWITCH EX1", 2, UNITS.Enum }, // - {"TURB ENG IGNITION SWITCH EX1", 3, UNITS.Enum }, // - {"TURB ENG IGNITION SWITCH EX1", 4, UNITS.Enum }, // - {"GENERAL ENG STARTER", 1, UNITS.Bool }, // - {"GENERAL ENG STARTER", 2, UNITS.Bool }, // - {"GENERAL ENG STARTER", 3, UNITS.Bool }, // - {"GENERAL ENG STARTER", 4, UNITS.Bool }, // - {"TURB ENG CORRECTED N1", 1, UNITS.Percent }, // - {"TURB ENG CORRECTED N1", 2, UNITS.Percent }, // - {"TURB ENG CORRECTED N1", 3, UNITS.Percent }, // - {"TURB ENG CORRECTED N1", 4, UNITS.Percent }, // - {"TURB ENG N1", 1, UNITS.Percent }, // - {"TURB ENG N1", 2, UNITS.Percent }, // - {"TURB ENG N1", 3, UNITS.Percent }, // - {"TURB ENG N1", 4, UNITS.Percent }, // - {"TURB ENG N2", 1, UNITS.Percent }, // - {"TURB ENG N2", 2, UNITS.Percent }, // - {"TURB ENG N2", 3, UNITS.Percent }, // - {"TURB ENG N2", 4, UNITS.Percent }, // - }; - DataDefinitionVariablePtr simVarsDataPtr; - - // Client events - // TODO: not yet used in this A380x implementation but kept for future use - ClientEventPtr toggleEngineStarter1Event; - ClientEventPtr toggleEngineStarter2Event; - ClientEventPtr toggleEngineStarter3Event; - ClientEventPtr toggleEngineStarter4Event; - CallbackID toggleEngineStarter1EventCallback{}; - CallbackID toggleEngineStarter2EventCallback{}; - CallbackID toggleEngineStarter3EventCallback{}; - CallbackID toggleEngineStarter4EventCallback{}; - ClientEventPtr setStarterHeldEvent[4]; - ClientEventPtr setStarterEvent[4]; - - // SimVars - AircraftVariablePtr engineCombustion[4]; // 0 or 1 - AircraftVariablePtr engineTime[4]; // in Seconds - - // LVars - NamedVariablePtr airlinerToFlexTemp; // Celsius - NamedVariablePtr apuRpmPercent; // Percent - NamedVariablePtr engineEgt[4]; // Celsius - NamedVariablePtr engineFF[4]; // kg/hour - NamedVariablePtr engineFuelUsed[4]; // kg - NamedVariablePtr engineIdleEGT; // Celsius - NamedVariablePtr engineIdleFF; - NamedVariablePtr engineIdleN1; // Percent - NamedVariablePtr engineIdleN3; // Percent - NamedVariablePtr engineN1[4]; // Percent - NamedVariablePtr engineN2[4]; // Percent - NamedVariablePtr engineN3[4]; // Percent - NamedVariablePtr engineOilTotal[4]; - NamedVariablePtr engineOil[4]; - NamedVariablePtr enginePreFF[4]; // kg/hour - NamedVariablePtr engineState[4]; - NamedVariablePtr engineTimer[4]; - NamedVariablePtr fuelLeftOuterPre; // Pounds - NamedVariablePtr fuelFeedOnePre; // Pounds - NamedVariablePtr fuelLeftMidPre; // Pounds - NamedVariablePtr fuelLeftInnerPre; // Pounds - NamedVariablePtr fuelFeedTwoPre; // Pounds - NamedVariablePtr fuelFeedThreePre; // Pounds - NamedVariablePtr fuelRightInnerPre; // Pounds - NamedVariablePtr fuelRightMidPre; // Pounds - NamedVariablePtr fuelFeedFourPre; // Pounds - NamedVariablePtr fuelRightOuterPre; // Pounds - NamedVariablePtr fuelTrimPre; // Pounds - NamedVariablePtr fuelPumpState[4]; - NamedVariablePtr packsState[2]; - NamedVariablePtr refuelRate; - NamedVariablePtr refuelStartedByUser; - NamedVariablePtr startState; - NamedVariablePtr thrustLimitClimb; - NamedVariablePtr thrustLimitFlex; - NamedVariablePtr thrustLimitIdle; - NamedVariablePtr thrustLimitMct; - NamedVariablePtr thrustLimitToga; - NamedVariablePtr thrustLimitType; - NamedVariablePtr wingAntiIce; - - // =============================================================================================== - - /** - * @brief Initializes the FadecSimData_A380X object. - * @param dm Pointer to the DataManager object that is used to manage the data definitions and variables. - */ - void initialize(DataManager* dm) { - initDataDefinitions(dm); - initEvents(dm); - initSimvars(dm); - initLvars(dm); - LOG_INFO("Fadec::FadecSimData_A32NX initialized"); - } - - void initDataDefinitions(DataManager* dm) { - atcIdDataPtr = dm->make_datadefinition_var("ATC ID DATA", atcIdDataDef, NO_AUTO_UPDATE); - - fuelFeedTankDataPtr = dm->make_datadefinition_var("FUEL FEED TANK DATA", fuelFeedTankDataDef); - fuelFeedTankDataPtr->requestPeriodicDataFromSim(SIMCONNECT_PERIOD_VISUAL_FRAME); - - fuelTankDataPtr = dm->make_datadefinition_var("FUEL TANK DATA", fuelTankDataDef); - fuelTankDataPtr->requestPeriodicDataFromSim(SIMCONNECT_PERIOD_VISUAL_FRAME); - - oilTempDataPtr[E1] = dm->make_datadefinition_var("GENERAL ENG OIL TEMPERATURE 1", oilTempE1DataDef, NO_AUTO_UPDATE); - oilTempDataPtr[E2] = dm->make_datadefinition_var("GENERAL ENG OIL TEMPERATURE 2", oilTempE2DataDef, NO_AUTO_UPDATE); - oilTempDataPtr[E3] = dm->make_datadefinition_var("GENERAL ENG OIL TEMPERATURE 3", oilTempE3DataDef, NO_AUTO_UPDATE); - oilTempDataPtr[E4] = dm->make_datadefinition_var("GENERAL ENG OIL TEMPERATURE 4", oilTempE4DataDef, NO_AUTO_UPDATE); - - oilPsiDataPtr[E1] = dm->make_datadefinition_var("GENERAL ENG OIL PRESSURE 1", oilPsiE1DataDef, NO_AUTO_UPDATE); - oilPsiDataPtr[E2] = dm->make_datadefinition_var("GENERAL ENG OIL PRESSURE 2", oilPsiE2DataDef, NO_AUTO_UPDATE); - oilPsiDataPtr[E3] = dm->make_datadefinition_var("GENERAL ENG OIL PRESSURE 3", oilPsiE3DataDef, NO_AUTO_UPDATE); - oilPsiDataPtr[E4] = dm->make_datadefinition_var("GENERAL ENG OIL PRESSURE 4", oilPsiE4DataDef, NO_AUTO_UPDATE); - - // TURB ENG CN2 is used as a proxy for N3 as the sim does not have a direct N3 value - engineCorrectedN3DataPtr[E1] = dm->make_datadefinition_var("TURB ENG CN2 1", engine1CN3DataDef, NO_AUTO_UPDATE); - engineCorrectedN3DataPtr[E2] = dm->make_datadefinition_var("TURB ENG CN2 2", engine2CN3DataDef, NO_AUTO_UPDATE); - engineCorrectedN3DataPtr[E3] = dm->make_datadefinition_var("TURB ENG CN2 3", engine3CN3DataDef, NO_AUTO_UPDATE); - engineCorrectedN3DataPtr[E4] = dm->make_datadefinition_var("TURB ENG CN2 4", engine4CN3DataDef, NO_AUTO_UPDATE); - - simVarsDataPtr = dm->make_datadefinition_var("SIMVARS DATA", simVarsDataDef); - simVarsDataPtr->requestPeriodicDataFromSim(SIMCONNECT_PERIOD_VISUAL_FRAME); - } - - void initEvents(DataManager* dm) { - // Create the client events for the engine starter toggles - // we just want to mask the events, not do anything with them - toggleEngineStarter1Event = dm->make_client_event("TOGGLE_STARTER1", true); - toggleEngineStarter1Event->addClientEventToNotificationGroup(NOTIFICATION_GROUP_0, true); - toggleEngineStarter2Event = dm->make_client_event("TOGGLE_STARTER2", true); - toggleEngineStarter2Event->addClientEventToNotificationGroup(NOTIFICATION_GROUP_0, true); - toggleEngineStarter3Event = dm->make_client_event("TOGGLE_STARTER3", true); - toggleEngineStarter3Event->addClientEventToNotificationGroup(NOTIFICATION_GROUP_0, true); - toggleEngineStarter4Event = dm->make_client_event("TOGGLE_STARTER4", true); - toggleEngineStarter4Event->addClientEventToNotificationGroup(NOTIFICATION_GROUP_0, true); - - setStarterHeldEvent[E1] = dm->make_client_event("SET_STARTER1_HELD", true, NOTIFICATION_GROUP_0); - setStarterHeldEvent[E2] = dm->make_client_event("SET_STARTER2_HELD", true, NOTIFICATION_GROUP_0); - setStarterHeldEvent[E3] = dm->make_client_event("SET_STARTER3_HELD", true, NOTIFICATION_GROUP_0); - setStarterHeldEvent[E4] = dm->make_client_event("SET_STARTER4_HELD", true, NOTIFICATION_GROUP_0); - - setStarterEvent[E1] = dm->make_client_event("STARTER1_SET", true, NOTIFICATION_GROUP_0); - setStarterEvent[E2] = dm->make_client_event("STARTER2_SET", true, NOTIFICATION_GROUP_0); - setStarterEvent[E3] = dm->make_client_event("STARTER3_SET", true, NOTIFICATION_GROUP_0); - setStarterEvent[E4] = dm->make_client_event("STARTER4_SET", true, NOTIFICATION_GROUP_0); - } - - void initSimvars(DataManager* dm) { - // not read each tick (mainly only once in initialization) - will be updated in code - engineCombustion[E1] = dm->make_aircraft_var("GENERAL ENG COMBUSTION", 1, "", nullptr, UNITS.Bool, NO_AUTO_UPDATE); - engineCombustion[E2] = dm->make_aircraft_var("GENERAL ENG COMBUSTION", 2, "", nullptr, UNITS.Bool, NO_AUTO_UPDATE); - engineCombustion[E3] = dm->make_aircraft_var("GENERAL ENG COMBUSTION", 3, "", nullptr, UNITS.Bool, NO_AUTO_UPDATE); - engineCombustion[E4] = dm->make_aircraft_var("GENERAL ENG COMBUSTION", 4, "", nullptr, UNITS.Bool, NO_AUTO_UPDATE); - - engineTime[E1] = dm->make_aircraft_var("GENERAL ENG ELAPSED TIME", 1, "", nullptr, UNITS.Seconds, NO_AUTO_UPDATE); - engineTime[E2] = dm->make_aircraft_var("GENERAL ENG ELAPSED TIME", 2, "", nullptr, UNITS.Seconds, NO_AUTO_UPDATE); - engineTime[E3] = dm->make_aircraft_var("GENERAL ENG ELAPSED TIME", 3, "", nullptr, UNITS.Seconds, NO_AUTO_UPDATE); - engineTime[E4] = dm->make_aircraft_var("GENERAL ENG ELAPSED TIME", 4, "", nullptr, UNITS.Seconds, NO_AUTO_UPDATE); - } - - void initLvars(DataManager* dm) { - // TODO: consider DataDefinition for the groups tha are read/write each tick - startState = dm->make_named_var("A32NX_START_STATE", UNITS.Number, NO_AUTO_UPDATE); - - engineIdleN1 = dm->make_named_var("A32NX_ENGINE_IDLE_N1", UNITS.Percent, AUTO_READ_WRITE); - engineIdleN3 = dm->make_named_var("A32NX_ENGINE_IDLE_N3", UNITS.Percent, AUTO_READ_WRITE); - engineIdleEGT = dm->make_named_var("A32NX_ENGINE_IDLE_EGT", UNITS.Number, AUTO_READ_WRITE); - engineIdleFF = dm->make_named_var("A32NX_ENGINE_IDLE_FF", UNITS.Number, AUTO_READ_WRITE); - - engineState[E1] = dm->make_named_var("A32NX_ENGINE_STATE:1", UNITS.Enum, AUTO_READ_WRITE); - engineState[E2] = dm->make_named_var("A32NX_ENGINE_STATE:2", UNITS.Enum, AUTO_READ_WRITE); - engineState[E3] = dm->make_named_var("A32NX_ENGINE_STATE:3", UNITS.Enum, AUTO_READ_WRITE); - engineState[E4] = dm->make_named_var("A32NX_ENGINE_STATE:4", UNITS.Enum, AUTO_READ_WRITE); - - engineN1[E1] = dm->make_named_var("A32NX_ENGINE_N1:1", UNITS.Percent, AUTO_READ_WRITE); - engineN1[E2] = dm->make_named_var("A32NX_ENGINE_N1:2", UNITS.Percent, AUTO_READ_WRITE); - engineN1[E3] = dm->make_named_var("A32NX_ENGINE_N1:3", UNITS.Percent, AUTO_READ_WRITE); - engineN1[E4] = dm->make_named_var("A32NX_ENGINE_N1:4", UNITS.Percent, AUTO_READ_WRITE); - - engineN2[E1] = dm->make_named_var("A32NX_ENGINE_N2:1", UNITS.Percent, AUTO_READ_WRITE); - engineN2[E2] = dm->make_named_var("A32NX_ENGINE_N2:2", UNITS.Percent, AUTO_READ_WRITE); - engineN2[E3] = dm->make_named_var("A32NX_ENGINE_N2:3", UNITS.Percent, AUTO_READ_WRITE); - engineN2[E4] = dm->make_named_var("A32NX_ENGINE_N2:4", UNITS.Percent, AUTO_READ_WRITE); - - engineN3[E1] = dm->make_named_var("A32NX_ENGINE_N3:1", UNITS.Percent, AUTO_READ_WRITE); - engineN3[E2] = dm->make_named_var("A32NX_ENGINE_N3:2", UNITS.Percent, AUTO_READ_WRITE); - engineN3[E3] = dm->make_named_var("A32NX_ENGINE_N3:3", UNITS.Percent, AUTO_READ_WRITE); - engineN3[E4] = dm->make_named_var("A32NX_ENGINE_N3:4", UNITS.Percent, AUTO_READ_WRITE); - - engineEgt[E1] = dm->make_named_var("A32NX_ENGINE_EGT:1", UNITS.Celsius, AUTO_READ_WRITE); - engineEgt[E2] = dm->make_named_var("A32NX_ENGINE_EGT:2", UNITS.Celsius, AUTO_READ_WRITE); - engineEgt[E3] = dm->make_named_var("A32NX_ENGINE_EGT:3", UNITS.Celsius, AUTO_READ_WRITE); - engineEgt[E4] = dm->make_named_var("A32NX_ENGINE_EGT:4", UNITS.Celsius, AUTO_READ_WRITE); - - engineFF[E1] = dm->make_named_var("A32NX_ENGINE_FF:1", UNITS.Number, AUTO_READ_WRITE); - engineFF[E2] = dm->make_named_var("A32NX_ENGINE_FF:2", UNITS.Number, AUTO_READ_WRITE); - engineFF[E3] = dm->make_named_var("A32NX_ENGINE_FF:3", UNITS.Number, AUTO_READ_WRITE); - engineFF[E4] = dm->make_named_var("A32NX_ENGINE_FF:4", UNITS.Number, AUTO_READ_WRITE); - - engineFuelUsed[E1] = dm->make_named_var("A32NX_FUEL_USED:1", UNITS.Number, AUTO_READ_WRITE); - engineFuelUsed[E2] = dm->make_named_var("A32NX_FUEL_USED:2", UNITS.Number, AUTO_READ_WRITE); - engineFuelUsed[E3] = dm->make_named_var("A32NX_FUEL_USED:3", UNITS.Number, AUTO_READ_WRITE); - engineFuelUsed[E4] = dm->make_named_var("A32NX_FUEL_USED:4", UNITS.Number, AUTO_READ_WRITE); - - engineOil[E1] = dm->make_named_var("A32NX_ENGINE_OIL_QTY:1", UNITS.Number, AUTO_READ_WRITE); - engineOil[E2] = dm->make_named_var("A32NX_ENGINE_OIL_QTY:2", UNITS.Number, AUTO_READ_WRITE); - engineOil[E3] = dm->make_named_var("A32NX_ENGINE_OIL_QTY:3", UNITS.Number, AUTO_READ_WRITE); - engineOil[E4] = dm->make_named_var("A32NX_ENGINE_OIL_QTY:4", UNITS.Number, AUTO_READ_WRITE); - - engineOilTotal[E1] = dm->make_named_var("A32NX_ENGINE_OIL_TOTAL:1", UNITS.Number, AUTO_READ_WRITE); - engineOilTotal[E2] = dm->make_named_var("A32NX_ENGINE_OIL_TOTAL:2", UNITS.Number, AUTO_READ_WRITE); - engineOilTotal[E3] = dm->make_named_var("A32NX_ENGINE_OIL_TOTAL:3", UNITS.Number, AUTO_READ_WRITE); - engineOilTotal[E4] = dm->make_named_var("A32NX_ENGINE_OIL_TOTAL:4", UNITS.Number, AUTO_READ_WRITE); - - enginePreFF[E1] = dm->make_named_var("A32NX_ENGINE_PRE_FF:1", UNITS.Number, AUTO_READ_WRITE); - enginePreFF[E2] = dm->make_named_var("A32NX_ENGINE_PRE_FF:2", UNITS.Number, AUTO_READ_WRITE); - enginePreFF[E3] = dm->make_named_var("A32NX_ENGINE_PRE_FF:3", UNITS.Number, AUTO_READ_WRITE); - enginePreFF[E4] = dm->make_named_var("A32NX_ENGINE_PRE_FF:4", UNITS.Number, AUTO_READ_WRITE); - - engineTimer[E1] = dm->make_named_var("A32NX_ENGINE_TIMER:1", UNITS.Number, AUTO_READ_WRITE); - engineTimer[E2] = dm->make_named_var("A32NX_ENGINE_TIMER:2", UNITS.Number, AUTO_READ_WRITE); - engineTimer[E3] = dm->make_named_var("A32NX_ENGINE_TIMER:3", UNITS.Number, AUTO_READ_WRITE); - engineTimer[E4] = dm->make_named_var("A32NX_ENGINE_TIMER:4", UNITS.Number, AUTO_READ_WRITE); - - fuelLeftOuterPre = dm->make_named_var("A32NX_FUEL_LEFTOUTER_PRE", UNITS.Pounds, AUTO_READ_WRITE); - fuelFeedOnePre = dm->make_named_var("A32NX_FUEL_FEED1_PRE", UNITS.Pounds, AUTO_READ_WRITE); - fuelLeftMidPre = dm->make_named_var("A32NX_FUEL_LEFTMID_PRE", UNITS.Pounds, AUTO_READ_WRITE); - fuelLeftInnerPre = dm->make_named_var("A32NX_FUEL_LEFTINNER_PRE", UNITS.Pounds, AUTO_READ_WRITE); - fuelFeedTwoPre = dm->make_named_var("A32NX_FUEL_FEED2_PRE", UNITS.Pounds, AUTO_READ_WRITE); - fuelFeedThreePre = dm->make_named_var("A32NX_FUEL_FEED3_PRE", UNITS.Pounds, AUTO_READ_WRITE); - fuelRightInnerPre = dm->make_named_var("A32NX_FUEL_RIGHTINNER_PRE", UNITS.Pounds, AUTO_READ_WRITE); - fuelRightMidPre = dm->make_named_var("A32NX_FUEL_RIGHTMID_PRE", UNITS.Pounds, AUTO_READ_WRITE); - fuelFeedFourPre = dm->make_named_var("A32NX_FUEL_FEED4_PRE", UNITS.Pounds, AUTO_READ_WRITE); - fuelRightOuterPre = dm->make_named_var("A32NX_FUEL_RIGHTOUTER_PRE", UNITS.Pounds, AUTO_READ_WRITE); - fuelTrimPre = dm->make_named_var("A32NX_FUEL_TRIM_PRE", UNITS.Pounds, AUTO_READ_WRITE); - - fuelPumpState[E1] = dm->make_named_var("A32NX_PUMP_STATE:1", UNITS.Number, AUTO_READ_WRITE); - fuelPumpState[E2] = dm->make_named_var("A32NX_PUMP_STATE:2", UNITS.Number, AUTO_READ_WRITE); - fuelPumpState[E3] = dm->make_named_var("A32NX_PUMP_STATE:3", UNITS.Number, AUTO_READ_WRITE); - fuelPumpState[E4] = dm->make_named_var("A32NX_PUMP_STATE:4", UNITS.Number, AUTO_READ_WRITE); - - thrustLimitType = dm->make_named_var("A32NX_AUTOTHRUST_THRUST_LIMIT_TYPE", UNITS.Number, AUTO_READ); - thrustLimitIdle = dm->make_named_var("A32NX_AUTOTHRUST_THRUST_LIMIT_IDLE", UNITS.Number, AUTO_WRITE); - thrustLimitClimb = dm->make_named_var("A32NX_AUTOTHRUST_THRUST_LIMIT_CLB", UNITS.Number, AUTO_WRITE); - thrustLimitFlex = dm->make_named_var("A32NX_AUTOTHRUST_THRUST_LIMIT_FLX", UNITS.Number, AUTO_WRITE); - thrustLimitMct = dm->make_named_var("A32NX_AUTOTHRUST_THRUST_LIMIT_MCT", UNITS.Number, AUTO_WRITE); - thrustLimitToga = dm->make_named_var("A32NX_AUTOTHRUST_THRUST_LIMIT_TOGA", UNITS.Number, AUTO_WRITE); - - packsState[0] = dm->make_named_var("A32NX_COND_PACK_FLOW_VALVE_1_IS_OPEN", UNITS.Number, AUTO_READ); - packsState[1] = dm->make_named_var("A32NX_COND_PACK_FLOW_VALVE_2_IS_OPEN", UNITS.Number, AUTO_READ); - wingAntiIce = dm->make_named_var("A32NX_PNEU_WING_ANTI_ICE_SYSTEM_ON", UNITS.Number, AUTO_READ); - refuelRate = dm->make_named_var("A32NX_EFB_REFUEL_RATE_SETTING", UNITS.Number, AUTO_READ); - refuelStartedByUser = dm->make_named_var("A32NX_REFUEL_STARTED_BY_USR", UNITS.Gallons, AUTO_READ); - airlinerToFlexTemp = dm->make_named_var("AIRLINER_TO_FLEX_TEMP", UNITS.Number, AUTO_READ); - apuRpmPercent = dm->make_named_var("A32NX_APU_N_RAW", UNITS.Number, AUTO_READ); - - // reset LVars to 0 - engineEgt[E1]->setAndWriteToSim(0); - engineEgt[E2]->setAndWriteToSim(0); - engineEgt[E3]->setAndWriteToSim(0); - engineEgt[E4]->setAndWriteToSim(0); - engineFF[E1]->setAndWriteToSim(0); - engineFF[E2]->setAndWriteToSim(0); - engineFF[E3]->setAndWriteToSim(0); - engineFF[E4]->setAndWriteToSim(0); - engineFuelUsed[E1]->setAndWriteToSim(0); - engineFuelUsed[E2]->setAndWriteToSim(0); - engineFuelUsed[E3]->setAndWriteToSim(0); - engineFuelUsed[E4]->setAndWriteToSim(0); - engineIdleEGT->setAndWriteToSim(0); - engineIdleFF->setAndWriteToSim(0); - engineIdleN1->setAndWriteToSim(0); - engineIdleN3->setAndWriteToSim(0); - engineN1[E1]->setAndWriteToSim(0); - engineN1[E2]->setAndWriteToSim(0); - engineN1[E3]->setAndWriteToSim(0); - engineN1[E4]->setAndWriteToSim(0); - engineN2[E1]->setAndWriteToSim(0); - engineN2[E2]->setAndWriteToSim(0); - engineN2[E3]->setAndWriteToSim(0); - engineN2[E4]->setAndWriteToSim(0); - engineOilTotal[E1]->setAndWriteToSim(0); - engineOilTotal[E2]->setAndWriteToSim(0); - engineOilTotal[E3]->setAndWriteToSim(0); - engineOilTotal[E4]->setAndWriteToSim(0); - engineOil[E1]->setAndWriteToSim(0); - engineOil[E2]->setAndWriteToSim(0); - engineOil[E3]->setAndWriteToSim(0); - engineOil[E4]->setAndWriteToSim(0); - enginePreFF[E1]->setAndWriteToSim(0); - enginePreFF[E2]->setAndWriteToSim(0); - enginePreFF[E3]->setAndWriteToSim(0); - enginePreFF[E4]->setAndWriteToSim(0); - engineTimer[E1]->setAndWriteToSim(0); - engineTimer[E2]->setAndWriteToSim(0); - engineTimer[E3]->setAndWriteToSim(0); - engineTimer[E4]->setAndWriteToSim(0); - fuelPumpState[E1]->setAndWriteToSim(0); - fuelPumpState[E2]->setAndWriteToSim(0); - fuelPumpState[E3]->setAndWriteToSim(0); - fuelPumpState[E4]->setAndWriteToSim(0); - thrustLimitClimb->setAndWriteToSim(0); - thrustLimitFlex->setAndWriteToSim(0); - thrustLimitIdle->setAndWriteToSim(0); - thrustLimitMct->setAndWriteToSim(0); - thrustLimitToga->setAndWriteToSim(0); - } -}; - -#endif // FLYBYWIRE_AIRCRAFT_FADECSIMDATA_A380X_HPP diff --git a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Fadec_A380X.cpp b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Fadec_A380X.cpp deleted file mode 100644 index cd8f18aacc0..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Fadec_A380X.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#include "Fadec_A380X.h" - -bool Fadec_A380X::initialize() { - engineControl.initialize(&msfsHandler); - - _isInitialized = true; - LOG_INFO("Fadec_A380X initialized"); - return true; -} - -bool Fadec_A380X::update([[maybe_unused]] sGaugeDrawData* pData) { - if (!_isInitialized) { - std::cerr << "Fadec_A380X::update() - not initialized" << std::endl; - return false; - } - - engineControl.update(pData); - - return true; -} - -bool Fadec_A380X::shutdown() { - _isInitialized = false; - LOG_INFO("Fadec_A380X::shutdown()"); - return true; -} diff --git a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Fadec_A380X.h b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Fadec_A380X.h deleted file mode 100644 index 689d97144f5..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Fadec_A380X.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_FADEC_A380X_H -#define FLYBYWIRE_AIRCRAFT_FADEC_A380X_H - -#include "EngineControl_A380X.h" -#include "Fadec.h" - -/** - * @brief: The Fadec_A380X class is responsible for managing the FADEC system for the A380X aircraft. - * - * In this current implementation is only holding the EngineControl_A380X instance and is - * responsible for calling its initialize, update and shutdown methods. - * The actual fadec logic is implemented in the EngineControl_A380X class. - */ -class Fadec_A380X : public Fadec { - private: - // Engine control instance - EngineControl_A380X engineControl{}; - - public: - /** - * Creates a new Fadec_A32NX instance and takes a reference to the MsfsHandler instance. - * @param msfsHandler The MsfsHandler instance that is used to communicate with the simulator. - */ - explicit Fadec_A380X(MsfsHandler& msfsHandler) : Fadec(msfsHandler) {} - - bool initialize() override; - bool preUpdate(sGaugeDrawData*) override { return true; } - bool update(sGaugeDrawData* pData) override; - bool postUpdate(sGaugeDrawData*) override { return true; } - bool shutdown() override; -}; - -#endif // FLYBYWIRE_AIRCRAFT_FADEC_A380X_H diff --git a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/FuelConfiguration_A380X.cpp b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/FuelConfiguration_A380X.cpp deleted file mode 100644 index a4b076f030a..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/FuelConfiguration_A380X.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#include -#include - -#include "inih/ini.h" -#include "inih/ini_type_conversion.h" - -#include "logging.h" - -#include "FuelConfiguration_A380X.h" - -void FuelConfiguration_A380X::loadConfigurationFromIni() { - LOG_INFO("Fadec::FuelConfiguration: loading configuration file " + configFilename); - - mINI::INIStructure ini; - mINI::INIFile iniFile(configFilename); - - if (!iniFile.read(ini)) { - LOG_ERROR("Fadec::FuelConfiguration_A380X: failed to read configuration file " + configFilename + " due to error \"" + strerror(errno) + - "\" -> using default fuel quantities."); - return; - } - - fuelLeftOuterGallons = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_LEFT_OUTER_QTY, fuelLeftOuterDefault); - fuelFeedOneGallons = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_FEED_ONE_QTY, fuelFeedOneDefault); - fuelLeftMidGallons = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_LEFT_MID_QTY, fuelLeftMidDefault); - fuelLeftInnerGallons = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_LEFT_INNER_QTY, fuelLeftInnerDefault); - fuelFeedTwoGallons = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_FEED_TWO_QTY, fuelFeedTwoDefault); - fuelFeedThreeGallons = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_FEED_THREE_QTY, fuelFeedThreeDefault); - fuelRightInnerGallons = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_RIGHT_INNER_QTY, fuelRightInnerDefault); - fuelRightMidGallons = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_RIGHT_MID_QTY, fuelRightMidDefault); - fuelFeedFourGallons = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_FEED_FOUR_QTY, fuelFeedFourDefault); - fuelRightOuterGallons = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_RIGHT_OUTER_QTY, fuelRightOuterDefault); - fuelTrimGallons = mINI::INITypeConversion::getDouble(ini, INI_SECTION_FUEL, INI_SECTION_FUEL_TRIM_QTY, fuelTrimDefault); - - LOG_DEBUG("Fadec::FuelConfiguration_A380X: loaded fuel configuration from " + configFilename + " with the following values:"); - LOG_DEBUG("Fadec::FuelConfiguration_A380X: " + this->toString()); -} - -void FuelConfiguration_A380X::saveConfigurationToIni() { - LOG_DEBUG("Fadec::FuelConfiguration_A380X: saving configuration file " + configFilename); - - mINI::INIStructure ini; - mINI::INIFile iniFile(configFilename); - - // Do not check a possible error since the file may not exist yet - iniFile.read(ini); - - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_LEFT_OUTER_QTY] = std::to_string(this->fuelLeftOuterGallons); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_FEED_ONE_QTY] = std::to_string(this->fuelFeedOneGallons); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_LEFT_MID_QTY] = std::to_string(this->fuelLeftMidGallons); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_LEFT_INNER_QTY] = std::to_string(this->fuelLeftInnerGallons); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_FEED_TWO_QTY] = std::to_string(this->fuelFeedTwoGallons); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_FEED_THREE_QTY] = std::to_string(this->fuelFeedThreeGallons); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_RIGHT_INNER_QTY] = std::to_string(this->fuelRightInnerGallons); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_RIGHT_MID_QTY] = std::to_string(this->fuelRightMidGallons); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_FEED_FOUR_QTY] = std::to_string(this->fuelFeedFourGallons); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_RIGHT_OUTER_QTY] = std::to_string(this->fuelRightOuterGallons); - ini[INI_SECTION_FUEL][INI_SECTION_FUEL_TRIM_QTY] = std::to_string(this->fuelTrimGallons); - - if (!iniFile.write(ini, true)) { - LOG_ERROR("Fadec::FuelConfiguration_A380X: failed to write engine conf " + configFilename + " due to error \"" + strerror(errno) + "\""); - return; - } - - LOG_DEBUG("Fadec::FuelConfiguration_A380X: saved fuel configuration to " + configFilename + " with the following values:"); - LOG_DEBUG("Fadec::FuelConfiguration_A380X: " + this->toString()); -} - -std::string FuelConfiguration_A380X::toString() const { - std::ostringstream oss; - oss << "FuelConfiguration_A380X { " << "\n" - << " fuelLeftOuter: " << fuelLeftOuterGallons << "\n" - << " fuelFeedOne: " << fuelFeedOneGallons << "\n" - << " fuelLeftMid: " << fuelLeftMidGallons << "\n" - << " fuelLeftInner: " << fuelLeftInnerGallons << "\n" - << " fuelFeedTwo: " << fuelFeedTwoGallons << "\n" - << " fuelFeedThree: " << fuelFeedThreeGallons << "\n" - << " fuelRightInner: " << fuelRightInnerGallons << "\n" - << " fuelRightMid: " << fuelRightMidGallons << "\n" - << " fuelFeedFour: " << fuelFeedFourGallons << "\n" - << " fuelRightOuter: " << fuelRightOuterGallons << "\n" - << " fuelTrim: " << fuelTrimGallons << "\n" - << "}"; - return oss.str(); -} diff --git a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/FuelConfiguration_A380X.h b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/FuelConfiguration_A380X.h deleted file mode 100644 index 928ce794d67..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/FuelConfiguration_A380X.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_FUELCONFIGURATION_A380X_H -#define FLYBYWIRE_AIRCRAFT_FUELCONFIGURATION_A380X_H - -#include - -#define INI_SECTION_FUEL "FUEL" -#define INI_SECTION_FUEL_LEFT_OUTER_QTY "FUEL_LEFT_OUTER_QTY" -#define INI_SECTION_FUEL_FEED_ONE_QTY "FUEL_FEED_ONE_QTY" -#define INI_SECTION_FUEL_LEFT_MID_QTY "FUEL_LEFT_MID_QTY" -#define INI_SECTION_FUEL_LEFT_INNER_QTY "FUEL_LEFT_INNER_QTY" -#define INI_SECTION_FUEL_FEED_TWO_QTY "FUEL_FEED_TWO_QTY" -#define INI_SECTION_FUEL_FEED_THREE_QTY "FUEL_FEED_THREE_QTY" -#define INI_SECTION_FUEL_RIGHT_INNER_QTY "FUEL_RIGHT_INNER_QTY" -#define INI_SECTION_FUEL_RIGHT_MID_QTY "FUEL_RIGHT_MID_QTY" -#define INI_SECTION_FUEL_FEED_FOUR_QTY "FUEL_FEED_FOUR_QTY" -#define INI_SECTION_FUEL_RIGHT_OUTER_QTY "FUEL_RIGHT_OUTER_QTY" -#define INI_SECTION_FUEL_TRIM_QTY "FUEL_TRIM_QTY" - -/** - * @class FuelConfiguration - * @brief This struct represents the fuel configuration for the aircraft. - * - * This class provides methods to load and save the fuel configuration from/to an INI file. - * It also provides getter and setter methods for each fuel tank quantity. - */ -class FuelConfiguration_A380X { - // Fuel tank default quantities in gallons - static constexpr double fuelFeedOneDefault = 1082.0; // tank 2 - static constexpr double fuelFeedTwoDefault = fuelFeedOneDefault; // tank 5 - static constexpr double fuelFeedThreeDefault = fuelFeedOneDefault; // tank 6 - static constexpr double fuelFeedFourDefault = fuelFeedOneDefault; // tank 9 - - static constexpr double fuelLeftOuterDefault = 2731.0; // tank 1 - static constexpr double fuelRightOuterDefault = fuelLeftOuterDefault; // tank 10 - static constexpr double fuelLeftMidDefault = 9630.0; // tank 3 - static constexpr double fuelRightMidDefault = fuelLeftMidDefault; // tank 8 - static constexpr double fuelLeftInnerDefault = 12187.0; // tank 4 - static constexpr double fuelRightInnerDefault = fuelLeftInnerDefault; // tank 7 - - static constexpr double fuelTrimDefault = 6259.0; // tank 11 - - private: - // Actual fuel tank quantities in gallons - double fuelLeftOuterGallons; - double fuelFeedOneGallons; - double fuelLeftMidGallons; - double fuelLeftInnerGallons; - double fuelRightOuterGallons; - double fuelFeedTwoGallons; - double fuelRightMidGallons; - double fuelRightInnerGallons; - double fuelFeedThreeGallons; - double fuelFeedFourGallons; - double fuelTrimGallons; - - std::string configFilename{"A380X-default-fuel-config.ini"}; - - public: - - /** - * @brief Returns the filename of the INI file to use for loading and saving the fuel configuration. - */ - std::string getConfigFilename() const { return configFilename; } - - /** - * @brief Sets the filename of the INI file to use for loading and saving the fuel configuration. - * - * This must to be done before calling loadConfigurationFromIni or saveConfigurationToIni otherwise - * the default filename will be used. - * - * @param configFilename The filename of the INI file to use for loading and saving the fuel configuration. - */ - void setConfigFilename(const std::string& configFilename) { this->configFilename = configFilename; } - - /** - * @brief Loads the fuel configuration from an INI file. - * - * This method reads the INI file specified in the configFilename member variable and updates the fuel quantities accordingly. - * If the INI file cannot be read, an error message is logged and the method returns without making any changes. - */ - void loadConfigurationFromIni(); - - /** - * @brief Saves the current fuel configuration to an INI file. - * - * This method writes the current fuel quantities to the INI file specified in the configFilename member variable. - * If the INI file cannot be written, an error message is logged. - */ - void saveConfigurationToIni(); - - public: - double getFuelFeedOneGallons() const { return fuelFeedOneGallons; } - double getFuelLeftOuterGallons() const { return fuelLeftOuterGallons; } - double getFuelLeftMidGallons() const { return fuelLeftMidGallons; } - double getFuelLeftInnerGallons() const { return fuelLeftInnerGallons; } - double getFuelFeedTwoGallons() const { return fuelFeedTwoGallons; } - double getFuelFeedThreeGallons() const { return fuelFeedThreeGallons; } - double getFuelRightInnerGallons() const { return fuelRightInnerGallons; } - double getFuelRightMidGallons() const { return fuelRightMidGallons; } - double getFuelFeedFourGallons() const { return fuelFeedFourGallons; } - double getFuelRightOuterGallons() const { return fuelRightOuterGallons; } - double getFuelTrimGallons() const { return fuelTrimGallons; } - - void setFuelLeftOuterGallons(double fuelLeftOuterGallons) { this->fuelLeftOuterGallons = fuelLeftOuterGallons; } - void setFuelFeedOneGallons(double fuelFeedOneGallons) { this->fuelFeedOneGallons = fuelFeedOneGallons; } - void setFuelLeftMidGallons(double fuelLeftMidGallons) { this->fuelLeftMidGallons = fuelLeftMidGallons; } - void setFuelLeftInnerGallons(double fuelLeftInnerGallons) { this->fuelLeftInnerGallons = fuelLeftInnerGallons; } - void setFuelFeedTwoGallons(double fuelFeedTwoGallons) { this->fuelFeedTwoGallons = fuelFeedTwoGallons; } - void setFuelFeedThreeGallons(double fuelFeedThreeGallons) { this->fuelFeedThreeGallons = fuelFeedThreeGallons; } - void setFuelRightInnerGallons(double fuelRightInnerGallons) { this->fuelRightInnerGallons = fuelRightInnerGallons; } - void setFuelRightMidGallons(double fuelRightMidGallons) { this->fuelRightMidGallons = fuelRightMidGallons; } - void setFuelFeedFourGallons(double fuelFeedFourGallons) { this->fuelFeedFourGallons = fuelFeedFourGallons; } - void setFuelRightOuterGallons(double fuelRightOuterGallons) { this->fuelRightOuterGallons = fuelRightOuterGallons; } - void setFuelTrimGallons(double fuelTrimGallons) { this->fuelTrimGallons = fuelTrimGallons; } - - std::string toString() const; -}; - -#endif // FLYBYWIRE_AIRCRAFT_FUELCONFIGURATION_A380X_H diff --git a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Polynomials_A380X.hpp b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Polynomials_A380X.hpp deleted file mode 100644 index 316a4fbee59..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Polynomials_A380X.hpp +++ /dev/null @@ -1,449 +0,0 @@ -#ifndef FLYBYWIRE_AIRCRAFT_POLYNOMIAL_H -#define FLYBYWIRE_AIRCRAFT_POLYNOMIAL_H - -#include - -/** - * @class Polynomial - * @brief A collection of multi-variate regression polynomials for engine parameters. - * - * This class provides a set of static methods that represent multi-variate regression polynomials. - * These methods are used to calculate various engine parameters such as N1, N3, EGT, Fuel Flow, - * Oil Temperature, Oil Gulping, and Oil Pressure. - * Each method takes specific inputs related to the engine state and returns the calculated parameter value. - * - * TODO: Many of the values/polynomials used in these methods are identical to the A32NX values and - * likely need to be adjusted to match the A380X engine model. - */ -class Polynomial_A380X { - public: - /** - * @brief Calculates the N3 value during engine startup using real-life modeled polynomials. - * - * @param currentSimN3 The current N3 value in percent (taken from the sim's N2 value). - * @param previousN3 The previous N3 value in percent. - * @param idleN3 The idle N3 value in percent. - * @return The calculated N3 value in percent. - */ - static double startN3(double currentSimN3, double previousN3, double idleN3) { - // Normalize the current N3 percentage by scaling it with the idle N3 - // percentage and a constant factor. - // The constant factor 60.0 is likely derived from empirical data or a mathematical model of the - // engine's behavior. - double normalizedN3 = currentSimN3 * 60.0 / idleN3; - - // Coefficients for the polynomial used to calculate the N3 percentage. - constexpr double coefficients[16] = { - 4.03649879e+00, // coefficient for x^0 - -9.41981960e-01, // coefficient for x^1 - 1.98426614e-01, // coefficient for x^2 - -2.11907840e-02, // coefficient for x^3 - 1.00777507e-03, // coefficient for x^4 - -1.57319166e-06, // coefficient for x^5 - -2.15034888e-06, // coefficient for x^6 - 1.08288379e-07, // coefficient for x^7 - -2.48504632e-09, // coefficient for x^8 - 2.52307089e-11, // coefficient for x^9 - -2.06869243e-14, // coefficient for x^10 - 8.99045761e-16, // coefficient for x^11 - -9.94853959e-17, // coefficient for x^12 - 1.85366499e-18, // coefficient for x^13 - -1.44869928e-20, // coefficient for x^14 - 4.31033031e-23 // coefficient for x^15 - }; - - // Calculate the N3 value during engine startup using a polynomial model - double outN3 = 0; - for (int i = 0; i < 16; i++) { - outN3 += coefficients[i] * (std::pow)(normalizedN3, i); - } - outN3 *= currentSimN3; - - // Ensure the calculated N3 value is within the expected range - if (outN3 < previousN3) { - outN3 = previousN3 + 0.002; - } - if (outN3 >= idleN3 + 0.1) { - outN3 = idleN3 + 0.05; - } - - // Return the calculated N3 value - return outN3; - } - - /** - * @brief Calculates the N1 value during engine startup. - * - * @param fbwN3 The current custom N3 value in percent. - * @param idleN3 The idle N3 value in percent. - * @param idleN1 The idle N1 value in percent. - * @return The calculated N1 value in percent. - */ - static double startN1(double fbwN3, double idleN3, double idleN1) { - // Normalize the current N3 value - double normalizedN3 = fbwN3 / idleN3; - - // Coefficients for the polynomial used to calculate the N1 percentage. - constexpr double coefficients[9] = { - -2.2812156e-12, // coefficient for x^0 - -5.9830374e+01, // coefficient for x^1 - 7.0629094e+02, // coefficient for x^2 - -3.4580361e+03, // coefficient for x^3 - 9.1428923e+03, // coefficient for x^4 - -1.4097740e+04, // coefficient for x^5 - 1.2704110e+04, // coefficient for x^6 - -6.2099935e+03, // coefficient for x^7 - 1.2733071e+03 // coefficient for x^8 - }; - - // Calculate the N1 value during engine startup using a polynomial model - double normalN1pre = - (-2.4698087 * (std::pow)(normalizedN3, 3)) + (0.9662026 * (std::pow)(normalizedN3, 2)) + (0.0701367 * normalizedN3); - double normalN1post = 0; - for (int i = 0; i < 9; i++) { - normalN1post += coefficients[i] * (std::pow)(normalizedN3, i); - } - - // Return the calculated N1 value - if (normalN1post >= normalN1pre) { - return normalN1post * idleN1; - } else { - return normalN1pre * idleN1; - } - } - - /** - * @brief Calculates the Fuel Flow (Kg/hr) during engine startup using real-life modeled polynomials. - * - * @param fbwN3 The current N3 value in percent. - * @param idleN3 The idle N3 value in percent. - * @param idleFF The idle Fuel Flow value in Kg/hr. - * @return The calculated Fuel Flow value in Kg/hr. - */ - static double startFF(double fbwN3, double idleN3, double idleFF) { - // Normalize the current N3 value - double normalizedN3 = fbwN3 / idleN3; - - // Initialize the normalized Fuel Flow value - double normalizedFF = 0; - - // Coefficients for the polynomial calculation - constexpr double coefficients[9] = { - 3.1110282e-12, // coefficient for x^0 - 1.0804331e+02, // coefficient for x^1 - -1.3972629e+03, // coefficient for x^2 - 7.4874131e+03, // coefficient for x^3 - -2.1511983e+04, // coefficient for x^4 - 3.5957757e+04, // coefficient for x^5 - -3.5093994e+04, // coefficient for x^6 - 1.8573033e+04, // coefficient for x^7 - -4.1220062e+03 // coefficient for x^8 - }; - - // Calculate the normalized Fuel Flow value using a polynomial model if the normalized N3 value is greater than 0.37 - if (normalizedN3 > 0.37) { - for (int i = 0; i < 9; i++) { - normalizedFF += coefficients[i] * (std::pow)(normalizedN3, i); - } - } - - // Ensure the calculated normalized Fuel Flow value is non-negative - if (normalizedFF < 0) { - normalizedFF = 0; - } - - // Return the calculated Fuel Flow value - return normalizedFF * idleFF; - } - - /** - * @brief Calculates the Exhaust Gas Temperature (EGT) during engine startup using real-life modeled polynomials. - * - * @param fbwN3 The current N3 value in percent. - * @param idleN3 The idle N3 value in percent. - * @param ambientTemp The ambient temperature in degrees Celsius. - * @param idleEGT The idle EGT value in degrees Celsius. - * @return The calculated EGT value in degrees Celsius. - */ - static double startEGT(double fbwN3, double idleN3, double ambientTemp, double idleEGT) { - // Normalize the current N3 value - double normalizedN3 = fbwN3 / idleN3; - - // Calculate the normalized EGT value based on the normalized N3 value - double normalizedEGT = 0; - if (normalizedN3 < 0.17) { - normalizedEGT = 0; - } else if (normalizedN3 <= 0.4) { - normalizedEGT = (0.04783 * normalizedN3) - 0.00813; - } else { - // Coefficients for the polynomial calculation - double egtCoefficients[9] = { - -6.8725167e+02, // coefficient for x^0 - 7.7548864e+03, // coefficient for x^1 - -3.7507098e+04, // coefficient for x^2 - 1.0147016e+05, // coefficient for x^3 - -1.6779273e+05, // coefficient for x^4 - 1.7357157e+05, // coefficient for x^5 - -1.0960924e+05, // coefficient for x^6 - 3.8591956e+04, // coefficient for x^7 - -5.7912600e+03 // coefficient for x^8 - }; - - // Calculate the normalized EGT value using a polynomial model - for (int i = 0; i < 9; i++) { - normalizedEGT += egtCoefficients[i] * (std::pow)(normalizedN3, i); - } - } - - // Calculate and return the EGT value - return (normalizedEGT * (idleEGT - ambientTemp)) + ambientTemp; - } - - /** - * @brief Calculates the Oil Temperature during engine start-up. - * - * @param fbwN3 The current custom N3 percentage. - * @param idleN3 The custom idle N3 percentage. - * @param ambientTemperature The ambient temperature. - * @return The calculated Oil Temperature. - */ - static double startOilTemp(double fbwN3, double idleN3, double ambientTemperature) { - if (fbwN3 < 0.79 * idleN3) { - return ambientTemperature; - } - if (fbwN3 < 0.98 * idleN3) { - return ambientTemperature + 5; - } - return ambientTemperature + 10; - } - - /** - * @brief Calculates the N3 value during engine shutdown. - * - * @param previousN3 The previous N3 value in percent. - * @param deltaTime The elapsed time since the last update in seconds. - * @return The calculated N3 value in percent. - */ - static double shutdownN3(double previousN3, double deltaTime) { - // The decayRate is used to model the rate at which the N3 percentage decreases during the engine - // shutdown process. - // The specific values -0.0515 and -0.08183 are likely derived from empirical - // data or a mathematical model of the engine's behavior. - // The choice to use a different decay rate for 'previousN2' values below 30 suggests that the - // engine's shutdown behavior changes at this threshold. - double decayRate = previousN3 < 30 ? -0.0515 : -0.08183; - return previousN3 * (std::exp)(decayRate * deltaTime); - } - - /** - * @brief Calculates the N1 value during engine shutdown. - * - * @param previousN1 The previous N1 value in percent. - * @param deltaTime The elapsed time since the last update in seconds. - * @return The calculated N1 value in percent. - */ - static double shutdownN1(double previousN1, double deltaTime) { - // The decayRate is used to model the rate at which the N1 percentage decreases during the engine - // shutdown process. - // The specific values -0.08 and -0.164 are likely derived from empirical data or a mathematical - // model of the engine's behavior. The choice to use a different decay rate for 'previousN1' values - // below 4 suggests that the engine's shutdown behavior changes at this threshold. - double decayRate = previousN1 < 4 ? -0.08 : -0.164; - return previousN1 * exp(decayRate * deltaTime); - } - - /** - * @brief Calculates the Exhaust Gas Temperature (EGT) during engine shutdown. - * - * @param previousEGT The previous EGT value in degrees Celsius. - * @param ambientTemp The ambient temperature in degrees Celsius. - * @param deltaTime The elapsed time since the last update in seconds. - * @return The calculated EGT value in degrees Celsius. - */ - static double shutdownEGT(double previousEGT, double ambientTemp, double deltaTime) { - // The specific values used (140, 0.0257743, 135, 0.00072756, and 30) are likely derived from empirical - // data or a mathematical model of the engine's behavior. - // The choice to use different decay rates and steady state temperatures based on the previous - // EGT suggests that the engine's shutdown behavior changes at this threshold. - double threshold = ambientTemp + 140; - double decayRate = previousEGT > threshold ? 0.0257743 : 0.00072756; - double steadyStateTemp = previousEGT > threshold ? 135 + ambientTemp : 30 + ambientTemp; - return steadyStateTemp + (previousEGT - steadyStateTemp) * exp(-decayRate * deltaTime); - } - - /** - * @brief Calculates the corrected Exhaust Gas Temperature (EGT) based on corrected fan speed, - * corrected fuel flow, Mach number, and altitude. Real-life modeled polynomials. - * - * @param cn1 The corrected fan speed in percent. - * @param cff The corrected fuel flow in pounds per hour. - * @param mach The Mach number. - * @param alt The altitude in feet. - * @return The calculated corrected EGT in Celsius. - */ - static double correctedEGT(double cn1, double cff, double mach, double alt) { - // TODO: Adjust the corrected fuel flow to account for the A380 double fuel flow. Will have to be taken care of. - cff = cff / 2; - - // Coefficients for the polynomial calculation - double c_EGT[16] = { - 3.2636e+02, // coefficient for x^0 - 0.0000e+00, // coefficient for x^1 - 9.2893e-01, // coefficient for x^2 - 3.9505e-02, // coefficient for x^3 - 3.9070e+02, // coefficient for x^4 - -4.7911e-04, // coefficient for x^5 - 7.7679e-03, // coefficient for x^6 - 5.8361e-05, // coefficient for x^7 - -2.5566e+00, // coefficient for x^8 - 5.1227e-06, // coefficient for x^9 - 1.0178e-07, // coefficient for x^10 - -7.4602e-03, // coefficient for x^11 - 1.2106e-07, // coefficient for x^12 - -5.1639e+01, // coefficient for x^13 - -2.7356e-03, // coefficient for x^14 - 1.9312e-08 // coefficient for x^15 - }; - - // Calculate and return the Corrected EGT value using a polynomial model - return c_EGT[0] // - + c_EGT[1] // - + (c_EGT[2] * cn1) // - + (c_EGT[3] * cff) // - + (c_EGT[4] * mach) // - + (c_EGT[5] * alt) // - + (c_EGT[6] * (std::pow)(cn1, 2)) // - + (c_EGT[7] * cn1 * cff) // - + (c_EGT[8] * cn1 * mach) // - + (c_EGT[9] * cn1 * alt) // - + (c_EGT[10] * (std::pow)(cff, 2)) // - + (c_EGT[11] * mach * cff) // - + (c_EGT[12] * cff * alt) // - + (c_EGT[13] * (std::pow)(mach, 2)) // - + (c_EGT[14] * mach * alt) // - + (c_EGT[15] * (std::pow)(alt, 2)); // - } - - /** - * @brief Calculates the customer corrected fuel flow based on cn1, mach, and altitude based on - * real-life modeled polynomials. - * - * @param cn1 The corrected fan speed in percent. - * @param mach The Mach number. - * @param alt The altitude in feet. - * @return The calculated Corrected Fuel Flow value in pounds per hour. - */ - static double correctedFuelFlow(double cn1, double mach, double alt) { - double c_Flow[21] = { - -1.7630e+02, // coefficient for x^0 - -2.1542e-01, // coefficient for x^1 - 4.7119e+01, // coefficient for x^2 - 6.1519e+02, // coefficient for x^3 - 1.8047e-03, // coefficient for x^4 - -4.4554e-01, // coefficient for x^5 - -4.3940e+01, // coefficient for x^6 - 4.0459e-05, // coefficient for x^7 - -3.2912e+01, // coefficient for x^8 - -6.2894e-03, // coefficient for x^9 - -1.2544e-07, // coefficient for x^10 - 1.0938e-02, // coefficient for x^11 - 4.0936e-01, // coefficient for x^12 - -5.5841e-06, // coefficient for x^13 - -2.3829e+01, // coefficient for x^14 - 9.3269e-04, // coefficient for x^15 - 2.0273e-11, // coefficient for x^16 - -2.4100e+02, // coefficient for x^17 - 1.4171e-02, // coefficient for x^18 - -9.5581e-07, // coefficient for x^19 - 1.2728e-11 // coefficient for x^20 - }; - - double outCFF = c_Flow[0] // - + c_Flow[1] // - + (c_Flow[2] * cn1) // - + (c_Flow[3] * mach) // - + (c_Flow[4] * alt) // - + (c_Flow[5] * (std::pow)(cn1, 2)) // - + (c_Flow[6] * cn1 * mach) // - + (c_Flow[7] * cn1 * alt) // - + (c_Flow[8] * (std::pow)(mach, 2)) // - + (c_Flow[9] * mach * alt) // - + (c_Flow[10] * (std::pow)(alt, 2)) // - + (c_Flow[11] * (std::pow)(cn1, 3)) // - + (c_Flow[12] * (std::pow)(cn1, 2) * mach) // - + (c_Flow[13] * (std::pow)(cn1, 2) * alt) // - + (c_Flow[14] * cn1 * (std::pow)(mach, 2)) // - + (c_Flow[15] * cn1 * mach * alt) // - + (c_Flow[16] * cn1 * (std::pow)(alt, 2)) // - + (c_Flow[17] * (std::pow)(mach, 3)) // - + (c_Flow[18] * (std::pow)(mach, 2) * alt) // - + (c_Flow[19] * mach * (std::pow)(alt, 2)) // - + (c_Flow[20] * (std::pow)(alt, 3)); // - - // TODO: Adjust the corrected fuel flow to account for the A380 double fuel flow. Will have to be taken care of. - return 2 * outCFF; - } - - /** - * @brief Calculates the oil temperature based on energy, previous oil temperature, maximum oil temperature, and time interval. - * - * @param thermalEnergy The thermal energy in Joules. - * @param previousOilTemp The previous oil temperature in Celsius. - * @param maxOilTemperature The maximum oil temperature in Celsius. - * @param deltaTime The time interval in seconds. - * @return The calculated oil temperature in Celsius. - * - * TODO: Currently not used in the code. - */ - static double oilTemperature(double thermalEnergy, double previousOilTemp, double maxOilTemperature, double deltaTime) { - // Initialize the steady temperature and the decay constant - double k = 0.001; - - // Calculate the change in temperature due to the energy - double dt = thermalEnergy * deltaTime * 0.002; - - // Calculate the steady temperature based on the maximum oil temperature, the decay constant, and the previous oil temperature - double t_steady = ((maxOilTemperature * k * deltaTime) + previousOilTemp) / (1 + (k * deltaTime)); - - // Calculate the oil temperature based on the steady temperature and the change in temperature - double oilTemp_out; - if (t_steady - dt >= maxOilTemperature) { - oilTemp_out = maxOilTemperature; - } else if (t_steady - dt >= maxOilTemperature - 10) { - oilTemp_out = (t_steady - dt) * 0.999997; - } else { - oilTemp_out = (t_steady - dt); - } - - // Return the calculated oil temperature - return oilTemp_out; - } - - /** - * @brief Calculates the Oil Gulping percentage based on thrust. - * Real-life modeled polynomials - Oil Gulping (%) - * - * @param thrust The thrust in Newton. - * @return The calculated Oil Gulping percentage. - */ - static double oilGulpPct(double thrust) { - const double oilGulpCoefficients[3] = {20.1968848, -1.2270302e-4, 1.78442e-8}; - const double oilGulpPercentage = - oilGulpCoefficients[0] + (oilGulpCoefficients[1] * thrust) + (oilGulpCoefficients[2] * (std::pow)(thrust, 2)); - return oilGulpPercentage / 100; - } - - /** - * @brief Calculates the Oil Pressure (PSI) based on simulated N3 value. - * Real-life modeled polynomials - Oil Pressure (PSI) - * - * @param simN3 The simulated N3 value in percent. - * @return The calculated Oil Pressure value in PSI. - */ - static double oilPressure(double simN3) { - double oilPressureCoefficients[3] = {-0.88921, 0.23711, 0.00682}; - return oilPressureCoefficients[0] + (oilPressureCoefficients[1] * simN3) + (oilPressureCoefficients[2] * (std::pow)(simN3, 2)); - } -}; - -#endif // FLYBYWIRE_AIRCRAFT_POLYNOMIAL_H diff --git a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Table1502_A380X.hpp b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Table1502_A380X.hpp deleted file mode 100644 index fb77e660385..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Table1502_A380X.hpp +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_TABLE1502_A380X_HPP -#define FLYBYWIRE_AIRCRAFT_TABLE1502_A380X_HPP - -#include - -#include "Fadec.h" - -/** - * @class Table1502_A380X - * - * This class contains methods and data used in the calculation of the corrected fan speed (CN1 and CN3). - * The class has a 2D array `table` that contains values used in the calculation of the corrected fan speed. - * Each row in the `table` represents a set of values. The columns represent different parameters used in the calculation. - * The class also has two static methods `iCN3` and `iCN1` that calculate the corrected fan speed (CN3 and CN1) respectively. - * - * TODO: extract the reusable code to a common library - */ -class Table1502_A380X { - /** - * @brief Table 1502 (CN3 vs correctedN1) representations with FSX nomenclature. - * - * This table represents the relationship between CN3 and correctedN1 in the FSX nomenclature. - * Each row in the table represents a different state of the engine, with the first column being the CN3 value, - * the second and third columns being the lower and upper bounds of the correctedN1 value at Mach 0.2, - * and the fourth column being the correctedN1 value at Mach 0.9. - * - * @return A 2D array representing the CN2 - correctedN1 pairs. - */ - static constexpr double table1502[13][4] = { - {16.012, 0.000, 0.000, 17.000}, // CN3 = 18.20, correctedN1 = [0.00, 0.00] at Mach 0.2, correctedN1 = 17.00 at Mach 0.9 - {19.355, 1.845, 1.845, 17.345}, // CN3 = 22.00, correctedN1 = [1.90, 1.90] at Mach 0.2, correctedN1 = 17.40 at Mach 0.9 - {22.874, 2.427, 2.427, 18.127}, // CN3 = 26.00, correctedN1 = [2.50, 2.50] at Mach 0.2, correctedN1 = 18.20 at Mach 0.9 - {50.147, 12.427, 12.427, 26.627}, // CN3 = 57.00, correctedN1 = [12.80, 12.80] at Mach 0.2, correctedN1 = 27.00 at Mach 0.9 - {60.000, 18.500, 18.500, 33.728}, // CN3 = 68.20, correctedN1 = [19.60, 19.60] at Mach 0.2, correctedN1 = 34.83 at Mach 0.9 - {67.742, 25.243, 25.243, 40.082}, // CN3 = 77.00, correctedN1 = [26.00, 26.00] at Mach 0.2, correctedN1 = 40.84 at Mach 0.9 - {73.021, 30.505, 30.505, 43.854}, // CN3 = 83.00, correctedN1 = [31.42, 31.42] at Mach 0.2, correctedN1 = 44.77 at Mach 0.9 - {78.299, 39.779, 39.779, 48.899}, // CN3 = 89.00, correctedN1 = [40.97, 40.97] at Mach 0.2, correctedN1 = 50.09 at Mach 0.9 - {81.642, 49.515, 49.515, 53.557}, // CN3 = 92.80, correctedN1 = [51.00, 51.00] at Mach 0.2, correctedN1 = 55.04 at Mach 0.9 - {85.337, 63.107, 63.107, 63.107}, // CN3 = 97.00, correctedN1 = [65.00, 65.00] at Mach 0.2, correctedN1 = 65.00 at Mach 0.9 - {87.977, 74.757, 74.757, 74.757}, // CN3 = 100.00, correctedN1 = [77.00, 77.00] at Mach 0.2, correctedN1 = 77.00 at Mach 0.9 - {97.800, 97.200, 97.200, 97.200}, // CN3 = 104.00, correctedN1 = [85.00, 85.00] at Mach 0.2, correctedN1 = 85.50 at Mach 0.9 - {118.000, 115.347, 115.347, 115.347} // CN3 = 116.50, correctedN1 = [101.00, 101.00] at Mach 0.2, correctedN1 = 101.00 at Mach 0.9 - }; - - public: - - /** - * @brief Calculates the expected CN3 at idle. - * - * @param pressureAltitude The pressure altitude in feet. - * @param mach The Mach number. - * @return The calculated expected CN2 at idle in percent. - */ - static double iCN3(double pressureAltitude, double mach) { - // The specific values are likely derived from empirical data or a mathematical model of the engine's behavior. - // The original source code does not provide any information on the origin of these values. - return 68.2 / ((std::sqrt)((288.15 - (1.98 * pressureAltitude / 1000)) / 288.15) * (std::sqrt)(1 + (0.2 * (std::pow)(mach, 2)))); - } - - /** - * @brief Calculates the corrected fan speed (CN1). - * - * @param pressureAltitude The pressure altitude in feet. - * @param mach The Mach number. - * @param ambientTemp The ambient temperature in degrees Celsius. - * @return The calculated corrected fan speed (CN1). - */ - static double iCN1(double pressureAltitude, double mach, [[maybe_unused]] double ambientTemp) { - // Calculate the expected CN3 value - const double cn3 = iCN3(pressureAltitude, mach); - - // Find the row in the table that contains the CN3 value and store the index in i - int i = 0; - while (table1502[i][0] <= cn3 && i < 13) { - i++; - } - - // Retrieve the lower and upper bounds of the CN3 value and the correctedN1 value at Mach 0.2 and Mach 0.9 - const double cn3lo = table1502[i - 1][0]; - const double cn3hi = table1502[i][0]; - const double cn1lolo = table1502[i - 1][1]; - const double cn1hilo = table1502[i][1]; - const double cn1lohi = table1502[i - 1][3]; - const double cn1hihi = table1502[i][3]; - - // Interpolate the correctedN1 value based on the CN3 value and the Mach number - const double cn1_lo = Fadec::interpolate(cn3, cn3lo, cn3hi, cn1lolo, cn1hilo); - const double cn1_hi = Fadec::interpolate(cn3, cn3lo, cn3hi, cn1lohi, cn1hihi); - - return Fadec::interpolate(mach, 0.2, 0.9, cn1_lo, cn1_hi); - } -}; - -#endif // FLYBYWIRE_AIRCRAFT_TABLE1502_A380X_HPP diff --git a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/ThrustLimits_A380X.hpp b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/ThrustLimits_A380X.hpp deleted file mode 100644 index 8cff773f45c..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/ThrustLimits_A380X.hpp +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_THRUSTLIMITS_A380X_HPP -#define FLYBYWIRE_AIRCRAFT_THRUSTLIMITS_A380X_HPP - -#include -#include -#include - -#include "logging.h" - -#include "EngineRatios.hpp" -#include "Fadec.h" - -/** - * @class ThrustLimits_A3800X - * @brief Static class containing the thrust limits for the A380X. - * - * TODO: extract the reusable code to a common library - */ -class ThrustLimits_A380X { - /** - * @brief A 2D array representing various engine thrust limits. - * - * This 2D array contains 72 rows, each with 6 columns. Each row represents a different altitude - * level and the corresponding engine thrust - * limits. The columns in each row represent the following parameters: - * 1. Altitude (in feet) - * 2. Corner Point (CP) - the temperature below which the engine can operate at full thrust without any restrictions. - * 3. Limit Point (LP) - the temperature above which the engine thrust starts to be limited. - * 4. CN1 Flat - the engine's N1 fan speed limit at the CP temperature. - * 5. CN1 Last - the engine's N1 fan speed limit at the LP temperature. - * 6. CN1 Flex - the engine's N1 fan speed limit at a temperature of 100 degrees Celsius. - * - * The array is divided into sections for different flight phases: Takeoff (TO), Go-Around (GA), - * Climb (CLB), and Maximum Continuous Thrust (MCT). - */ - static constexpr double limits[72][6] = { - // TO - {-2000, 48.000, 55.000, 81.351, 79.370, 61.535}, // row 0 - {-1000, 46.000, 55.000, 82.605, 80.120, 62.105}, // - {0, 44.000, 55.000, 83.832, 80.776, 62.655}, // - {500, 42.000, 52.000, 84.210, 81.618, 62.655}, // - {1000, 42.000, 52.000, 84.579, 81.712, 62.655}, // - {2000, 40.000, 50.000, 85.594, 82.720, 62.655}, // - {3000, 36.000, 48.000, 86.657, 83.167, 61.960}, // - {4000, 32.000, 46.000, 87.452, 83.332, 61.206}, // - {5000, 29.000, 44.000, 88.833, 84.166, 61.206}, // - {6000, 25.000, 42.000, 90.232, 84.815, 61.206}, // - {7000, 21.000, 40.000, 91.711, 85.565, 61.258}, // - {8000, 17.000, 38.000, 93.247, 86.225, 61.777}, // - {9000, 15.000, 36.000, 94.031, 86.889, 60.968}, // - {10000, 13.000, 34.000, 94.957, 88.044, 60.935}, // - {11000, 12.000, 32.000, 95.295, 88.526, 59.955}, // - {12000, 11.000, 30.000, 95.568, 88.818, 58.677}, // - {13000, 10.000, 28.000, 95.355, 88.819, 59.323}, // - {14000, 10.000, 26.000, 95.372, 89.311, 59.965}, // - {15000, 8.000, 24.000, 95.686, 89.907, 58.723}, // - {16000, 5.000, 22.000, 96.160, 89.816, 57.189}, // - {16600, 5.000, 22.000, 96.560, 89.816, 57.189}, // - {-2000, 47.751, 54.681, 84.117, 81.901, 63.498}, // row 20 - // GA - {-1000, 45.771, 54.681, 85.255, 82.461, 63.920}, // row 21 - {0, 43.791, 54.681, 86.411, 83.021, 64.397}, // - {500, 42.801, 52.701, 86.978, 83.740, 64.401}, // - {1000, 41.811, 52.701, 87.568, 83.928, 64.525}, // - {2000, 38.841, 50.721, 88.753, 84.935, 64.489}, // - {3000, 36.861, 48.741, 89.930, 85.290, 63.364}, // - {4000, 32.901, 46.761, 91.004, 85.836, 62.875}, // - {5000, 28.941, 44.781, 92.198, 86.293, 62.614}, // - {6000, 24.981, 42.801, 93.253, 86.563, 62.290}, // - {7000, 21.022, 40.821, 94.273, 86.835, 61.952}, // - {8000, 17.062, 38.841, 94.919, 87.301, 62.714}, // - {9000, 15.082, 36.861, 95.365, 87.676, 61.692}, // - {10000, 13.102, 34.881, 95.914, 88.150, 60.906}, // - {11000, 12.112, 32.901, 96.392, 88.627, 59.770}, // - {12000, 11.122, 30.921, 96.640, 89.206, 58.933}, // - {13000, 10.132, 28.941, 96.516, 89.789, 60.503}, // - {14000, 9.142, 26.961, 96.516, 90.475, 62.072}, // - {15000, 9.142, 24.981, 96.623, 90.677, 59.333}, // - {16000, 7.162, 23.001, 96.845, 90.783, 58.045}, // - {16600, 5.182, 21.022, 97.366, 91.384, 58.642}, // row 40 - // CLB - {-2000, 30.800, 56.870, 80.280, 72.000, 0.000}, // row 41 - {2000, 20.990, 48.157, 82.580, 74.159, 0.000}, // - {5000, 16.139, 43.216, 84.642, 75.737, 0.000}, // - {8000, 7.342, 38.170, 86.835, 77.338, 0.000}, // - {10000, 4.051, 34.518, 88.183, 77.999, 0.000}, // - {10000.1, 4.051, 34.518, 87.453, 77.353, 0.000}, // - {12000, 0.760, 30.865, 88.303, 78.660, 0.000}, // - {15000, -4.859, 25.039, 89.748, 79.816, 0.000}, // - {17000, -9.934, 19.813, 90.668, 80.895, 0.000}, // - {20000, -15.822, 13.676, 92.106, 81.894, 0.000}, // - {24000, -22.750, 6.371, 93.651, 82.716, 0.000}, // - {27000, -29.105, -0.304, 93.838, 83.260, 0.000}, // - {29314, -32.049, -3.377, 93.502, 82.962, 0.000}, // - {31000, -34.980, -6.452, 95.392, 84.110, 0.000}, // - {35000, -45.679, -17.150, 96.104, 85.248, 0.000}, // - {39000, -45.679, -17.150, 96.205, 84.346, 0.000}, // - {41500, -45.679, -17.150, 95.676, 83.745, 0.000}, // row 58 - // MCT - {-1000, 26.995, 54.356, 82.465, 74.086, 0.000}, // row 59 - {3000, 18.170, 45.437, 86.271, 77.802, 0.000}, // - {7000, 9.230, 40.266, 89.128, 79.604, 0.000}, // - {11000, 4.019, 31.046, 92.194, 82.712, 0.000}, // - {15000, -5.226, 21.649, 95.954, 85.622, 0.000}, // - {17000, -9.913, 20.702, 97.520, 85.816, 0.000}, // - {20000, -15.129, 15.321, 99.263, 86.770, 0.000}, // - {22000, -19.947, 10.382, 98.977, 86.661, 0.000}, // - {25000, -25.397, 4.731, 98.440, 85.765, 0.000}, // - {27000, -30.369, -0.391, 97.279, 85.556, 0.000}, // - {31000, -36.806, -7.165, 98.674, 86.650, 0.000}, // - {35000, -43.628, -14.384, 98.386, 85.747, 0.000}, // - {39000, -47.286, -18.508, 97.278, 85.545, 0.000} // row 71 - }; - - public: - /** - * @brief Finds the top-row boundary in the limits array. - * - * @param altitude The altitude to find the top-row boundary for. - * @param index The index to start the search from. - * @return The index of the top-row boundary in the limits array. - */ - static int finder(double altitude, int index) { - while (altitude >= limits[index][0]) { - index++; - } - return index; - } - - /** - * @brief Calculates the total bleed for the engine. - * - * @param type The type of operation (0-TO, 1-GA, 2-CLB, 3-MCT). - * @param altitude The current altitude of the aircraft in feet. - * @param oat The outside air temperature in degrees Celsius. - * @param cp The corner point - the temperature below which the engine can operate at full thrust without any restrictions (in degrees - * Celsius). - * @param lp The limit point - the temperature above which the engine thrust starts to be limited (in degrees Celsius). - * @param flexTemp The flex temperature in degrees Celsius. - * @param packs The status of the air conditioning (0 for off, 1 for on). - * @param nacelle The status of the nacelle anti-ice (0 for off, 1 for on). - * @param wing The status of the wing anti-ice (0 for off, 1 for on). - * @return The total bleed for the engine - */ - static double bleedTotal(int type, // - double altitude, // - double oat, // - double cp, // - double lp, // - double flexTemp, // - int packs, // - int nacelle, // - int wing // - ) { - if (flexTemp > lp && type <= 1) { - return packs * -0.6 + nacelle * -0.7 + wing * -0.7; - } - - // Define a map to store the bleed values for different conditions - // Keys: - // int - Represents the type of operation (0-TO, 1-GA, 2-CLB, 3-MCT). - // bool - Represents whether the altitude is less than 8000. - // bool - Represents whether the outside air temperature is less than the corner point. - // Values: - // double - Represents the bleed value for the packs. - // double - Represents the bleed value for the nacelle anti-ice. - // double - Represents the bleed value for the wing anti-ice. - std::map, std::tuple> bleedValues = { - {{0, true, true}, {-0.4, -0.6, -0.7}}, // - {{0, true, false}, {-0.5, -0.6, -0.7}}, // - {{0, false, true}, {-0.6, -0.8, -0.8}}, // - {{0, false, false}, {-0.7, -0.8, -0.8}}, // - {{1, true, true}, {-0.4, -0.6, -0.6}}, // - {{1, true, false}, {-0.4, -0.6, -0.6}}, // - {{1, false, true}, {-0.6, -0.7, -0.8}}, // - {{1, false, false}, {-0.6, -0.7, -0.8}}, // - {{2, true, false}, {-0.2, -0.8, -0.4}}, // - {{2, false, false}, {-0.3, -0.8, -0.4}}, // - {{3, true, false}, {-0.6, -0.9, -1.2}}, // - {{3, false, false}, {-0.6, -0.9, -1.2}} // - }; - - double n1Packs = 0; - double n1Nai = 0; - double n1Wai = 0; - - // Use the map to get the bleed values - std::tie(n1Packs, n1Nai, n1Wai) = bleedValues[{type, altitude < 8000, oat < cp}]; - - return packs * n1Packs + nacelle * n1Nai + wing * n1Wai; - } - - /** - * @brief Calculates the N1 limit based on the given parameters. - * - * This function calculates the N1 limit based on the type of limit, altitude, ambient temperature, - * ambient pressure, flex temperature, and the status of the air conditioning, nacelle anti-ice, - * and wing anti-ice. It uses a series of calculations and interpolations - * to determine the N1 limit. - * - * @param type An integer representing the type of limit (0-TO, 1-GA, 2-CLB, 3-MCT). - * @param altitude A double representing the altitude. - * @param ambientTemp A double representing the ambient temperature. - * @param ambientPressure A double representing the ambient pressure. - * @param flexTemp A double representing the flex temperature. - * @param packs A double representing the air conditioning status. - * @param nacelle A double representing the nacelle anti-ice status. - * @param wing A double representing the wing anti-ice status. - * @return The N1 limit as a double. - */ - static double limitN1(int type, // - double altitude, // - double ambientTemp, // - double ambientPressure, // - double flexTemp, // - double packs, // - double nacelle, // - double wing // - ) { - int rowMin = 0; - int rowMax = 0; - int loAltRow = 0; - int hiAltRow = 0; - double mach = 0; - - // Set main variables per Limit Type - switch (type) { - case 0: // TO - rowMin = 0; - rowMax = 20; - mach = 0; - break; - case 1: // GA - rowMin = 21; - rowMax = 41; - mach = 0.225; - break; - case 2: // CLB - rowMin = 42; - rowMax = 58; - if (altitude <= 10000) { - mach = Fadec::cas2mach(250, ambientPressure); - } else { - mach = Fadec::cas2mach(300, ambientPressure); - if (mach > 0.78) - mach = 0.78; - } - break; - case 3: // MCT - rowMin = 59; - rowMax = 71; - mach = Fadec::cas2mach(230, ambientPressure); - break; - } - - // Check for over/under flows. Else, find top row value - if (altitude <= limits[rowMin][0]) { - hiAltRow = rowMin; - loAltRow = rowMin; - } else if (altitude >= limits[rowMax][0]) { - hiAltRow = rowMax; - loAltRow = rowMax; - } else { - hiAltRow = finder(altitude, rowMin); - loAltRow = hiAltRow - 1; - } - - // Define key table variables and interpolation - const double cp = Fadec::interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][1], limits[hiAltRow][1]); - const double lp = Fadec::interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][2], limits[hiAltRow][2]); - const double cn1Flat = Fadec::interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][3], limits[hiAltRow][3]); - const double cn1Last = Fadec::interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][4], limits[hiAltRow][4]); - const double cn1Flex = Fadec::interpolate(altitude, limits[loAltRow][0], limits[hiAltRow][0], limits[loAltRow][5], limits[hiAltRow][5]); - - double cn1 = 0; - double m = 0; - double b = 0; - if (flexTemp > 0 && type <= 1) { // CN1 for Flex Case - if (flexTemp <= cp) { - cn1 = cn1Flat; - } else if (flexTemp > lp) { - m = (cn1Flex - cn1Last) / (100 - lp); - b = cn1Flex - m * 100; - cn1 = (m * flexTemp) + b; - } else { - m = (cn1Last - cn1Flat) / (lp - cp); - b = cn1Last - m * lp; - cn1 = (m * flexTemp) + b; - } - } - else { // CN1 for All other cases - if (ambientTemp <= cp) { - cn1 = cn1Flat; - } else { - m = (cn1Last - cn1Flat) / (lp - cp); - b = cn1Last - m * lp; - cn1 = (m * ambientTemp) + b; - } - } - - // Define bleed rating/ de-rating - const double bleed = bleedTotal(type, altitude, ambientTemp, cp, lp, flexTemp, packs, nacelle, wing); - - return (cn1 * (std::sqrt)(EngineRatios::theta2(mach, ambientTemp))) + bleed; - } -}; - -#endif // FLYBYWIRE_AIRCRAFT_THRUSTLIMITS_A380X_HPP diff --git a/fbw-a380x/src/wasm/fadec_a380x/src/Gauge_Fadec_v2.cpp b/fbw-a380x/src/wasm/fadec_a380x/src/Gauge_Fadec_v2.cpp deleted file mode 100644 index ae6f5e563a6..00000000000 --- a/fbw-a380x/src/wasm/fadec_a380x/src/Gauge_Fadec_v2.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2023 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef __INTELLISENSE__ -#define MODULE_EXPORT __attribute__((visibility("default"))) -#define MODULE_WASM_MODNAME(mod) __attribute__((import_module(mod))) -#else -#define MODULE_EXPORT -#define MODULE_WASM_MODNAME(mod) -#define __attribute__(x) -#define __restrict__ -#endif - -#include -#include - -#include "Fadec/Fadec_A380X.h" -#include "MsfsHandler.h" - -// Create an instance of the MsfsHandler -// We do not use a prefix and use the full LVar name in the code. -// Prefixes in framework code are not ideal and also prevent the creation -// of an LVar without a prefix -MsfsHandler msfsHandler("Gauge_Fadec_A380X", ""); - -// ADD ADDITIONAL MODULES HERE -// This is the only place these have to be added - everything else is handled automatically -Fadec_A380X fadec(msfsHandler); - -/** - * Gauge Callback - * There can by multiple gauges in a single wasm module. Just add another gauge callback function - * and register it in the panel.cfg file. - * - * Avoid putting any logic in the gauge callback function. Instead, create a new class and put - * the logic there. - * - * @see - * https://docs.flightsimulator.com/html/Content_Configuration/SimObjects/Aircraft_SimO/Instruments/C_C++_Gauges.htm?rhhlterm=_gauge_callback&rhsearch=_gauge_callback - */ -extern "C" { -[[maybe_unused]] MSFS_CALLBACK bool Gauge_Fadec_gauge_callback([[maybe_unused]] FsContext ctx, int svcId, void* pData) { - switch (svcId) { - case PANEL_SERVICE_PRE_INSTALL: { - return msfsHandler.initialize(); - } - case PANEL_SERVICE_PRE_DRAW: { - return msfsHandler.update(static_cast(pData)); - } - case PANEL_SERVICE_PRE_KILL: { - return msfsHandler.shutdown(); - } - default: - break; - } - return false; -} -} diff --git a/fbw-common/src/systems/instruments/src/EFB/Ground/Pages/Fuel/A380_842/A380Fuel.tsx b/fbw-common/src/systems/instruments/src/EFB/Ground/Pages/Fuel/A380_842/A380Fuel.tsx index 1513317a1bf..e5fec76d365 100644 --- a/fbw-common/src/systems/instruments/src/EFB/Ground/Pages/Fuel/A380_842/A380Fuel.tsx +++ b/fbw-common/src/systems/instruments/src/EFB/Ground/Pages/Fuel/A380_842/A380Fuel.tsx @@ -27,7 +27,7 @@ const ValueSimbriefInput: React.FC = ({ min, max, value
= ({ min, max, value && (
@@ -271,13 +271,13 @@ export const A380Fuel: React.FC = ({ return (
-
+
@@ -383,23 +367,13 @@ export const A380Fuel: React.FC = ({ />
- Feed Two + Feed One = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={feedTwoGal * FUEL_GALLONS_TO_KG / INNER_FEED_MAX_KG * 100} + completed={feedOneGal * FUEL_GALLONS_TO_KG / OUTER_FEED_MAX_KG * 100} /> - +
- Left Inner + Feed Two = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={leftInnerGal * FUEL_GALLONS_TO_KG / INNER_TANK_MAX_KG * 100} + completed={feedTwoGal * FUEL_GALLONS_TO_KG / INNER_FEED_MAX_KG * 100} /> - +
- Left Mid + Left Inner = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={leftMidGal * FUEL_GALLONS_TO_KG / MID_TANK_MAX_KG * 100} + completed={leftInnerGal * FUEL_GALLONS_TO_KG / INNER_TANK_MAX_KG * 100} /> - +
- Feed One + Left Mid = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={feedOneGal * FUEL_GALLONS_TO_KG / OUTER_FEED_MAX_KG * 100} + completed={leftMidGal * FUEL_GALLONS_TO_KG / MID_TANK_MAX_KG * 100} /> - +
- +
- {' '} - {' '} - {' '} - + @@ -418,16 +392,12 @@ export const A380Fuel: React.FC = ({ /> @@ -510,25 +468,14 @@ export const A380Fuel: React.FC = ({ />
- +
- Right Inner + Feed Four = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={rightInnerGal * FUEL_GALLONS_TO_KG / INNER_TANK_MAX_KG * 100} + completed={feedFourGal * FUEL_GALLONS_TO_KG / OUTER_FEED_MAX_KG * 100} /> - +
- Right Mid + Right Inner = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={rightMidGal * FUEL_GALLONS_TO_KG / MID_TANK_MAX_KG * 100} + completed={rightInnerGal * FUEL_GALLONS_TO_KG / INNER_TANK_MAX_KG * 100} /> - +
- Feed Four + Right Mid = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={feedFourGal * FUEL_GALLONS_TO_KG / OUTER_FEED_MAX_KG * 100} + completed={rightMidGal * FUEL_GALLONS_TO_KG / MID_TANK_MAX_KG * 100} /> - +
- +
- {' '} - {' '} - {' '} - {' '} - {' '} - {' '} - {' '} = ({
-
+

{t('Ground.Fuel.RefuelTime')}

setRefuelRate('2')}>{t('Settings.Instant')} diff --git a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataManager.h b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataManager.h index bc9ffd98b98..9174887335c 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataManager.h +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataManager.h @@ -36,7 +36,6 @@ using SimObjectBasePtr = std::shared_ptr; using ClientEventPtr = std::shared_ptr; template using DataDefinitionVariablePtr = std::shared_ptr>; -using DataDefinitionVector = std::vector; template using ClientDataAreaVariablePtr = std::shared_ptr>; template diff --git a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/AircraftVariable.h b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/AircraftVariable.h index c790d71c5a8..13a2af5afd4 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/AircraftVariable.h +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/AircraftVariable.h @@ -16,9 +16,9 @@ class DataManager; /** * @brief The AircraftVariable class is a specialized class for aircraft cacheable variables (aka simvars or A:VARS). - * AircraftVariables are always FLOAT64. * - * This class uses events or calculator code to write to a variable as AircraftVariables are read-only.

+ * This class uses events or calculator code to write to a variable as + * AircraftVariables are read-only.

* * If a setter event or event name is provided the variable will be writable.

* @@ -60,6 +60,7 @@ class AircraftVariable : public CacheableVariable { * @param updateMode The DataManager update mode of the variable. (default: UpdateMode::NO_AUTO_UPDATE) * @param maxAgeTime The maximum age of an auto updated the variable in seconds. * @param maxAgeTicks The maximum age of an auto updated the variable in sim ticks. + * @param setterEventName The calculator code to write to the variable. */ explicit AircraftVariable(const std::string& varName, int varIndex = 0, diff --git a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/CacheableVariable.cpp b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/CacheableVariable.cpp index 28edeea3aa3..fb80073d666 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/CacheableVariable.cpp +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/CacheableVariable.cpp @@ -10,7 +10,7 @@ FLOAT64 CacheableVariable::get() const { if (cachedValue.has_value()) { - if (_warnIfDirty && dirty) { + if (dirty) { LOG_WARN("CacheableVariable::get() called on " + name + " but the value is dirty"); } return cachedValue.value(); @@ -34,7 +34,7 @@ FLOAT64 CacheableVariable::updateFromSim(FLOAT64 timeStamp, UINT64 tickCounter) FLOAT64 CacheableVariable::readFromSim() { const FLOAT64 fromSim = rawReadFromSim(); // compare the value from the sim with the cached value - const bool changed = skipChangeCheckFlag || !cachedValue.has_value() || !helper::Math::almostEqual(fromSim, cachedValue.value(), epsilon); + bool changed = skipChangeCheckFlag || !cachedValue.has_value() || !helper::Math::almostEqual(fromSim, cachedValue.value(), epsilon); if (changed) cachedValue = fromSim; dirty = false; diff --git a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/CacheableVariable.h b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/CacheableVariable.h index e1f964e10cb..279c4aa7844 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/CacheableVariable.h +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/CacheableVariable.h @@ -16,8 +16,6 @@ /** * @brief Virtual base class for sim variables like named variables, aircraft variables that support value caching. * - * These variable are always stored as FLOAT64 values. - * * Specialized classes must implement the rawReadFromSim and rawWriteToSim methods and can * overwrite any other method if the default implementation is not sufficient. */ @@ -50,13 +48,6 @@ class CacheableVariable : public ManagedDataObjectBase { */ bool dirty = false; - /** - * Flag to indicate if a warning should be printed to std::cerr if the variable is read via get() - * but is dirty (has been written to after read from sim) and has not been written to the sim yet. - */ - bool _warnIfDirty = false; - - protected: /** * The epsilon required to change a variable after a read from the sim. This is used to * set the changed flag and cache the new value if it is different by >epsilon from the last @@ -69,6 +60,7 @@ class CacheableVariable : public ManagedDataObjectBase { */ ID dataID = -1; + /** * Constructor * @param name The name of the variable in the sim @@ -142,8 +134,6 @@ class CacheableVariable : public ManagedDataObjectBase { /** * Sets the cache value and marks the variable as dirty.

* Does not write the value to the sim or update the time and tick stamps. - * Check this variable's updateMode to see if the value will be written automatically to the sim or - * if you need to write it manually. * @param value the value to set */ virtual void set(FLOAT64 value); @@ -193,20 +183,6 @@ class CacheableVariable : public ManagedDataObjectBase { */ [[nodiscard]] bool isDirty() const { return dirty; } - /** - * @brief If true a warning will be printed to std::cerr if the variable is read via get() - * but is dirty (has been written to after read from sim) and has not been written to the sim yet. - * @return true if a warning will be printed - */ - [[nodiscard]] bool warnIfDirty() const { return _warnIfDirty; } - - /** - * @brief If true a warning will be printed to std::cerr if the variable is read via get() - * but is dirty (has been written to after read from sim) and has not been written to the sim yet. - * @param warnIfDirty true if a warning should be printed, false otherwise - */ - void setWarnIfDirty(bool warnIfDirty) { CacheableVariable::_warnIfDirty = warnIfDirty; } - /** * OBS: This method only does a simple static cast to a bool. Make sure that a cast from * the double (FLOAT64) is sufficient for your use case. Otherwise use get() and almostEqual(). diff --git a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/DataDefinitionVariable.hpp b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/DataDefinitionVariable.hpp index bf36835848a..48075619677 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/DataDefinitionVariable.hpp +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/DataDefinitionVariable.hpp @@ -128,11 +128,11 @@ class DataDefinitionVariable : public SimObjectBase { } public: - DataDefinitionVariable() = delete; // no default constructor - DataDefinitionVariable(const DataDefinitionVariable&) = delete; // no copy constructor - DataDefinitionVariable& operator=(const DataDefinitionVariable&) = delete; // no copy assignment - DataDefinitionVariable(DataDefinitionVariable&&) = delete; // no move constructor - DataDefinitionVariable& operator=(DataDefinitionVariable&&) = delete; // no move assignment + DataDefinitionVariable() = delete; // no default constructor + DataDefinitionVariable(const DataDefinitionVariable&) = delete; // no copy constructor + DataDefinitionVariable& operator=(const DataDefinitionVariable&) = delete; // no copy assignment + DataDefinitionVariable(DataDefinitionVariable&&) = delete; // no move constructor + DataDefinitionVariable& operator=(DataDefinitionVariable&&) = delete; // no move assignment /** * Destructor - clears the client data definition but does not free any sim memory. The sim memory @@ -224,8 +224,7 @@ class DataDefinitionVariable : public SimObjectBase { }; bool writeDataToSim() override { - if (!SUCCEEDED(SimConnect_SetDataOnSimObject(hSimConnect, dataDefId, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_DATA_SET_FLAG_DEFAULT, 0, - sizeof(T), &dataStruct))) { + if (!SUCCEEDED(SimConnect_SetDataOnSimObject(hSimConnect, dataDefId, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(T), &dataStruct))) { LOG_ERROR("Setting data to sim for " + name + " with dataDefId=" + std::to_string(dataDefId) + " failed!"); return false; } diff --git a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/NamedVariable.h b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/NamedVariable.h index 15e6e14fea0..4e669b6ed89 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/NamedVariable.h +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataTypes/NamedVariable.h @@ -14,7 +14,6 @@ class DataManager; /** * @brief The NamedVariable class is a specialization of CacheableVariable for named variables (LVARS). - * NamedVariables are always FLOAT64. * * It is recommended to use the DataManager's make_named_var() to create instances of NamedVariable * as it de-duplicates variables and only creates one instance of each name-unit combination.

@@ -49,6 +48,9 @@ class NamedVariable : public CacheableVariable { FLOAT64 maxAgeTime = 0.0, UINT64 maxAgeTicks = 0) : CacheableVariable(NamedVariable::AIRCRAFT_PREFIX + varName, unit, updateMode, maxAgeTime, maxAgeTicks) { + // this makes sure to quickly spot an issue with the prefix + SIMPLE_ASSERT(NamedVariable::AIRCRAFT_PREFIX == "A32NX_" || NamedVariable::AIRCRAFT_PREFIX == "A380X_", + "Aircraft prefix is not set correctly!"); dataID = register_named_variable(name.c_str()); }; diff --git a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/MsfsHandler.cpp b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/MsfsHandler.cpp index 75a7819cf07..4ba44e3752a 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/MsfsHandler.cpp +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/MsfsHandler.cpp @@ -53,8 +53,6 @@ bool MsfsHandler::initialize() { // base sim data mainly for pause detection std::vector baseDataDef = {{"SIMULATION TIME", 0, UNITS.Number}, - {"SIMULATION RATE", 0, UNITS.Number}, - {"SIM ON GROUND", 0, UNITS.Bool}, {"L:" + NamedVariable::getAircraftPrefix() + "IS_READY", 0, UNITS.Number}, {"L:" + NamedVariable::getAircraftPrefix() + "DEVELOPER_STATE", 0, UNITS.Number}}; baseSimData = dataManager.make_datadefinition_var("BASE DATA", baseDataDef); @@ -70,7 +68,6 @@ bool MsfsHandler::initialize() { // PAUSE_STATE_FLAG_ACTIVE_PAUSE 4 // Pause was activated using the "Active Pause" Button // PAUSE_STATE_FLAG_SIM_PAUSE 8 // Pause the player sim but traffic, multi, etc... will still run a32nxPauseDetected = dataManager.make_named_var("PAUSE_DETECTED", UNITS.Number, UpdateMode::AUTO_READ_WRITE); - a32nxPauseDetected->set(0); pauseDetectedEvent = dataManager.make_client_event("A32NX.PAUSE_DETECTED_EVENT", false); pauseDetectedEvent->addCallback([&](const int, const DWORD param0, const DWORD, const DWORD, const DWORD, const DWORD) { LOG_INFO(simConnectName + ": Pause detected: " + std::to_string(param0)); @@ -105,7 +102,7 @@ bool MsfsHandler::update(sGaugeDrawData* pData) { profiler.start(); #endif - // Initial request of data from sim to retrieve all requests which have + // initial request of data from sim to retrieve all requests which have // periodic updates enabled. This includes the base sim data for pause detection. // Other data without periodic updates are requested either in the data manager or // in the modules. @@ -113,11 +110,17 @@ bool MsfsHandler::update(sGaugeDrawData* pData) { // Pause detection // In all pause states except active pause return immediately. - // Active pause can be handled by the modules, but usually simulation should run normally in + // Active pause can be handled by the modules but usually simulation should run normally in // active pause with just the aircraft not moving. - // See the comments above for the different pause states. - if (a32nxPauseDetected->getAsInt64() > 0 && a32nxPauseDetected->getAsInt64() != 4) { - return true; + // See comments above for the different pause states. + if (a32nxPauseDetected->readFromSim()) { + if (a32nxPauseDetected->getAsInt64() > 0) { + if (a32nxPauseDetected->getAsInt64() != 4) { + // LOG_DEBUG(simConnectName + ": Pause detected = " + std::to_string(a32nxPauseDetected->getAsInt64())); + return true; + } + // LOG_DEBUG(simConnectName + ": Active Pause detected = " + std::to_string(a32nxPauseDetected->getAsInt64())); + } } // read and update base data from sim @@ -128,37 +131,18 @@ bool MsfsHandler::update(sGaugeDrawData* pData) { // Datamanager is always called first to ensure that all variables are updated before the modules // are called. - bool result = true; - // PRE UPDATE -#ifdef PROFILING - preUpdate.start(); -#endif + bool result = true; result &= dataManager.preUpdate(pData); result &= std::all_of(modules.begin(), modules.end(), [&pData](Module* pModule) { return pModule->preUpdate(pData); }); -#ifdef PROFILING - preUpdate.stop(); -#endif // UPDATE -#ifdef PROFILING - mainUpdate.start(); -#endif result &= dataManager.update(pData); result &= std::all_of(modules.begin(), modules.end(), [&pData](Module* pModule) { return pModule->update(pData); }); -#ifdef PROFILING - mainUpdate.stop(); -#endif // POST UPDATE -#ifdef PROFILING - postUpdate.start(); -#endif result &= dataManager.postUpdate(pData); result &= std::all_of(modules.begin(), modules.end(), [&pData](Module* pModule) { return pModule->postUpdate(pData); }); -#ifdef PROFILING - postUpdate.stop(); -#endif if (!result) { LOG_ERROR(simConnectName + ": MsfsHandler::update() - failed"); @@ -166,13 +150,8 @@ bool MsfsHandler::update(sGaugeDrawData* pData) { #ifdef PROFILING profiler.stop(); - if (tickCounter % 100 == 0) { - LOG_INFO("Profiler Info for " + this->simConnectName); - preUpdate.print(); - mainUpdate.print(); - postUpdate.print(); + if (tickCounter % 120 == 0) { profiler.print(); - std::cout << std::endl; } #endif @@ -188,3 +167,10 @@ bool MsfsHandler::shutdown() { return result; } +bool MsfsHandler::getAircraftIsReadyVar() const { + return static_cast(baseSimData->data().aircraftIsReady); +} + +FLOAT64 MsfsHandler::getAircraftDevelopmentStateVar() const { + return baseSimData->data().aircraftDevelopmentState; +} diff --git a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/MsfsHandler.h b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/MsfsHandler.h index 0ccb5837c6c..a0026321a52 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/MsfsHandler.h +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/MsfsHandler.h @@ -65,8 +65,6 @@ class MsfsHandler { */ struct BaseSimData { FLOAT64 simulationTime; - FLOAT64 simulationRate; - FLOAT64 simOnGround; FLOAT64 aircraftIsReady; FLOAT64 aircraftDevelopmentState; }; @@ -102,10 +100,7 @@ class MsfsHandler { // Allows immediate view on runtime performance issue. Add additional instances into // Modules while developing and profiling a module's performance. #ifdef PROFILING - SimpleProfiler preUpdate{"MsfsHandler::preUpdate()", 100}; - SimpleProfiler mainUpdate{"MsfsHandler::mainUpdate()", 100}; - SimpleProfiler postUpdate{"MsfsHandler::postUpdate()", 100}; - SimpleProfiler profiler{"MsfsHandler::update()", 100}; + SimpleProfiler profiler{"MsfsHandler::update()", 120}; #endif public: @@ -157,31 +152,16 @@ class MsfsHandler { */ DataManager& getDataManager() { return dataManager; } - /** - * @return current simulation time in seconds - */ - [[nodiscard]] FLOAT64 getSimulationTime() const { return baseSimData->data().simulationTime; } - - /** - * @return current simulation rate - */ - [[nodiscard]] FLOAT64 getSimulationRate() const { return baseSimData->data().simulationRate; } - - /** - * @return value of SimOnGround simvar - */ - [[nodiscard]] bool getSimOnGround() const { return baseSimData->data().simOnGround != 0.0; } - /** * @return value of LVAR A32NX_IS_READY */ - [[nodiscard]] bool getAircraftIsReadyVar() const { return baseSimData->data().aircraftIsReady != 0.0; } + [[nodiscard]] bool getAircraftIsReadyVar() const; /** * * @return value of LVAR A32NX_DEVELOPMENT_STATE */ - [[nodiscard]] FLOAT64 getAircraftDevelopmentStateVar() const { return baseSimData->data().aircraftDevelopmentState; } + [[nodiscard]] FLOAT64 getAircraftDevelopmentStateVar() const; /** * @return the current simulation time diff --git a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/SimUnits.h b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/SimUnits.h index c05f0c7941e..2d939f5367b 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/SimUnits.h +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/SimUnits.h @@ -38,7 +38,6 @@ class SimUnits { const SimUnit FeetSecSquared{"feet per second squared"}; const SimUnit FootPounds{"Foot pounds"}; const SimUnit Gallons{"Gallons"}; - const SimUnit Gph{"Gallons per hour"}; const SimUnit Hours{"Hours"}; const SimUnit Mach{"Mach"}; const SimUnit Millibars{"Millibars"}; diff --git a/fbw-common/src/wasm/cpp-msfs-framework/lib/SimpleProfiler.hpp b/fbw-common/src/wasm/cpp-msfs-framework/lib/SimpleProfiler.hpp index 6299f0db1e5..ce4d5810b1b 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/lib/SimpleProfiler.hpp +++ b/fbw-common/src/wasm/cpp-msfs-framework/lib/SimpleProfiler.hpp @@ -109,8 +109,6 @@ class SimpleProfiler { /** * @brief Return a string with the average execution time of the collected samples at the time of calling this method - * @format Profiler: 207 ( 100 / 202 / 400) nanoseconds for Perft::update (avg of 100 samples)
- * Profiler: average ( minimum / trimmed average 5% / maximum ) * @return String with the average execution time of the collected samples at the time of calling this method */ [[nodiscard]] std::string str() { diff --git a/fbw-common/src/wasm/cpp-msfs-framework/lib/inih/ini_type_conversion.h b/fbw-common/src/wasm/cpp-msfs-framework/lib/inih/ini_type_conversion.h index 69e8ec8599d..54b628cf278 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/lib/inih/ini_type_conversion.h +++ b/fbw-common/src/wasm/cpp-msfs-framework/lib/inih/ini_type_conversion.h @@ -3,10 +3,9 @@ #pragma once +#include #include -#include "ini.h" - namespace mINI { class INITypeConversion { public: diff --git a/fbw-common/src/wasm/cpp-msfs-framework/lib/string_utils.hpp b/fbw-common/src/wasm/cpp-msfs-framework/lib/string_utils.hpp index 3f45dfddbf4..dea8d0954f2 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/lib/string_utils.hpp +++ b/fbw-common/src/wasm/cpp-msfs-framework/lib/string_utils.hpp @@ -4,8 +4,6 @@ #ifndef FLYBYWIRE_AIRCRAFT_STRING_UTILS_HPP #define FLYBYWIRE_AIRCRAFT_STRING_UTILS_HPP -#include -#include #include namespace helper { @@ -14,31 +12,17 @@ namespace helper { * @brief Helper class for string operations. */ class StringUtils { - public: - /** - * @brief This function is used to format a number by inserting a thousands separator. - * - * @tparam T This template parameter represents the type of the number. It should be an integral type. - * @param n This is the number that will be formatted. It should be of type T. - * @param separator This is the character that will be used as the thousands separator. It is optional and defaults to a comma (','). - * @return This function returns a new string. The string is a representation of the input number 'n', but with the specified thousands - * separator inserted at every thousandth place. - * - * @note The function uses the std::to_string function to convert the number to a string. - * It then iterates over the string in reverse, inserting the separator at every thousandth place. - * The function can handle both positive and negative input numbers. - */ +public: + /** + * @brief Inserts a thousands separator into a number. + * @tparam T type of the number + * @param n the number + * @param separator the separator to use + * @return new string with the separator + */ template static std::string insertThousandsSeparator(T n, const std::string_view separator = ",") { static_assert(std::is_integral::value, "T must be an integral type"); - - // Handle negative numbers - bool isNegative = false; - if (n < 0) { - isNegative = true; - n = -n; // Make the number positive for processing - } - std::string s = std::to_string(n); const int len = s.length(); const int numCommas = (len - 1) / 3; @@ -46,37 +30,8 @@ class StringUtils { for (int i = len - 4; i >= 0; i -= 3) { s.insert(i + 1, separator); } - - // Add the negative sign back if the original number was negative - if (isNegative) { - s.insert(0, "-"); - } - return s; } - - /** - * @brief Converts a number to a string with zero padding. - * - * This function takes a number and a total length as input. It converts the number to a string and - * adds leading zeros until the string reaches the specified total length. If the number is negative, - * the minus sign is not counted in the total length and the zeros are inserted after the minus sign. - * - * @tparam T The type of the number. This should be an integral type. - * @param value The number to be converted to a string. - * @param total_length The total length of the resulting string including the number and the leading zeros. - * @return A string representation of the number, padded with leading zeros to reach the specified total length. - */ - template >> - static std::string to_string_with_zero_padding(const T& value, std::size_t total_length) { - std::string str = std::to_string(value); - if (str.length() >= total_length) { - return str; - } - std::ostringstream oss; - oss << std::setw(total_length) << std::setfill('0') << value; - return oss.str(); - } }; } // namespace helper diff --git a/fbw-common/src/wasm/extra-backend/Pushback/Pushback.cpp b/fbw-common/src/wasm/extra-backend/Pushback/Pushback.cpp index 4ce67923796..905e3e4d364 100644 --- a/fbw-common/src/wasm/extra-backend/Pushback/Pushback.cpp +++ b/fbw-common/src/wasm/extra-backend/Pushback/Pushback.cpp @@ -50,8 +50,9 @@ bool Pushback::initialize() { // Pushback Base Data // will be updated every visual frame - DataDefinitionVector pushbackBaseDataDef = {{"L:A32NX_PUSHBACK_SYSTEM_ENABLED", 0, UNITS.Bool}, + DataDefVector pushbackBaseDataDef = {{"L:A32NX_PUSHBACK_SYSTEM_ENABLED", 0, UNITS.Bool}, {"L:A32NX_PARK_BRAKE_LEVER_POS", 0, UNITS.Bool}, + {"SIM ON GROUND", 0, UNITS.Bool}, {"PUSHBACK ATTACHED", 0, UNITS.Bool}, {"PLANE HEADING DEGREES TRUE", 0, UNITS.degrees}, {"RELATIVE WIND VELOCITY BODY Z", 0, UNITS.FeetSec}}; @@ -60,7 +61,7 @@ bool Pushback::initialize() { // Data definitions for PushbackDataID // Will only be written to sim if the pushback system is enabled and the tug connected - DataDefinitionVector pushBackDataDef = {{"PUSHBACK WAIT", 0, UNITS.Bool}, + DataDefVector pushBackDataDef = {{"PUSHBACK WAIT", 0, UNITS.Bool}, {"VELOCITY BODY X", 0, UNITS.FeetSec}, {"VELOCITY BODY Y", 0, UNITS.FeetSec}, {"VELOCITY BODY Z", 0, UNITS.FeetSec}, @@ -81,7 +82,7 @@ bool Pushback::initialize() { // debug purposes pushbackDebug = dataManager->make_named_var("PUSHBACK_DEBUG", UNITS.Bool, UpdateMode::AUTO_READ); - DataDefinitionVector pushbackDebugDataDef = {{"L:A32NX_PUSHBACK_UPDT_DELTA", 0, UNITS.Number}, + DataDefVector pushbackDebugDataDef = {{"L:A32NX_PUSHBACK_UPDT_DELTA", 0, UNITS.Number}, {"L:A32NX_PUSHBACK_SPD", 0, UNITS.FeetSec}, {"L:A32NX_PUSHBACK_HDG", 0, UNITS.degrees}, {"L:A32NX_PUSHBACK_INERTIA_SPD", 0, UNITS.FeetSec}, @@ -106,7 +107,7 @@ bool Pushback::update(sGaugeDrawData* pData) { // Check if the pushback system is enabled and conditions are met if (!msfsHandler.getAircraftIsReadyVar() || !pushbackBaseInfoPtr->data().pushbackSystemEnabled || - !pushbackBaseInfoPtr->data().pushbackAttached || !msfsHandler.getSimOnGround()) { + !pushbackBaseInfoPtr->data().pushbackAttached || !pushbackBaseInfoPtr->data().simOnGround) { return true; } diff --git a/fbw-common/src/wasm/extra-backend/Pushback/Pushback.h b/fbw-common/src/wasm/extra-backend/Pushback/Pushback.h index 9b4ebc9c7ee..007dd43dddf 100644 --- a/fbw-common/src/wasm/extra-backend/Pushback/Pushback.h +++ b/fbw-common/src/wasm/extra-backend/Pushback/Pushback.h @@ -17,7 +17,7 @@ constexpr double PI = 3.14159265358979323846; class MsfsHandler; -using DataDefinitionVector = std::vector; +using DataDefVector = std::vector; /** * This module is responsible for the pushback process. @@ -49,6 +49,7 @@ class Pushback : public Module { struct PushbackBaseInfo { FLOAT64 pushbackSystemEnabled; FLOAT64 parkingBrakeEngaged; + FLOAT64 simOnGround; FLOAT64 pushbackAttached; FLOAT64 aircraftHeading; FLOAT64 windVelBodyZ; diff --git a/fbw-common/src/wasm/fadec_common/src/EngineRatios.hpp b/fbw-common/src/wasm/fadec_common/src/EngineRatios.hpp deleted file mode 100644 index 17f9fd5cc8b..00000000000 --- a/fbw-common/src/wasm/fadec_common/src/EngineRatios.hpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_ENGINERATIOS_HPP -#define FLYBYWIRE_AIRCRAFT_ENGINERATIOS_HPP - -#include - -/** - * @class EngineRatios - * @brief A class that provides methods for calculating various engine performance ratios. - */ -class EngineRatios { - public: - - /** - * @brief Calculates the ratio of the ambient temperature to the standard temperature at sea level. - * - * This function calculates the ratio of the ambient temperature (in degrees Celsius) to the standard - * temperature at sea level (15 degrees Celsius). - * The result is dimensionless and is used in various aerodynamic and engine performance calculations. - * - * @param ambientTemp The ambient temperature in degrees Celsius. - * @return The ratio of the ambient temperature to the standard temperature at sea level in degrees Celsius. - */ - static FLOAT64 theta(double ambientTemp) { - return (273.15 + ambientTemp) / 288.15; - } - - /** - * @brief Calculates the ratio of the ambient pressure to the standard pressure at sea level. - * - * This function calculates the ratio of the ambient pressure (in hPa) to the standard pressure at - * sea level (1013 hPa). - * The result is dimensionless and is used in various aerodynamic and engine performance calculations. - * - * @param ambientPressure The ambient pressure in hPa. - * @return The ratio of the ambient pressure to the standard pressure at sea level in hPa. - */ - static FLOAT64 delta(double ambientPressure) { - return ambientPressure / 1013.0; - } - - /** - * @brief Calculates the ratio of the total temperature to the standard temperature at sea level, - * accounting for the effects of Mach number. - * - * This function calculates the ratio of the total temperature (in degrees Celsius) to the standard - * temperature at sea level (15 degrees Celsius), - * accounting for the effects of Mach number. The result is dimensionless and is used in various - * aerodynamic and engine performance calculations. - * - * @param mach The Mach number. - * @param ambientTemp The ambient temperature in degrees Celsius. - * @return The ratio of the total temperature to the standard temperature at sea level, accounting - * for the effects of Mach number. - */ - static FLOAT64 theta2(double mach, double ambientTemp) { - return theta(ambientTemp) * (1 + 0.2 * (std::pow)(mach, 2)); - } - - /** - * @brief Calculates the ratio of the total pressure to the standard pressure at sea level, - * accounting for the effects of Mach number. - * - * This function calculates the ratio of the total pressure (in hPa) to the standard pressure at - * sea level (1013 hPa), accounting for the effects of Mach number. The result is dimensionless and - * is used in various aerodynamic and engine performance calculations. - * - * @param mach The Mach number. - * @param ambientPressure The ambient pressure in hPa. - * @return The ratio of the total pressure to the standard pressure at sea level, accounting for the effects of Mach number. - */ - static FLOAT64 delta2(double mach, double ambientPressure) { - return delta(ambientPressure) * (std::pow)((1 + 0.2 * (std::pow)(mach, 2)), 3.5); - } -}; - -#endif // FLYBYWIRE_AIRCRAFT_ENGINERATIOS_HPP diff --git a/fbw-common/src/wasm/fadec_common/src/Fadec.cpp b/fbw-common/src/wasm/fadec_common/src/Fadec.cpp deleted file mode 100644 index 513ea5c2917..00000000000 --- a/fbw-common/src/wasm/fadec_common/src/Fadec.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#include "Fadec.h" - -double Fadec::interpolate(double x, double x0, double x1, double y0, double y1) { - if (x0 == x1) - return y0; - if (x < x0) - return y0; - if (x > x1) - return y1; - return ((y0 * (x1 - x)) + (y1 * (x - x0))) / (x1 - x0); -} - -double Fadec::cas2mach(double cas, double ambientPressure) { - double k = 2188648.141; - double delta = ambientPressure / 1013; - return sqrt((5 * (std::pow)((((std::pow)((((std::pow)(cas, 2) / k) + 1), 3.5) * (1 / delta)) - (1 / delta) + 1), 0.285714286)) - 5); -} diff --git a/fbw-common/src/wasm/fadec_common/src/Fadec.h b/fbw-common/src/wasm/fadec_common/src/Fadec.h deleted file mode 100644 index f15c52e542f..00000000000 --- a/fbw-common/src/wasm/fadec_common/src/Fadec.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2023-2024 FlyByWire Simulations -// SPDX-License-Identifier: GPL-3.0 - -#ifndef FLYBYWIRE_AIRCRAFT_FADEC_H -#define FLYBYWIRE_AIRCRAFT_FADEC_H - -#include - -#include "DataManager.h" -#include "Module.h" - -class MsfsHandler; - - -class Fadec : public Module { - public: - static constexpr double LBS_TO_KGS = 0.4535934; - static constexpr double KGS_TO_LBS = 1 / 0.4535934; - - // delete the default constructor for this virtual class - Fadec() = delete; - - /** - * Creates a new Fadec instance and takes a reference to the MsfsHandler instance. - * @param msfsHandler The MsfsHandler instance that is used to communicate with the simulator. - */ - explicit Fadec(MsfsHandler& msfsHandler) : Module(msfsHandler) {} - - virtual bool initialize() override = 0; - virtual bool preUpdate(sGaugeDrawData* pData) override = 0; - virtual bool update(sGaugeDrawData* pData) override = 0; - virtual bool postUpdate(sGaugeDrawData* pData) override = 0; - virtual bool shutdown() override = 0; - - public: - - /** - * @brief Interpolates a value using linear interpolation. - * - * This function performs linear interpolation based on the input parameters. It calculates the value of 'y' at a given 'x' - * using the formula for linear interpolation: y = ((y0 * (x1 - x)) + (y1 * (x - x0))) / (x1 - x0). - * If x0 is equal to x1, it returns y0 to avoid division by zero. - * - * This function is used by MSFS for the engine tables. - * - * @param x The 'x' value at which to interpolate. - * @param x0 The 'x' value of the first data point. - * @param x1 The 'x' value of the second data point. - * @param y0 The 'y' value of the first data point. - * @param y1 The 'y' value of the second data point. - * @return The interpolated 'y' value at 'x'. - */ - static double interpolate(double x, double x0, double x1, double y0, double y1); - - /** - * @brief Converts calibrated airspeed (CAS) to Mach number. - * - * This function converts the calibrated airspeed (CAS) to the Mach number. The conversion is based on the ambient pressure and a constant - * `k`. The Mach number is calculated using the formula: sqrt((5 * pow(((pow(((pow(cas, 2) / k) + 1), 3.5) * (1 / delta)) - (1 / delta) + - * 1), 0.285714286)) - 5), where delta is the ratio of the ambient pressure to the standard pressure at sea level. - * - * @param cas The calibrated airspeed in knots. - * @param ambientPressure The ambient pressure in hPa. - * @return The Mach number. - */ - static double cas2mach(double cas, double ambientPressure); -}; - -#endif // FLYBYWIRE_AIRCRAFT_FADEC_H diff --git a/fbw-common/src/wasm/fadec_common/src/common.h b/fbw-common/src/wasm/fadec_common/src/common.h new file mode 100644 index 00000000000..5442aa365a5 --- /dev/null +++ b/fbw-common/src/wasm/fadec_common/src/common.h @@ -0,0 +1,138 @@ +#pragma once + +#include +#include +#include + +#include +#include + + +class SimVars; + +HANDLE hSimConnect; + +///

+/// Interpolation function being used by MSFS for the engine tables +/// +/// Interpolated 'y' for a given 'x'. +double interpolate(double x, double x0, double x1, double y0, double y1) { + double y = 0; + + if (x0 == x1) { + y = y0; + } else { + y = ((y0 * (x1 - x)) + (y1 * (x - x0))) / (x1 - x0); + } + + return y; +} + +/// +/// Custom POW function +/// +double powFBW(double base, size_t exponent) { + double power = 1.0; + + while (exponent > 0) { + power *= base; + --exponent; + } + + return power; +} + +/// +/// Custom EXP function +/// +double expFBW(double x) { + int n = 8; + x = 1.0 + x / 256.0; + + while (n > 0) { + x *= x; + --n; + } + return x; +} + +/// +/// Environmental corrected ratios +/// +class EngineRatios { + public: + FLOAT64 theta(double ambientTemp) { + double t = (273.15 + ambientTemp) / 288.15; + return t; + } + + FLOAT64 delta(double ambientPressure) { + double d = ambientPressure / 1013; + return d; + } + + FLOAT64 theta2(double mach, double ambientTemp) { + double t2 = this->theta(ambientTemp) * (1 + 0.2 * powFBW(mach, 2)); + return t2; + } + + FLOAT64 delta2(double mach, double ambientPressure) { + double d2 = this->delta(ambientPressure) * pow((1 + 0.2 * powFBW(mach, 2)), 3.5); + return d2; + } +}; + +/// +/// Padding for the imbalance function +/// +template >*/> +std::string to_string_with_zero_padding(const T& value, std::size_t total_length) { + auto str = std::to_string(value); + if (str.length() < total_length) + str.insert(str.front() == '-' ? 1 : 0, total_length - str.length(), '0'); + return str; +} + +/// +/// Imbalance decoder function +/// +/// The imbalance coded word (2-bytes per parameter). +/// The engine parameter which is being imbalanced. +double imbalanceExtractor(double imbalanceCode, int parameter) { + double reg = 0; + + parameter = 9 - parameter; + + while (parameter > 0) { + reg = fmod(imbalanceCode, 100); + imbalanceCode /= 100; + parameter--; + } + + return int(reg); +} + +/// +/// Timer Class for Performance Profiling purposes. TO BE DELETED! +/// +class Timer { + public: + Timer() : m_StartTimepoint{clock_type::now()} {} + ~Timer() {} + + void reset() { m_StartTimepoint = clock_type::now(); } + + double elapsed() { + auto start = std::chrono::time_point_cast(m_StartTimepoint).time_since_epoch().count(); + auto end = std::chrono::time_point_cast(clock_type::now()).time_since_epoch().count(); + + auto duration = end - start; + double ms = duration * 0.001; + return ms; // ms + } + + private: + using clock_type = std::chrono::steady_clock; + + std::chrono::time_point m_StartTimepoint; +}; diff --git a/igniter.config.mjs b/igniter.config.mjs index 4ab742c689d..8132c2214c7 100644 --- a/igniter.config.mjs +++ b/igniter.config.mjs @@ -111,6 +111,14 @@ export default new TaskOfTasks("all", [ "Cargo.toml", "fbw-a32nx/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/systems.wasm" ]), + new ExecTask("systems-fadec", + "npm run build-a32nx:fadec", + [ + "fbw-a32nx/src/wasm/fadec_a320", + "fbw-common/src/wasm/fbw_common", + "fbw-common/src/wasm/fadec_common", + "fbw-a32nx/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/fadec.wasm" + ]), new ExecTask("systems-fbw", "npm run build-a32nx:fbw", [ @@ -125,16 +133,13 @@ export default new TaskOfTasks("all", [ "fbw-a32nx/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/terronnd.wasm", "fbw-common/src/wasm/terronnd/out/terronnd.wasm", ]), - new ExecTask('cpp-wasm-cmake', + new ExecTask('extra-backend-a32nx', "npm run build:cpp-wasm-cmake", [ 'fbw-common/src/wasm/cpp-msfs-framework', 'fbw-common/src/wasm/extra-backend', - 'fbw-common/src/wasm/fadec_common_v2', 'fbw-a32nx/src/wasm/extra-backend-a32nx', - 'fbw-a32nx/src/wasm/fadec_a32nx', - 'fbw-a32nx/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/extra-backend-a32nx.wasm', - 'fbw-a32nx/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/fadec-a32nx.wasm' + 'fbw-a32nx/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/extra-backend-a32nx.wasm' ]), ], true), @@ -190,6 +195,14 @@ export default new TaskOfTasks("all", [ "fbw-a380x/src/wasm/systems", "fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/systems.wasm" ]), + new ExecTask("systems-fadec", + "npm run build-a380x:fadec", + [ + "fbw-common/src/wasm/fbw_common", + "fbw-common/src/wasm/fadec_common", + "fbw-a380x/src/wasm/fadec_a380", + "fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/fadec.wasm" + ]), new ExecTask("systems-fbw", "npm run build-a380x:fbw", [ @@ -204,16 +217,13 @@ export default new TaskOfTasks("all", [ "fbw-common/src/wasm/terronnd/out/terronnd.wasm", "fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/terronnd.wasm", ]), - new ExecTask('cpp-wasm-cmake', + new ExecTask('extra-backend', "npm run build:cpp-wasm-cmake", [ 'fbw-common/src/wasm/cpp-msfs-framework', 'fbw-common/src/wasm/extra-backend', - 'fbw-common/src/wasm/fadec_common_v2', 'fbw-a380x/src/wasm/extra-backend-a380x', - 'fbw-a380x/src/wasm/fadec_a380x', - 'fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/extra-backend-a380x.wasm', - 'fbw-a32nx/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/fadec-a380x.wasm' + 'fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/extra-backend-a380x.wasm' ]), ], true), @@ -227,7 +237,7 @@ export default new TaskOfTasks("all", [ // InGamePanels Checklist Fix Tasks new TaskOfTasks("ingamepanels-checklist-fix", [ // Prepare the out folder and any other pre tasks. - // Currently, these can be run in parallel, but in the future, we may need to run them in sequence if there are any dependencies. + // Currently, these can be run in parallel but in the future, we may need to run them in sequence if there are any dependencies. new TaskOfTasks("preparation", [ new ExecTask("copy-base-files", "npm run build-ingamepanels-checklist-fix:copy-base-files") ], true), diff --git a/package.json b/package.json index b5fa2913414..b919e090820 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "build-a32nx:ewd": "cd fbw-a32nx/src/systems/instruments/src/EWD && rollup -c", "build-a32nx:systems": "cargo build -p a320_systems_wasm --target wasm32-wasi --release && wasm-opt -O1 --signext-lowering --enable-bulk-memory -o /external/fbw-a32nx/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/systems.wasm /external/target/wasm32-wasi/release/a320_systems_wasm.wasm", + "build-a32nx:fadec": "cd fbw-a32nx/src/wasm/fadec_a320 && ./build.sh && wasm-opt -O1 --signext-lowering -o /external/fbw-a32nx/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/fadec.wasm /external/fbw-a32nx/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/fadec.wasm", "build-a32nx:fbw": "cd fbw-a32nx/src/wasm/fbw_a320 && ./build.sh && wasm-opt -O1 --signext-lowering -o /external/fbw-a32nx/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/fbw.wasm /external/fbw-a32nx/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/fbw.wasm", "build-a32nx:terronnd": "cd fbw-common/src/wasm/terronnd && ./build.sh && wasm-opt -O1 --signext-lowering -o /external/fbw-a32nx/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/terronnd.wasm /external/fbw-common/src/wasm/terronnd/out/terronnd.wasm", @@ -66,6 +67,7 @@ "build-a380x:systems-host": "node fbw-a380x/src/systems/systems-host/build.js", "build-a380x:systems": "cargo build -p a380_systems_wasm --target wasm32-wasi --release && wasm-opt -O1 --signext-lowering --enable-bulk-memory -o /external/fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/systems.wasm /external/target/wasm32-wasi/release/a380_systems_wasm.wasm", + "build-a380x:fadec": "cd fbw-a380x/src/wasm/fadec_a380 && ./build.sh && wasm-opt -O1 --signext-lowering -o /external/fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/fadec.wasm /external/fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/fadec.wasm", "build-a380x:fbw": "cd fbw-a380x/src/wasm/fbw_a380 && ./build.sh && wasm-opt -O1 --signext-lowering -o /external/fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/fbw.wasm /external/fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/fbw.wasm", "build-a380x:terronnd": "cd fbw-common/src/wasm/terronnd && ./build.sh && wasm-opt -O1 --signext-lowering -o /external/fbw-a380x/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/terronnd.wasm /external/fbw-common/src/wasm/terronnd/out/terronnd.wasm",