So I recently went through the process of deciphering how to use the "new" touchdown point model for my J-3 add-on, and I thought it might be generally useful to those who struggle with it punting their add-ons into solar orbit for no apparent reason. There are reasons for that behavior, and there are ways to avoid it and to get a well-behaved add-on. Thanks to @dgatsoulis and others who provided me with guidance to help sort out the use of this model.
The Touchdown Point Models
Originally (Orbiter 2010) you could only set three contact points to define a plane to orient the vessel on the ground something like this (I'm using Lua syntax but the C++ syntax is generally similar, see API_Reference.pdf in OrbiterSDK/Docs):
where each of the arguments is a vector describing the contact point. This gives a downward Z normal for tricycle gear vessels. Left and right contacts had to be specified as second and third arguments as apparently they are automatically tied to a default braking system. There is also a displacement model that provided a certain springiness to these contact points.
The "new" (Orbiter 2016+) touchdown point model gives us some rope to hang ourselves with:
In this model, tdvtx is a list of contact vertices with their stiffness, damping, and dynamic friction coefficients, and ntdvtx is the number of vertices (*it does not seem to need to be supplied for the script method, but it is described in the API Reference for the C++ (?)). This allows a cloud of points to be defined for a general contact model so your vessel will tumble about on the surface realistically if your landings go awry.
The list of vertices tdvtx looks something like this:
Seems easy enough, but we need to supply the stiffness and dampening coefficients (and dynamic friction, which is another matter).
The Problem
So, what is the problem? The problem is that, for certain combinations of vessel mass, stiffness, and damping coefficients, you can make a touchdown point so "twangy" that it's vibration period is faster than the simulation time step (simdt), which means the numerical integration routines cannot resolve time well enough to accurately compute the motion of the vessel.
A typical situation would be an add-on developer making a vessel with a specified mass, and they want to make some contact points. Knowing that the touchdown points for the Atlantis and DG are available in the code base where those coefficients are available, and they are known to work, the obvious thing might be to just copy and paste them into their code. Seems innocent enough, but while those coefficients may work well for those vessels, they won't work the same for a vessel with a different mass. If your add-on mass is much greater, your add-on will sink into the ground because the springs aren't stiff enough, and if your add-on is much lighter, the oscillatory period of your excessively stiff landing struts will cause numerical stability issues that will yeet it to Alpha Centauri. So what do you do?
Keep reading, that's what you do...
Setting the Stiffness and Damping Coefficients: A Case Study
Full disclosure - I still haven't found where these are applied in the Orbiter source code, but my educated guess is that the forced mass-spring-damper model was used for each of the contact points:[math]mz'' + cz' + kz = \sum F_z[/math]where m is the mass supported by the contact, z is the displacement, z' is the rate of displacement, and z'' is the acceleration. The coefficients are defined as:
Stiffness coefficient (Applied spring force per unit displacement): [math]k = F_S/z[/math]Damping coefficient (Applied damping force per unit rate of displacement): [math]c = F_D/z'[/math]
Our goal is to apply physically appropriate values for k and c so the add-on behaves correctly and Orbiter has a hope of properly integrating the equations of motion for the vessel. The applied forces will be variable, but order-of-magnitude estimates can be made at certain limits, which is good enough for our needs. I'll use numbers for my J-3 add-on as an example, but you should be able to follow along with numbers for your add-on.
Estimating the Spring Stiffness Coefficient k
A basic requirement is that the contact points can support the stationary weight of the vessel with a sensible static displacement. The spring equation reduces to [math]kz = mg[/math] where we can solve for k: [math]k = (mg)/z[/math]In this case, the mass of the J-3 is about 450 kg, g is the local acceleration of gravity (~9.81 [imath]m/{s^2}[/imath] on Earth's surface) for a weight of about 4500 N and a sensible deflection would be maybe 10 cm. The weight is primarily on the main wheels, so we'll divide the weight by two. This gives a k = 2250 N / 0.1 m = 22,500 N/m or 2.25e4 N/m. Note that if you move a spacecraft to another planet where the surface gravitational acceleration is different from Earth, that will affect how this touchdown system behaves.
Estimating the Damping Coefficient c
Now for the damping coefficient. For the J-3, we are aiming for a slightly under-damped oscillator (the spring will have a little "bounce" with an overshoot or two before it comes to equilibrium. Critical damping is the value of the damping coefficient that just prevents any overshoot and is defined as [math]c_c=\sqrt{4mk}[/math]For the J-3, again splitting the mass across the two main gear, [imath]c_c[/imath] would be 4500 N[imath]\cdot[/imath]s/m. We will want a damping coefficient slightly lower than this value. I'll start with c = 4000 N[imath]\cdot[/imath]s/m and adjust later if needed.
Determining Numerical Stability
So something to consider is how "twangy" this system will be, more accurately what will be the period of oscillation T. If this period of oscillation comes out to be on the same order of magnitude as the simulation timestep [imath]\varDelta {t}[/imath] (simdt) or smaller, then the numerical time propagation will be unable to accurately resolve the vessel motion and this will generally lead to getting punted into solar orbit. The period of oscillation T is given by: [math]T=2m/\sqrt{4mk-c^2}[/math]
For m = 225 kg, k = 22,500 N/m, and c = 4000 N[imath]\cdot[/imath]s/m, we get [imath]T \approx[/imath] 0.2 s.
Looking into the time propagation settings in Orbiter, it's a little hard to determine if this is a resolvable period or not:
The Runge-Kutta routines are numerical differential equation solver routines, and I think the differing steps are limits associated with time acceleration (if your timestep is under 0.1 seconds, RK2 provides sufficient accuracy, if under 2.0 seconds RK4 is sufficient, etc..). Higher order Runge-Kutta routines are more expensive than lower order ones, but higher order routines maintain accuracy for larger time steps, so transitioning like this makes sense for computational efficiency. If someone knows what is actually going on in Orbiter please comment if you have some information on this.
Going into the simulation with the J-3 and getting the value for simdt gives a value of about 0.015 s, which is about 15 times smaller than the period of oscillation. So at least we're not the same order of magnitude, so this may work out (assuming this is all correct and this is actually what Orbiter is doing under the hood).
Results
So I apply the touchdown model for my J-3 with the following Lua script code:
Empty J-3 vessel mass is 450 kg plus full fuel load of 12 gallons of gasoline, so not quite 500 kg. Firing up a simulation puts me on the runway and supports the vessel nicely:
I tried using the FlightData function to record some forced landing bounces but the resolution is insufficient to see centimeter displacements, but I get a light bounce that quickly stabilizes, as expected for a slightly under-damped system. And the best news is that, even taxiing around for several hours at this point, I haven't been yeeted to solar orbit even once.
Landing Status
A common issue with this model is that, even when nominally parked and stationary on the surface, it seems unwilling to let you achieve Landed status in the scenario file. That seems to be the case here as well. Here is my (Current Status).scn corresponding to the above image:
I experimented with increasing the damping coefficient to see if it would ever settle down and achieve Landed status, but no luck. Practically it is stopped and isn't going anywhere, so this seems to be a numerical issue about the definition of "landed". If anyone has any information on this, please comment below.
Conclusion
I hope this helps overcome the frustrations with this contact model, and perhaps this can start a conversation where maybe the remaining issues can be addressed. Happy add-on development! -TC
The Touchdown Point Models
Originally (Orbiter 2010) you could only set three contact points to define a plane to orient the vessel on the ground something like this (I'm using Lua syntax but the C++ syntax is generally similar, see API_Reference.pdf in OrbiterSDK/Docs):
Code:
set_touchdownpoints(nose_wheel, left_wheel, right_wheel)
where each of the arguments is a vector describing the contact point. This gives a downward Z normal for tricycle gear vessels. Left and right contacts had to be specified as second and third arguments as apparently they are automatically tied to a default braking system. There is also a displacement model that provided a certain springiness to these contact points.
The "new" (Orbiter 2016+) touchdown point model gives us some rope to hang ourselves with:
Code:
set_touchdownpoints(tdvtx, ntdvtx*)
In this model, tdvtx is a list of contact vertices with their stiffness, damping, and dynamic friction coefficients, and ntdvtx is the number of vertices (*it does not seem to need to be supplied for the script method, but it is described in the API Reference for the C++ (?)). This allows a cloud of points to be defined for a general contact model so your vessel will tumble about on the surface realistically if your landings go awry.
The list of vertices tdvtx looks something like this:
Code:
nose_wheel_vtx = {pos=nose_wheel_contact, stiffness = value, damping = value, mu = value, mu_lng = value}
left_wheel_vtx = {pos=left_wheel_contact, stiffness = value, damping = value, mu = value, mu_lng = value}
right_wheel_vtx = {pos=right_wheel_contact, stiffness = value, damping = value, mu = value, mu_lng = value}
4vtx = {pos=4vtx_contact, stiffness = value, damping = value, mu = value, mu_lng = value}
...
tdvtx = {
nose_wheel_vtx,
left_wheel_vtx,
right_wheel_vtx,
4_vtx,
...
}
set_touchdownpoints(tdvtx)
Seems easy enough, but we need to supply the stiffness and dampening coefficients (and dynamic friction, which is another matter).
The Problem
So, what is the problem? The problem is that, for certain combinations of vessel mass, stiffness, and damping coefficients, you can make a touchdown point so "twangy" that it's vibration period is faster than the simulation time step (simdt), which means the numerical integration routines cannot resolve time well enough to accurately compute the motion of the vessel.
A typical situation would be an add-on developer making a vessel with a specified mass, and they want to make some contact points. Knowing that the touchdown points for the Atlantis and DG are available in the code base where those coefficients are available, and they are known to work, the obvious thing might be to just copy and paste them into their code. Seems innocent enough, but while those coefficients may work well for those vessels, they won't work the same for a vessel with a different mass. If your add-on mass is much greater, your add-on will sink into the ground because the springs aren't stiff enough, and if your add-on is much lighter, the oscillatory period of your excessively stiff landing struts will cause numerical stability issues that will yeet it to Alpha Centauri. So what do you do?
Keep reading, that's what you do...
Setting the Stiffness and Damping Coefficients: A Case Study
Full disclosure - I still haven't found where these are applied in the Orbiter source code, but my educated guess is that the forced mass-spring-damper model was used for each of the contact points:[math]mz'' + cz' + kz = \sum F_z[/math]where m is the mass supported by the contact, z is the displacement, z' is the rate of displacement, and z'' is the acceleration. The coefficients are defined as:
Stiffness coefficient (Applied spring force per unit displacement): [math]k = F_S/z[/math]Damping coefficient (Applied damping force per unit rate of displacement): [math]c = F_D/z'[/math]
Our goal is to apply physically appropriate values for k and c so the add-on behaves correctly and Orbiter has a hope of properly integrating the equations of motion for the vessel. The applied forces will be variable, but order-of-magnitude estimates can be made at certain limits, which is good enough for our needs. I'll use numbers for my J-3 add-on as an example, but you should be able to follow along with numbers for your add-on.
Estimating the Spring Stiffness Coefficient k
A basic requirement is that the contact points can support the stationary weight of the vessel with a sensible static displacement. The spring equation reduces to [math]kz = mg[/math] where we can solve for k: [math]k = (mg)/z[/math]In this case, the mass of the J-3 is about 450 kg, g is the local acceleration of gravity (~9.81 [imath]m/{s^2}[/imath] on Earth's surface) for a weight of about 4500 N and a sensible deflection would be maybe 10 cm. The weight is primarily on the main wheels, so we'll divide the weight by two. This gives a k = 2250 N / 0.1 m = 22,500 N/m or 2.25e4 N/m. Note that if you move a spacecraft to another planet where the surface gravitational acceleration is different from Earth, that will affect how this touchdown system behaves.
Estimating the Damping Coefficient c
Now for the damping coefficient. For the J-3, we are aiming for a slightly under-damped oscillator (the spring will have a little "bounce" with an overshoot or two before it comes to equilibrium. Critical damping is the value of the damping coefficient that just prevents any overshoot and is defined as [math]c_c=\sqrt{4mk}[/math]For the J-3, again splitting the mass across the two main gear, [imath]c_c[/imath] would be 4500 N[imath]\cdot[/imath]s/m. We will want a damping coefficient slightly lower than this value. I'll start with c = 4000 N[imath]\cdot[/imath]s/m and adjust later if needed.
Determining Numerical Stability
So something to consider is how "twangy" this system will be, more accurately what will be the period of oscillation T. If this period of oscillation comes out to be on the same order of magnitude as the simulation timestep [imath]\varDelta {t}[/imath] (simdt) or smaller, then the numerical time propagation will be unable to accurately resolve the vessel motion and this will generally lead to getting punted into solar orbit. The period of oscillation T is given by: [math]T=2m/\sqrt{4mk-c^2}[/math]
For m = 225 kg, k = 22,500 N/m, and c = 4000 N[imath]\cdot[/imath]s/m, we get [imath]T \approx[/imath] 0.2 s.
Looking into the time propagation settings in Orbiter, it's a little hard to determine if this is a resolvable period or not:
The Runge-Kutta routines are numerical differential equation solver routines, and I think the differing steps are limits associated with time acceleration (if your timestep is under 0.1 seconds, RK2 provides sufficient accuracy, if under 2.0 seconds RK4 is sufficient, etc..). Higher order Runge-Kutta routines are more expensive than lower order ones, but higher order routines maintain accuracy for larger time steps, so transitioning like this makes sense for computational efficiency. If someone knows what is actually going on in Orbiter please comment if you have some information on this.
Going into the simulation with the J-3 and getting the value for simdt gives a value of about 0.015 s, which is about 15 times smaller than the period of oscillation. So at least we're not the same order of magnitude, so this may work out (assuming this is all correct and this is actually what Orbiter is doing under the hood).
Results
So I apply the touchdown model for my J-3 with the following Lua script code:
Code:
stiffness_value = 22500
damping_value = 4000
td_points = {{pos=tail_wheel_contact, stiffness=stiffness_value, damping=damping_value, mu=0.0, mu_lng=0.0},
{pos=left_wheel_contact, stiffness=stiffness_value, damping=damping_value, mu=0.0, mu_lng=0.0},
{pos=right_wheel_contact, stiffness=stiffness_value, damping=damping_value, mu=0.0, mu_lng=0.0},
{pos=nose_tip, stiffness=stiffness_value, damping=damping_value, mu=0.2, mu_lng=0.2},
{pos=left_wing_tip, stiffness=stiffness_value, damping=damping_value, mu=0.2, mu_lng=0.2},
{pos=right_wing_tip, stiffness=stiffness_value, damping=damping_value, mu=0.2, mu_lng=0.2},
{pos=left_elevator_tip, stiffness=stiffness_value, damping=damping_value, mu=0.2, mu_lng=0.2},
{pos=right_elevator_tip, stiffness=stiffness_value, damping=damping_value, mu=0.2, mu_lng=0.2},
{pos=rudder_tip, stiffness=stiffness_value, damping=damping_value, mu=0.2, mu_lng=0.2}}
vi:set_touchdownpoints(td_points)
Empty J-3 vessel mass is 450 kg plus full fuel load of 12 gallons of gasoline, so not quite 500 kg. Firing up a simulation puts me on the runway and supports the vessel nicely:
I tried using the FlightData function to record some forced landing bounces but the resolution is insufficient to see centimeter displacements, but I get a light bounce that quickly stabilizes, as expected for a slightly under-damped system. And the best news is that, even taxiing around for several hours at this point, I haven't been yeeted to solar orbit even once.
Landing Status
A common issue with this model is that, even when nominally parked and stationary on the surface, it seems unwilling to let you achieve Landed status in the scenario file. That seems to be the case here as well. Here is my (Current Status).scn corresponding to the above image:
Code:
J3Script:J3Script
STATUS Orbiting Earth
RPOS 5072669.944 3735414.422 -951068.658
RVEL 171.9824 -147.1474 339.3490
AROT 75.051 20.522 72.382
VROT 0.0012 -0.0226 -0.0012
RCSMODE 0
AFCMODE 7
PRPLEVEL 0:1.000000
NAVFREQ 94 524
coordinate_hold false
brake_hold true
END
I experimented with increasing the damping coefficient to see if it would ever settle down and achieve Landed status, but no luck. Practically it is stopped and isn't going anywhere, so this seems to be a numerical issue about the definition of "landed". If anyone has any information on this, please comment below.
Conclusion
I hope this helps overcome the frustrations with this contact model, and perhaps this can start a conversation where maybe the remaining issues can be addressed. Happy add-on development! -TC
Last edited: