diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 93ba1eef46e7..e1d0d414205d 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -84,6 +84,7 @@ 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) 1. [ATHR/FADEC] Improved reverse thrust limit - @aguther (Andreas Guther) ## 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 2b299cde8ea6..2595ff9116d0 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.wasm&wasm_gauge=FadecGauge,0,0,1,1 +htmlgauge02 = WasmInstrument/WasmInstrument.html?wasm_module=fadec-a32nx.wasm&wasm_gauge=Gauge_Fadec,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 cdf2102b7ba5..e6e907ee0805 100644 --- a/fbw-a32nx/src/wasm/CMakeLists.txt +++ b/fbw-a32nx/src/wasm/CMakeLists.txt @@ -5,9 +5,10 @@ set(OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../out/flybywire-aircraft-a3 add_definitions(-DA32NX) add_subdirectory(extra-backend-a32nx) +add_subdirectory(fadec_a32nx) -# FIXME: remove the if-clause as soon as all components are using CMake +# these are only here to allow CMake compatible IDEs (JetBrains' Clion for example) to show systax +# highlighting and allow code navigation 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 deleted file mode 100644 index d51e4342832f..000000000000 --- a/fbw-a32nx/src/wasm/fadec_a320/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -# 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 deleted file mode 100644 index baec37e12ac6..000000000000 --- a/fbw-a32nx/src/wasm/fadec_a320/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# 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 deleted file mode 100755 index 08374a9227c7..000000000000 --- a/fbw-a32nx/src/wasm/fadec_a320/build.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/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 deleted file mode 100644 index a50ede74ef1f..000000000000 --- a/fbw-a32nx/src/wasm/fadec_a320/src/EngineControl.h +++ /dev/null @@ -1,1358 +0,0 @@ -#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 deleted file mode 100644 index f502997ceccc..000000000000 --- a/fbw-a32nx/src/wasm/fadec_a320/src/FadecGauge.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#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 deleted file mode 100644 index 811e37092a96..000000000000 --- a/fbw-a32nx/src/wasm/fadec_a320/src/FadecGauge.h +++ /dev/null @@ -1,441 +0,0 @@ -#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 deleted file mode 100644 index f2d17901a4ac..000000000000 --- a/fbw-a32nx/src/wasm/fadec_a320/src/RegPolynomials.h +++ /dev/null @@ -1,268 +0,0 @@ -#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 deleted file mode 100644 index 7fee11865d96..000000000000 --- a/fbw-a32nx/src/wasm/fadec_a320/src/SimVars.h +++ /dev/null @@ -1,407 +0,0 @@ -#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 deleted file mode 100644 index 976c37ff8429..000000000000 --- a/fbw-a32nx/src/wasm/fadec_a320/src/Tables.h +++ /dev/null @@ -1,71 +0,0 @@ -#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 deleted file mode 100644 index 33e2dd57f224..000000000000 --- a/fbw-a32nx/src/wasm/fadec_a320/src/ThrustLimits.h +++ /dev/null @@ -1,260 +0,0 @@ -#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 new file mode 100644 index 000000000000..adb0d3c83e55 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/CMakeLists.txt @@ -0,0 +1,41 @@ +# 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 new file mode 100644 index 000000000000..cbb3dadb936a --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/README.md @@ -0,0 +1,5 @@ +# 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 new file mode 100644 index 000000000000..97d81ec50e96 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/EngineControlA32NX.cpp @@ -0,0 +1,1180 @@ +// 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 new file mode 100644 index 000000000000..e3b37e9558d8 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/EngineControlA32NX.h @@ -0,0 +1,342 @@ +// 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 new file mode 100644 index 000000000000..08b7907cecc4 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FadecSimData_A32NX.hpp @@ -0,0 +1,377 @@ +// 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.Celsius, 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 new file mode 100644 index 000000000000..ce75b37778df --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Fadec_A32NX.cpp @@ -0,0 +1,29 @@ +// 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 new file mode 100644 index 000000000000..4add0f4921d5 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Fadec_A32NX.h @@ -0,0 +1,36 @@ +// 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 new file mode 100644 index 000000000000..e30877cc31d6 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FuelConfiguration_A32NX.cpp @@ -0,0 +1,77 @@ +// 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 new file mode 100644 index 000000000000..4911463168cf --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FuelConfiguration_A32NX.h @@ -0,0 +1,99 @@ +// 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 new file mode 100644 index 000000000000..9552594172bf --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Polynomials_A32NX.hpp @@ -0,0 +1,428 @@ +#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 new file mode 100644 index 000000000000..5bfd8691629a --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Tables1502_A32NX.hpp @@ -0,0 +1,97 @@ +// 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 new file mode 100644 index 000000000000..392ddbdb94d6 --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/ThrustLimits_A32NX.hpp @@ -0,0 +1,329 @@ +// 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 new file mode 100644 index 000000000000..018f44fdda9b --- /dev/null +++ b/fbw-a32nx/src/wasm/fadec_a32nx/src/Gauge_Fadec_v2.cpp @@ -0,0 +1,58 @@ +// 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 0a1953fa0064..8edba55a73a1 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.wasm&wasm_gauge=FadecGauge, 0,0,1,1 +htmlgauge02=WasmInstrument/WasmInstrument.html?wasm_module=fadec-a380x.wasm&wasm_gauge=Gauge_Fadec,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 a0c146971e94..d8c904990f53 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}`, 'bool', 500); + const [engineState] = useSimVar(`L:A32NX_ENGINE_STATE:${engine}`, 'enum', 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 b68e233e5c0e..a7894cbd9099 100644 --- a/fbw-a380x/src/wasm/CMakeLists.txt +++ b/fbw-a380x/src/wasm/CMakeLists.txt @@ -5,9 +5,10 @@ set(OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../../out/flybywire-aircraft-a3 add_definitions(-DA380X) add_subdirectory(extra-backend-a380x) +add_subdirectory(fadec_a380x) -# FIXME: remove the if-clause as soon as all components are using CMake +# these are only here to allow CMake compatible IDEs (JetBrains' Clion for example) to show systax +# highlighting and allow code navigation 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 deleted file mode 100644 index 0d2e084ca1e5..000000000000 --- a/fbw-a380x/src/wasm/fadec_a380/CMakeLists.txt +++ /dev/null @@ -1,25 +0,0 @@ -# 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 deleted file mode 100755 index c06fdb76fd46..000000000000 --- a/fbw-a380x/src/wasm/fadec_a380/build.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/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 deleted file mode 100644 index d58d0e8bcc00..000000000000 --- a/fbw-a380x/src/wasm/fadec_a380/src/EngineControl.h +++ /dev/null @@ -1,1536 +0,0 @@ -#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 deleted file mode 100644 index f502997ceccc..000000000000 --- a/fbw-a380x/src/wasm/fadec_a380/src/FadecGauge.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#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 deleted file mode 100644 index d5f35196d18e..000000000000 --- a/fbw-a380x/src/wasm/fadec_a380/src/FadecGauge.h +++ /dev/null @@ -1,409 +0,0 @@ -#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 deleted file mode 100644 index 70786d438b4b..000000000000 --- a/fbw-a380x/src/wasm/fadec_a380/src/RegPolynomials.h +++ /dev/null @@ -1,272 +0,0 @@ -#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 deleted file mode 100644 index 531d1877929e..000000000000 --- a/fbw-a380x/src/wasm/fadec_a380/src/SimVars.h +++ /dev/null @@ -1,547 +0,0 @@ -#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 deleted file mode 100644 index eaef2b201ad4..000000000000 --- a/fbw-a380x/src/wasm/fadec_a380/src/Tables.h +++ /dev/null @@ -1,71 +0,0 @@ -#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 deleted file mode 100644 index 33008e867f22..000000000000 --- a/fbw-a380x/src/wasm/fadec_a380/src/ThrustLimits.h +++ /dev/null @@ -1,259 +0,0 @@ -#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 new file mode 100644 index 000000000000..a6f0c0d2e5a8 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/CMakeLists.txt @@ -0,0 +1,41 @@ +# 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 new file mode 100644 index 000000000000..b3d2525a9f8d --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/README.md @@ -0,0 +1,5 @@ +# 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 new file mode 100644 index 000000000000..87de1e3a55d5 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/EngineControl_A380X.cpp @@ -0,0 +1,1090 @@ +// 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 new file mode 100644 index 000000000000..7e1d99c5d158 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/EngineControl_A380X.h @@ -0,0 +1,264 @@ +// 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 new file mode 100644 index 000000000000..c21694f3bf90 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/FadecSimData_A380X.hpp @@ -0,0 +1,462 @@ +// 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.Celsius, 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 new file mode 100644 index 000000000000..cd8f18aacc08 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Fadec_A380X.cpp @@ -0,0 +1,29 @@ +// 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 new file mode 100644 index 000000000000..689d97144f5d --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Fadec_A380X.h @@ -0,0 +1,36 @@ +// 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 new file mode 100644 index 000000000000..a4b076f030a6 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/FuelConfiguration_A380X.cpp @@ -0,0 +1,88 @@ +// 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 new file mode 100644 index 000000000000..928ce794d67b --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/FuelConfiguration_A380X.h @@ -0,0 +1,122 @@ +// 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 new file mode 100644 index 000000000000..316a4fbee59c --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Polynomials_A380X.hpp @@ -0,0 +1,449 @@ +#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 new file mode 100644 index 000000000000..fb77e6603859 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/Table1502_A380X.hpp @@ -0,0 +1,97 @@ +// 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 new file mode 100644 index 000000000000..8cff773f45c0 --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/src/Fadec/ThrustLimits_A380X.hpp @@ -0,0 +1,311 @@ +// 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 new file mode 100644 index 000000000000..ae6f5e563a6b --- /dev/null +++ b/fbw-a380x/src/wasm/fadec_a380x/src/Gauge_Fadec_v2.cpp @@ -0,0 +1,58 @@ +// 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/Checklists/Checklists.tsx b/fbw-common/src/systems/instruments/src/EFB/Checklists/Checklists.tsx index 6cfa700c362e..f1d7e121aebe 100644 --- a/fbw-common/src/systems/instruments/src/EFB/Checklists/Checklists.tsx +++ b/fbw-common/src/systems/instruments/src/EFB/Checklists/Checklists.tsx @@ -42,8 +42,8 @@ export const getRelevantChecklistIndices = () => { // iterate over all checklists and check if they are relevant for the current flight phase aircraftChecklists.forEach((cl, clIndex) => { - // check if the checklist is relevant for the current flight phase - if (cl.flightphase && cl.flightphase === flightPhase) { + // check if the checklist is relevant for the previous or the current flight phase + if (cl.flightphase && cl.flightphase <= flightPhase) { relevantChecklistIndices.push(clIndex); } }); 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 e5fec76d3650..1513317a1bfc 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 (
-
+
@@ -367,13 +383,23 @@ export const A380Fuel: React.FC = ({ />
- Feed One + Feed Two = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={feedOneGal * FUEL_GALLONS_TO_KG / OUTER_FEED_MAX_KG * 100} + completed={feedTwoGal * FUEL_GALLONS_TO_KG / INNER_FEED_MAX_KG * 100} /> - +
- Feed Two + Left Inner = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={feedTwoGal * FUEL_GALLONS_TO_KG / INNER_FEED_MAX_KG * 100} + completed={leftInnerGal * FUEL_GALLONS_TO_KG / INNER_TANK_MAX_KG * 100} /> - +
- Left Inner + Left Mid = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={leftInnerGal * FUEL_GALLONS_TO_KG / INNER_TANK_MAX_KG * 100} + completed={leftMidGal * FUEL_GALLONS_TO_KG / MID_TANK_MAX_KG * 100} /> - +
- Left Mid + Feed One = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={leftMidGal * FUEL_GALLONS_TO_KG / MID_TANK_MAX_KG * 100} + completed={feedOneGal * FUEL_GALLONS_TO_KG / OUTER_FEED_MAX_KG * 100} /> - +
- +
- + {' '} + {' '} + {' '} + @@ -392,12 +418,16 @@ export const A380Fuel: React.FC = ({ /> @@ -468,14 +510,25 @@ export const A380Fuel: React.FC = ({ />
- +
- Feed Four + Right Inner = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={feedFourGal * FUEL_GALLONS_TO_KG / OUTER_FEED_MAX_KG * 100} + completed={rightInnerGal * FUEL_GALLONS_TO_KG / INNER_TANK_MAX_KG * 100} /> - +
- Right Inner + Right Mid = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={rightInnerGal * FUEL_GALLONS_TO_KG / INNER_TANK_MAX_KG * 100} + completed={rightMidGal * FUEL_GALLONS_TO_KG / MID_TANK_MAX_KG * 100} /> - +
- Right Mid + Feed Four = ({ completedBarBegin={100} isLabelVisible={false} bgcolor="var(--color-highlight)" - completed={rightMidGal * FUEL_GALLONS_TO_KG / MID_TANK_MAX_KG * 100} + completed={feedFourGal * FUEL_GALLONS_TO_KG / OUTER_FEED_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 9174887335cf..bc9ffd98b981 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataManager.h +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/DataManager.h @@ -36,6 +36,7 @@ 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 13a2af5afd4b..c790d71c5a8b 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,7 +60,6 @@ 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 fb80073d666d..28edeea3aa3d 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 (dirty) { + if (_warnIfDirty && 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 - bool changed = skipChangeCheckFlag || !cachedValue.has_value() || !helper::Math::almostEqual(fromSim, cachedValue.value(), epsilon); + const 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 279c4aa78446..e1f964e10cb1 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,6 +16,8 @@ /** * @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. */ @@ -48,6 +50,13 @@ 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 @@ -60,7 +69,6 @@ class CacheableVariable : public ManagedDataObjectBase { */ ID dataID = -1; - /** * Constructor * @param name The name of the variable in the sim @@ -134,6 +142,8 @@ 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); @@ -183,6 +193,20 @@ 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 480756196771..bf36835848a3 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,7 +224,8 @@ class DataDefinitionVariable : public SimObjectBase { }; bool writeDataToSim() override { - if (!SUCCEEDED(SimConnect_SetDataOnSimObject(hSimConnect, dataDefId, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(T), &dataStruct))) { + if (!SUCCEEDED(SimConnect_SetDataOnSimObject(hSimConnect, dataDefId, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_DATA_SET_FLAG_DEFAULT, 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 4e669b6ed89f..15e6e14fea03 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,6 +14,7 @@ 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.

@@ -48,9 +49,6 @@ 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 4ba44e3752a0..75a7819cf075 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/MsfsHandler.cpp +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/MsfsHandler.cpp @@ -53,6 +53,8 @@ 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); @@ -68,6 +70,7 @@ 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)); @@ -102,7 +105,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. @@ -110,17 +113,11 @@ 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 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())); - } + // See the comments above for the different pause states. + if (a32nxPauseDetected->getAsInt64() > 0 && a32nxPauseDetected->getAsInt64() != 4) { + return true; } // read and update base data from sim @@ -131,18 +128,37 @@ bool MsfsHandler::update(sGaugeDrawData* pData) { // Datamanager is always called first to ensure that all variables are updated before the modules // are called. - // PRE UPDATE bool result = true; + + // PRE UPDATE +#ifdef PROFILING + preUpdate.start(); +#endif 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"); @@ -150,8 +166,13 @@ bool MsfsHandler::update(sGaugeDrawData* pData) { #ifdef PROFILING profiler.stop(); - if (tickCounter % 120 == 0) { + if (tickCounter % 100 == 0) { + LOG_INFO("Profiler Info for " + this->simConnectName); + preUpdate.print(); + mainUpdate.print(); + postUpdate.print(); profiler.print(); + std::cout << std::endl; } #endif @@ -167,10 +188,3 @@ 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 a0026321a52e..0ccb5837c6cf 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/MsfsHandler.h +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/MsfsHandler.h @@ -65,6 +65,8 @@ class MsfsHandler { */ struct BaseSimData { FLOAT64 simulationTime; + FLOAT64 simulationRate; + FLOAT64 simOnGround; FLOAT64 aircraftIsReady; FLOAT64 aircraftDevelopmentState; }; @@ -100,7 +102,10 @@ 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 profiler{"MsfsHandler::update()", 120}; + SimpleProfiler preUpdate{"MsfsHandler::preUpdate()", 100}; + SimpleProfiler mainUpdate{"MsfsHandler::mainUpdate()", 100}; + SimpleProfiler postUpdate{"MsfsHandler::postUpdate()", 100}; + SimpleProfiler profiler{"MsfsHandler::update()", 100}; #endif public: @@ -152,16 +157,31 @@ 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; + [[nodiscard]] bool getAircraftIsReadyVar() const { return baseSimData->data().aircraftIsReady != 0.0; } /** * * @return value of LVAR A32NX_DEVELOPMENT_STATE */ - [[nodiscard]] FLOAT64 getAircraftDevelopmentStateVar() const; + [[nodiscard]] FLOAT64 getAircraftDevelopmentStateVar() const { return baseSimData->data().aircraftDevelopmentState; } /** * @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 2d939f5367b2..c05f0c7941e2 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/SimUnits.h +++ b/fbw-common/src/wasm/cpp-msfs-framework/MsfsHandler/SimUnits.h @@ -38,6 +38,7 @@ 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 ce4d5810b1b1..6299f0db1e5d 100644 --- a/fbw-common/src/wasm/cpp-msfs-framework/lib/SimpleProfiler.hpp +++ b/fbw-common/src/wasm/cpp-msfs-framework/lib/SimpleProfiler.hpp @@ -109,6 +109,8 @@ 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 54b628cf2780..69e8ec8599d7 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,9 +3,10 @@ #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 dea8d0954f23..3f45dfddbf4b 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,6 +4,8 @@ #ifndef FLYBYWIRE_AIRCRAFT_STRING_UTILS_HPP #define FLYBYWIRE_AIRCRAFT_STRING_UTILS_HPP +#include +#include #include namespace helper { @@ -12,17 +14,31 @@ namespace helper { * @brief Helper class for string operations. */ class StringUtils { -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 - */ + 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. + */ 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; @@ -30,8 +46,37 @@ 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 905e3e4d364a..4ce67923796c 100644 --- a/fbw-common/src/wasm/extra-backend/Pushback/Pushback.cpp +++ b/fbw-common/src/wasm/extra-backend/Pushback/Pushback.cpp @@ -50,9 +50,8 @@ bool Pushback::initialize() { // Pushback Base Data // will be updated every visual frame - DataDefVector pushbackBaseDataDef = {{"L:A32NX_PUSHBACK_SYSTEM_ENABLED", 0, UNITS.Bool}, + DataDefinitionVector 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}}; @@ -61,7 +60,7 @@ bool Pushback::initialize() { // Data definitions for PushbackDataID // Will only be written to sim if the pushback system is enabled and the tug connected - DataDefVector pushBackDataDef = {{"PUSHBACK WAIT", 0, UNITS.Bool}, + DataDefinitionVector pushBackDataDef = {{"PUSHBACK WAIT", 0, UNITS.Bool}, {"VELOCITY BODY X", 0, UNITS.FeetSec}, {"VELOCITY BODY Y", 0, UNITS.FeetSec}, {"VELOCITY BODY Z", 0, UNITS.FeetSec}, @@ -82,7 +81,7 @@ bool Pushback::initialize() { // debug purposes pushbackDebug = dataManager->make_named_var("PUSHBACK_DEBUG", UNITS.Bool, UpdateMode::AUTO_READ); - DataDefVector pushbackDebugDataDef = {{"L:A32NX_PUSHBACK_UPDT_DELTA", 0, UNITS.Number}, + DataDefinitionVector 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}, @@ -107,7 +106,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 || !pushbackBaseInfoPtr->data().simOnGround) { + !pushbackBaseInfoPtr->data().pushbackAttached || !msfsHandler.getSimOnGround()) { 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 007dd43dddf9..9b4ebc9c7ee0 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 DataDefVector = std::vector; +using DataDefinitionVector = std::vector; /** * This module is responsible for the pushback process. @@ -49,7 +49,6 @@ 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 new file mode 100644 index 000000000000..17f9fd5cc8b3 --- /dev/null +++ b/fbw-common/src/wasm/fadec_common/src/EngineRatios.hpp @@ -0,0 +1,79 @@ +// 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 new file mode 100644 index 000000000000..513ea5c2917a --- /dev/null +++ b/fbw-common/src/wasm/fadec_common/src/Fadec.cpp @@ -0,0 +1,20 @@ +// 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 new file mode 100644 index 000000000000..f15c52e542f8 --- /dev/null +++ b/fbw-common/src/wasm/fadec_common/src/Fadec.h @@ -0,0 +1,69 @@ +// 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 deleted file mode 100644 index 5442aa365a59..000000000000 --- a/fbw-common/src/wasm/fadec_common/src/common.h +++ /dev/null @@ -1,138 +0,0 @@ -#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 8132c2214c77..4ab742c689d0 100644 --- a/igniter.config.mjs +++ b/igniter.config.mjs @@ -111,14 +111,6 @@ 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", [ @@ -133,13 +125,16 @@ 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('extra-backend-a32nx', + new ExecTask('cpp-wasm-cmake', "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/out/flybywire-aircraft-a320-neo/SimObjects/AirPlanes/FlyByWire_A320_NEO/panel/extra-backend-a32nx.wasm' + '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' ]), ], true), @@ -195,14 +190,6 @@ 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", [ @@ -217,13 +204,16 @@ 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('extra-backend', + new ExecTask('cpp-wasm-cmake', "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/out/flybywire-aircraft-a380-842/SimObjects/AirPlanes/FlyByWire_A380_842/panel/extra-backend-a380x.wasm' + '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' ]), ], true), @@ -237,7 +227,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 b919e0908202..b5fa2913414a 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "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", @@ -67,7 +66,6 @@ "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",