Skip to content

feat(falcon): v0.5.0 — position controller + waypoint mission#16

Merged
avrabe merged 1 commit into
mainfrom
falcon/v0.5
May 19, 2026
Merged

feat(falcon): v0.5.0 — position controller + waypoint mission#16
avrabe merged 1 commit into
mainfrom
falcon/v0.5

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 19, 2026

Summary

The full outer-loop cascade closes. Vehicle flies from NED origin to a 10 m north waypoint in pure-Rust SITL, settles within centimetres. Five layers (POS → ATT → RATE → MIX → plant with translational dynamics) closing in one bench.

What's in

  • crates/relay-pos — cascaded P-PI position controller, 50 Hz, NED. 13 unit + proptest cases.
  • examples/falcon-sitl-hover mission scenario — plant gains translational dynamics; new scenario commands a 10 m waypoint. 2 added integration tests.
  • FV-FALCON-POS-001 verification artifact, 5 extractable steps.
  • FEAT-FALCON-v0.5 bumped pendingapproved with achieved metrics inline.

Achieved bench metrics

metric value budget
convergence to <0.5 m 4.045 s ≤ 10 s
final distance 0.010 m ≤ 0.5 m
RMS-steady (last 2 s) 0.015 m ≤ 1.0 m
loop wall time (12k samples) 1.3 ms
NaN/∞ none none
check result
cargo test --workspace 61 suites green (was 59 in v0.4)
cargo test -p relay-pos 13/13 PASS
cargo test -p falcon-sitl-hover 10/10 PASS
cargo run -p falcon-sitl-hover PASS on all 5 scenarios
Verification gate ✅ 8/8 artifacts, 31/31 steps
rivet validate 0 broken cross-references

Test plan

  • CI green
  • Verification gate posts ✅ 8/8 sticky comment
  • cargo run -p falcon-sitl-hover --release PASSes all 5 scenarios
  • cargo run -p falcon-sitl-hover --release -- --scenario mission prints "final distance 0.010 m"

After merge

TAG=falcon-v0.5.0 bash scripts/tag-and-release.sh

Honestly deferred to v0.6

  • host/relay-gps host service (v0.5 plant feeds position directly; POS-P03 source-agnostic interface is the v0.6 hook for real GPS noise + latency)
  • cFS-DNA stored-command mission via relay-sc + relay-tbl (the dual-DNA showcase)
  • cargo-mutants on full cascade
  • Sanitizer pass (ASAN / TSAN / Miri)
  • Gazebo Harmonic SITL → v0.7 (pure-Rust SITL covers v0.5; Gazebo earns its overhead when wind + sensor latency matter)

🤖 Generated with Claude Code

Full outer-loop cascade closes in pure-Rust SITL. Vehicle starts at
NED origin, flies to a 10 m north waypoint, settles within
centimetres. Five layers of control (POS → ATT → RATE → MIX → plant
with translational dynamics) closing in one bench.

What lands:

Crates
- relay-pos — cascaded P-PI position controller. 50 Hz outer loop,
  NED frame. Position P + velocity PI with bounded integral; small-
  angle accel→tilt map; thrust from vertical accel; yaw hold or
  follow. NaN-safe sanitisation throughout. 13 unit + proptest cases.

Example
- examples/falcon-sitl-hover gains a `mission` scenario + the plant
  gains translational dynamics. Vehicle from NED origin to (10,0,0).
  Achieved: convergence to <0.5 m in 4.045 s, final distance 0.010 m,
  RMS-steady 0.015 m. Loop wall time 1.3 ms for 12000 samples.
  2 added integration tests.

Rivet
- FV-FALCON-POS-001 with 5 extractable fields.steps. Falcon gate
  goes 7 → 8 artifacts, 26 → 31 steps.
- FEAT-FALCON-v0.5 bumped pending → approved with achieved metrics.

Verification posture
- cargo test --workspace: 61 test suites green (was 59 in v0.4)
- python3 scripts/run-falcon-verification.py: ✅ 8/8 artifacts,
  31/31 steps green
- rivet validate: 0 broken cross-references
- cargo run -p falcon-sitl-hover --release: PASS on all 5 scenarios
  (step, disturbance, hover, attitude, mission)

Honestly deferred to v0.6
- host/relay-gps host service (v0.5 plant feeds position directly
  to POS; POS-P03 source-agnostic interface is the v0.6 hook for
  real GPS noise/latency)
- cFS-DNA stored-command mission via relay-sc + relay-tbl
- cargo-mutants on full cascade
- Sanitizer pass (ASAN / TSAN / Miri)
- Gazebo Harmonic SITL (v0.7; pure-Rust SITL still covers v0.5's
  verification needs)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

running 13 tests
test tests::at_zero_error_output_is_zero ... ok
test tests::fresh_pid_has_zero_state ... ok
test tests::negative_error_drives_negative_torque ... ok
test tests::out_of_range_dt_clamped_safely ... ok
test tests::positive_error_drives_positive_torque ... ok
test tests::rate_p02_infinite_setpoint_does_not_propagate ... ok
test tests::rate_p02_nan_input_does_not_propagate ... ok
test tests::output_clamped_to_torque_max ... ok
test tests::rate_p03_step_response_drives_error_to_zero ... ok
test tests::reset_clears_state ... ok
test tests::rate_p02_property ... ok
test tests::rate_p01_anti_windup_holds_integral_at_bound ... ok
test tests::rate_p01_property ... ok

test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 13 tests
test tests::at_zero_error_output_is_zero ... ok
test tests::fresh_pid_has_zero_state ... ok
test tests::negative_error_drives_negative_torque ... ok
test tests::out_of_range_dt_clamped_safely ... ok
test tests::positive_error_drives_positive_torque ... ok
test tests::rate_p02_infinite_setpoint_does_not_propagate ... ok
test tests::rate_p02_nan_input_does_not_propagate ... ok
test tests::output_clamped_to_torque_max ... ok
test tests::rate_p03_step_response_drives_error_to_zero ... ok
test tests::reset_clears_state ... ok
test tests::rate_p02_property ... ok
test tests::rate_p01_property ... ok
test tests::rate_p01_anti_windup_holds_integral_at_bound ... ok

test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 13 tests
test tests::at_zero_error_output_is_zero ... ok
test tests::negative_error_drives_negative_torque ... ok
test tests::fresh_pid_has_zero_state ... ok
test tests::out_of_range_dt_clamped_safely ... ok
test tests::positive_error_drives_positive_torque ... ok
test tests::rate_p02_infinite_setpoint_does_not_propagate ... ok
test tests::rate_p02_nan_input_does_not_propagate ... ok
test tests::output_clamped_to_torque_max ... ok
test tests::rate_p03_step_response_drives_error_to_zero ... ok
test tests::reset_clears_state ... ok
test tests::rate_p01_anti_windup_holds_integral_at_bound ... ok
test tests::rate_p02_property ... ok
test tests::rate_p01_property ... ok

test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.60s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 10 tests
test tests::deterministic_disturbance_recovers ... ok
test tests::deterministic_hover_settles ... ok
test tests::deterministic_attitude_cascade_passes ... ok
test tests::deterministic_step_response_passes ... ok
test tests::noisy_attitude_cascade_still_passes ... ok
test tests::plant_step_full_preserves_unit_quaternion_and_finite_state ... ok
test tests::plant_step_preserves_unit_quaternion ... ok
test tests::noisy_step_response_still_passes ... ok
test tests::deterministic_mission_passes ... ok
test tests::noisy_mission_still_reaches_waypoint ... ok

test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

--- scenario: step ---
samples 5000
final ω (rad/s) [+0.5007, -0.3003, +0.4006]
peak ω above sp 0.0061 rad/s
overshoot 1.2 %
RMS error (steady) 0.0012 rad/s
convergence time 0.139s
loop wall time 718 µs
NaN/∞ seen false
outcome PASS
--- scenario: disturbance ---
samples 5000
final ω (rad/s) [+0.0000, -0.0042, +0.0000]
peak ω above sp 0.8958 rad/s
recovery time 0.141s after impulse
loop wall time 685 µs
NaN/∞ seen false
outcome PASS
--- scenario: hover ---
samples 5000
final ω (rad/s) [-0.0020, +0.0013, -0.0007]
peak ω above sp 0.7683 rad/s
convergence time 0.175s
loop wall time 730 µs
NaN/∞ seen false
outcome PASS
--- scenario: attitude ---
samples 5000
final ω (rad/s) [-0.0001, +0.0000, +0.0000]
peak attitude err 19.989°
RMS error (steady) 0.029° (last 1s)
convergence time 0.352s
loop wall time 903 µs
NaN/∞ seen false
outcome PASS
--- scenario: mission ---
samples 12000
final v (m/s NED) [-0.004, +0.000, +0.003]
peak distance error 10.000 m
final distance 0.010 m
RMS distance (steady)0.015 m (last 2s)
convergence time 4.045s
loop wall time 2239 µs
NaN/∞ seen false
outcome PASS
falcon-sitl-hover: PASS

running 33 tests
test crc::tests::accumulate_slice_equals_individual ... ok
test crc::tests::mavlink_reference_vector_123456789 ... ok
test crc::tests::empty_input_keeps_seed ... ok
test crc::tests::order_matters ... ok
test crc::tests::single_byte_zero ... ok
test frame::tests::encode_rejects_payload_length_mismatch ... ok
test frame::tests::encode_rejects_too_small_output ... ok
test frame::tests::frame_msg_id_three_bytes_little_endian ... ok
test frame::tests::frame_payload_length_byte_matches ... ok
test frame::tests::frame_starts_with_magic_v2 ... ok
test frame::tests::peek_message_id_finds_heartbeat ... ok
test frame::tests::peek_rejects_v1_magic ... ok
test frame::tests::rejects_bad_crc ... ok
test frame::tests::rejects_truncated_header ... ok
test frame::tests::rejects_truncated_payload ... ok
test frame::tests::rejects_v1_magic ... ok
test frame::tests::rejects_wrong_crc_extra ... ok
test frame::tests::round_trip_heartbeat_through_frame ... ok
test heartbeat::tests::crc_extra_is_fifty ... ok
test heartbeat::tests::custom_mode_little_endian ... ok
test heartbeat::tests::decode_empty_payload ... ok
test heartbeat::tests::decode_rejects_long_payload ... ok
test heartbeat::tests::decode_rejects_short_payload ... ok
test heartbeat::tests::field_offsets_match_spec ... ok
test heartbeat::tests::mav_mode_flag_or_and_contains ... ok
test heartbeat::tests::payload_length_constant ... ok
test heartbeat::tests::round_trip_falcon_default ... ok
test heartbeat::tests::round_trip_gcs_default ... ok
test heartbeat::tests::round_trip_max_values ... ok
test heartbeat::tests::round_trip_zero ... ok
test heartbeat::tests::round_trip_arbitrary ... ok
test frame::tests::encode_parse_round_trip_arbitrary_seq ... ok
test frame::tests::parser_never_panics ... ok

test result: ok. 33 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 33 tests
test crc::tests::accumulate_slice_equals_individual ... ok
test crc::tests::empty_input_keeps_seed ... ok
test crc::tests::mavlink_reference_vector_123456789 ... ok
test crc::tests::order_matters ... ok
test crc::tests::single_byte_zero ... ok
test frame::tests::encode_rejects_payload_length_mismatch ... ok
test frame::tests::encode_rejects_too_small_output ... ok
test frame::tests::frame_msg_id_three_bytes_little_endian ... ok
test frame::tests::frame_payload_length_byte_matches ... ok
test frame::tests::frame_starts_with_magic_v2 ... ok
test frame::tests::peek_message_id_finds_heartbeat ... ok
test frame::tests::encode_parse_round_trip_arbitrary_seq ... ok
test frame::tests::peek_rejects_v1_magic ... ok
test frame::tests::rejects_bad_crc ... ok
test frame::tests::rejects_truncated_header ... ok
test frame::tests::rejects_truncated_payload ... ok
test frame::tests::rejects_v1_magic ... ok
test frame::tests::rejects_wrong_crc_extra ... ok
test frame::tests::round_trip_heartbeat_through_frame ... ok
test heartbeat::tests::crc_extra_is_fifty ... ok
test heartbeat::tests::custom_mode_little_endian ... ok
test heartbeat::tests::decode_empty_payload ... ok
test heartbeat::tests::decode_rejects_long_payload ... ok
test heartbeat::tests::decode_rejects_short_payload ... ok
test heartbeat::tests::field_offsets_match_spec ... ok
test heartbeat::tests::mav_mode_flag_or_and_contains ... ok
test heartbeat::tests::payload_length_constant ... ok
test heartbeat::tests::round_trip_falcon_default ... ok
test heartbeat::tests::round_trip_gcs_default ... ok
test heartbeat::tests::round_trip_max_values ... ok
test heartbeat::tests::round_trip_zero ... ok
test heartbeat::tests::round_trip_arbitrary ... ok
test frame::tests::parser_never_panics ... ok

test result: ok. 33 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 33 tests
test crc::tests::accumulate_slice_equals_individual ... ok
test crc::tests::empty_input_keeps_seed ... ok
test crc::tests::mavlink_reference_vector_123456789 ... ok
test crc::tests::order_matters ... ok
test crc::tests::single_byte_zero ... ok
test frame::tests::encode_rejects_payload_length_mismatch ... ok
test frame::tests::encode_rejects_too_small_output ... ok
test frame::tests::frame_msg_id_three_bytes_little_endian ... ok
test frame::tests::frame_payload_length_byte_matches ... ok
test frame::tests::frame_starts_with_magic_v2 ... ok
test frame::tests::peek_message_id_finds_heartbeat ... ok
test frame::tests::peek_rejects_v1_magic ... ok
test frame::tests::rejects_bad_crc ... ok
test frame::tests::rejects_truncated_header ... ok
test frame::tests::rejects_truncated_payload ... ok
test frame::tests::rejects_v1_magic ... ok
test frame::tests::rejects_wrong_crc_extra ... ok
test frame::tests::round_trip_heartbeat_through_frame ... ok
test heartbeat::tests::crc_extra_is_fifty ... ok
test heartbeat::tests::custom_mode_little_endian ... ok
test heartbeat::tests::decode_empty_payload ... ok
test heartbeat::tests::decode_rejects_long_payload ... ok
test heartbeat::tests::decode_rejects_short_payload ... ok
test heartbeat::tests::field_offsets_match_spec ... ok
test heartbeat::tests::mav_mode_flag_or_and_contains ... ok
test heartbeat::tests::payload_length_constant ... ok
test heartbeat::tests::round_trip_falcon_default ... ok
test heartbeat::tests::round_trip_gcs_default ... ok
test heartbeat::tests::round_trip_max_values ... ok
test heartbeat::tests::round_trip_zero ... ok
test heartbeat::tests::round_trip_arbitrary ... ok
test frame::tests::encode_parse_round_trip_arbitrary_seq ... ok
test frame::tests::parser_never_panics ... ok

test result: ok. 33 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.40s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 9 tests
test tests::args_default_ports_for_gcs_mode ... ok
test tests::args_default_ports_for_vehicle_mode ... ok
test tests::args_rejects_missing_mode ... ok
test tests::args_rejects_unknown_mode ... ok
test tests::handle_inbound_propagates_bad_crc ... ok
test tests::handle_inbound_rejects_unsupported_message ... ok
test tests::handle_inbound_truncated ... ok
test tests::current_timestamp_is_monotone_within_a_run ... ok
test tests::vehicle_and_gcs_exchange_heartbeats_over_udp ... ok

test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s

[falcon-hello-demo] building release binary...
[falcon-hello-demo] launching gcs on 127.0.0.1:14700
[falcon-hello-demo] launching vehicle (4 Hz × 4s)
[falcon-hello-demo] vehicle sent 13 heartbeat(s)
[falcon-hello-demo] gcs received 13 heartbeat(s)
[falcon-hello-demo] PASS

running 10 tests
test tests::att_p01_shortest_arc_q_err_scalar_non_negative ... ok
test tests::att_p03_small_angle_within_one_percent ... ok
test tests::att_p01_quaternion_error_is_unit_for_unit_inputs ... ok
test tests::degenerate_input_does_not_propagate_nan ... ok
test tests::identity_setpoint_at_identity_gives_zero_rate ... ok
test tests::negative_roll_setpoint_gives_negative_x_rate ... ok
test tests::rate_command_clamped_to_rate_max ... ok
test tests::sqrt_f32_matches_libm_within_tolerance ... ok
test tests::twenty_deg_roll_setpoint_gives_positive_x_rate ... ok
test tests::att_p01_property ... ok

test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 10 tests
test tests::att_p01_quaternion_error_is_unit_for_unit_inputs ... ok
test tests::att_p01_shortest_arc_q_err_scalar_non_negative ... ok
test tests::att_p03_small_angle_within_one_percent ... ok
test tests::degenerate_input_does_not_propagate_nan ... ok
test tests::identity_setpoint_at_identity_gives_zero_rate ... ok
test tests::negative_roll_setpoint_gives_negative_x_rate ... ok
test tests::rate_command_clamped_to_rate_max ... ok
test tests::sqrt_f32_matches_libm_within_tolerance ... ok
test tests::twenty_deg_roll_setpoint_gives_positive_x_rate ... ok
test tests::att_p01_property ... ok

test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 10 tests
test tests::att_p01_quaternion_error_is_unit_for_unit_inputs ... ok
test tests::att_p01_shortest_arc_q_err_scalar_non_negative ... ok
test tests::att_p03_small_angle_within_one_percent ... ok
test tests::degenerate_input_does_not_propagate_nan ... ok
test tests::identity_setpoint_at_identity_gives_zero_rate ... ok
test tests::negative_roll_setpoint_gives_negative_x_rate ... ok
test tests::rate_command_clamped_to_rate_max ... ok
test tests::sqrt_f32_matches_libm_within_tolerance ... ok
test tests::twenty_deg_roll_setpoint_gives_positive_x_rate ... ok
test tests::att_p01_property ... ok

test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 10 tests
test tests::deterministic_disturbance_recovers ... ok
test tests::deterministic_hover_settles ... ok
test tests::deterministic_attitude_cascade_passes ... ok
test tests::deterministic_step_response_passes ... ok
test tests::noisy_attitude_cascade_still_passes ... ok
test tests::plant_step_full_preserves_unit_quaternion_and_finite_state ... ok
test tests::plant_step_preserves_unit_quaternion ... ok
test tests::noisy_step_response_still_passes ... ok
test tests::deterministic_mission_passes ... ok
test tests::noisy_mission_still_reaches_waypoint ... ok

test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

--- scenario: attitude ---
samples 5000
final ω (rad/s) [-0.0001, +0.0000, +0.0000]
peak attitude err 19.989°
RMS error (steady) 0.029° (last 1s)
convergence time 0.352s
loop wall time 894 µs
NaN/∞ seen false
outcome PASS
falcon-sitl-hover: PASS

running 13 tests
test tests::altitude_above_setpoint_reduces_thrust ... ok
test tests::altitude_below_setpoint_increases_thrust ... ok
test tests::at_setpoint_with_zero_velocity_gives_hover_thrust ... ok
test tests::degenerate_input_does_not_propagate ... ok
test tests::east_position_error_rolls_right ... ok
test tests::forward_position_error_pitches_nose_down ... ok
test tests::pos_p01_bounds_enforced ... ok
test tests::reset_clears_integral ... ok
test tests::yaw_held_when_setpoint_is_nan ... ok
test tests::tilt_clamped_to_tilt_max ... ok
test tests::v_setpoint_clamped_to_v_max ... ok
test tests::yaw_setpoint_is_followed ... ok
test tests::pos_p01_property ... ok

test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 13 tests
test tests::altitude_above_setpoint_reduces_thrust ... ok
test tests::at_setpoint_with_zero_velocity_gives_hover_thrust ... ok
test tests::altitude_below_setpoint_increases_thrust ... ok
test tests::degenerate_input_does_not_propagate ... ok
test tests::east_position_error_rolls_right ... ok
test tests::forward_position_error_pitches_nose_down ... ok
test tests::pos_p01_bounds_enforced ... ok
test tests::reset_clears_integral ... ok
test tests::tilt_clamped_to_tilt_max ... ok
test tests::yaw_held_when_setpoint_is_nan ... ok
test tests::v_setpoint_clamped_to_v_max ... ok
test tests::yaw_setpoint_is_followed ... ok
test tests::pos_p01_property ... ok

test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 13 tests
test tests::altitude_above_setpoint_reduces_thrust ... ok
test tests::degenerate_input_does_not_propagate ... ok
test tests::altitude_below_setpoint_increases_thrust ... ok
test tests::at_setpoint_with_zero_velocity_gives_hover_thrust ... ok
test tests::east_position_error_rolls_right ... ok
test tests::forward_position_error_pitches_nose_down ... ok
test tests::pos_p01_bounds_enforced ... ok
test tests::reset_clears_integral ... ok
test tests::tilt_clamped_to_tilt_max ... ok
test tests::yaw_held_when_setpoint_is_nan ... ok
test tests::yaw_setpoint_is_followed ... ok
test tests::v_setpoint_clamped_to_v_max ... ok
test tests::pos_p01_property ... ok

test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.11s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 10 tests
test tests::deterministic_disturbance_recovers ... ok
test tests::deterministic_hover_settles ... ok
test tests::deterministic_attitude_cascade_passes ... ok
test tests::deterministic_step_response_passes ... ok
test tests::noisy_attitude_cascade_still_passes ... ok
test tests::plant_step_full_preserves_unit_quaternion_and_finite_state ... ok
test tests::plant_step_preserves_unit_quaternion ... ok
test tests::noisy_step_response_still_passes ... ok
test tests::deterministic_mission_passes ... ok
test tests::noisy_mission_still_reaches_waypoint ... ok

test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

--- scenario: mission ---
samples 12000
final v (m/s NED) [-0.004, +0.000, +0.003]
peak distance error 10.000 m
final distance 0.010 m
RMS distance (steady)0.015 m (last 2s)
convergence time 4.045s
loop wall time 2213 µs
NaN/∞ seen false
outcome PASS
falcon-sitl-hover: PASS

running 9 tests
test tests::deterministic_across_ticks_with_same_time ... ok
test tests::fresh_stub_has_zero_time ... ok
test tests::innovation_is_quiet_in_stub ... ok
test tests::tick_passes_time_through ... ok
test tests::tick_returns_identity_quaternion ... ok
test tests::tick_returns_zero_position_and_velocity ... ok
test tests::unit_quaternion_check_rejects_clearly_non_unit ... ok
test tests::tick_always_emits_unit_quaternion ... ok
test tests::tick_innovation_within_healthy_range ... ok

test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 9 tests
test tests::deterministic_across_ticks_with_same_time ... ok
test tests::fresh_stub_has_zero_time ... ok
test tests::innovation_is_quiet_in_stub ... ok
test tests::tick_passes_time_through ... ok
test tests::tick_returns_identity_quaternion ... ok
test tests::tick_returns_zero_position_and_velocity ... ok
test tests::tick_always_emits_unit_quaternion ... ok
test tests::tick_innovation_within_healthy_range ... ok
test tests::unit_quaternion_check_rejects_clearly_non_unit ... ok

test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 9 tests
test tests::deterministic_across_ticks_with_same_time ... ok
test tests::fresh_stub_has_zero_time ... ok
test tests::innovation_is_quiet_in_stub ... ok
test tests::tick_passes_time_through ... ok
test tests::tick_returns_identity_quaternion ... ok
test tests::tick_returns_zero_position_and_velocity ... ok
test tests::unit_quaternion_check_rejects_clearly_non_unit ... ok
test tests::tick_innovation_within_healthy_range ... ok
test tests::tick_always_emits_unit_quaternion ... ok

test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.09s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 16 tests
test tests::cross_of_parallel_is_zero ... ok
test tests::ekf_p01_tick_preserves_unit_quaternion ... ok
test tests::ekf_p02_extreme_accel_does_not_produce_nan ... ok
test tests::ekf_p02_zero_accel_does_not_produce_nan ... ok
test tests::ekf_p03_innovation_monotone_with_tilt_disagreement ... ok
test tests::ekf_p04_static_rest_converges_to_gravity_aligned ... ok
test tests::ekf_p05_pure_yaw_gyro_does_not_destabilise_attitude ... ok
test tests::fresh_estimator_is_at_identity ... ok
test tests::normalise_nan_returns_none ... ok
test tests::normalise_zero_returns_none ... ok
test tests::quat_mul_identity_left ... ok
test tests::quat_mul_identity_right ... ok
test tests::rotate_inverse_identity_passes_through ... ok
test tests::ekf_p01_property ... ok
test tests::bias_estimate_bounded ... ok
test tests::ekf_p02_property_sequence ... ok

test result: ok. 16 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 16 tests
test tests::cross_of_parallel_is_zero ... ok
test tests::ekf_p01_tick_preserves_unit_quaternion ... ok
test tests::ekf_p02_extreme_accel_does_not_produce_nan ... ok
test tests::ekf_p02_zero_accel_does_not_produce_nan ... ok
test tests::ekf_p03_innovation_monotone_with_tilt_disagreement ... ok
test tests::ekf_p04_static_rest_converges_to_gravity_aligned ... ok
test tests::ekf_p05_pure_yaw_gyro_does_not_destabilise_attitude ... ok
test tests::fresh_estimator_is_at_identity ... ok
test tests::ekf_p01_property ... ok
test tests::normalise_nan_returns_none ... ok
test tests::normalise_zero_returns_none ... ok
test tests::quat_mul_identity_left ... ok
test tests::quat_mul_identity_right ... ok
test tests::rotate_inverse_identity_passes_through ... ok
test tests::ekf_p02_property_sequence ... ok
test tests::bias_estimate_bounded ... ok

test result: ok. 16 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 16 tests
test tests::cross_of_parallel_is_zero ... ok
test tests::ekf_p01_tick_preserves_unit_quaternion ... ok
test tests::ekf_p02_extreme_accel_does_not_produce_nan ... ok
test tests::ekf_p02_zero_accel_does_not_produce_nan ... ok
test tests::ekf_p03_innovation_monotone_with_tilt_disagreement ... ok
test tests::ekf_p04_static_rest_converges_to_gravity_aligned ... ok
test tests::ekf_p05_pure_yaw_gyro_does_not_destabilise_attitude ... ok
test tests::fresh_estimator_is_at_identity ... ok
test tests::normalise_nan_returns_none ... ok
test tests::normalise_zero_returns_none ... ok
test tests::quat_mul_identity_left ... ok
test tests::quat_mul_identity_right ... ok
test tests::rotate_inverse_identity_passes_through ... ok
test tests::bias_estimate_bounded ... ok
test tests::ekf_p01_property ... ok
test tests::ekf_p02_property_sequence ... ok

test result: ok. 16 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.81s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 5 tests
test tests::phases_are_consistent_with_trajectory_duration ... ok
test tests::quat_error_is_zero_for_identical_quaternions ... ok
test tests::rotate_ned_to_body_identity_passes_through ... ok
test tests::deterministic_bench_passes ... ok
test tests::noisy_bench_passes_loose ... ok

test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

--- noise=0 (deterministic) ---
samples 5000
RMS error (full) 3.306°
RMS error (steady) 3.312° (last 2.5 s)
peak error 19.804°
final error 3.023°
convergence time 0.68s (first sustained <5°)
estimator wall time 631 µs
NaN/∞ seen false
falcon-ekf-bench: PASS

running 10 tests
test tests::high_thrust_with_torque_saturates_gracefully ... ok
test tests::mix_p01_zero_command_gives_thrust_only ... ok
test tests::mix_p02_outputs_in_unit_interval ... ok
test tests::mix_p03_pure_pitch_drives_front_and_back_motors_opposite ... ok
test tests::mix_p03_pure_roll_drives_diagonal_motor_pairs_opposite ... ok
test tests::mix_p03_pure_yaw_drives_cw_and_ccw_pairs_opposite ... ok
test tests::nan_input_does_not_propagate ... ok
test tests::negative_thrust_clipped_to_zero ... ok
test tests::mix_p03_property_single_axis ... ok
test tests::mix_p02_property ... ok

test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 10 tests
test tests::high_thrust_with_torque_saturates_gracefully ... ok
test tests::mix_p02_outputs_in_unit_interval ... ok
test tests::mix_p01_zero_command_gives_thrust_only ... ok
test tests::mix_p03_pure_pitch_drives_front_and_back_motors_opposite ... ok
test tests::mix_p03_pure_roll_drives_diagonal_motor_pairs_opposite ... ok
test tests::mix_p03_pure_yaw_drives_cw_and_ccw_pairs_opposite ... ok
test tests::nan_input_does_not_propagate ... ok
test tests::negative_thrust_clipped_to_zero ... ok
test tests::mix_p03_property_single_axis ... ok
test tests::mix_p02_property ... ok

test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

running 10 tests
test tests::high_thrust_with_torque_saturates_gracefully ... ok
test tests::mix_p01_zero_command_gives_thrust_only ... ok
test tests::mix_p02_outputs_in_unit_interval ... ok
test tests::mix_p03_pure_pitch_drives_front_and_back_motors_opposite ... ok
test tests::mix_p03_pure_roll_drives_diagonal_motor_pairs_opposite ... ok
test tests::mix_p03_pure_yaw_drives_cw_and_ccw_pairs_opposite ... ok
test tests::nan_input_does_not_propagate ... ok
test tests::negative_thrust_clipped_to_zero ... ok
test tests::mix_p03_property_single_axis ... ok
test tests::mix_p02_property ... ok

test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

falcon verification gate (filter: (has-tag "falcon"))

8 artifact(s) matched: FV-FALCON-RATE-001, FV-FALCON-MAVLINK-001, FV-FALCON-WORLD-001, FV-FALCON-ATT-001, FV-FALCON-POS-001, FV-FALCON-EKF-STUB-001, FV-FALCON-EKF-001, FV-FALCON-MIX-001

[ PASS] ( 7.84s) FV-FALCON-RATE-001: cargo test -p relay-rate
[ PASS] ( 9.97s) FV-FALCON-RATE-001: cargo test -p relay-rate --release
[ PASS] ( 0.67s) FV-FALCON-RATE-001: PROPTEST_CASES=4096 cargo test -p relay-rate
[ PASS] ( 1.05s) FV-FALCON-RATE-001: cargo test -p falcon-sitl-hover
[ PASS] ( 1.35s) FV-FALCON-RATE-001: cargo run -q -p falcon-sitl-hover --release
[ PASS] ( 0.44s) FV-FALCON-MAVLINK-001: cargo test -p relay-mavlink
[ PASS] ( 1.03s) FV-FALCON-MAVLINK-001: cargo test -p relay-mavlink --release
[ PASS] ( 0.47s) FV-FALCON-MAVLINK-001: PROPTEST_CASES=4096 cargo test -p relay-mavlink
[ PASS] ( 0.32s) FV-FALCON-WORLD-001: cargo test -p falcon-hello
[ PASS] ( 4.71s) FV-FALCON-WORLD-001: scripts/falcon-hello-demo.sh
[ PASS] ( 0.32s) FV-FALCON-ATT-001: cargo test -p relay-att
[ PASS] ( 0.55s) FV-FALCON-ATT-001: cargo test -p relay-att --release
[ PASS] ( 0.17s) FV-FALCON-ATT-001: PROPTEST_CASES=4096 cargo test -p relay-att
[ PASS] ( 0.06s) FV-FALCON-ATT-001: cargo test -p falcon-sitl-hover
[ PASS] ( 0.04s) FV-FALCON-ATT-001: cargo run -q -p falcon-sitl-hover --release -- --scenario attitude
[ PASS] ( 0.32s) FV-FALCON-POS-001: cargo test -p relay-pos
[ PASS] ( 0.54s) FV-FALCON-POS-001: cargo test -p relay-pos --release
[ PASS] ( 0.18s) FV-FALCON-POS-001: PROPTEST_CASES=4096 cargo test -p relay-pos
[ PASS] ( 0.06s) FV-FALCON-POS-001: cargo test -p falcon-sitl-hover
[ PASS] ( 0.04s) FV-FALCON-POS-001: cargo run -q -p falcon-sitl-hover --release -- --scenario mission
[ PASS] ( 0.34s) FV-FALCON-EKF-STUB-001: cargo test -p relay-ekf-stub
[ PASS] ( 0.71s) FV-FALCON-EKF-STUB-001: cargo test -p relay-ekf-stub --release
[ PASS] ( 0.15s) FV-FALCON-EKF-STUB-001: PROPTEST_CASES=4096 cargo test -p relay-ekf-stub
[ PASS] ( 0.41s) FV-FALCON-EKF-001: cargo test -p relay-ekf
[ PASS] ( 0.81s) FV-FALCON-EKF-001: cargo test -p relay-ekf --release
[ PASS] ( 0.88s) FV-FALCON-EKF-001: PROPTEST_CASES=4096 cargo test -p relay-ekf
[ PASS] ( 0.15s) FV-FALCON-EKF-001: cargo test -p falcon-ekf-bench
[ PASS] ( 0.18s) FV-FALCON-EKF-001: cargo run -q -p falcon-ekf-bench --release
[ PASS] ( 0.32s) FV-FALCON-MIX-001: cargo test -p relay-mix-quad
[ PASS] ( 0.73s) FV-FALCON-MIX-001: cargo test -p relay-mix-quad --release
[ PASS] ( 0.16s) FV-FALCON-MIX-001: PROPTEST_CASES=4096 cargo test -p relay-mix-quad

✅ Rivet verification gate — falcon

8/8 passed

count
Passed 8
Failed 0
Skipped (no steps) 0

Source of truth: artifacts/verification/FV-FALCON-*.yaml.

@avrabe avrabe merged commit 05ca06c into main May 19, 2026
7 checks passed
@avrabe avrabe deleted the falcon/v0.5 branch May 19, 2026 18:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant