# ProjectAir Breathing Turbojet Engine Model for Orbiter

#### Max-Q

##### 99 40
I tried this and nothing happened:

throttle = GetManualControlLevel(THGROUP_MAIN, MANCTRL_ANYMODE, MANCTRL_ANYDEVICE);
That’s odd…
The other way to do it would be set it up the way you had it before using GetThrusterGroupLevel(), but change how the dummy thruster is created. When you create the dummy thruster, set it’s propellant resource to something valid (not NULL), set it’s ISP to a massive number, and set it’s maximum thrust to something tiny like 0.001 so it works like a thruster but won’t affect the flight model.

#### Thunder Chicken

Donator
That’s odd…
The other way to do it would be set it up the way you had it before using GetThrusterGroupLevel(), but change how the dummy thruster is created. When you create the dummy thruster, set it’s propellant resource to something valid (not NULL), set it’s ISP to a massive number, and set it’s maximum thrust to something tiny like 0.001 so it works like a thruster but won’t affect the flight model.
I take it back, that may have actually worked. Kev had some weird fake thrusters and AddForce commands for ground taxiing and he made the vessel mass very large, so the thrusters were all fighting each other. I'm trying to comment out everything except my engine thrusters. Stay tuned.

#### Thunder Chicken

Donator
Possibly some progress, but some things aren't right. Here are the thruster definitions from clbkSetClassCaps:

C++:
    //Make main propellant tank and thruster
//Note that this is a user-defined thruster NOT connected to THGROUP_MAIN

PROPELLANT_HANDLE main_fuel_tank = CreatePropellantResource(main_fuel_tank_max);
th_main = CreateThruster(_V(0, 0, -4.3), _V(0, 0, 1), max_thrust, main_fuel_tank, Isp_max);
CreateThrusterGroup(&th_main, 1, THGROUP_USER);

//Make dummy propellant tank and thruster to take user throttle input to be
//passed to main engine through the jet engine thrust model calculation
//Note that this is linked to THGROUP_MAIN to get main engine user throttle input

PROPELLANT_HANDLE dummy_fuel_tank = CreatePropellantResource(0.0); //no fuel
th_dummy = CreateThruster(_V(0, 0, 0), _V(0, 0, 1), 1.0, dummy_fuel_tank, 1.0); //produces no thrust, consumes no fuel
CreateThrusterGroup(&th_dummy, 1, THGROUP_MAIN);

And this is the implementation in clbkPreStep:

C++:
   //----JET ENGINE IMPLEMENTATION

//Get throttle input from user from dummy main thruster

throttle = GetThrusterGroupLevel(THGROUP_MAIN);

//determine afterburner status

afterburner = false; //How is afterburner engaged in real jet?

// Get local flight conditions from Orbiter

M_0 = GetMachNumber();            //flight Mach number [-]
T_0 = GetAtmTemperature();          //ambient static temperature [K]
P_0 = GetAtmPressure();             //ambient static pressure [Pa absolute]
rho = GetAtmDensity();             //ambient atmospheric density [kg/cubic meter]

//Use inputs and flight conditions to calculate jet engine thrust and Isp

std::pair<double, double> answer = GetJetEngineThrustIsp(throttle, afterburner, M_0, T_0, P_0, rho);

//Update thrust and Isp of user-defined main engine

SetThrusterIsp(th_main, Isp);
SetThrusterLevel(th_main, (thrust/max_thrust));

//AddForce(_V(0, 0, thrust), _V(0.0, 0.0, -4.3));

//----END JET ENGINE IMPLEMENTATION

For some reason if I use SetThrusterLevel I get a very low amount of thrust. If I use AddForce I actually get the amount of thrust I expect at full throttle.

@MaxQ it may be that your method could work where I only need to define the main thruster, but there is something strange that happens when I use the SetThrusterLevel to do so. It's like the throttle is getting over-ridden somehow. I'd like to use this method if possible as we can use the main engine tank and thrust indicators in the standard HUD.

I think we're close to something usable, but stuff is still not right.

Last edited:

#### Thunder Chicken

Donator
Is it possible to watch variables "live" during debugging - use Orbiter and monitor things like the thruster levels, variables, etc.. instead of stepping through the code? I'm having a hard time diagnosing run-time behavior as I can't change the throttle setting and see what it does to the thrust.

#### Urwumpe

##### Not funny anymore
Donator
Is it possible to watch variables "live" during debugging - use Orbiter and monitor things like the thruster levels, variables, etc.. instead of stepping through the code? I'm having a hard time diagnosing run-time behavior as I can't change the throttle setting and see what it does to the thrust.

The simplest approach I know there is to simply overwrite the HUD drawing code to display some variables. You could also create a vessel-specific MFD mode to display your engine data, like a EICAS MFD.

#### Thunder Chicken

Donator
The simplest approach I know there is to simply overwrite the HUD drawing code to display some variables. You could also create a vessel-specific MFD mode to display your engine data, like a EICAS MFD.
I already have a MFD, but I need to see the main throttle setting. For some reason it's not changing in the HUD display, it shows as max thrust even when it isn't. Very confused on what the state of the user throttle input is. I suppose I could recompile the MFD to take report the throttle status.

#### Thunder Chicken

Donator
I'm having a problem with displaying a VC mesh that I can't sort out.

The main external mesh and VC mesh are preloaded in the vessel class:

C++:
KMirage2000::KMirage2000(OBJHANDLE hVessel, int flightmodel)
: VESSEL2(hVessel, flightmodel)
{

//DefineAnimations();
}

At the end of the code in clbkSetClassCaps is where the meshes are displayed:

C++:
    const VECTOR3 offset = {0.0,0.2,4.5};

SetMeshVisibilityMode(AddMesh(vc_mesh, &offset), MESHVIS_VC);    //doesn't crash here, but nothing displays
//SetMeshVisibilityMode(AddMesh(vc_mesh, {0.0,0.2,4.5}), MESHVIS_VC);  //AddMesh doesn't match argument list type?

There seems to be something wrong with the VECTOR3 and/or the pointer, but I'm not seeing it.

#### Urwumpe

##### Not funny anymore
Donator
The code you posted is ok. &offset results in a VECTOR3 * variable.

The VC mesh should only be visible in VC mode.

#### Thunder Chicken

Donator
The code you posted is ok. &offset results in a VECTOR3 * variable.

The VC mesh should only be visible in VC mode.
Of course, I commented out clbkLoadVC ?‍. I'm learning the meaning of all the callback functions the hard way.

#### Urwumpe

##### Not funny anymore
Donator
Of course, I commented out clbkLoadVC ?‍. I'm learning the meaning of all the callback functions the hard way.

You learn it the Orbiter way.

Don't worry, we make progress.

#### Max-Q

##### 99 40
This is the issue.
C++:
PROPELLANT_HANDLE dummy_fuel_tank = CreatePropellantResource(0.0); //no fuel
th_dummy = CreateThruster(_V(0, 0, 0), _V(0, 0, 1), 1.0, dummy_fuel_tank, 1.0); //produces no thrust, consumes no fuel
CreateThrusterGroup(&th_dummy, 1, THGROUP_MAIN);

Try it this way; an empty prop tank can’t fuel a thruster.
No need to define a second tank anyway,
C++:
th_dummy = CreateThruster(_V(0, 0, 0), _V(0, 0, 1), 1.0, main_fuel_tank, INFINITY); //produces no thrust, consumes no fuel
CreateThrusterGroup(&th_dummy, 1, THGROUP_MAIN);

#### Thunder Chicken

Donator
Try it this way; an empty prop tank can’t fuel a thruster.
No need to define a second tank anyway,
C++:
th_dummy = CreateThruster(_V(0, 0, 0), _V(0, 0, 1), 1.0, main_fuel_tank, INFINITY); //produces no thrust, consumes no fuel
CreateThrusterGroup(&th_dummy, 1, THGROUP_MAIN);
Thanks! I'm not sure if this was the source of my issue, but I see that this is a much cleaner way to do it. Set the Isp to infinity for making infinitesimal thrust with even more infinitesimal fuel consumption. Got it.

#### Urwumpe

##### Not funny anymore
Donator
You can also use DBL_MIN for defining the thrust value then: Its the smallest positive number an IEEE 64bit floating point number can still display. 1.0 does still produce a small thrust.

#### Thunder Chicken

Donator
Trying to update some code and am trying to set touchdown points with the new method using lists of contact vertices.

C++:
    //Set touchdown points for mesh on ground

const VECTOR3 nose_wheel_contact = { 0, -2.428501, 5.0 };
const VECTOR3 left_wheel_contact = { -1.627791, -2.420502, -1.916788 };
const VECTOR3 rght_wheel_contact = { -1.627791, -2.420502, -1.916788 };

std::list<VECTOR3> contact_points = { nose_wheel_contact, left_wheel_contact, rght_wheel_contact };

SetTouchdownPoints(&contact_points, 3);

SetTouchDownPoints quick tips states that the arguments are incorrect, so something is wrong with the list.

Last edited:

#### kuddel

##### Donator
Donator
You can not use std::list ! Almost never any Orbiter API method would allow for std:: containers, as they are extremely unlikely to be transferable correctly over the EXE-DLL boundary (as sad as it is).

Actually there are two SetTouchdownPoints methods in the API, one is labeled "depricated" and should not be used.

The one you are trying to call has the following signature:
C++:
void SetTouchdownPoints (const TOUCHDOWNVTX *tdvtx, DWORD ntdvtx) const;
The documentation talks about "List of touchdown vertex points", but that has nothing to do with std::list<>.

You should give it something like this (untested code):
C++:
static TOUCHDOWNVTX tdvtx_list[] = {
{ _V(  0       , -2.428501,  5.0      ), 1e6, 1e5, 1.6, 0.1 },
{ _V( -1.627791, -2.420502, -1.916788 ), 1e6, 1e5, 3.0, 0.2 },
{ _V( -1.627791, -2.420502, -1.916788 ), 1e6, 1e5, 3.0, 0.2 }
};
SetTouchdownPoints (tdvtx_list, ARRAYSIZE(tdvtx_list));
In addition to the vertex positions, stiffness and damping parameters can be provided to define the compressibility of individual points, e.g. for simulating gear suspension.
Here I've just taken arbitrary numbers (from Delta Glider I think )

Last edited:

#### Thunder Chicken

Donator
You can not use std::list ! Almost never any Orbiter API method would allow for std:: containers, as they are extremely unlikely to be transferable correctly over the EXE-DLL boundary (as sad as it is).

Actually there are two SetTouchdownPoints methods in the API, one is labeled "depricated" and should not be used.

The one you are trying to call has the following signature:
C++:
void SetTouchdownPoints (const TOUCHDOWNVTX *tdvtx, DWORD ntdvtx) const;
The documentation talks about "List of touchdown vertex points", but that has nothing to do with std::list<>.

You should give it something like this (untested code):
C++:
static TOUCHDOWNVTX tdvtx_list[] = {
{ _V(  0       , -2.428501,  5.0      ), 1e6, 1e5, 1.6, 0.1 },
{ _V( -1.627791, -2.420502, -1.916788 ), 1e6, 1e5, 3.0, 0.2 },
{ _V( -1.627791, -2.420502, -1.916788 ), 1e6, 1e5, 3.0, 0.2 }
};
SetTouchdownPoints (tdvtx_list, ARRAYSIZE(tdvtx_list));
In addition to the vertex positions, stiffness and damping parameters can be provided to define the compressibility of individual points, e.g. for simulating gear suspension.
Here I've just taken arbitrary numbers (from Delta Glider I think )
This is a help. I saw this in the API reference but they only said "list" and I assumed std:list.

I'll have to do something with my engine code then because it currently is a std:: pair. Maybe that's what has been causing the problem. Now I have to find another way to return two results out of a method. What is the problem with std containers?

EDIT: Tried the above method and as soon as I touch the engines the jet (starting on the runway) "falls" into the Earth. Some of the code errors are ... interesting.

Last edited:

#### kuddel

##### Donator
Donator
You can use std:: containers if you don't "give" them to Orbiter API functions. If the std::pair only is used inside your code it's totally fine!
As far as I saw in your code, the usage of that container is totally sane there. Using standard container in general is a good thing.
Only when you have to interface, you have to think twice.
One example would be to use std::array all over your code and if you have to provide that to an API method make use of its data() and size() members, like in the reference exampe here.

#### Thunder Chicken

Donator
Good news! I've finally got Kev's full meshes and animations in Orbiter 2016!

Even better news, I also have quite a bit of my model code in as well. It still has some issues, for as soon as I touch the throttle Orbiter crashes, but at least I can apply the debugger.

I am still trying to sort out some implementation details with regard to the thrusters. Right now I have two thrusters defined, one dummy thruster associated with THGROUP_MAIN to take the throttle input, and the "main" engine that produces the actual thrust based on jet engine model.

C++:
    //Make main propellant tank and thruster
//Max. thrust max_thrust is user-defined in kN, so must be converted to N

PROPELLANT_HANDLE main_fuel_tank = CreatePropellantResource(main_fuel_tank_max);
th_main = CreateThruster(_V(0, 0, -4.3), _V(0, 0, 1), max_thrust*1000.0, main_fuel_tank, Isp_max);
CreateThrusterGroup(&th_main, 1, THGROUP_USER);

//Make dummy thruster to take user throttle input to be
//passed to main engine through the jet engine thrust model calculation
//Note that this is linked to THGROUP_MAIN to get main engine user throttle input

th_dummy = CreateThruster(_V(0, 0, -4.3), _V(0, 0, 1), DBL_MIN, main_fuel_tank, INFINITY); //produces no thrust, consumes no fuel
CreateThrusterGroup(&th_dummy, 1, THGROUP_MAIN);

In theory this works, and I have already tested it by using it as a normal thruster and passing the throttle input between the thrusters. My jet engine model would go between them as a translator of sorts. But this idea has some problems that I don't currently have plans to address:
1. The fuel mass in the "main" fuel tank (by design) doesn't connect to the THGROUP_MAIN thruster, so the usual Orbiter main engine propellant quantity indicators don't work.
2. Similarly, the thrust being delivered by the user-defined "main" engine isn't indicated on the usual Orbiter main engine thrust status indicators.
I am wondering if oapiGetPropellantMass and oapiGetPropellantMaxMass could be used to get the status of the fuel tank, and then somehow get it to the Orbiter main fuel tank status indicators. Similarly, I am wondering if there is a way to take the calculated thrust from my model and pass it to the Orbiter main engine indicators. I'm looking through the API Reference for ideas.

#### Thunder Chicken

Donator
In theory this works, and I have already tested it by using it as a normal thruster and passing the throttle input between the thrusters. My jet engine model would go between them as a translator of sorts. But this idea has some problems that I don't currently have plans to address:
1. The fuel mass in the "main" fuel tank (by design) doesn't connect to the THGROUP_MAIN thruster, so the usual Orbiter main engine propellant quantity indicators don't work.
2. Similarly, the thrust being delivered by the user-defined "main" engine isn't indicated on the usual Orbiter main engine thrust status indicators.
I am wondering if oapiGetPropellantMass and oapiGetPropellantMaxMass could be used to get the status of the fuel tank, and then somehow get it to the Orbiter main fuel tank status indicators. Similarly, I am wondering if there is a way to take the calculated thrust from my model and pass it to the Orbiter main engine indicators. I'm looking through the API Reference for ideas.
Actually, I realized that due to the modifications suggested by Max-Q that the propellant tank is connected to the dummy thruster in THGROUP_MAIN, so it does indicate fuel quantity correctly. Max fuel capacity specified is 3160 kg.

Still not sure what can be done with thrust. The dummy thruster is designed to consume no fuel and produce no thrust.

Last edited:

#### Thunder Chicken

Donator
Well, so much for debugging. I placed several breakpoints, started debugging (yes I built a debug version), started Orbiter and loaded all the meshes OK, and I get this at all of my breakpoints (even the one at the very top of main?):

The breakpoint will not be currently hit. No symbols have been loaded for this document.

Meshes load, animations seem to work, I can work the throttle without crashing, but I am unable to step through my code, see where problems are, etc.. F5 is greyed out for stepping.