API Question LKEY usage

Observation

New member
Joined
Jun 13, 2019
Messages
26
Reaction score
5
Points
3
Hello everyone,

I am trying to develop an addon and am having trouble with the correct usage of the OAPI_LKEYs. The idea is to have thrus vectoring independent of RCS or AF control.
I tried:
Code:
if (KEYDOWN(kstate, OAPI_LKEY_RCSPitchDown)) {
    // do stuff ...
}
but it doesn't seem to work. The documentation doesn't say anything about usage either I believe.

Now of course, if there's an easier way of finding out pitch, yaw and roll commands, I'll gladly take it...
 

asbjos

tuanibrO
Addon Developer
Joined
Jun 22, 2011
Messages
696
Reaction score
259
Points
78
Location
This place called "home".
For thrust vectoring, I found the easiest way to accomplish it was to create dummy RCS thrusters. Then I created control surfaces, which are automatically linked to the user numpad controls.
Finally, you can get the deflection from GetControlSurfaceLevel.
The benefit of this method is that you with the control surfaces get a realistic modeling of thruster delay and movement, without having to implement it yourself.
A drawback is that you have to define an airfoil for the control surfaces to work.

Here is the relevant code:
PHP:
// SetClassCaps:
// Dummy RCS for jet vanes and air rudders
th_rcsDummyBooster[0] = CreateThruster(_V(0, 1, -1), _V(0, 1, 0), 0,	propellant_handle, 0, 0);
th_rcsDummyBooster[1] = CreateThruster(_V(0, -1, -1), _V(0, -1, 0), 0,	propellant_handle, 0, 0);
th_rcsDummyBooster[2] = CreateThruster(_V(1, 0, -1), _V(1, 0, 0), 0,	propellant_handle, 0, 0);
th_rcsDummyBooster[3] = CreateThruster(_V(-1, 0, -1), _V(-1, 0, 0), 0,	propellant_handle, 0, 0);
th_rcsDummyBooster[4] = CreateThruster(_V(1, 0, -1), _V(0, 1, 0), 0,	propellant_handle, 0, 0);
th_rcsDummyBooster[5] = CreateThruster(_V(1, 0, -1), _V(0, -1, 0), 0,	propellant_handle, 0, 0);
CreateThrusterGroup(&th_rcsDummyBooster[0], 1, THGROUP_ATT_PITCHUP);
CreateThrusterGroup(&th_rcsDummyBooster[1], 1, THGROUP_ATT_PITCHDOWN);
CreateThrusterGroup(&th_rcsDummyBooster[2], 1, THGROUP_ATT_YAWLEFT);
CreateThrusterGroup(&th_rcsDummyBooster[3], 1, THGROUP_ATT_YAWRIGHT);
CreateThrusterGroup(&th_rcsDummyBooster[4], 1, THGROUP_ATT_BANKLEFT);
CreateThrusterGroup(&th_rcsDummyBooster[5], 1, THGROUP_ATT_BANKRIGHT);

ClearAirfoilDefinitions(); // delete previous airfoils
SetRotDrag(_V(0.25, 0.25, 0.1)); // from BrianJ's Falcon 9
CreateAirfoil3(LIFT_VERTICAL, _V(0, 0, 0.1), vlift, NULL, 3.0, 3.0 * 3.0 * PI / 4.0, 1.0); // Width from Wikipedia
CreateAirfoil3(LIFT_HORIZONTAL, _V(0, 0, 0.1), hlift, NULL, 3.0, 3.0 * 3.0 * PI / 4.0, 1.0); // spherical symmetric
Verniers[0] = CreateControlSurface3(AIRCTRL_ELEVATOR, 0.515 * 2, 0.0, _V(0.0, 0.0, -8.6), AIRCTRL_AXIS_XPOS, rudderDelay); // rudder lift is zero, as they do not provide any lift. The foil area could maybe also be zero, but doesn't matter with zero lift anyaway.
Verniers[1] = CreateControlSurface3(AIRCTRL_RUDDER, 0.515 * 2, 0.0, _V(0.0, 0.0, -8.6), AIRCTRL_AXIS_YPOS, rudderDelay);
Verniers[2] = CreateControlSurface3(AIRCTRL_AILERON, 0.515 * 2, 0.0, _V(0.0, 1.0, -8.6), AIRCTRL_AXIS_YPOS, rudderDelay);


...


// Pretty much stolen/borrowed from BrianJ's Falcon9
void rocket::vlift(VESSEL* v, double aoa, double M, double Re, void* context, double* cl, double* cm, double* cd)
{
	static const double step = RAD * 22.5;
	static const double istep = 1.0 / step;
	static const int nabsc = 17;
	static const double CL[nabsc] = { 0, 0.1, 0.2, 0.1, 0, 0.1, 0.2, 0.1, 0, -0.1, -0.2, -0.1, 0, -0.1, -0.2, -0.1, 0 };


	aoa += PI;
	int idx = max(0, min(15, (int)(aoa * istep)));
	double d = aoa * istep - idx;
	*cl = CL[idx] + (CL[idx + 1] - CL[idx]) * d;
	*cm = 0.0;

	static const  double mach[12] = {
		0.0, 0.50, 0.7, 0.90, 1.00, 1.15, 1.5, 2.0, 3.0, 5.0, 7.0, 9.6
	};
	static const double cdp[12] = { // drag coeff at 0 AoA (tip first) for different mach numbers.
		0.63, 0.64, 0.64, 0.72, 0.92, 0.9, 0.78, 0.66, 0.46, 0.3, 0.23, 0.18
	};

	double aoastep = 30.0 * RAD;
	idx = max(0, min(11, (int)(aoa / aoastep)));
	d = aoa / aoastep - idx;

	int i = 0;
	while (i < 14 && M > mach[i])
	{
		i++;
	}

	if (i == 12)
	{
		*cd = cdp[11];
	}
	else if (i == 0)
	{
		*cd = cdp[0];
	}
	else
	{
		*cd = cdp[i - 1] + (cdp[i] - cdp[i - 1]) * (M - mach[i - 1]) / (mach[i] - mach[i - 1]);
	}

	*cd *= 0.5;
}

void rocket::hlift(VESSEL* v, double beta, double M, double Re, void* context, double* cl, double* cm, double* cd)
{
	static const double step = RAD * 22.5;
	static const double istep = 1.0 / step;
	static const int nabsc = 17;
	static const double CL[nabsc] = { 0, 0.1, 0.2, 0.1, 0, 0.1, 0.2, 0.1, 0, -0.1, -0.2, -0.1, 0, -0.1, -0.2, -0.1, 0 };

	beta += PI;
	int idx = max(0, min(15, (int)(beta * istep)));
	double d = beta * istep - idx;
	*cl = CL[idx] + (CL[idx + 1] - CL[idx]) * d;
	*cm = 0.0;

	static const  double mach[12] = {
		0.0, 0.50, 0.7, 0.90, 1.00, 1.15, 1.5, 2.0, 3.0, 5.0, 7.0, 9.6
	};
	static const double cdp[12] = { // drag coeff at 0 AoA (tip first) for different mach numbers.
		0.63, 0.64, 0.64, 0.72, 0.92, 0.9, 0.78, 0.66, 0.46, 0.3, 0.23, 0.18
	};

	double aoastep = 30.0 * RAD;
	idx = max(0, min(11, (int)(beta / aoastep)));
	d = beta / aoastep - idx;

	int i = 0;
	while (i < 14 && M > mach[i])
	{
		i++;
	}

	if (i == 12)
	{
		*cd = cdp[11];
	}
	else if (i == 0)
	{
		*cd = cdp[0];
	}
	else
	{
		*cd = cdp[i - 1] + (cdp[i] - cdp[i - 1]) * (M - mach[i - 1]) / (mach[i] - mach[i - 1]);
	}

	*cd *= 0.5;
}


...


// PostStep:
SetADCtrlMode(7); // enable adc
double TotalPitch = GetControlSurfaceLevel(AIRCTRL_ELEVATOR);
double TotalYaw = GetControlSurfaceLevel(AIRCTRL_RUDDER);
double TotalRoll = GetControlSurfaceLevel(AIRCTRL_AILERON);

VECTOR3 vernier1Direction, vernier2Direction;
if (TotalRoll != 0.0)
{
	double deflRo = -TotalRoll * 70.0 * RAD; // play with the sign to get correct direction
	VECTOR3 t0 = _V(VERNIER_EXHAUST_DIR.x, VERNIER_EXHAUST_DIR.y, VERNIER_EXHAUST_DIR.z);
	vernier1Direction = _V(t0.x * cos(deflRo) + t0.z * sin(deflRo), t0.y, -t0.x * sin(deflRo) + t0.z * cos(deflRo));
	SetThrusterDir(th_vernier[0], vernier1Direction);
	t0 = FlipY(t0);
	vernier2Direction = _V(t0.x * cos(-deflRo) + t0.z * sin(-deflRo), t0.y, -t0.x * sin(-deflRo) + t0.z * cos(-deflRo));
	SetThrusterDir(th_vernier[1], vernier2Direction);

	if (VesselStatus == LAUNCH || VesselStatus == TOWERSEP) // get help from the two booster engines
	{
		// Thrust gimbal of booster thrusters. Swiveled max 5 deg in pitch and yaw (19630012071 page 97(
		VECTOR3 thrustDirection;
		thrustDirection.z = cos(TotalRoll * 5.0 * RAD);
		thrustDirection.y = sin(-TotalRoll * 5.0 * RAD); // negative because it was inverted
		thrustDirection.x = 0.0;
		SetThrusterDir(th_booster[0], thrustDirection);
		SetThrusterDir(th_booster[1], FlipY(thrustDirection));
	}
}
else
{
	double deflPi = TotalPitch * 25.0 * RAD;
	double deflYa = -TotalYaw * 70.0 * RAD; // play with the sign to get correct direction
	VECTOR3 t0 = VERNIER_EXHAUST_DIR;
	vernier1Direction = _V(t0.x * cos(deflYa) + (t0.z * cos(deflPi) + t0.y * sin(deflPi)) * sin(deflYa), t0.y * cos(deflPi) - t0.z * sin(deflPi), cos(deflYa) * (t0.y * sin(deflPi) + t0.z * cos(deflPi)) - t0.x * sin(deflYa));
	SetThrusterDir(th_vernier[0], vernier1Direction);
	t0 = FlipY(t0);
	vernier2Direction = _V(t0.x * cos(deflYa) + (t0.z * cos(deflPi) + t0.y * sin(deflPi)) * sin(deflYa), t0.y * cos(deflPi) - t0.z * sin(deflPi), cos(deflYa) * (t0.y * sin(deflPi) + t0.z * cos(deflPi)) - t0.x * sin(deflYa));
	SetThrusterDir(th_vernier[1], vernier2Direction);

	if (VesselStatus == LAUNCH || VesselStatus == TOWERSEP) // get help from the two booster engines
	{
		// Thrust gimbal of booster thrusters. Swiveled max 5 deg in pitch and yaw
		t0 = BOOSTER_EXHAUST_DIR;
		deflPi = TotalPitch * 5.0 * RAD;
		deflYa = -TotalYaw * 5.0 * RAD; // play with the sign to get correct direction
		VECTOR3 booster1Direction = _V(t0.x * cos(deflYa) + (t0.z * cos(deflPi) + t0.y * sin(deflPi)) * sin(deflYa), t0.y * cos(deflPi) - t0.z * sin(deflPi), cos(deflYa) * (t0.y * sin(deflPi) + t0.z * cos(deflPi)) - t0.x * sin(deflYa));
		SetThrusterDir(th_booster[1], booster1Direction);
		SetThrusterDir(th_booster[0], booster1Direction);
	}

	// Thrust gimbal of core thruster. Swiveled max 3 deg in pitch and yaw
	t0 = CORE_EXHAUST_DIR;
	deflPi = TotalPitch * 3.0 * RAD;
	deflYa = -TotalYaw * 3.0 * RAD; // negative because it was inverted
	VECTOR3 coreDirection = _V(t0.x * cos(deflYa) + (t0.z * cos(deflPi) + t0.y * sin(deflPi)) * sin(deflYa), t0.y * cos(deflPi) - t0.z * sin(deflPi), cos(deflYa) * (t0.y * sin(deflPi) + t0.z * cos(deflPi)) - t0.x * sin(deflYa));
	SetThrusterDir(th_main, coreDirection);
}
 

Observation

New member
Joined
Jun 13, 2019
Messages
26
Reaction score
5
Points
3
That looks interesting, although it does not allow for other control surfaces to be turned off, as my vehicle has some. Creating fake RCS or AF requires at least one of them to be turned on, so if we want to have both off, we need to create another user command for switching between fake and real, without going through the default Orbiter interface, making it confusing / unclean (yes, I'm a perfectionist :)).
Also, does it make a difference if we put all that control code in preStep or postStep other that knowing deltaT, eg. for thruster delay. Does it change anything on the Orbiter side?
 

asbjos

tuanibrO
Addon Developer
Joined
Jun 22, 2011
Messages
696
Reaction score
259
Points
78
Location
This place called "home".
But if you then want thrust vectoring to be independent of your RCS and control surfaces, why do you need to consider that input at all?
If I understand your situation correctly, you want to control your craft manually, while the engine does some gimbaling magic at the same time. Or is that interpretation false?

Also, does it make a difference if we put all that control code in preStep or postStep other that knowing deltaT, eg. for thruster delay. Does it change anything on the Orbiter side?

If my understanding of the flowchart by martins is correct ( https://www.orbiter-forum.com/showthread.php?p=439001&postcount=7 ), it should not matter if you put it in PreStep or PostStep, as you then either use the data from last frame to edit the state in the current frame (pre), or use data from current frame to edit the state in the next frame (post).
So probably no big deal whatever you decide, but I could be wrong, though.
 

Observation

New member
Joined
Jun 13, 2019
Messages
26
Reaction score
5
Points
3
I would like to get pitch, yaw and roll input for engine gimballing while having both RCS and AF turned off.

Thanks for the explanation on preStep and postStep!
 
Top