Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Read the complete run configurations

These are the versioned files that the configuration generator uses. Expand a vehicle to see its firmware binary, memory layout, QEMU timing, FMU selection, and helper commands together. fastdyn-config replaces tool and output locations for your machine and materializes the legacy environment defaults as literal TOML.

These are the same base configurations used in the first mission and other models. The outputs below use a separate directory so you can inspect them without replacing your mission TOML. The archived Plane recording uses an older plant; the Plane configuration here selects the current three-wheel model, whose FMI export is still pending compiler support.

Copter 4.6.2

fastdyn-config --base configs/copter462.toml --output out/current/copter.toml
configs/copter462.toml
# ==============================================================================
#                              Memory Configuration
# ==============================================================================
[Memory]

[Memory.main]   # required: goes to -machine ... memory-backend=<id>
id = "ram0"
base_address = "0x20000000"
memory_size  = "512M"
memory_type  = "SRAM"
backend      = "file"          # file | ram | memfd
memory_file  = "../qemu/ws/my_m4_ram3"
share        = true
prealloc     = false

[[Memory.ram1]]   # optional banks: go to -global cortexm-soc.ram_backendN / ram_baseaddrN
id = "ram1"
index = 1
base_address = "0x30000000"
memory_size  = "512K"
memory_type  = "SRAM"
backend      = "file"
memory_file  = "../qemu/ws/my_m4_ram"
share        = true
prealloc     = false

# ==============================================================================
#                                CPU Configuration
# ==============================================================================
[Machine]
platform = "STM32F427"
# --- Debugging, Control, and Logging ---
qemu_path = "../qemu/build/qemu-system-arm"
enable_gdb = false
stop_on_start = false
launch_gdb = false
monitor_port = 5555
qmp_socket = "/tmp/qmp.sock"
log_file = "qemu.log"
log_options = "none"
icount = { shift = 5, sleep = false, align = false }
timer_irq_period_ns = 1000000 # 1 ms OS tick; keep this for board-fidelity runs.
semihosting = true
semihosting_config = "enable=on,target=native"
coverage=false
finline='None'
print_command = false          #set to true if you want to print the final qemu on the terminal [Useful for debugging]

[CPU]
[[CPU.cpu0]]
# --- Core Emulation & Plugin Settings ---
arch = "arm"
machine = "cortexm"
cpu = "cortex-m4"
plugin_library = "build/libfastdyn.so"
binary = "virtuals/physics/flight_controllers/courbet/bin/arducopter_v462"
init_nsvtor = "0x08004000"
twintrace = "None" #options are record, replay or None
hardware_trace = 'hardware_log/io.log'   #useful in cases like replay to generate the replay binary
existing_config_path = "virtuals/physics/flight_controllers/courbet/copter462/unlabeled_conf" #path to an existing config to use for this run. Useful for replaying with a different binary or for debugging.


# --- Embedded CPU Behavior Configurations ---
logger_content = """
# --- Plugin Logger Configuration ---
# level = DEBUG
# output = stderr
"""

# ==============================================================================
#                                FMU Configuration
# ==============================================================================

[FMU]
active = "quadrotor"
auto_build = true

[FMU.models.quadrotor]
model = "FastDyn.Copter"
model_file = "modelica/FastDyn/Copter.mo"
source_roots = ["modelica", "third_party/common/modelica_models"]
output = "out/fmi3/Copter"
build = true
release = false

[FMU.models.quadrotor.parameters]
# Purdue University Airport (KLAF) tarmac start point.
lat0 = 40.414929
lon0 = -86.932387
ground_alt_wgs84 = 149.0

# Match the local Gazebo gs_drone/ArduPilot interface before tuning the
# controller. These are intentionally left as normal FMU parameters so users can
# adapt the vehicle without editing Modelica.
pwm_min = 1100.0
pwm_max = 1900.0
omega_max = 1300.0
mass = 2.5644001
inertia = [[0.02601237985, 0.0, 0.0], [0.0, 0.02590943825, 0.0], [0.0, 0.0, 0.045571756801]]
Ct = 8.54858e-6
Cm = 0.016
Cn_r = -0.1

# ==============================================================================
#                             Runtime Helpers
# ==============================================================================

# Optional Rumoca lockstep/webviewer process. The ArduCopter FMUv3 mission uses
# the FMU plant in the QEMU plugin and MAVCesium for the live web view; enable
# this only when you also want a separate standalone Rumoca viewer.
[Rumoca]
enabled = false
config = "third_party/common/rumoca/examples/quadrotor_sil/quadrotor_standby.toml"
features = ["lockstep"]
release = true
background = true

[Rumoca.webviewer]
http_port = 8080
ws_port = 8081
scene = "third_party/common/rumoca/examples/quadrotor_sil/quadrotor_scene.js"
debug = false

# Helper processes that should run alongside QEMU.
[Run]
cwd = "."

[Run.profiling]
# Timing is low overhead and stays on for startup/mission phase measurement.
timing = true
timing_echo = true
# cProfile wraps Python helpers and writes .cprofile files under work-dir/profiles.
python = false
# perf wraps the QEMU process when the host permits it. Use "record" for flamegraphs.
perf = "off" # off | stat | record
perf_frequency_hz = 99
# FMU timing prints cumulative doStep and realtime-factor stats from the C backend.
fmu = true

[Run.processes.mavproxy]
enabled = true
quiet = true
cwd = "virtuals/physics/flight_controllers/courbet/mavlink"
env = { PYTHONPATH = "." }
command = [
    "mavproxy.py",
    "--daemon",
    "--logfile=${FASTDYN_MAVLINK_LOG:-mav.tlog}",
    "--cmd=set flushlogs True",
    "--master=udpout:127.0.0.1:${FASTDYN_MAVLINK_FIRMWARE_PORT:-14551}",
    "--out=udpout:127.0.0.1:${FASTDYN_MAVLINK_GCS_PORT:-14552}",
    "--load-module=fastdyn_cesium:{\"port\":${FASTDYN_MAVCESIUM_PORT:-5000}}",
]
ready_message = "MAVCesium web viewer: open http://127.0.0.1:${FASTDYN_MAVCESIUM_PORT:-5000}/mavcesium/"

[Run.processes.mission]
enabled = true
start_delay_sec = 0
terminate_run_on_exit = true
command = [
    "python3",
    "virtuals/physics/flight_controllers/courbet/mavlink/mav_command_and_control.py",
    "--connect",
    "udpin:127.0.0.1:${FASTDYN_MAVLINK_GCS_PORT:-14552}",
    "--monitor-sec",
    "180",
    "${FASTDYN_PARAM_FILE:-virtuals/physics/flight_controllers/courbet/mavlink/copter_init.param}",
    "${FASTDYN_MISSION_FILE:-virtuals/physics/flight_controllers/courbet/mavlink/copter_mission.waypoints}",
]

# ==============================================================================
#                              Device Configuration
# ==============================================================================

# --- Model-Specific Configurations ---
# Define the global arguments for each type of plugin device model here.

[Device.Models.elder]
    # The libhw backend is a global argument for the elder model.

[Device.Models.passthrough]
    backend = "stlink"

[Device.Models.classic]
    # The classic model has no global arguments.

[Device.Models.twintrace]
    backend = "stlink"
    # The classic model has no global arguments.

[Device.Models.unhandled]
    # This model explicitly marks memory as unhandled.

# --- Peripheral Definitions ---
[Device.remaining_space]
    ranges = [["0x40000000", "0x400107FF"],["0x40010C00", "0x40010FFF"], ["0x40011400", "0xE0000000"], ["0xE0000000", "0xEFFFFFFF"]]
    irq = [['1','100']]
    description = "Memory regions that will handled by passthrough and are not of interest for the modeling."

    [[Device.remaining_space.handlers]]
        model = "classic"
        enabled = false

Plane 4.6.2

# Configuration inspection only: Plane FMI contact-event support is pending.
fastdyn-config --base configs/plane462.toml --output out/current/plane.toml
configs/plane462.toml
# ==============================================================================
#                              Memory Configuration
# ==============================================================================
[Memory]

[Memory.main]   # required: goes to -machine ... memory-backend=<id>
id = "ram0"
base_address = "0x20000000"
memory_size  = "512M"
memory_type  = "SRAM"
backend      = "file"          # file | ram | memfd
memory_file  = "../qemu/ws/my_m4_ram3"
share        = true
prealloc     = false

[[Memory.ram1]]   # optional banks: go to -global cortexm-soc.ram_backendN / ram_baseaddrN
id = "ram1"
index = 1
base_address = "0x30000000"
memory_size  = "512K"
memory_type  = "SRAM"
backend      = "file"
memory_file  = "../qemu/ws/my_m4_ram"
share        = true
prealloc     = false

# ==============================================================================
#                                CPU Configuration
# ==============================================================================
[Machine]
platform = "STM32F427"
# --- Debugging, Control, and Logging ---
qemu_path = "../qemu/build/qemu-system-arm"
enable_gdb = false
stop_on_start = false
launch_gdb = false
monitor_port = 5555
qmp_socket = "/tmp/qmp.sock"
log_file = "qemu.log"
log_options = "none"
icount = { shift = 5, sleep = false, align = false }
timer_irq_period_ns = 1000000
semihosting = true
semihosting_config = "enable=on,target=native"
coverage=false
finline='None'
print_command = false          #set to true if you want to print the final qemu on the terminal [Useful for debugging]

[CPU]
[[CPU.cpu0]]
# --- Core Emulation & Plugin Settings ---
arch = "arm"
machine = "cortexm"
cpu = "cortex-m4"
plugin_library = "build/libfastdyn.so"
monitor_elf = "../qemu/ws/monitor.elf" # The VMM-like monitor that runs with the target
binary = "virtuals/physics/flight_controllers/courbet/bin/arduplane_v462"
init_nsvtor = "0x08004000"
twintrace = "None" #options are record, replay or None
hardware_trace = 'hardware_log/io.log'   #useful in cases like replay to generate the replay binary
existing_config_path = "virtuals/physics/flight_controllers/courbet/plane462/unlabeled_conf"


# --- Embedded CPU Behavior Configurations ---
logger_content = """
# --- Plugin Logger Configuration ---
# level = DEBUG
# output = stderr
"""

# ==============================================================================
#                                FMU Configuration
# ==============================================================================

[FMU]
active = "fixedwing"
auto_build = true

[FMU.models.fixedwing]
model = "FastDyn.Plane"
model_file = "modelica/FastDyn/Plane.mo"
source_roots = ["modelica", "third_party/common/modelica_models"]
output = "out/fmi3/Plane"
build = true
release = false

[FMU.models.fixedwing.parameters]
lat0 = 40.414929
lon0 = -86.932387
ground_alt_wgs84 = 149.0

# ==============================================================================
#                             Runtime Helpers
# ==============================================================================

[Rumoca]
enabled = false

[Run]
cwd = "."

[Run.profiling]
timing = true
timing_echo = true
python = false
perf = "off"
perf_frequency_hz = 99
fmu = true

[Run.processes.mavproxy]
enabled = true
quiet = true
cwd = "virtuals/physics/flight_controllers/courbet/mavlink"
env = { PYTHONPATH = "." }
command = [
    "mavproxy.py",
    "--daemon",
    "--logfile=${FASTDYN_MAVLINK_LOG:-mav.tlog}",
    "--cmd=set flushlogs True",
    "--master=udpout:127.0.0.1:${FASTDYN_MAVLINK_FIRMWARE_PORT:-14551}",
    "--out=udpout:127.0.0.1:${FASTDYN_MAVLINK_GCS_PORT:-14552}",
    "--load-module=fastdyn_cesium:{\"port\":${FASTDYN_MAVCESIUM_PORT:-5000}}",
]
ready_message = "MAVCesium web viewer: open http://127.0.0.1:${FASTDYN_MAVCESIUM_PORT:-5000}/mavcesium/"

[Run.processes.mission]
enabled = true
terminate_run_on_exit = true
command = [
    "python3",
    "virtuals/physics/flight_controllers/courbet/mavlink/mav_command_and_control.py",
    "--connect",
    "udpin:127.0.0.1:${FASTDYN_MAVLINK_GCS_PORT:-14552}",
    "--arm-mode", "MANUAL",
    "--completion", "waypoints",
    "--monitor-sec", "180",
    "virtuals/physics/flight_controllers/courbet/mavlink/plane_fmu.param",
    "virtuals/physics/flight_controllers/courbet/mavlink/plane_circle_point.txt",
]

# ==============================================================================
#                              Device Configuration
# ==============================================================================

# --- Model-Specific Configurations ---
# Define the global arguments for each type of plugin device model here.

[Device.Models.elder]
    # The libhw backend is a global argument for the elder model.

[Device.Models.passthrough]
    backend = "stlink"

[Device.Models.classic]
    # The classic model has no global arguments.

[Device.Models.twintrace]
    backend = "stlink"
    # The classic model has no global arguments.

[Device.Models.unhandled]
    # This model explicitly marks memory as unhandled.

# --- Peripheral Definitions ---
[Device.remaining_space]
    ranges = [["0x40000000", "0x400107FF"],["0x40010C00", "0x40010FFF"], ["0x40011400", "0xE0000000"], ["0xE0000000", "0xEFFFFFFF"]]
    irq = [['1','100']]
    description = "Memory regions that will handled by passthrough and are not of interest for the modeling."

    [[Device.remaining_space.handlers]]
        model = "classic"
        enabled = false

Rover 4.6.2

fastdyn-config --base configs/rover462.toml --output out/current/rover.toml
configs/rover462.toml
# ==============================================================================
#                              Memory Configuration
# ==============================================================================
[Memory]

[Memory.main]   # required: goes to -machine ... memory-backend=<id>
id = "ram0"
base_address = "0x20000000"
memory_size  = "512M"
memory_type  = "SRAM"
backend      = "file"          # file | ram | memfd
memory_file  = "../qemu/ws/my_m4_ram3"
share        = true
prealloc     = false

[[Memory.ram1]]   # optional banks: go to -global cortexm-soc.ram_backendN / ram_baseaddrN
id = "ram1"
index = 1
base_address = "0x30000000"
memory_size  = "512K"
memory_type  = "SRAM"
backend      = "file"
memory_file  = "../qemu/ws/my_m4_ram"
share        = true
prealloc     = false

# ==============================================================================
#                                CPU Configuration
# ==============================================================================
[Machine]
platform = "STM32F427"
# --- Debugging, Control, and Logging ---
qemu_path = "../qemu/build/qemu-system-arm"
enable_gdb = true
stop_on_start = false
launch_gdb = false
monitor_port = 5555
qmp_socket = "/tmp/qmp.sock"
log_file = "qemu.log"
log_options = "none"
icount = { shift = 5, sleep = false, align = false }
timer_irq_period_ns = 1000000
semihosting = true
semihosting_config = "enable=on,target=native"
coverage=false
finline='None'
print_command = false          #set to true if you want to print the final qemu on the terminal [Useful for debugging]

[CPU]
[[CPU.cpu0]]
# --- Core Emulation & Plugin Settings ---
arch = "arm"
machine = "cortexm"
cpu = "cortex-m4"
plugin_library = "build/libfastdyn.so"
monitor_elf = "../qemu/ws/monitor.elf" # The VMM-like monitor that runs with the target
binary = "virtuals/physics/flight_controllers/courbet/bin/ardurover_v462"
init_nsvtor = "0x08004000"
twintrace = "None" #options are record, replay or None
hardware_trace = 'hardware_log/io.log'   #useful in cases like replay to generate the replay binary
existing_config_path = "virtuals/physics/flight_controllers/courbet/rover462/unlabeled_conf"

# --- Embedded CPU Behavior Configurations ---
logger_content = """
# --- Plugin Logger Configuration ---
# level = DEBUG
# output = stderr
"""

# ==============================================================================
#                                FMU Configuration
# ==============================================================================

[FMU]
active = "rover"
auto_build = true

[FMU.models.rover]
model = "FastDyn.Rover"
model_file = "modelica/FastDyn/Rover.mo"
source_roots = ["modelica", "third_party/common/modelica_models"]
output = "out/fmi3/Rover"
build = true
release = false

[FMU.models.rover.parameters]
lat0 = 40.414929
lon0 = -86.932387
ground_alt_wgs84 = 149.0

# ==============================================================================
#                             Runtime Helpers
# ==============================================================================

[Rumoca]
enabled = false

[Run]
cwd = "."

[Run.profiling]
timing = true
timing_echo = true
python = false
perf = "off"
perf_frequency_hz = 99
fmu = true

[Run.processes.mavproxy]
enabled = true
quiet = true
cwd = "virtuals/physics/flight_controllers/courbet/mavlink"
env = { PYTHONPATH = "." }
command = [
    "mavproxy.py",
    "--daemon",
    "--logfile=${FASTDYN_MAVLINK_LOG:-mav.tlog}",
    "--cmd=set flushlogs True",
    "--master=udpout:127.0.0.1:${FASTDYN_MAVLINK_FIRMWARE_PORT:-14551}",
    "--out=udpout:127.0.0.1:${FASTDYN_MAVLINK_GCS_PORT:-14552}",
    "--load-module=fastdyn_cesium:{\"port\":${FASTDYN_MAVCESIUM_PORT:-5000}}",
]
ready_message = "MAVCesium web viewer: open http://127.0.0.1:${FASTDYN_MAVCESIUM_PORT:-5000}/mavcesium/"

[Run.processes.mission]
enabled = true
terminate_run_on_exit = true
command = [
    "python3",
    "virtuals/physics/flight_controllers/courbet/mavlink/mav_command_and_control.py",
    "--connect",
    "udpin:127.0.0.1:${FASTDYN_MAVLINK_GCS_PORT:-14552}",
    "--arm-mode", "MANUAL",
    "--completion", "waypoints",
    "--monitor-sec", "180",
    "virtuals/physics/flight_controllers/courbet/mavlink/rover_fmu.param",
    "virtuals/physics/flight_controllers/courbet/mavlink/rover_rectangle.txt",
]

# ==============================================================================
#                              Device Configuration
# ==============================================================================

# --- Model-Specific Configurations ---
# Define the global arguments for each type of plugin device model here.

[Device.Models.elder]
    # The libhw backend is a global argument for the elder model.

[Device.Models.passthrough]
    backend = "stlink"

[Device.Models.classic]
    # The classic model has no global arguments.

[Device.Models.twintrace]
    backend = "stlink"
    # The classic model has no global arguments.

[Device.Models.unhandled]
    # This model explicitly marks memory as unhandled.

# --- Peripheral Definitions ---
[Device.remaining_space]
    ranges = [["0x40000000", "0x400107FF"],["0x40010C00", "0x40010FFF"], ["0x40011400", "0xE0000000"], ["0xE0000000", "0xEFFFFFFF"]]
    irq = [['1','100']]
    description = "Memory regions that will handled by passthrough and are not of interest for the modeling."

    [[Device.remaining_space.handlers]]
        model = "classic"
        enabled = false

Plane template and landing gear

FastDyn.Plane uses Vehicles.Templates.FixedWingPlant directly. Its three wheel contacts generate normal forces, tangential friction, and moments about the CG. The template includes a tailwheel steering term. The wrapper converts FLU plant signals to the FRD sensor interface expected by the firmware.

The 5.5 kg mass, 2.1 m span, inertia, and wheel locations below are explicit tutorial assumptions. They retain the larger aircraft scale; the template’s defaults describe a much smaller aircraft. This updated plant still requires FMI event support and subsequent flight validation and gain checks.

within FastDyn;

model Plane
  // Use the library's aerodynamics and three-wheel contact equations directly.
  // Retain the tutorial aircraft scale; these are modeling assumptions, not
  // measured specifications or a validated tune for this updated plant.
  parameter Real vehicle_mass = 5.5 "Equipped aircraft mass [kg]";
  parameter Real inertia[3] = {0.35, 0.80, 1.10} "Principal inertias [kg*m^2]";
  parameter Real wing_area = 0.55 "Wing reference area [m^2]";
  parameter Real wing_span = 2.1 "Wing span [m]";
  parameter Real mean_chord = 0.28 "Mean aerodynamic chord [m]";
  parameter Real thrust_max = 42.0 "Maximum propeller thrust [N]";
  parameter Real wheel_x[3] = {0.30, 0.30, -0.90} "Main wheels and tailwheel, forward [m]";
  parameter Real wheel_y[3] = {0.25, -0.25, 0.0} "Wheel positions, left [m]";
  parameter Real wheel_z[3] = {-0.25, -0.25, -0.15} "Wheel positions, up [m]";
  parameter Real ground_wn = 45.0 "Contact natural frequency [rad/s]";
  parameter Real ground_zeta = 0.6 "Contact damping ratio";
  parameter Real ground_max_force_per_wheel = 200.0 "Normal force cap per wheel [N]";
  parameter Real ground_c_xy = 1.5 "Tangential damping per wheel [N*s/m]";
  parameter Real mag_world[3] = {0.21, 0.0, -0.45} "Local N/W/U magnetic field [Gauss]";

  Vehicles.Templates.FixedWingPlant plant(
    vehicle_mass = vehicle_mass, gravity = 9.8,
    Jx = inertia[1], Jy = inertia[2], Jz = inertia[3], Jxz = 0,
    S = wing_area, span = wing_span, cbar = mean_chord, thr_max = thrust_max,
    wheel_x = wheel_x, wheel_y = wheel_y, wheel_z = wheel_z,
    ground_wn = ground_wn, ground_zeta = ground_zeta,
    ground_c_xy = ground_c_xy,
    ground_max_force_per_wheel = ground_max_force_per_wheel,
    p_start = {0, 0, 0.25}, v_b_start = {0, 0, 0});

  parameter Real pwm_min = 1000.0 "Minimum PWM";
  parameter Real pwm_trim = 1500.0 "Neutral PWM";
  parameter Real pwm_max = 2000.0 "Maximum PWM";
  parameter Real lat0 = 40.414929 "Reference latitude [deg]";
  parameter Real lon0 = -86.932387 "Reference longitude [deg]";
  parameter Real ground_alt_wgs84 = 149.0 "WGS84 ellipsoid altitude of the local ground plane [m]";
  parameter Real accel_bias[3] = {0, 0, 0} "Accelerometer bias [m/s^2]";
  parameter Real gyro_bias[3] = {0, 0, 0} "Gyroscope bias [rad/s]";
  parameter Real mag_bias[3] = {0, 0, 0} "Magnetometer bias [Gauss]";
  parameter Real gps_bias[3] = {0, 0, 0} "GPS bias N/E/altitude [m]";
  parameter Real baro_alt_bias = 0.0 "Barometer relative altitude bias [m]";
  parameter Real earth_radius_m = 6378137.0 "Spherical Earth radius used for local geodetic conversion [m]";
  parameter Real pi = 3.141592653589793;

  input Real pwm[4](start = {1500, 1500, 1000, 1500}) "Servo PWM commands";

  output Real accel[3] "Body FRD accelerometer [m/s^2]";
  output Real gyro[3] "Body FRD gyroscope [rad/s]";
  output Real mag[3] "Body FRD magnetometer [Gauss]";
  output Real gps[3] "GPS latitude, longitude, altitude";
  output Real vel_ned[3] "GPS velocity NED [m/s]";
  output Real yaw_deg "Yaw [deg]";
  output Real baro_altitude_m "Barometer relative altitude [m]";
  output Real baro_pressure_pa "Barometer pressure [Pa]";
  output Real baro_temperature_c "Barometer temperature [degC]";
  output Real baro_climb_rate_mps "Barometer climb rate [m/s]";
  output Real motor_cmd[4] "Normalized aileron/elevator/throttle/rudder commands";

protected
  Real aileron;
  Real elevator;
  Real throttle;
  Real rudder;
  Real gps_lat_lon[2];
  Real geodetic_origin[3] "Reference latitude, longitude, and Earth radius";
  Real yaw_rad;
  Real mag_body_flu[3];

equation
  aileron = min(1.0, max(-1.0, (pwm[1] - pwm_trim) / (pwm_max - pwm_trim)));
  elevator = min(1.0, max(-1.0, (pwm[2] - pwm_trim) / (pwm_max - pwm_trim)));
  throttle = min(1.0, max(0.0, (pwm[3] - pwm_min) / (pwm_max - pwm_min)));
  rudder = min(1.0, max(-1.0, (pwm[4] - pwm_trim) / (pwm_max - pwm_trim)));

  plant.ail = aileron;
  plant.elev = elevator;
  plant.thr = throttle;
  plant.rud = rudder;

  // The template supplies body FLU signals; firmware devices use body FRD.
  accel = {plant.a_b[1], -plant.a_b[2], -plant.a_b[3]} + accel_bias;
  gyro = {plant.omega[1], -plant.omega[2], -plant.omega[3]} + gyro_bias;
  mag_body_flu = transpose(plant.R) * mag_world;
  mag = {mag_body_flu[1], -mag_body_flu[2], -mag_body_flu[3]} + mag_bias;

  // Avoid collisions between the caller parameters and the function locals
  // during function projection in the pinned Rumoca compiler.
  geodetic_origin = {lat0, lon0, earth_radius_m};
  gps_lat_lon = Geodesy.localNorthEastToLatLon(
    geodetic_origin[1],
    geodetic_origin[2],
    plant.p[1] + gps_bias[1],
    -plant.p[2] + gps_bias[2],
    geodetic_origin[3]);
  gps[1] = gps_lat_lon[1];
  gps[2] = gps_lat_lon[2];
  gps[3] = ground_alt_wgs84 + plant.p[3] + gps_bias[3];

  vel_ned[1] = plant.v_w[1];
  vel_ned[2] = -plant.v_w[2];
  vel_ned[3] = -plant.v_w[3];

  yaw_rad = atan2(2.0 * (plant.q[1] * plant.q[4] + plant.q[2] * plant.q[3]),
                  1.0 - 2.0 * (plant.q[3] * plant.q[3] + plant.q[4] * plant.q[4]));
  yaw_deg = -yaw_rad * 180.0 / pi;

  baro_altitude_m = plant.p[3] + baro_alt_bias;
  baro_temperature_c = 15.0 - 0.0065 * (ground_alt_wgs84 + baro_altitude_m);
  baro_pressure_pa = 101325.0 * (1.0 - 2.25577e-5 * (ground_alt_wgs84 + baro_altitude_m)) ^ 5.25588;
  baro_climb_rate_mps = plant.v_w[3];
  motor_cmd = {aileron, elevator, throttle, rudder};
end Plane;