Jet Engine Simulation

Eggheads talking about bytes and stuff.
Post Reply
User avatar
Krishty
Site Admin
Posts: 1271
Joined: 2022-Jan-09, 00:59

Jet Engine Simulation

Post by Krishty »

I’m currently moving the jet engine simulation from the TFX3 plugin into the TFXplorer core.

Here’s the issue: Simulating engines is hard. TAW does the bare minimum, and it’s already complex with much room for errors (more on this later). You rather don’t want to re-implement this for every new plane.

So the idea is:
  1. Let extensions declare engine models, e.g. F199-PW-100 for the F-22. Let them provide properties like maximal thrust, throttle range, etc. Also provide the standard sound effects.
  2. Extensions, upon spawning a plane, say what engine models the plane uses and where they are located.
  3. TFXplorer does internally and automatically:
    • Thrust computation taking into account altitude and weather.
    • Sound effects according to current RPM and afterburner setting.
    • Graphics of the exhaust plumes, shock diamonds, etc.
    • Computing fuel consumption according to throttle settings.
    • Updating the contrails with water vapor from the burnt fuel.
    • Providing an IR signature for enemies according to burnt fuel.
  4. Remove all code doing the above from the F-22 model, and also the KC-135/Aurora models.
  5. ???
  6. PROFIT
This would save ~300 lines of code from the F-22 model alone, and 300 ugly ones.

What could go wrong?


How to compute thrust?

TAW simulates thrust as growing linear with throttle setting, starting with 0 % thrust at 50 % throttle and 100 % thrust at 100 % throttle. This is unrealistic (throst grows exponentially with RPM?), but enough for gameplay.

Altitude is waaaaay more of a problem. TAW uses the following formula to compute thrust with altitude:

Code: Select all

double getSigma(double altitude) {
	return eRaisedToPower(altitude * -(altitude * 0.000000001628 + 0.00009503));
}

[…]

auto const n = 0.75;
auto const sigma = getSigma(altitude);
if(altitude < 11'000) {
	thrust *= raisedToPower(sigma, n);
} else {
	auto const sigma_strat = getSigma(11'000); // at start of stratosphere
	thrust *= raisedToPower(sigma_strat, n) * sigma / sigma_strat;
}
This looks a lot like (air density / air density at sea level) ^ 0.75 … until we get to a height of 11 km (start of the stratosphere). Then the term becomes much more complicated.

I have no sources for that algorithm, and I don’t find the numbers on the internet. I don’t know how realistic it is or how good it will be with other engine models. I only know: If I remove the odd fix for high altitudes, my F-22 can reach Mach 8 at 100.000 ft.


What is RPM, anyway?

The game needs to display turbine RPM on the HUD. So we just take it from the simulation.

Real turbines have two RPMs: N1 is the fan RPM in [0…1] range, and N2 is the RPM of the engine core (also in [0…1] range). Boeing 7x7s display N1, but many jet fighters display N2. Shall we simulate both?

A quick look at ADF/TAW tells us that it simulates neither and just takes an imaginary number to multiply with thrust.

Problem solved, until you reach the next point …


Realistic Sound Needs actual RPM

I’ve heard TAW’s turbine sound for 25 years. I hate it, my wife hates it, my kids hate it.

Nowadays, you don’t take a three-second sound loop and repeat it infinitely with pitch adjusted for throttle. You generate sounds on-the-fly. They never repeat and they can be customized infinitely without having to go out and record anything from a real plane.

For example, Ace Combat uses Turbine:

(More videos here)
If you want to program something like this into the game, you really really want to have correct N1/N2 RPM.


Connecting to FCS

The F-22 needs access to the turbine data for a simple fact: Turbines can never be aligned perfectly with the plane’s center of gravity, and hence their thrust generates torque. The torque must be trimmed out.

So we have to feed the data from the jet engine simulation back into the FCS.

The hardest part is the thrust vectoring control, which uses the nozzle position to estimate the torque that could theoretically be applied.


… and that’s why my weekend project turned out taking two weeks 🙂 I’ll keep doing it, though, because there is another motivation I haven’t mentioned: Rocket engines.

Jet engines need sophisticated code to simulate them, so we should have it in one place and ready to use. Rocket engines don’t need as much sophistication – at least solid-rocket boosters don’t – but they are plentiful. Any missile you want to add to the game? Needs a rocket motor simulation, no matter if guided or unguided. The ejection seat? SAMs? Aren’t flares a kind of rocket motor, too? I don’t want to repeat that code 400 times.

Btw, the sound code has proven to be the most annoying, because it is just stupid boilerplate. Look up RPM, multiply with X, set as pitch for engine sound. Multiply with Y, set as loudness of engine sound. Afterburner on? Set afterburner sound on, else off. Very dull code with lots of repetitions.

Just so you know what I’m up to.
User avatar
Krishty
Site Admin
Posts: 1271
Joined: 2022-Jan-09, 00:59

Re: Jet Engine Simulation

Post by Krishty »

Thrust from Throttle

I decided to handle throttle in the engine core as well.

One reason being, it is needed in a lot of places. Not just in handling input or simulating the engine, but also in the autopilot: The autopilot needs an indication on how far it is allowed to move the throttle without triggering the afterburner.

The other reason being, it is more complicated that you think. On the F-22 throttle, the highest range triggers the afterburner. Some other planes have no afterburners. Some have thrust reversal. Did you know you can trigger thrust reversal by moving the throttle back to the lowest 10 %? I may use this stuff in an interview question; it’s certainly complicated enough to require fundamental programming skills, and open enough to produce very weird solutions.

And that’s why I don’t want to see it implemented twice in my game.


Btw, here’s a nice video of the F-22’s engines being tested:


At 3:22, you can see a stick and a number. It reads “PLA Position”, with PLA meaning “Power Lever Angle”. I was told it’s just a fancy name for “throttle”.

You can see the number going from 10 to 150, with a click at 100. It’s probably not exactly like the F-22’s throttle, but I do see the possibility that the throttle goes to 150 in the real F-22.

It also serves as a nice reference on how fast the engine can spin up and down.
mikew
Data Genius
Posts: 558
Joined: 2022-Jan-09, 20:21

Re: Jet Engine Simulation

Post by mikew »

The other reason being, it is more complicated that you think
Very incisive since I'd say it's already too complicated to model in a general way, and knowing that probably still underestimates how complicated it is. :D

Regarding the jet engine itself,I guess some sort of 'black box' model is the way to go, as I don't suppose we want to be trying to model the fluid flow through it. That would be hard even if we knew the exact construction details.
User avatar
Krishty
Site Admin
Posts: 1271
Joined: 2022-Jan-09, 00:59

Re: Jet Engine Simulation

Post by Krishty »

I’ll go for a blackbox model like you suggested, but with some very strict input to avoid repetitions in things like air density calculation 👍

Just hooked up the new engine interface to contrails. Works better than I hoped for, and really cleans the plane code a lot …

btw some beautiful contrails
User avatar
Krishty
Site Admin
Posts: 1271
Joined: 2022-Jan-09, 00:59

Re: Jet Engine Simulation

Post by Krishty »

Repository has been updated with the new engine API.

You have to do git pull --rebase because I fixed a few bugs in previous commits (related to error handling in other parts of the code).

The new engine API should feel just like the old one, with a small difference: Thrust-specific fuel consumption is now more realistic. I already overhauled it earlier, but I confused lb/lbf/h with kg/N/s like one often does. I now use the dry thrust fuel consumption provided for the F-22 in Wikipedia’s table, with a factor of 3 for wet thrust. (Seems like a good guess based on the F-15/F-35 engines; gives me 30 min of afterburner.)

Rant: Those units are screwed up big time. Wikipedia rightfully states g/(s·kN) (grams of fuel per second-kilonewton) as the non-imperial unit, but in SI it’s actually kg/(s·N). Because nobody wants to write the six leading zeros on that one.
User avatar
Krishty
Site Admin
Posts: 1271
Joined: 2022-Jan-09, 00:59

Re: Jet Engine Simulation

Post by Krishty »

Quick How-To on the API for later reference (taken from UAW Objects F22.cpp):

Declaring the engine model

Upon initialization of the F-22 extension, we declare a new jet engine model. UAW API.h defines the data structure UAW_JetEngineModelSpecs for this:

UAW_JetEngineModelSpecs specs;

We give that model an arbitrary number so we can find it later. Think of it as a handle or magic number you’re free to choose, as long as it’s unique to the engine models in this extension:

  specs.id = 0xf119;

We provide the engine model designation via the standard string API that I need to explain another time:

  specs.name = string_f119pw100;

A few flags. Maybe this should be actual bitwise flags …

  specs.hasAfterburner     = 1;
  specs.hasThrustReversal  = 0;
  specs.hasThrustVectoring = 1;


Now the turbofan specification:

  specs.turbofan.rpmIdle =  7'000; // wild guess
  specs.turbofan.rpmMax  = 14'000; // wild guess
  specs.turbofan.maxThrust = 113'000.f; // dry thrust (N)
  specs.turbofan.tsfc = 17.3f; // that fuel consumption we found on Wikipedia


On to the afterburner:

  specs.afterburner.maxThrust = 55'000.f; // *without* turbofan
  specs.afterburner.tsfc = 17.3f * 3.f; // guessed


Thrust vectoring:

  specs.thrustVectoring.degreesPitch = 20; // vertical thrust vectoring: ±20°
  specs.thrustVectoring.degreesYaw   =  0; // no horizontal thrust vectoring


Finally, the throttle:

  specs.throttle.reverse = 0; // no thrust reversal
  specs.throttle.idle    = 0; // throttle pulled back to 0 % is idle RPM
  specs.throttle.maxDry  = 56; // 56 % throttle: max dry thrust ((140 - 50) / (100 - 50))
  specs.throttle.maxWet  = 100; // anything above 56 % is afterburner


That’s all TFXplorer needs to know. Well we could add sound effects but that’s for another day.

Tell TFXplorer about that exciting new jet engine model:

  api->newModel(specs, { /* no SFX */ });

From now on, we can reference it under the ID 0xf119 whenever needed.


Declaring the aircraft

TFXplorer expects the description of the F-22 in a UAW_Vehicle_Model structure (which may change a lot in an upcoming commit). It already includes a list of contrails (a.k.a. exhaust trails) and vortex trails:

  UAW_TrailSpecs trails[] = {
    // Contrails (left/right):
    { uaw_trailtype_exhaust, … },
    { uaw_trailtype_exhaust, … },
    // Wingtip vortices (left/right):
    { uaw_trailtype_vortex, … },
    { uaw_trailtype_vortex, … },
    // Rudder tip vortices (left/right):
    { uaw_trailtype_vortex, … },
    { uaw_trailtype_vortex, … },
  };


The trails will always be addressed in the order of their declaration here, so index [0] is always the left contrail, and index [1] the right. On a 737, indices [2] & [3] would address contrails as well. But here we only have two, so other indices are for the vortex trails, which are not relevant here.

Declare an array of UAW_Vehicle_EngineSpecs, one for each actual engine in the aircraft:

  // Left engine:
  engines[0].model = 0xf119; // the ID we used in the engine model declaration
  engines[0].exhaustPosition = { -1.f, 0.f, 3.64f };
  engines[0].trail = 0; // feed the left contrail

  // Right engine:
  engines[1].model = …


The order of declaration, again, determines the ID of the engines. Index 0 will from now on always designate the left engine.

Stuff this array into the vehicle model and TFXplorer knows:
  1. What kinds of engines are used, and their full specification regarding thrust, fuel consumption, etc.
  2. Where they are placed relative to the plane (for force, torque, etc)
  3. Which contrail to feed with the burnt fuel

Using the engines

During the vehicle’s physics routine, the API offers you a number of functions to use. For example, to fire up the right engine:

  api->setThrottle(1, true, 0.2f); // set right engine (1) to on (true) and move throttle to 20 %

At this point, TFXplorer will handle the sounds, the slow spinning up, feed contrails 0 and 1, etc.

To get the RPM of the left engine, e.g. for displaying it in the HUD:

  api->getJetN1(0);

Note that TFXplorer has no infrastructure (yet?) to manage fuel. You need to poll the fuel consumption since the last physics iteration and process it manually:

  fuel -= (api->getConsumedFuel(0) + api->getConsumedFuel(1));
  if(fuel <= 0) {
    api->setThrottle(0, false, …); // shut left engine down
    api->setThrottle(1, false, …); // shut right engine down
    // handle HUD warning, cue, etc.
  }


TFXplorer will handle the engine spinning down, even if its throttle is still forward.

Of course, there’s more functions for handling thrust vectoring etc.

Apart from the actual physics part, this is pretty much data-driven. We could read the engine specification / vehicle specification from a text file instead of coding it out. This will be a future goal.
mikew
Data Genius
Posts: 558
Joined: 2022-Jan-09, 20:21

Re: Jet Engine Simulation

Post by mikew »

Looks good! Hopefully, I'll get to try it out this weekend.
mikew
Data Genius
Posts: 558
Joined: 2022-Jan-09, 20:21

Re: Jet Engine Simulation

Post by mikew »

Well, maybe this weekend instead...

I'm getting an occasional error box which as the word 'Torque' in it. Not often enough to pin it down to a specific condition.
Also, the plane seems sluggish in level flight at say 36000ft. Applying full afterburner, it seems to take a long time to go from 200 to 400 knots. It's been a while since I've flown though, so it may have always been like that.
User avatar
Krishty
Site Admin
Posts: 1271
Joined: 2022-Jan-09, 00:59

Re: Jet Engine Simulation

Post by Krishty »

mikew wrote: 2022-Apr-09, 21:10I'm getting an occasional error box which as the word 'Torque' in it. Not often enough to pin it down to a specific condition.
Wow, I got it once. Couldn’t reproduce it and thought it was a hickup or compilation failure. Good to know; gonna see that I can enable additional tests on this …
mikew wrote: 2022-Apr-09, 21:10Applying full afterburner, it seems to take a long time to go from 200 to 400 knots. It's been a while since I've flown though, so it may have always been like that.
I’ll compare with ADF. Remember that the speed is airspeed; your ground speed (GS on the HUD) is probably at 800 at that time.
mikew
Data Genius
Posts: 558
Joined: 2022-Jan-09, 20:21

Re: Jet Engine Simulation

Post by mikew »

That 'Bad Torque' error might only be when pressing F5 to spawn a new plane with some 'heavy' joystick input at the same time.

Yeah, the speed thing may just be my expectations.

Some strange things happening if I go into 'explore' mode and move to a really high altitude before pressing F5. Once it just hung, once the altitude wrapped around at about 100000 feet, but the aircraft movements were completely wrong (maybe appyling 107000' conditions at an indicated 7000') and one I got the 'bad torque' error on pressing F5.
User avatar
Krishty
Site Admin
Posts: 1271
Joined: 2022-Jan-09, 00:59

Re: Jet Engine Simulation

Post by Krishty »

Fixed that error; it was an uninitialized variable. Got in when I added the new engine API because TAW’s physics require access to the total torque on the plane.

Since I added it to the original commit, you’ll need to do a git pull --rebase
User avatar
Krishty
Site Admin
Posts: 1271
Joined: 2022-Jan-09, 00:59

Re: Jet Engine Simulation

Post by Krishty »

I just noticed that the new API “broke” contrails. They still work, just a lot less intensive than before. This is an effect of the F-22’s corrected fuel consumption – less fuel means less water vapor emission.

I’ll adjust it and release a fix. The water vapor emission code was mostly guesswork; some day I’ll have to make it right.
Post Reply