Skip to content

Types, Quantities and Units

Aerospace uses both imperial and metric units, which are easy to mix up. In 1983, Air Canada Flight 143 ("Gimli Glider") ran out of fuel midflight. It was later determined to be caused by a confusion between pounds and kilograms when refuelling. Minisky mitigates this risk by:

  1. internally using SI units everywhere
  2. providing developers with explicit conversion functions to convert between imperial and metric units (e.g. q.ft_to_m)
  3. requiring users to always specify the unit when issuing a command (for example, 30000FT[MSL] instead of 30000)
  4. requiring developers to always use newtype wrappers at method callback boundaries (for example, minisky.types.MslAltM instead of a bare float)
  5. encouraging developers to use unit/quantity type annotations in minisky.quantities

Minisky also never guesses what kind of value you mean: there is no implicit CAS/Mach threshold1, and QNH, QNE and QFE altitudes are distinct types2.

Quantity Kinds

In addition to units, minisky also distinguishes between various quantity kinds.

Calibrated airspeed, true airspeed and ground airspeed can all be expressed in m/s, but are not interchangeable.

Likewise, standard pressure altitude, altitude above MSL, height above ground level all have the canonical SI unit of m but refer to different references/geoids.

Minisky provides developers with the minisky.quantities and minisky.types modules to help distinguish them.

Guide for Users

Always specify the value, unit and quantity kind instead of just the value. For example, to express a height of 13000 feet above mean sea level:

13000FT[MSL]

The quantity kind ([MSL]) can sometimes be omitted depending on the command.

Here are some commonly used quantity kinds:

  • Altitude
    • StdPressureAltM is pressure altitude on the standard-pressure reference (QNE), e.g. FL350, or 35000FT[STD], or 10000M[STD]
    • MslAltM is altitude above mean sea level (QNH), e.g. 13000FT[MSL], or 4000M[MSL]
    • q.AglHeightM is the height above the terrain. minisky does not properly support parsing from a string.
  • Speed

See the API reference for the full list.

Guide for developers

For commands that take in a scalar value, minisky follows the newtype idiom in Rust to prevent mixing up different quantity kinds.

For performance-critical internal logic, we strongly recommend using typing.Annotated to embed minisky.quantities metadata into the type instead of newtypes.

Commands

When defining your own plugin command, simply annotate the arguments of your method:

from minisky import command
from minisky.types import CasMps, Mach

@command
def set_speed(self, speed: CasMps | Mach):
    match speed:
        case CasMps(value):
            # handle cas...
        case Mach(value):
            # handle mach...

Here, the @command decorator internally extracts the annotation of the speed argument and understands how to parse both forms (e.g. 130KT[CAS] or M.78).

Conceptually, CasMps and Mach are just simple wrappers over a float:

from dataclasses import dataclass


@dataclass(frozen=True, slots=True)
class CasMps:
    value: float


@dataclass(frozen=True, slots=True)
class Mach:
    value: float

so they can be pattern matched easily.

Internal functions

In many cases though, we do not recommend using newtypes defined in minisky.types because they incur a runtime cost and create friction for downstream consumers3.

Instead, minisky follows the FastAPI convention of embedding metadata into types. Use type aliases under minisky.quantities, for example:

from dataclasses import dataclass
from minisky import quantities as q


# use in data structures:
@dataclass
class GasState:
    temperature: q.StaticTemperatureK
    pressure: q.StaticPressurePa
    density: q.DensityKgPerM3


# use in functions:
def mach(tas: q.TrueAirspeedMps, a: q.SpeedOfSoundMps):
    return tas / a

Conceptually, these type aliases are just:

from typing import TypeAlias, TypeVar, Annotated
import isqx
from isqx import aerospace

_T = TypeVar("_T")
StaticTemperature: TypeAlias = Annotated[_T, aerospace.STATIC_TEMPERATURE(isqx.K)]

If you wish, you can further constrain the type, for example using q.StaticTemperature[np.ndarray], however we recommend leaving them unconstrained since many Python libraries employ duck typing4.

Unit conversions

Manual conversion factors (e.g. alt * 0.3048) are prone to mistakes. Instead, minisky provides conversion functions like minisky.quantities.ft_to_m:

>>> from minisky import quantities as q
>>> q.ft_to_m(1300)
396.24

To learn how to define your own units and conversions, visit the isqx documentation.


  1. See: https://github.com/open-aviation/minisky/issues/40 

  2. See: https://github.com/open-aviation/minisky/issues/22 

  3. See https://abc8747.github.io/isqx/design/#problem-1-the-friction-of-newtypes for a detailed explanation of why we discourage using newtypes for performance-critical code. 

  4. See https://docs.jax.dev/en/latest/jep/12049-type-annotations.html#challenge-2-array-duck-typing for why we don't recommend excessively annotating np.ndarray or float unless absolutely necessary.