Who does what#
Zibo's VNAV is split in two, as in the original:
- The FMS (Zibo's Lua,
B738.a_fms, run by our XLua host, M5c): the vertical path. It computes the climb and descent profile, T/C and T/D, the speed schedule (climb, cruise, descent speeds, restrictions), and the flight phase (laminar/B738/FMS/flight_phase: 0 takeoff, 1 climb, 2 cruise, 3 cruise climb, 4 cruise descent, 5 descent, ...). It publishes them as datarefs. - zibomod's AFDS (
ap_vnavand its callees, translated): the VNAV mode. It reads the FMS's datarefs (zibomod'sRead_B738_DR), engages and disengages VNAV, picks VNAV SPD / VNAV PTH / VNAV ALT, the A/T mode (FMC N1, FMC SPD, RETARD, ARM), the speed target and the pitch command for the flight director; and writes some of the FMS's datarefs back (flight phase, descent mode, descent now, climb / descent speed limits, the VNAV ALT mode, the preselected altitude,ap_on).
The translation#
Before build 65 the AFDS translation had ap_vnav, vnav_init and vnav_calc_speed as empty stubs. They are now
translated like the rest of the AFDS (tools/re/afds_functions.txt: ap_vnav, vnav_init, vnav_calc_speed,
climb_perf, rest_wpt_spd_kts, rest_wpt_spd_mach; tools/re/gen_afds.sh; docs/AFDS.md "How it is ported"):
199 functions, 1118 globals.
Verification#
afds_vnav_replay_matches_oracle (wasm/test/test_afds_replay.cpp): the oracle (xphost running zibomod and Zibo's
Lua) flies xphost/test/sci/afds_vnav_trace.json (tools/re/afds_vnav_scenario.py):
- power on the ground, airborne at 10000 ft / 250 kt near KSEA (the LNAV scenario's start);
- PERF INIT before the route: ZFW 120.0, reserves 4.0, cost index 30, cruise altitude FL130 (with no cruise altitude when the route is executed, Zibo's FMS treats the aircraft as above it: flight phase 5, descent);
- the route KSEA - LNVAA .. LNVAE - KPDX, ACTIVATE, EXEC, DIRECT TO LNVAA;
- MCP altitude 13000, both engines burning fuel (the oracle has no engine model: zibomod's A/T drops out after 1 s without), A/T ARM, FD, LNAV, VNAV, CMD A;
- a scripted climb at 2000 ft/min to 13000 ft, then 30 s level.
What zibomod does there (frame by frame in the oracle): VNAV engages in VNAV SPD with the A/T in FMC N1
(FMS phase 1, climb); approaching 13000 ft, VNAV PTH with FMC SPD, the FMS's phase becomes 2 (cruise), the
speed target 295 kt. The test feeds the same inputs to the translation and compares all 734 AFDS state values
after every frame: identical on every frame (3760 frames; golden file
wasm/test/data/afds_vnav_trace_oracle.txt.gz). The first AFDS scenario (afds_trace) is identical too, with
the VNAV code now in it.
Found on the way, fixed for every mode:
- LNAV heading in whole degrees.
ap_lnavcopies the course to fly through a local that Ghidra typed "unknown 4 bytes"; the translator made it anint, so the LNAV heading (ap_lnav_hdg,cmd_heading_dial) was cut to whole degrees, and the flight director's roll command differed.tools/re/port_cpp.py: such a local that only holds floats is a float. - The PFD's GPS / ANP deviation scales stood still. zibomod calls
lnav_vnav_horiz_vertevery frame after the flight director and the servo; the systems module never called it. Now it does (Afds::frameLate), and the replays check it. - The leg before the first leg. On the first leg,
fmc_calc'scalc_regionasksget_act_crsfor leg -1. zibomod then reads the element before eachlegs_*array: the last (256th) element of the array before it in memory, a leg no route has (0, an empty string). The translation read the C++ member before it (in WASM, a string made from floats).tools/re/lnav_fixups.py: -1 reads the array's own unused 256th element.
In the systems module#
- FMS -> AFDS (
afdsFmsReads,kAfdsReadsgenerated bytools/re/lnav_sync.py): every frame, the AFDS's inputs that zibomod'sRead_B738_DRtakes from the FMS's datarefs, now including the VNAV's (flight phase, descent mode, descent now, VREF 40, VNAV minimum altitude, descent speeds, ...): 161 values. - AFDS -> FMS (
afdsFmsWrites,kAfdsWrites): new in build 65. zibomod writes 21 of the FMS's datarefs (XPLMSetData*); X-Plane puts the value into the Lua dataref and calls its Lua handler at once. The systems module does the same with the AFDS's writes, in order: those ofGlareshieldbefore the navigation runs, the rest after the flight director. Before, the FMS never saw them (VNAV's flight phase changes,ap_on, ...). vnav_climbs_and_levels_in_the_systems(wasm/test/test_fms.cpp): the whole systems module, the same steps as the oracle's scenario (but starting in the air: Zibo's FMS calls the climb to the cruise altitude CRZ CLB, phase 3): VNAV SPD with FMC N1, the FMS's phase in the AFDS,ap_onin the FMS; at 13000 ft VNAV PTH with FMC SPD, the FMS in cruise.
The descent (build 66)#
afds_vnav_descent_replay_matches_oracle: xphost/test/sci/afds_vnav_descent_trace.json
(tools/re/afds_vnav_descent_scenario.py): PERF INIT FL240, the route, level at FL240, A/T, FD, LNAV, VNAV, CMD A,
the MCP altitude 16000. The aircraft flies the route (scripted) in two traced windows, the cruise between them
untraced and 8 times faster:
- from 3 nm before the T/D to 4 nm after it, at its real speed, a straight line each second (the host's new
ramp): cruise in VNAV PTH, the FMS's descent phase (5) about 2 nm before the T/D, the descent on Zibo's path (318 ft/nm: the FMS's own path error stays within 90 ft on it); - from 2.5 nm before the path reaches 16000 ft: down the path, VNAV ALT at the MCP altitude, 30 s level, the MCP altitude lowered to 13000, 15 s more.
The replay starts each window again from the oracle's state at its first frame (trace_restart: the host logs
it): identical on every frame (4920 frames, 734 values).
Limits of the scenario:
- The route has no arrival (it ends at LNVAE, about 12800 ft on the path): the 240 kt below 10000 ft, altitude restrictions and DES NOW are not in it.
- The oracle has no engine model: after the MCP altitude is lowered, zibomod's idle model puts N1 at 0 when the FMS's N1 mode becomes 12, and its A/T drops out. Window 2 ends before that.
- Right after the T/D, Zibo's FMS recomputes the path for about 80 s (its path error reads 9999,
descent_now2). That is the FMS's behaviour; the replay has it as it is.
In the systems module: vnav_descends_in_the_systems (wasm/test/test_fms.cpp): the same steps, the aircraft on
Zibo's path by the FMS's own path error: the T/D at 43.3 nm (the oracle: 43.5), VNAV PTH on 99 % of the descent,
the path error within 5 ft, the FMS's VNAV vertical speed about -1500 ft/min, VNAV ALT at 16000 ft.
Found by it: the FMS never saw the IRSs in NAV (docs/FMS_INPUTS.md item 5). Zibo's IRS script writes the IRS modes into datarefs the FMS creates; we run our own IRS, so they stayed 0. The FMS then stopped the route's distances (the distance to the next waypoint stayed 0, the T/D moved only at a leg change) and asked for LNAV / VNAV disconnect. This was the case since build 60 (LNAV).
The arrival and the approach (build 67)#
The world (wasm/test/approach_world.h, written with our own navdata and CIFP writers, as the navdata provider
writes MSFS's procedures): KPDX's STAR LNVE1 (LNVAE - PDXA1 at or below 12000 - PDXA2 at or above 6000, 230 kt) and
RNAV approach R10R (transition PDXA2; PDXIF at or above 3000, PDXFF the FAF at 1700, RW10R at 80 ft on a -3.00
degree path; missed approach to 3000, direct PDXMA). The CDU shows it as Zibo does: KPDX ARRIVALS, LNVE1 / RNV 10R /
transition PDXA2, and on the LEGS page PDXA1 /12000B, PDXA2 230/6000A, PDXIF /3000A, PDXFF /1700,
RW10R /80 GP3.00.
afds_vnav_approach_replay_matches_oracle: xphost/test/sci/afds_vnav_approach_trace.json
(tools/re/afds_vnav_approach_scenario.py): PERF INIT FL240, the route, DEP/ARR (STAR, approach, transition),
cruise with VNAV, the MCP altitude 0; the flight to the runway. The oracle host flies the aircraft's altitude on the
FMS's own vertical path (follow: by its path error, an autopilot that keeps it there), so the restrictions, level
segments and the final come from Zibo itself. Traced from 2 nm before PDXA1 to 1.3 nm before the threshold (about
500 ft): VNAV PTH with the A/T in ARM, 240 kt at 10000 ft, the approach phase, VNAV's level-offs at PDXA2 (6000),
PDXIF (3000) and PDXFF (1700) with FMC SPD and the speeds stepping down, then the 3 degree final. Identical on
every frame (13020 frames, 734 values).
How the oracle's world is made: LU737_APPR_WORLD=<dir> with lnav_oracle_world, then Zibo's caches
(B738X_*.dat) copied into <dir>/xp (zibomod reads them from X-Plane's folder there). The oracle has no engine
model: after about 7 minutes zibomod's own engine N1 / N2 (eng_N1_idx, eng_N2_idx) go to 0 (its engine-out
logic follows, and its A/T drops out; the cause is in zibomod's flight start code, not found). The host's
zibo_hold keeps them at idle before each of zibomod's frames, as X-Plane's engines would.
vnav_approach_in_the_systems (wasm/test/test_fms.cpp): the whole systems module, the same CDU steps and the same
flight (8 times the ground speed), the altitude on the FMS's path by its path error: the MCP speed never above 245
kt below 10000 ft, at or above 6000 ft to PDXA2, the approach phase, VNAV PTH on 96 % of the final with the path
error within 25 ft, down to about 500 ft, no FMS errors.
Found by it: VNAV dropped out after the FAF. zibomod reads a few of the FMS's datarefs into its globals at the
start of each frame (MainFlightLoopCallback, before its navigation block); one global serves both the navigation
and the AFDS. We only filled the navigation's copy: the AFDS's rnav_idx_last (the route's last RNAV approach leg)
stayed 0, and ap_vnav drops VNAV (A/T to SPEED) on an approach without it. Now the AFDS's copies are filled too
(kAfdsPreReads, 10 globals, tools/re/lnav_sync.py).
The takeoff and the climb (build 69)#
The world gets KSEA's SID LNVS1 (approach_world.h addKseaSid): runway 16L, the runway heading to 1000 ft, direct
LNVSA (at or above 5000, at or below 210 kt), LNVAA.
afds_vnav_takeoff_replay_matches_oracle: xphost/test/sci/afds_vnav_takeoff_trace.json
(tools/re/afds_vnav_takeoff_scenario.py): on the ground at KSEA, PERF INIT FL150, RTE KSEA / KPDX / RW16L and the
route, DEP/ARR: runway 16L, LNVS1, EXEC; TAKEOFF REF: flaps 5, the QRH V-speeds selected (V2 143); the MCP altitude
15000, the MCP speed to V2 (Zibo's Lua knob sim/autopilot/airspeed_up); FD both sides, A/T arm, LNAV and VNAV
armed; flaps 5; TO/GA. The roll (3.5 kt/s), the rotation at VR, the climb at 2500 ft/min are scripted; once
airborne the host's follow_spd moves the airspeed towards the AFDS's speed (the MCP speed dial) at 2 kt/s. Gear up,
flaps 1 at 1500 ft, up at 2500 ft. Two traced windows:
- TO/GA to 3000 ft: TO/GA (pitch and roll), THR HLD at 84 kt, LNAV at 50 ft, VNAV SPD at 400 ft with V2 + 20 (163), the SID's 210 kt, at the thrust reduction (1500 ft) the FMS's climb phase and FMC N1;
- from 9000 ft: SPD INTV (the MCP speed window opens on the FMS's speed), 10 kt dialled down, SPD INTV again (back to the FMS's speed); the MCP altitude to 17000 (above the cruise altitude) and ALT INTV: the FMS's cruise altitude becomes 17000.
Identical on every frame (5659 frames, 732 values). Below 10000 ft Zibo's climb speed is its flaps-up
manoeuvre speed limit (fmc_climb_speed_l, 230 at this weight in the oracle), not 250.
vnav_takeoff_in_the_systems (wasm/test/test_fms.cpp): the same steps through the cockpit's events (CDU keys, MCP
knobs and buttons, ALT INTV): TO/GA, THR HLD on the roll, LNAV at 51 ft, VNAV SPD at 401 ft with V2 + 20, the
climb phase at 1503 ft with FMC N1, 210 kt to LNVSA; SPD INTV, ALT INTV.
Found by it: the MCP's ALT INTV button did nothing. It is a command of Zibo's FMS (laminar/B738/autopilot/ alt_interv), not of zibomod; the cockpit's ALT_INTERVENTION events now reach it, and its position
(alt_interv_pos) is L:LU737_AUTOPILOT_ALT_INTERV_POS.
DES NOW (build 69)#
afds_vnav_desnow_replay_matches_oracle: xphost/test/sci/afds_vnav_desnow_trace.json
(tools/re/afds_vnav_desnow_scenario.py): the descent scenario's start (FL240, VNAV, the MCP altitude 16000); 10 nm
before the T/D (Zibo offers DES NOW within about 31 nm of it at FL240) the DES page, DES NOW (6R), EXEC. The FMS's
descent phase and descent_now 2; zibomod's VNAV flies -1000 ft/min (cmd_vvi_dial, the host's follow flies it
while descent_now is 2); the FMS's path error reads 9999 until the aircraft reaches the path (about 18000 ft), then
descent_now 3, VNAV PTH down the path, VNAV ALT at 16000. Identical on every frame (13836 frames).
vnav_des_now_in_the_systems: the same through the CDU: -1000 ft/min on 99 % of the DES NOW descent, the path
captured at 18600 ft, VNAV PTH on it, VNAV ALT at 16000.
Not yet#
- Holds, a missed approach (the ILS approach and autoland: docs/AFDS.md, build 70).
- The PFD / ND's VNAV displays (the vertical deviation on the PFD, the ND's VSD) are not checked in the sim yet.
sim/version/xplane_internal_versionis not given to the FMS / AFDS (X-Plane's version: zibomod's ski effect only).