Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into improved-reverse-thrust-limit
Browse files Browse the repository at this point in the history
Saschl authored Apr 11, 2024
2 parents e24a9d3 + 85d0414 commit f05bf0b
Showing 73 changed files with 6,589 additions and 6,434 deletions.
1 change: 1 addition & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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]
5 changes: 3 additions & 2 deletions fbw-a32nx/src/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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 ()
26 changes: 0 additions & 26 deletions fbw-a32nx/src/wasm/fadec_a320/CMakeLists.txt

This file was deleted.

9 changes: 0 additions & 9 deletions fbw-a32nx/src/wasm/fadec_a320/README.md

This file was deleted.

75 changes: 0 additions & 75 deletions fbw-a32nx/src/wasm/fadec_a320/build.sh

This file was deleted.

1,358 changes: 0 additions & 1,358 deletions fbw-a32nx/src/wasm/fadec_a320/src/EngineControl.h

This file was deleted.

32 changes: 0 additions & 32 deletions fbw-a32nx/src/wasm/fadec_a320/src/FadecGauge.cpp

This file was deleted.

441 changes: 0 additions & 441 deletions fbw-a32nx/src/wasm/fadec_a320/src/FadecGauge.h

This file was deleted.

268 changes: 0 additions & 268 deletions fbw-a32nx/src/wasm/fadec_a320/src/RegPolynomials.h

This file was deleted.

407 changes: 0 additions & 407 deletions fbw-a32nx/src/wasm/fadec_a320/src/SimVars.h

This file was deleted.

71 changes: 0 additions & 71 deletions fbw-a32nx/src/wasm/fadec_a320/src/Tables.h

This file was deleted.

260 changes: 0 additions & 260 deletions fbw-a32nx/src/wasm/fadec_a320/src/ThrustLimits.h

This file was deleted.

41 changes: 41 additions & 0 deletions fbw-a32nx/src/wasm/fadec_a32nx/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)

5 changes: 5 additions & 0 deletions fbw-a32nx/src/wasm/fadec_a32nx/README.md
Original file line number Diff line number Diff line change
@@ -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.
1,180 changes: 1,180 additions & 0 deletions fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/EngineControlA32NX.cpp

Large diffs are not rendered by default.

342 changes: 342 additions & 0 deletions fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/EngineControlA32NX.h

Large diffs are not rendered by default.

377 changes: 377 additions & 0 deletions fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FadecSimData_A32NX.hpp

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Fadec_A32NX.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
36 changes: 36 additions & 0 deletions fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Fadec_A32NX.h
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) 2023-2024 FlyByWire Simulations
// SPDX-License-Identifier: GPL-3.0

#include <iostream>
#include <string>

#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();
}
99 changes: 99 additions & 0 deletions fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/FuelConfiguration_A32NX.h
Original file line number Diff line number Diff line change
@@ -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 <string>

// 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
428 changes: 428 additions & 0 deletions fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Polynomials_A32NX.hpp

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/Tables1502_A32NX.hpp
Original file line number Diff line number Diff line change
@@ -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 <cmath>

#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
329 changes: 329 additions & 0 deletions fbw-a32nx/src/wasm/fadec_a32nx/src/Fadec/ThrustLimits_A32NX.hpp
Original file line number Diff line number Diff line change
@@ -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 <algorithm>
#include <cmath>

#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<int, BleedValues> 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<int, bool, bool>, std::tuple<double, double, double>> 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
58 changes: 58 additions & 0 deletions fbw-a32nx/src/wasm/fadec_a32nx/src/Gauge_Fadec_v2.cpp
Original file line number Diff line number Diff line change
@@ -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 <MSFS/Legacy/gauges.h>
#include <MSFS/MSFS.h>

#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<sGaugeDrawData*>(pData));
}
case PANEL_SERVICE_PRE_KILL: {
return msfsHandler.shutdown();
}
default:
break;
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ import { Position, EngineNumber, FadecActive } from '@instruments/common/types';
import React from 'react';

const IgnitionBorder: React.FC<Position & EngineNumber & FadecActive> = ({ 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));
5 changes: 3 additions & 2 deletions fbw-a380x/src/wasm/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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 ()
25 changes: 0 additions & 25 deletions fbw-a380x/src/wasm/fadec_a380/CMakeLists.txt

This file was deleted.

75 changes: 0 additions & 75 deletions fbw-a380x/src/wasm/fadec_a380/build.sh

This file was deleted.

1,536 changes: 0 additions & 1,536 deletions fbw-a380x/src/wasm/fadec_a380/src/EngineControl.h

This file was deleted.

32 changes: 0 additions & 32 deletions fbw-a380x/src/wasm/fadec_a380/src/FadecGauge.cpp

This file was deleted.

409 changes: 0 additions & 409 deletions fbw-a380x/src/wasm/fadec_a380/src/FadecGauge.h

This file was deleted.

272 changes: 0 additions & 272 deletions fbw-a380x/src/wasm/fadec_a380/src/RegPolynomials.h

This file was deleted.

547 changes: 0 additions & 547 deletions fbw-a380x/src/wasm/fadec_a380/src/SimVars.h

This file was deleted.

71 changes: 0 additions & 71 deletions fbw-a380x/src/wasm/fadec_a380/src/Tables.h

This file was deleted.

259 changes: 0 additions & 259 deletions fbw-a380x/src/wasm/fadec_a380/src/ThrustLimits.h

This file was deleted.

41 changes: 41 additions & 0 deletions fbw-a380x/src/wasm/fadec_a380x/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
)

5 changes: 5 additions & 0 deletions fbw-a380x/src/wasm/fadec_a380x/README.md
Original file line number Diff line number Diff line change
@@ -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.
1,090 changes: 1,090 additions & 0 deletions fbw-a380x/src/wasm/fadec_a380x/src/Fadec/EngineControl_A380X.cpp

Large diffs are not rendered by default.

264 changes: 264 additions & 0 deletions fbw-a380x/src/wasm/fadec_a380x/src/Fadec/EngineControl_A380X.h
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit f05bf0b

Please sign in to comment.