Hi author here, I have worked in the aerospace industry on flight control systems. I'm very familiar with knots as a unit.
I'm just annoyed that so many educational resources and even flight code still use customary units for everything. I believe that metric should be used for all internal calculations and knots should only exist at the UI layer for the pilot.
When I play flight games though, I only have intuition for speed in terms of knots. Like 150 kts is takeoff speed and 400 kts is the corner speed.
I just think that the pilot letting out a spool of knotted rope from their plane is a very silly practice to defend :)
In my experience in both the aerospace industry and the video game industry, there are no tools like this in use. In aerospace specifically, errors like that are caught by manual human review, including third-party validation companies. Unit tests are used to sanity check every calculation. And finally, everything is run in a flight simulator before ever going onto a real aircraft.
In video games however, it's the wild west. Math errors there are funny, not deadly.
I wanted to keep the data tables exactly the same in the C# translation. While formulas can be converted to metric easily, the data tables do not actually have units defined for any of them, so translating them to metric is non-trivial. And the way data is mixed from multiple tables is very complex. So verifying that the calculations have equivalent results in metric is well beyond my ability.
Or rather, the best way to verify it would be to write a flight model that uses customary units (what I describe in the article), and then use that to verify that the metric flight model is equivalent.
At the end of the day, only the inputs and outputs of the flight model need to be converted, which is just handful of multiplication operations. The flight model is very cheap to run, even with conversions at runtime.
This is exactly what unit tests are for. You did the first (hard part), dissecting the Fortran ark for goodies. You built a Unity equivalent (slightly less hard than deciphering ancient text but still high on difficulty). Now, write tests that test those inputs and outputs (translation calls included). Great, now that all the tests pass, change to metric until the tests pass again.
I know it sounds daunting. Making a painting does too to someone who doesn’t paint. However, there are steps you can follow that make it super easy to create masterpieces and all the greats follow the same process.
So where you translate ft to m or slug/ft to slug/m or however - the surrounding math is perfect for a unit test. Keeping you from having to build another flight model and do analog mark-2 eyeball testing.
You could just leave it - someone will pick it up and do the work.
*EDIT*
I ran some of it through my models and converted AirDataComputer to Kelvin, kg/m, m/s:
public class AirDataComputer {
/// <summary>
/// Density at sea level in kg/m³ (standard atmosphere)
/// </summary>
public const float SeaLevelDensity = 1.225f;
/// <summary>
/// Max altitude in meters
/// </summary>
public const float MaxAltitude = 10668.0f; // ~35,000 ft in meters
/// <summary>
/// Calculates air data based on velocity and altitude using SI units
/// </summary>
/// <param name="velocity">Velocity in m/s</param>
/// <param name="altitude">Altitude in meters</param>
/// <returns>Air data</returns>
public AirData CalculateAirData(float velocity, float altitude) {
const float baseTemperature = 288.15f; // Sea level temp in K (~15°C)
const float minTemperature = 216.65f; // Tropopause temp in K (~ -56.5°C)
const float temperatureGradient = 0.0065f; // Lapse rate in K/m
const float gamma = 1.4f; // Ratio of specific heats
const float gasConstant = 287.05f; // J/(kg·K), specific gas constant for air
const float densityPower = 4.14f;
altitude = Mathf.Clamp(altitude, 0, MaxAltitude);
// Calculate temperature in Kelvin using linear lapse rate model
float temperatureFactor = 1.0f - (temperatureGradient * altitude / baseTemperature);
float T = Mathf.Max(minTemperature, baseTemperature * temperatureFactor);
// Speed of sound in m/s
float speedOfSound = Mathf.Sqrt(gamma * gasConstant * T);
float altitudeMach = velocity / speedOfSound;
// Air density using barometric approximation
float rho = SeaLevelDensity * Mathf.Pow(temperatureFactor, densityPower);
// Dynamic pressure in Pascals (N/m²)
float qBar = 0.5f * rho * velocity * velocity;
return new AirData() {
altitudeMach = altitudeMach,
qBar = qBar
};
}
A programmer could make simple mistake thinking that is declaring a variable of type Foo. Carbon eliminates this by having explicit keywords for variable and function declaration. (This style is much more Rust based than C++)
fn bar() -> Foo;
var bar : Foo;
It makes parsing easier for both the users and maintainers of the language.
If that's the only criterion then Pascal's syntax, which doesn't have "the most vexing parse" problem either, and for the same reason, is also almost better than anything else.
> The change is simple and the code remains almost the same, but the understanding grows a lot.
Does that mean you could rename 'class Quaternion' to 'class Rotor' and be done? I watched the video on rotors and some other videos on quaternions and I found them exactly as unintuitive as each other. I don't really see any advantage to using rotors and I'm quite happy to continue treating quaternions as black boxes.
The article says that rotors are better since they can be generalized to any number of dimensions, unlike quaternions which only work in 3D. But that only seems useful to one person: Marc ten Bosch, the guy who writes games in 4D.
I'm just annoyed that so many educational resources and even flight code still use customary units for everything. I believe that metric should be used for all internal calculations and knots should only exist at the UI layer for the pilot.
When I play flight games though, I only have intuition for speed in terms of knots. Like 150 kts is takeoff speed and 400 kts is the corner speed.
I just think that the pilot letting out a spool of knotted rope from their plane is a very silly practice to defend :)