Aerospace Fundamentals
Core Aerospace Toolbox functions for unit conversions, time conversions, coordinate transformations, and rotation representations.
When to Use
- Converting between aerospace unit systems (SI, imperial, nautical)
- Computing Julian dates, modified Julian dates, decimal years, or TDB time
- Transforming positions between ECEF, ECI, LLA, or flat Earth frames
- Converting between geodetic and geocentric latitude
- Building DCMs for frame transformations (ECI↔ECEF, ECEF↔NED, body↔wind, body↔stability)
- Converting between Euler angles, DCMs, quaternions, and Rodrigues vectors
- Using the
quaternion object for rotation math, interpolation, or composition
- Identifying which coordinate frame data is expressed in
When NOT to Use
- Atmosphere models (
atmosisa, atmoscoesa) — use matlab-compute-aerospace-environment
- Airspeed corrections (
correctairspeed) — not covered here
- Orbit propagation or satellite maneuvers — use Aerospace Toolbox, Aerospace Blockset or Satellite Communications Toolbox
- Navigation-specific transforms (
lla2enu, lla2ned) — use Navigation Toolbox
- Simulink blocks — use
/model-based-design-core:building-simulink-models with Aerospace Blockset
Workflow
- Identify the coordinate frame — Determine what frame your data is in and what frame you need. See
references/coordinate-systems.md for frame definitions and decision guide.
- Convert units first — Ensure inputs match the function's expected units before calling transforms.
- Apply the transformation — Use the appropriate function with correct argument ordering.
- Verify — Round-trip the result back to the original frame; error should be < 1e-10.
Key Functions
Unit Conversions
| Function |
Converts |
Units |
convlength |
Length |
'ft', 'm', 'km', 'in', 'mi', 'naut mi' |
convvel |
Velocity |
'ft/s', 'm/s', 'km/s', 'in/s', 'km/h', 'mph', 'kts', 'ft/min' |
convang |
Angle |
'deg', 'rad', 'rev' |
convacc |
Acceleration |
'ft/s^2', 'm/s^2', 'km/s^2', 'in/s^2', 'km/h-s', 'mph/s', 'G''s' |
convangacc |
Angular acceleration |
'deg/s^2', 'rad/s^2', 'rpm/s' |
convangvel |
Angular velocity |
'deg/s', 'rad/s', 'rpm' |
convforce |
Force |
'N', 'lbf' |
convmass |
Mass |
'kg', 'lbm', 'slug' |
convpres |
Pressure |
'Pa', 'psi', 'psf', 'atm' |
convtemp |
Temperature |
'K', 'R', 'F', 'C' |
convdensity |
Density |
'kg/m^3', 'slug/ft^3', 'lbm/ft^3', 'lbm/in^3' |
All conversion functions use the same signature: output = convXXX(value, fromUnit, toUnit)
Time Conversions
| Function |
Purpose |
Since |
juliandate |
Calendar → Julian Date |
R2006b |
mjuliandate |
Calendar → Modified Julian Date (JD − 2400000.5) |
R2006b |
decyear |
Calendar → decimal year |
R2006b |
leapyear |
Test if year is leap year |
R2006b |
tdbjuliandate |
Terrestrial Time → TDB Julian Date |
R2015a |
Coordinate Transformations
| Function |
From |
To |
Since |
lla2ecef |
LLA (geodetic) |
ECEF |
R2006b |
ecef2lla |
ECEF |
LLA (geodetic) |
R2006b |
lla2eci |
LLA |
ECI |
R2014a |
eci2lla |
ECI |
LLA |
R2014a |
ecef2eci |
ECEF (pos/vel/acc) |
ECI |
R2019a |
eci2ecef |
ECI (pos/vel/acc) |
ECEF |
R2019a |
eci2aer |
ECI |
AER (azimuth, elevation, range) |
R2015a |
lla2flat |
LLA |
Flat Earth |
R2011a |
flat2lla |
Flat Earth |
LLA |
R2011a |
geod2geoc |
Geodetic latitude |
Geocentric latitude |
R2006b |
geoc2geod |
Geocentric latitude |
Geodetic latitude |
R2006b |
ijk2keplerian |
ECI position/velocity |
Keplerian elements |
R2019a |
keplerian2ijk |
Keplerian elements |
ECI position/velocity |
R2019a |
siderealTime |
UTC Julian date |
GMST (deg) / GAST (s) |
R2021a |
dcmeci2ecef |
— |
ECI-to-ECEF DCM |
R2013b |
dcmecef2ned |
— |
ECEF-to-NED DCM |
R2006b |
dcm2latlon |
ECEF-to-NED DCM |
Lat/Lon |
R2006b |
dcmbody2wind |
Alpha, Beta |
Body-to-Wind DCM |
R2006b |
dcm2alphabeta |
Body-to-Wind DCM |
Alpha, Beta |
R2006b |
dcmbody2stability |
Alpha |
Body-to-Stability DCM |
R2022a |
Rotation Representations
| Function |
From |
To |
Since |
angle2dcm |
Euler angles |
DCM |
R2006b |
dcm2angle |
DCM |
Euler angles |
R2006b |
angle2quat |
Euler angles |
Quaternion (1×4) |
R2006b |
quat2angle |
Quaternion (1×4) |
Euler angles |
R2007b |
dcm2quat |
DCM |
Quaternion (1×4) |
R2006b |
quat2dcm |
Quaternion (1×4) |
DCM |
R2006b |
angle2rod |
Euler angles |
Rodrigues vector |
R2017a |
rod2angle |
Rodrigues vector |
Euler angles |
R2017a |
dcm2rod |
DCM |
Rodrigues vector |
R2017a |
rod2dcm |
Rodrigues vector |
DCM |
R2017a |
quat2rod |
Quaternion (1×4) |
Rodrigues vector |
R2017a |
rod2quat |
Rodrigues vector |
Quaternion (1×4) |
R2017a |
Quaternion Object
| Method |
Purpose |
quaternion(E,'eulerd',RS,PF) |
Create from Euler angles (degrees) |
quaternion(E,'euler',RS,PF) |
Create from Euler angles (radians) |
quaternion(RM,'rotmat',PF) |
Create from rotation matrix |
quaternion(RV,'rotvec') |
Create from rotation vector (radians) |
compact(q) |
Extract [w x y z] array |
eulerd(q,RS,PF) |
Convert to Euler angles (degrees) |
euler(q,RS,PF) |
Convert to Euler angles (radians) |
rotmat(q,PF) |
Convert to rotation matrix |
rotvec(q) / rotvecd(q) |
Convert to rotation vector (rad/deg) |
rotatepoint(q,pts) |
Rotate points (active rotation) |
rotateframe(q,pts) |
Rotate frame (passive rotation) |
normalize(q) |
Normalize to unit quaternion |
slerp(q1,q2,t) |
Spherical linear interpolation |
meanrot(q) |
Mean rotation of array |
dist(q1,q2) |
Angular distance (radians) |
angvel(q,dt,PF) |
Angular velocity from quaternion array |
randrot(n) |
Uniform random rotations |
Quaternion Math (Array-Based)
| Function |
Purpose |
Since |
quatmultiply(q,r) |
Quaternion product (compose rotations) |
R2006b |
quatconj(q) |
Conjugate (negate vector part) |
R2006b |
quatinv(q) |
Inverse (conjugate / norm²) |
R2006b |
quatnormalize(q) |
Normalize to unit quaternion |
R2006b |
quatnorm(q) |
Squared norm (q·q) |
R2006b |
quatmod(q) |
Modulus (sqrt of norm) |
R2006b |
quatrotate(q,v) |
Rotate vector by quaternion |
R2006b |
quatdivide(q,r) |
Divide quaternion by quaternion |
R2006b |
quatinterp(p,q,f,method) |
Interpolate ('slerp', 'lerp', 'nlerp') |
R2016a |
quatexp(q) |
Exponential of quaternion |
R2016a |
quatlog(q) |
Natural logarithm of quaternion |
R2016a |
quatpower(q,pow) |
Quaternion raised to a power |
R2016a |
Patterns
Unit Conversion
% Always: convXXX(value, 'from', 'to')
alt_m = convlength(35000, 'ft', 'm'); % 10668.0 m
speed_ms = convvel(250, 'kts', 'm/s'); % 128.61 m/s
angle_rad = convang(45, 'deg', 'rad'); % 0.7854 rad
accel_g = convacc(9.81, 'm/s^2', 'G''s'); % 1.0 G
omega_rpm = convangvel(360, 'deg/s', 'rpm'); % 60 rpm
Time Conversion
% Julian Date from components (year, month, day, hour, min, sec)
jd = juliandate(2024, 6, 15, 12, 0, 0); % 2460477.0
% Modified Julian Date
mjd = mjuliandate(2024, 6, 15, 12, 0, 0); % 60476.5
% From datetime objects
dt = datetime(2024, 6, 15, 12, 0, 0);
jd = juliandate(dt);
% Decimal year
dy = decyear(2024, 6, 15); % 2024.4536
% TDB Julian Date from Terrestrial Time [yr mo day hr min sec]
tt = [2024 6 15 12 0 0];
jdTDB = tdbjuliandate(tt);
LLA ↔ ECEF
% LLA is [latitude_deg, longitude_deg, altitude_m]
lla = [40, -74, 0];
ecef = lla2ecef(lla); % [1348613.0, -4703172.4, 4077985.6] m
% Round-trip verification
lla_check = ecef2lla(ecef); % [40.0, -74.0, 0.0]
LLA ↔ ECI (time-dependent)
% ECI transforms require UTC time
lla = [40, -74, 1000];
utc = [2024 6 15 12 0 0];
posECI = lla2eci(lla, utc);
% Back to LLA
lla_check = eci2lla(posECI, utc);
Keplerian Elements ↔ ECI (IJK)
% Convert ECI position/velocity to Keplerian orbital elements
rijk = [-2981784; 5207055; 3161595]; % ECI position (m), 3x1
vijk = [-3384; -4887; 4843]; % ECI velocity (m/s), 3x1
[a, ecc, incl, RAAN, argp, nu] = ijk2keplerian(rijk, vijk);
% a=m, angles in degrees
% Convert Keplerian elements back to ECI position/velocity
[rijk_back, vijk_back] = keplerian2ijk(a, ecc, incl, RAAN, argp, nu);
% For a different central body (e.g., Mars)
[a_mars, ecc_mars, incl_mars, RAAN_mars, argp_mars, nu_mars] = ...
ijk2keplerian(rijk, vijk, CentralBody="Mars");
Sidereal Time
% Compute Greenwich Mean Sidereal Time (GMST) and Apparent (GAST)
utcJD = juliandate(2019, 1, 4, 12, 0, 0); % UTC as Julian date
dUT1 = 0; % UT1-UTC offset (seconds)
dAT = 37; % TAI-UTC offset (leap seconds)
[thGMST, thGAST] = siderealTime(utcJD, dUT1, dAT);
% thGMST in degrees, thGAST in seconds
Flat Earth Approximation
% Good for short-range simulations (< ~100 km from reference)
llo = [40, -74]; % reference lat/lon (deg)
psio = 0; % angular direction of flat Earth x-axis (rad, 0=North)
href = 0; % reference height (m)
lla_point = [40.01, -73.99, 100];
flatPos = lla2flat(lla_point, llo, psio, href); % [x, y, z] in meters
lla_back = flat2lla(flatPos, llo, psio, href);
Euler Angles ↔ DCM
% CRITICAL: angle2dcm expects RADIANS, not degrees
yaw = deg2rad(30); pitch = deg2rad(10); roll = deg2rad(5);
dcm = angle2dcm(yaw, pitch, roll, 'ZYX');
% Extract angles back (returns radians)
[y, p, r] = dcm2angle(dcm, 'ZYX');
fprintf('Yaw=%.1f, Pitch=%.1f, Roll=%.1f deg\n', rad2deg(y), rad2deg(p), rad2deg(r));
DCM ↔ Quaternion (Aerospace Toolbox format)
% Aerospace Toolbox quaternion format: [q0 q1 q2 q3] = [scalar, vector]
q = dcm2quat(dcm); % 1x4, scalar-first
dcm_back = quat2dcm(q); % 3x3
% Verify orthogonality
err = max(abs(dcm' * dcm - eye(3)), [], 'all');
assert(err < 1e-14, 'DCM is not orthogonal');
Quaternion Object (Modern Approach)
% Create from Euler angles — specify sequence AND frame type
q = quaternion([30 10 5], 'eulerd', 'ZYX', 'frame');
% Rotate a point (active rotation)
pt = [1 0 0];
pt_rotated = rotatepoint(q, pt); % [0.8529, 0.4924, -0.1736]
% Compose rotations by multiplication
q_total = q2 * q1; % applies q1 first, then q2
% Always normalize after arithmetic accumulation
q = normalize(q);
Quaternion Interpolation (SLERP)
% Smooth interpolation between two orientations
q1 = quaternion([0 0 0], 'eulerd', 'ZYX', 'frame');
q2 = quaternion([90 0 0], 'eulerd', 'ZYX', 'frame');
t = linspace(0, 1, 5)';
q_interp = slerp(q1, q2, t);
% Verify: yaw progresses linearly for pure yaw rotation
e = eulerd(q_interp, 'ZYX', 'frame');
% e(:,1) = [0, 22.5, 45, 67.5, 90]
Quaternion Math (Array-Based Functions)
% Compose two rotations: q2 applied after q1
q1 = angle2quat(deg2rad(30), 0, 0, 'ZYX');
q2 = angle2quat(0, deg2rad(10), 0, 'ZYX');
q_total = quatmultiply(q2, q1); % q2 * q1 (apply q1 first)
% Inverse rotation
q_inv = quatinv(q1);
q_identity = quatmultiply(q1, q_inv); % [1 0 0 0]
% Rotate a vector
v = [1 0 0];
v_rot = quatrotate(q1, v); % rotate v by q1
% Normalize after accumulation
q_accumulated = quatnormalize(q_total);
% Interpolation (slerp, lerp, nlerp)
q_mid = quatinterp(q1, q2, 0.5, 'slerp');
% Relative rotation: what rotation takes q1 to q2?
q_rel = quatdivide(q2, q1); % q_rel such that q2 = q_rel * q1
% Exponential/logarithm (useful for angular velocity integration)
q_log = quatlog(q1); % maps to tangent space
q_back = quatexp(q_log); % back to quaternion
% Fractional rotation (half the rotation of q1)
q_half = quatpower(q1, 0.5);
Rodrigues Vector
% Compact 3-element representation (singular at 180 deg)
rod = dcm2rod(dcm); % 1x3 vector
dcm_back = rod2dcm(rod);
% Convert between all representations
rod = quat2rod(q_array); % q is M×4 (scalar-first)
q_back = rod2quat(rod);
Body-to-Wind and Stability Frames
% Body-to-wind DCM from angle of attack and sideslip
alpha = deg2rad(5);
beta = deg2rad(2);
dcm_bw = dcmbody2wind(alpha, beta);
% Extract alpha/beta from a DCM
[alpha_out, beta_out] = dcm2alphabeta(dcm_bw);
% Body-to-stability (alpha only, no sideslip)
dcm_bs = dcmbody2stability(alpha);
ECEF-to-NED Frame DCM
% Get the DCM to rotate vectors from ECEF to local NED
lat = deg2rad(40);
lon = deg2rad(-74);
dcm_ecef2ned = dcmecef2ned(lat, lon);
% Transform an ECEF velocity to NED
v_ecef = [10; 20; 30];
v_ned = dcm_ecef2ned * v_ecef;
ECI to AER (Azimuth, Elevation, Slant Range)
% Compute look angles from a ground station to a satellite in ECI
posECI = [-2981784, 5207055, 3161595]; % satellite ECI position (m)
utc = [2019 1 4 12 0 0]; % observation time
lla0 = [28.5, -80.5, 0]; % ground station [lat, lon, alt] (deg, deg, m)
aer = eci2aer(posECI, utc, lla0);
% aer = [azimuth_deg, elevation_deg, slant_range_m]
Conventions
angle2dcm and angle2quat expect radians — always convert with deg2rad() or convang first
- Rotation sequence default is
'ZYX' (yaw-pitch-roll) — always specify explicitly for clarity
- Aerospace Toolbox quaternion format is
[scalar, i, j, k] — the 1×4 array functions use scalar-first
quaternion object also uses scalar-first (compact returns [w x y z])
rotatepoint vs rotateframe — rotatepoint rotates the point (active); rotateframe rotates the frame (passive). They are inverses.
- LLA ordering is
[lat, lon, alt] in degrees and meters
geod2geoc and geoc2geod expect radians for latitude, meters for height/radius
- ECI functions require UTC time — results change with Earth's rotation
- Normalize quaternions after arithmetic operations to prevent drift
- Rodrigues vector is singular at 180° — use quaternions for arbitrary rotations
Common Mistakes
| Mistake |
Why It's Wrong |
Correct Approach |
Pass degrees to angle2dcm |
Function expects radians |
Use deg2rad() or convang(val,'deg','rad') |
| Omit rotation sequence |
Default ZYX may not match your convention |
Always pass 'ZYX', 'XYZ', etc. explicitly |
Assume quaternion is [x y z w] |
Aerospace Toolbox uses [w x y z] (scalar-first) |
Check format; use compact(q) to verify |
Use rotatepoint when meaning rotateframe |
They are inverses — wrong one flips the rotation |
Active rotation = rotatepoint; passive = rotateframe |
| Skip normalization after quaternion math |
Quaternion drift causes non-unit norm, distorted rotations |
Call normalize(q) after accumulating rotations |
Pass degrees to geod2geoc |
Expects radians for latitude |
Convert: geod2geoc(deg2rad(lat), alt) |
| Forget UTC for ECI transforms |
ECI position depends on Earth rotation at that instant |
Always provide [yr mo day hr min sec] |
| Use Rodrigues near 180° rotation |
Rodrigues vector has a singularity at π |
Use quaternion representation instead |
| Ignore gimbal lock near ±90° pitch |
Euler angle extraction loses a degree of freedom |
Use quaternion or DCM directly for computations |
Gimbal Lock
Euler angle representations lose one degree of freedom when the second rotation reaches ±90° (for ZYX: pitch = ±90°). Symptoms:
dcm2angle returns unexpected yaw/roll values near pitch = ±90°
- Interpolating Euler angles produces erratic paths near singularity
Solution: Use quaternions for computation and interpolation. Only convert to Euler angles for display or human interpretation.
% Detect gimbal lock risk
[~, pitch, ~] = dcm2angle(dcm, 'ZYX');
if abs(pitch) > deg2rad(85)
warning('Near gimbal lock — use quaternion representation');
end
References
- See
references/coordinate-systems.md for detailed frame definitions (ECEF, ECI, NED, body, wind, stability), axis conventions, and a decision guide for choosing the right frame.
- See
references/rotation-conventions.md for detailed rotation math: DCM properties, quaternion algebra, gimbal lock theory, and conversion paths between all representations.
- See
references/unit-conversion-guide.md for complete unit string tables and chaining conversions.
Copyright 2026 The MathWorks, Inc.