# TerraHawk flight parameters: common to every airframe and flight controller. # Load order: this file, then the board file (board-*.parm), then the airframe file (airframe.parm). # th-avionics owns this file. Every value names its reason; "provisional" means it changes after a # named flight test. SITL evidence is in docs/agent-threads/test-battery/ and sim/terrahawk.parm. # --- Frame (kit ArduPilot guide, step 2) --- Q_ENABLE 1 Q_FRAME_CLASS 1 Q_FRAME_TYPE 1 # Hover thrust, a THRUST fraction (what CTUN.ThO logs and Q_M_HOVER_LEARN 2 learns), not the ESC command: # 0.244 at 2.75 kg on the XING2 2809 (th-aeronautics 154ab6b); about 0.254 on the EMAX ECO II 2807 1300KV that Ryan # chose on 2026-10-04 (th-aeronautics, from an estimated winding resistance). 0.38 here before 2026-10-01 was a command. # It rises as the pack sags. AP_Motors scales the demand by lift_max = v(1 - e) + e v^2 (v = V / 25.2, # e = Q_M_THST_EXPO), so the hover needs 0.254 / lift_max: 0.274 at 23.9 V, 0.32 at 21.6 V, 0.34 at 20.4 V # (Thrust_Linearization.cpp at 4c98c92; on the XING2, 0.263, 0.30 and 0.33, and SITL learned 0.264, 07af754). # 0.25 stays the start value: Q_M_HOVER_LEARN 2 learns the real one on the first hovers. On EMAX's own ECO II 2807 # sheet (58 mOhm, th-aeronautics 2d2917e) the prediction is about 0.242 at 2.85 kg on the 2-blade, about 0.275 on # the 3-blade (c49c8d9). Q_M_THST_HOVER 0.25 # Explicit, not left to the default. The lift props are the Gemfan F8041 3-blade (Ryan, 2026-10-08), which on a fresh # 25.2 V pack would draw about 64 A a motor at full throttle (62.5 to 65.7), past EMAX's 52 A peak and the Lucid's # 50 A continuous. 0.82 holds 49 A in every case th-aeronautics modelled (central cap 0.84; T/W about 3.6 at 2.85 kg; # c49c8d9, docs/propulsion.md "The 3-blade F8041"). It was 0.95 for the 2-blade. On the thrust stand: start at 0.82 # and raise it in steps of 0.01 only while a motor stays under 49 A; never above what the stand shows. Q_M_SPIN_MAX 0.82 # range-ok value-ok: under the documented 0.9 on purpose (current cap); the code takes any value # Thrust curve for the F8041 3-blade on the EMAX 2807 (th-aeronautics c49c8d9: fit 0.31 full, 0.34 nominal; it was # 0.43 for the 2-blade). AN ESTIMATE: fitted again on the thrust stand's run, and checked against the first hover log. Q_M_THST_EXPO 0.33 Q_M_HOVER_LEARN 2 # --- Speeds (docs/flight-parameters.md; schedule decided 2026-09-27) --- # AIRSPEED_MIN 20 (th-avionics, 2026-10-18): the forward transition ends when EAS passes it (quadplane.cpp:1522), and # Fuselage C's side static ports over-read at the transition's low and negative alpha. On the static-port sensor model # (SIM_ARSPD_CP 1, table to -5 deg) on the C model (th-testpilot b9b5632f, 1d51b2d9) 18 ended 19 % of transitions under # 18 m/s true and 20 none. It also raises the fixed-wing floor and the approach speeds, so TECS_LAND_ARSPD is filed # below. lift_check.lua v1.6.5 keys its near-assist margin and its spin floor on Q_ASSIST_SPEED, not this. AIRSPEED_MIN 20 # RTL flies at AIRSPEED_CRUISE. On a low pack the pusher still has margin: at 20.1 V (31 % charge under # load) the propulsion model gives about 12 N at full throttle at 22 m/s and 9 N at 27 m/s, against 4 to 6 N # of drag. SITL's pusher gives much less at speed on a low pack (8.8 N at 22 m/s, 3.9 N at 27 m/s), because # its momentum model loses thrust as airspeed squared (th-simulator, 2026-10-01), so a SITL speed limit on a # low pack is not the aircraft's. To raise AIRSPEED_CRUISE, check the pusher against propulsion.py or a thrust # test, not SITL, and re-fly S5 for the return energy. AIRSPEED_CRUISE 22 # The fixed-wing landing approach speed, pinned: at -1 it would follow AIRSPEED_MIN (halfway to AIRSPEED_CRUISE), and the # belly-landing lane is sized from it (th-hangar). Lane stops at +45 m or less in a 5 m/s tailwind (th-testpilot 5f21a1f0). TECS_LAND_ARSPD 20 # TECS's throttle feed-forward: the throttle that holds AIRSPEED_CRUISE level on a full pack. 65 since 2026-10-20: # on Fuselage C (c24b-w78) 22 m/s indicated level in calm air on a fresh pack takes 64.6 % (th-simulator's # film_flights.py cruise-trim, eaf7758e; it was 58 % on the earlier CFD drag, fadf08c). Unset it is 45 %. With the # pitot in use TECS finds the steady value itself: 58 against 65 on C made no difference (th-testpilot, thread # 7fcfe781: S1 80.6 to 80.8 Wh both, S5 return 1,029 to 1,037 mAh both, TR lowest 19.1 both, no assists). It # matters most with the pitot disabled (ARSPD_USE 0), when TECS sets the throttle from this value and the height # error: the lowest true airspeed on the wing went from 21.3 to 22.5 m/s (S3), 22.1 to 23.3 (LN-tail3) and 17.5 to # 18.3 (F11 assist circle, 0.3 to 0.7 m/s over C's trimmed stall at 58), no assists in 12 flights an arm. It is also # the pusher's floor in a forward transition (already 84 to 86 %), and the FBWA pitch-down below it costs 0.1 m/s # of sink at mid stick. TRIM_THROTTLE 65 AIRSPEED_MAX 30 # A margin setting until the FBWA stall test (docs/stall-test.md), which sets it to the measured 1 g stall, # referred, x 1.08. 20.0 (2026-10-01, was 19.2 = 17.8 x 1.08) is 9 % over SITL's 18.34 m/s stall at 2.733 kg # and about 20 % over the CFD's 16.6 (th-aeronautics). TECS's turn floor is this x sqrt(load factor), 22.1 m/s # at 35 deg: at the 21 m/s survey the turns fly faster, and the AoA peak in th-testpilot's gusty S2 turns # came down on every paired seed (9.9 to 11.0 deg) for about 1 Wh per 100 ha. params/README.md has the rest. AIRSPEED_STALL 20.0 Q_TRANSITION_MS 5000 # --- Assist (guide step 6, raised from the guide's 14 because the stall is higher) --- Q_ASSIST_SPEED 18 # Angle assist: the lift motors come in when the bank is past ROLL_LIMIT_DEG + 5 AND the attitude error # exceeds this (VTOL_Assist.cpp at 4c98c92). ArduPilot's default 30, lower than the manual's 45 (assembly # manual and beginner's guide, "catches an attitude upset before it becomes a spin"; 2026-10-01): the CFD shows a # wing-drop tendency near 17 deg AoA that can match the full elevon roll authority (th-aeronautics), and in # a 35 deg turn 45 would act only past 80 deg of bank, 30 past 65. Survey turns stay inside the 40 deg envelope. Q_ASSIST_ANGLE 30 # Mission legs at least 10 m above this; every forward transition starts at least 20 m above it. Q_ASSIST_ALT 30 # --- Fixed-wing control (docs/cg.md: 25 degree elevon travel, MIXING_GAIN 0.75) --- # Provisional: rate gains are ArduPilot's quadplane defaults scaled for the larger authority, not # tuned. The real aircraft needs AUTOTUNE (fixed wing) and QAUTOTUNE (hover) before survey work. MIXING_GAIN 0.75 PTCH_RATE_P 0.132 PTCH_RATE_I 0.091 # PTCH_RATE_FF: scaled for the CFD elevons (th-testpilot 2026-09-29: no verdict changes, bank tracking better). It # matches today's plant: the doublet's pitch ratio (0.535 deg/s per us of elevon) is the quasi-steady # c_m_deltae / c_m_q, and the SITL inertia rescales moved both by the same factor, so the ratio and the matched # feed-forward held (th-testpilot 3cc1417, th-aeronautics, 2026-10-05; the 0.76 of 99080b6 is withdrawn). The flight # retune (the first-flight card's pitch feed-forward step; named, not numbered, as steps get renumbered): PTCH_RATE_FF = 0.801 x 0.535 / measured ratio (0.43 / measured). PTCH_RATE_FF 0.801 RLL_RATE_P 0.060 # range-ok: below the documented 0.08 on purpose, scaled for 2.34x elevon authority RLL_RATE_I 0.091 RLL_RATE_FF 0.311 # 0.236 / 0.758: roll authority from the up-side CFD elevon slope (th-aeronautics 1bcc0b1) ROLL_LIMIT_DEG 35 # TECS: pitch capped near the stall angle; speed before height (home-leg stall fix, 2026-09-28). # The flight script lowers the weighting to 1 below Q_ASSIST_ALT + 5 m and restores it above # Q_ASSIST_ALT + 15 m, without saving it, so this stored value is the protected state. The cap is 12 deg # absolute: the row is relative to PTCH_TRIM_DEG (below), so it reads 7 with the trim at 5. TECS_PITCH_MAX 7 # Pitch trim (th-avionics, 2026-10-18; th-testpilot dd6577cd). Plane adds PTCH_TRIM_DEG to every fixed-wing pitch # demand after the limits (Attitude.cpp:244), so FBWA's centre stick holds 5 deg, about level cruise on the C model # (AUTO level cruise flies 5.3 deg at 23-24 m/s true, ESTIMATE). At 0 a hands-off FBWA transition held the nose near # 1 deg through the lift ramp and lost up to 10.7 m (126 SITL transitions on c24b-w78); at 5, 1.0 m at most. Set it # from the first flight log: the pitch in level cruise. The pitch limits are relative to it, so each row below # states its absolute angle, the one flown before the trim. lift_check.lua subtracts it from its own limits (v1.6.6). PTCH_TRIM_DEG 5 PTCH_LIM_MAX_DEG 15 # absolute 20, the default PTCH_LIM_MIN_DEG -30 # absolute -25, the default TECS_LAND_PMAX 5 # absolute 10, the default LAND_PITCH_DEG -5 # absolute 0: the lane flare as flown (peaks 1.4 to 4.6 deg) # Elevon trim is learned in flight on the first flights, then copied into the airframe file. # th-simulator, 2026-09-28: a transition started 1.4 deg of elevon out of trim sagged 12 m deeper. SERVO_AUTO_TRIM 1 TECS_SPDWEIGHT 2 # --- Hover and landing --- Q_A_RAT_YAW_P 0.3 Q_WVANE_GAIN 1 Q_WVANE_LAND 0 Q_LAND_FINAL_ALT 3 Q_LAND_FINAL_SPD 0.3 # Pilot's fastest descent in QHOVER and QLOITER (full-down stick). ArduPlane's 0 uses Q_PILOT_SPD_UP, 2.5 m/s, too fast # for a hand landing. 1.5 (th-testpilot, 2026-10-12, 30 m full-down, calm and 5 m/s gusts): touchdown 2.0 -> 1.0 m/s, # peak sink 2.2 -> 1.2, roll 4.2 deg at most. The GPS-loss guard's QHOVER hands the landing to the pilot. Q_PILOT_SPD_DN 1.5 # Horizontal position hold in VTOL, firmer than Plane's defaults (0.5, 0.7, 0.35; AC_PosControl.cpp), near # Copter's. SITL dock landings, same 84 gusty seeds (th-testpilot, 2026-09-30): 84 of 84 captured against 53 # with the defaults (scored on the folding pusher's 142 mm aft limit; the fixed APC's 279 mm can only raise the # 53, not the 84), no ringing (roll and pitch peaks unchanged). A faster final descent did nothing; the # defaults let a gust push the aircraft aft faster than they pull it back. SITL STARTING VALUE: check the # first real hover's log for oscillation before any dock landing (first-flight card). Q_P_NE_POS_P 1.0 Q_P_NE_VEL_P 1.4 Q_P_NE_VEL_I 0.7 # Q_FWD_THR_USE stays 0 (ArduPlane's default, not set here). 1 holds a windy hover with the pusher instead of a # nose-down tilt, but ArduPlane keeps the pusher running to contact, which pushed dock landings up to 0.34 m # forward. With the RTL mission ending it bought only a flatter hold (11.6 against 19.7 deg) and 0.8 A, at the # same energy (th-testpilot, 2026-10-01). Use 1 only together with sim/scripts/fwd_thr_cutoff.lua. # Bits 5, 12, 16, 17: QRTL on RC loss in a Q mode, no approach circle, land reposition. Bit 0 (Level # Transition) off. docs/clear-to-launch.md, "No low approach circle". Bit 15 (throttle landing control) # is off since 2026-09-30: with it, a throttle stick left above 70 % (cruise) turned a battery-failsafe # landing into a climb (quadplane.cpp landing_descent_rate_ms; th-testpilot F6: 61 to 640 m, then impact), # and a QLAND entered with the stick high then held height at mid stick (F7). With 196640 the F6 flight # landed 0.1 m from home with the stick at 82 %. To stop a descent, select QLOITER or QHOVER. # Bit 12 (since 2026-10-19): no speed assist on a synthetic airspeed, only on a real sensor (VTOL_Assist.cpp:94). # Without a sensor, or after ArduPilot disables a failed one, assisted flight sets fly_forward false, the EKF drops # its wind states and the estimate falls back to DCM's (about 13 against 21 true): under Q_ASSIST_SPEED, so assist # restarted every 5 s through cruise with the lift motors running (th-testpilot 83567e78). With bit 12: no loop and # no height assist on three motors, and with the sensor nothing changes (cc6d5d92). Height and angle assist stay. Q_OPTIONS 200736 # Returns: RTL flies home as a plane and hands over to QRTL near home. With Q_OPTIONS bit 16 a QRTL # hovers home from any distance, so far returns are RTL and QRTL is used only near the pad. Q_RTL_MODE 1 Q_RTL_ALT 50 RTL_ALTITUDE 80 STICK_MIXING 1 # --- Modes, channel 5: QSTABILIZE, QHOVER, QLOITER, AUTO, RTL, FBWA (slot 5 is RTL, never QRTL) --- FLTMODE_CH 5 FLTMODE1 17 FLTMODE2 18 FLTMODE3 19 FLTMODE4 10 FLTMODE5 11 FLTMODE6 5 # Stick trims pinned: in a VTOL landing ArduPlane adds a velocity from off-trim sticks to the target. RC1_TRIM 1500 RC2_TRIM 1500 RC4_TRIM 1500 # --- Failsafes (docs/clear-to-launch.md, remote piloting table) --- # RC failsafe on; the aircraft always has an RC source (handset, or the site node's CRSF link). THR_FAILSAFE 1 FS_SHORT_ACTN 0 # The short failsafe acts after RC_FS_TIMEOUT (default 1 s); this ArduPlane has no FS_SHORT_TIMEOUT. RC_FS_TIMEOUT 1 # Backup RC over the LoRa link, way C (gps thread d8e1d6c; Ryan's form in docs/hangar.md): the operator's GCS sends # RC_CHANNELS_OVERRIDE at 5 Hz as MAV_GCS_SYSID. Measured through the full-load link (th-mlrs-xiao run 4): 99.87 % # arrived, worst gap 452 ms. Each overridden channel returns to ELRS this long after the last override: 1.5 s is # three times the worst gap, and hands back to a live ELRS sooner than the 3 s default if the dock link drops in the # middle of a takeover. With ELRS also lost, the RC failsafe starts RC_FS_TIMEOUT after the last override anyway. # Never set RC_OPTIONS bit 14: it reads the receiver's frozen sticks after an ELRS loss and cancels the backup. RC_OVERRIDE_TIME 1.5 FS_LONG_ACTN 0 FS_LONG_TIMEOUT 5 FS_GCS_ENABL 1 # Battery: the 6S2P Molicel P50B, 10 Ah, 216 Wh (Ryan, 2026-10-01). Low starts RTL, critical lands. # The capacity triggers act first, with no delay. Critical at 3000 mAh left (30 %, Ryan, 2026-10-01): a 39 s # QLand from 30 m holds 3.0 V a cell under load from 25 % in 5 m/s and 30 % in 8 m/s; 20 % sags to 17.6 V # (th-simulator 22343fb; its 45 mOhm is a mid-charge figure, so 30 % keeps 0.5 V of margin). Low at 4500 # (45 %): critical plus the RTL home from the survey's far point in an 8 m/s headwind, with the back- # transition and 60 s of hover (11.4 % at 1.5 km, 13.0 % at 2.5 km, th-aeronautics planning figures), # rounded up to 5 %. The same 45 % also pays for the dock's landing hold: up to 2 minutes of hover at about 5.5 % a # minute, until the hold leaves at CTL_RESERVE_PCT 36 % (clear-to-launch.md). On Fuselage C, S5 (1.5 km into 8 m/s, # th-testpilot 7fcfe781) reaches the hover over home at 41.9 % and lands at 34.7 %; at its 34 A hover, after # about 19 s of positioning, about 45 s of hold is left; at 40 % none (2026-10-22). Lower this only with the hold # budgeted. The voltages are the backstop for a wrong mAh count, sag-compensated (BATT_FS_VOLTSRC # 1), at the same charge: 21.6 V is 45 % and 20.4 V is 30 % at rest on the P50B table (th-simulator). A # 27 A hover reads 1.2 V lower, so a raw trigger would fire early. Was 30 / 15 % until 2026-10-01. # th-testpilot S5 checks it: the low trigger fires at the far point and the aircraft lands at home before # critical. Revisit with real logs of the sag near 30 %. The Tattu: params/pack-lipo-4500.parm. BATT_CAPACITY 10000 BATT_LOW_MAH 4500 BATT_CRT_MAH 3000 BATT_FS_VOLTSRC 1 BATT_LOW_VOLT 21.6 BATT_CRT_VOLT 20.4 BATT_FS_LOW_ACT 1 # Critical: QLand where it is (4). Loiter to QLand (6) was flown against it (th-testpilot F7 pair, # 2026-09-30, trigger at 1.58 km and 67 m): 6 went to QLAND 0.1 s after entering, so both flew the # same descent, 4.3 and 4.7 Wh, landing 259 and 260 m from the trigger. No benefit seen, so 4 stays. BATT_FS_CRT_ACT 4 # Geofence (not enabled yet; FENCE_ENABLE stays at its default 0): FENCE_OPTIONS 0, set now so that it is # already right on the day the fence is enabled. Plane's default (bit 0, disable mode # change) refuses every mode change during fence recovery except a landing sequence, and a battery # failsafe is not one, so the critical QLand is denied (system.cpp at 4c98c92; th-testpilot F9, # 2026-09-30: "Mode change to QLand denied, in fence recovery"). # This row was only a comment until 2026-10-01; a fence turned on without it kept the lock, and in # th-testpilot's F14 the guard's QLand was denied for 15 s while RTL climbed from 15 to 82 m at critical. FENCE_OPTIONS 0 Q_M_BAT_VOLT_MAX 25.2 # Lowest voltage under load seen in SITL near empty is about 17.2 V (th-simulator): compensate down to 17.0. Q_M_BAT_VOLT_MIN 17.0 # --- Sensors --- ARSPD_USE 1 # ARSPD_OPTIONS 9 (bits 0 and 3; since 2026-10-19): ArduPilot disables a pitot that disagrees with GPS less the EKF wind # (bit 0) or with the EKF (bit 3), and does NOT enable it again in flight (bit 1 off). With bit 1 the EKF can learn a wind # that explains a stuck reading and the health check then re-enables the stuck pitot: in SITL that handed TECS the stuck # value about 45 s after the disable, and the flight lost 8.8 to 11.2 m before the stuck guard caught it again # (th-testpilot 4fa36c21, 184610e8). At 9 the worst was 3.1 m. The cost: a good pitot rejected after a sudden wind change # stays off for the flight; the aircraft flies on synthetic airspeed (no speed assist, Q_OPTIONS bit 12) and the card # says to circle and land. The GPS-loss fall-back re-enable (AP_Airspeed_Health.cpp:44-50) does not depend on bit 1. ARSPD_OPTIONS 9 # Airspeed fault cover has two layers (th-simulator, 2026-10-01, steps from 25.7 to 37.3 m/s at a true 21): the # EKF stops using a pitot that reads more than about 7 m/s high within 3.1 to 3.4 s (the AHRS then gives the # synthetic airspeed), and arspd_stuck_guard.lua covers smaller errors, which the EKF accepts. Bit 2 # (DontDisableAirspeedUsingEKF) would remove the first layer: keep it clear. The default is 0; this row keeps it so. # Bit 1 (DisableDCMFallbackVTOL) stays clear here too: set from boot it lets a quadplane arm before the EKF has a GPS # origin (SITL armed at 10.7 s, origin at 19.4; th-avionics 2026-10-15). gps_loss_guard v2.4 sets it in flight for a # GPS loss only, with param:set (never saved), and puts 0 back at the fix or the disarm. AHRS_OPTIONS 0 # EKF source set 2 is the no-GPS set gps_loss_guard v2.4 selects for a GPS loss (a jammed GPS's short "locks" with wrong # velocities otherwise reach the EKF). Its defaults already take no GPS position or velocity and the barometer for # height; its yaw defaults to None, so the compass is set here. EK3_SRC_OPTIONS 0 (FuseAllVelocities off, the default) # so set 2 really drops the GPS velocity. Neither row changes a flight that never selects set 2. EK3_SRC2_YAW 1 EK3_SRC_OPTIONS 0 # Either line order at the nose joint reads correctly (the pitot lines disconnect with the nose). ARSPD_TUBE_ORDR 2 # GPS: u-blox NEO-F9P (Waveshare Q43); L5 health taken from L1 while GPS L5 is pre-operational. GPS_DRV_OPTIONS 32 GPS1_RATE_MS 100 # Sensor positions from the design CG point (0, 270, -5.6), body axes X fwd, Y right, Z down: INS_POS1 (board file) # and every GPS_POS share this one origin (th-avionics, th-telemetry, th-blender W52 6ac8c58e). # The Q43 on Fuselage C v2.4B: base centre (0, 300, 26.7) on the copper floor, dome 37.0 mm; phase centre at two-thirds # of the dome, Z 51.4, an ESTIMATE within about 10 mm (th-telemetry 20261010T185835Z); Waveshare publishes no phase # centre. SITL's SIM_GPS1_POS matches it (params_sync). GPS1_POS_X -0.030 GPS1_POS_Y 0 GPS1_POS_Z -0.057 # The IMU offset from the same origin, INS_POS1, is in each board file: it belongs to the board and its mount # (moved from this file 2026-10-04). Without it the IMU is the origin and GPS1_POS is off by the IMU's offset. # --- Logging and scripting --- # Raw IMU batch logging for the first flights' vibration spectrum (IMU filters and rev B island). INS_LOG_BAT_MASK 1 SCR_ENABLE 1