Technical reference

Conversion progress log

docs/PROGRESS_LOG.md

2026-10-29 — session 1 (autonomous, overnight)#

Source: main@095b875 (partial upload: no objects/, liveries/, plugins/).

  1. Analysis. Inventoried the upload, parsed the .acf files of all five variants, airfoils and b738_cockpit.obj; identified missing folders and the EULA terms. Confirmed the .acf CG envelope maps to 3–33 % MAC with the published reference wing (sanity check of the axis/MAC conversion).
  2. Configs. tools/msfs/generate_configs.py writes all 737-800 SimObject configs in the MSFS 2024 modular layout (structure taken from FlyByWire's 2024-native A380X; key semantics from their A32NX). Fuel: 6,875 gal in three tanks ✓. Contact points: tyre bottom at full extension (verified against A32NX numbers).
  3. Models. OBJ8→glTF converter (validated with the Khronos glTF validator, 0 errors). Placeholder exterior from the .acf geometry, rendered in headless Chromium to check axes and surface/gear motion directions (fixed flap/slat/rudder/gear signs after the first render). Cockpit click spots (988 mouse rects) and 11 display surfaces extracted from b738_cockpit.obj.
  4. Systems. TypeScript systems simulation, 8 vitest scenarios passing (cold & dark power-up, ground power, generators + bus transfer, APU start, engine start, fuel pumps + master caution, AFDS CMD/LVL CHG).
  5. Avionics. PFD, ND, upper/lower engine displays, CDU (10 pages), ISFD, EFB; rendered with the browser harness for visual checks.
  6. Package. tools/build_package.py rebuilds everything; generated output committed (msfs2024/, fallback msfs2024-legacy/).

Not tested in MSFS (no simulator in the build environment). Next: user's first build/test per TEST_PLAN.md; upload of objects/ and liveries/.

Assumptions to verify in the first sim session:

  • glTF animation time base 24 fps (ANIM_FPS in the converters).
  • $-prefixed screen texture names bind to panel.cfg texture = $NAME.
  • Asobo stock light effect names (LIGHT_ASOBO_*).
  • SET_AUTOBRAKE_CONTROL value mapping, TURBINE_IGNITION_SWITCH_SET values.
  • MSFS attitude sign conventions used by the PFD (positive bank = left).
  • H-events reach the headless systems gauge (onInteractionEvent).

2026-10-29 — session 2: original models, original instruments#

Owner's direction: complete restoration with the original models, flight deck and instruments; the instruments must be the original ones, not redrawn.

  1. Original models. All 65 objects of the 737-800 (_obja list) converted with their original textures (DDS → PNG, X-Plane normal/metalness → MSFS normal + ORM), animations shared per dataref, visibility, LIT/emissive, click spots. Exterior and interior models replace the placeholder exterior and cockpit shell (removed).
  2. Original panel. Interpreter of X-Plane's generic instruments over the original PANEL_3D and bitmaps; conventions recovered from the panel and X-Plane's render of it; validated against Panel_Preview.png. The self-drawn DU/CDU/ISFD/EFB instruments and the approximated TypeScript systems were removed.
  3. Original logic. xphost hosts the original xlua.xpl (all Zibo/LevelUp Lua) and zibomod.xpl unmodified by implementing the X-Plane plugin API they use. Tested on Linux with the lin_x64 plugins and as the Windows build with the win_x64 plugins under wine (same results).
  4. Gauge and bridge. XPanel gauge on one $LU_PANEL render target; MSFS ↔ X-Plane sim mapping with plugin ownership, AP/AT authority gating, engine-start following and in-flight control-sign learning; cockpit clicks and MSFS key bindings relayed to the original commands.
  5. Package. tools/build_package.py builds the new layout; Windows host binaries in xphost/dist/windows.

Not tested in MSFS. Open items: sound, IRS LED layout, X-Plane EFIS base map, FDE tuning, first in-sim test (TEST_PLAN).

Later the same session#

  • Sound: the original FMOD banks and .snd logic are played by the host through X-Plane 12's FMOD runtime (tested with a logging FMOD stand-in).
  • glTF tangents for normal-mapped materials; validator: 0 errors, 0 warnings.
  • Fixed duplicated display surfaces that covered the panel render target.
  • Renders: docs/exterior_render.png (exterior), docs/cockpit_render.png (flight deck with the live panel on its displays; overlapping alternative DU surfaces are switched by the visibility rules in MSFS), docs/panel_live_render.png (panel from the plugins' live state).

2026-10-30/31 — session 3: first in-sim feedback#

First runs in MSFS 2024 (user's tests); fixed from their reports, checked offline with the Linux host, the original plugins and the gauge's bridge.

  • Black displays. panel.cfg's texture = names the material the gauge draws on; the screens' material was PANEL_LU_PANEL (only its placeholder image was $LU_PANEL). Now material and render target are LU_PANEL (no $: in MSFS 2024 it marks a collision material), the material has no textures (black base, white emissive), and the preset holds the only panel.cfg (a [MODULAR_MERGE] stub there left the gauge unloaded).

  • Gauge start. /JS/netbingmap.js imported (the ND Bing layer needs its EWeatherRadar/EBingMode globals); a missing Bing layer no longer stops the panel. The host link retries an attempt that never opens (Coherent GT does not always fire onclose), so a host started after MSFS is found.

  • MCP not clickable. The EFB stand drag (drag_xy) stayed active over the rest of 737_cockpit_pdstl_efis.obj: the glareshield, EFIS panels and knobs were 43 "Adjust stand" click areas, one 1.5 m wide over the MCP.

  • Model centre. The acf origin (cfg datum) is at the nose; the models now have the empty CG at their origin and reference_datum_position = 60.22, 0, 2.05 puts the datum back at the nose (all other cfg positions unchanged).

  • Brightness. Screen emissive ×2 via L:LU737_DU_GAIN (live adjustable, remembered by the gauge); the DU knobs still dim in the gauge.

  • FO PFD/ND black on battery is the original logic: with the gauge's bridge against a cold host, battery only gives display_units 0 for the captain DUs and the upper DU, −1 (blank) for the FO DUs and the lower DU, as on the real aircraft. AC (APU generator, GPU via ground services, engine generators) powers them.

  • Host. start = cold / --cold; gear handle down at load (the plugins' lever started at OFF and the gauge sent GEAR_SET 0); commands held from press to release again (they were begun and ended in the same instant, so taps on A/T ARM, trims, test buttons and every hold-type control did nothing; "once" clicks now hold for one frame); EECs on at load. Windows binaries rebuilt (the committed ones predated the 10-30/31 host changes).

  • Dead controls (audit of all click spots against the OBJs and the live plugins): the flap, gear and speedbrake levers were lost to the OBJ parser reading ATTR_manip_keyframe as a manipulator; they now step between their detents. The 188 EFB tablet buttons were cut by a skip list (only the stand drags are skipped now). Two-command switches and knobs hold the command from press to release (spring-loaded switches, test buttons, knob repeat), the wheel steps once. EEC buttons were mapped to a constant. Model, behaviours and gauge tables regenerated: 991 click spots. Known, from the original OBJ: sim4flight_controls/door_open_4, MARKER volume (obs_audio_vol_marker) and HF SENS (rtp_D) are typos of the original and stay dead; throttle/yoke click drags are overridden by the MSFS controls; drag_xy only steps its X axis.

  • Second in-sim run (displays frozen, APU/DU knob/flap lever dead, z-fighting): the gauge's frame steps are now guarded one by one (errors in the Coherent console as LU737 XPanel: <step> failed, link health every 30 s) and only one gauge instance talks to the host (duplicate panels show LU737 DUPLICATE PANEL). Flaps reached MSFS only at the first sync (the plugins write flap_ratio); hardware flap levers/axes now step the plugins' lever. MSFS fuel valves/pumps follow the plugins (all were closed). Display surfaces lifted 0.5 mm off an opaque panel layer 0.1 mm behind them (z-fighting). Checked offline: OUTBD DU BRT → PFD brightness works in the host (0.3 → 0.3). Not solved: the APU. Held at START, the switch reaches START but Zibo (compiled zibomod) never calls X-Plane's sim/electrical/APU_start in the host, so MSFS never starts it; one of its start conditions is not met outside X-Plane. Open: APU start (above), speedbrake axis to the plugins' lever, cockpit seen from far outside, liveries (the LevelUp livery folder is not in the repository), LODs, checklists (clist.txt), EFB fuel/payload → MSFS, X-Plane EFIS base map, IRS LED layout, FDE tuning, sound in the sim.

2026-10-31 — session 4: stable cockpit interface (SCI), P0 + P1#

Redesign of the cockpit interaction layer (docs/COCKPIT_IO_REDESIGN.md, details in docs/SCI_IMPLEMENTATION.md): MSFS uses only stable names (H:LU737_<CONTROL>_<ACTION>, L:LU737_<STATE>); the host maps them onto the plugins (data/sci), with a closed-loop result for every click.

  • Host: SCI core (contract, profile with alias chains / synthesized expressions / shadow state / drive modes / gates / effects), synthesized 737NG electrical model, protocol 0x10–0x12 / 0x90–0x92, --sci-test scripts. X-Plane's bus volts/avionics now follow the synthesized buses (cold and dark was 24.5 V and avionics on: the old code read switch positions). The battery special case in the bridge is gone.
  • Gauge: relays SCI events by name, writes the states to L-vars, draws a display only while its device is powered, powers everything down on link loss.
  • Model: the 17 electrical panel/APU controls, their animations and 16 electrical lights use SCI names only (generator --sci, data/sci/model_map.json).
  • APU found: Zibo calls X-Plane's APU_start (passed to MSFS) only when START follows 5–6 s at ON, and not once the MSFS APU N1 is ~20 % already. The SCI gate holds a quicker START back; the gauge no longer starts the MSFS APU at START itself.
  • Measured: battery guard closes onto ON; BUS TRANSFER is not modelled by Zibo 2.S1.50 (cross_tie forced to 1: shadow state); power_on_buses does not exist in 2.S1.50 (nothing used it); the GPU needs on-ground.
  • Tests: elec_cold_start.json 54 checks, elec_degraded.json (plugins' electrical and display datarefs hidden) 53 checks, gauge e2e 11 checks, vitest 21 — all pass. Not yet run in MSFS; the Windows host is rebuilt but only the Linux build was run.

Session 4 (continued): every control on SCI#

  • tools/sci/generate_cockpit_sci.py puts the remaining 974 click spots and 924 cockpit datarefs of the model on the stable interface (generic controls with the original click semantics, raw-valued states); the host runs them (mode: actions, queued per control) and checks each click against a watch list it measured itself (discover.json → 91_watch.json).
  • The legacy path is gone: no model_io.json, the model has no plugin names, the gauge only relays SCI events and states. The generator fails the build on any plugin name or unknown L-var in the model.
  • Fixed through the profile: the OBJ typos sim4flight_controls/door_open_4 and obs_audio_vol_marker (dead before), renamed standby_altimeter_baro and placard/cat3; HF SENS 3 is shadow state. The exterior landing lights read the SCI switch states.
  • all_controls.json: every action of every generic control (see SCI_IMPLEMENTATION.md §8 for the result counts); e2e: quick wheel steps are not lost.
  • Exterior engine fans turn again: L:LU737_fan_angle_1/2 had no writer since the first build; the gauge now integrates them from the MSFS N1 on the sim clock (capped shown rate, still when paused).

2026-10-31 — rewrite, milestone 1 (toolchain)#

Owner's decisions (REWRITE_PLAN.md §1): rewrite all logic (zibomod + Lua) as an MSFS WASM module and FBW-style HTML display units, Zibo AFDS and the CFM56 tables reproduced exactly, MSFS navigation data, no sound in phase 1, host removed from the product, builds in GitHub Actions, personal use only.

  • SDK and module. MSFS 2024 SDK headers, wasi-sysroot and SimConnect.h in third_party/msfs2024-sdk (SDK clang 15.0.1). wasm/: portable core behind SimIO, MSFS gauge adapter (LU737_Systems_gauge_callback), heartbeat L:vars, native tests (ASan/UBSan). Built with Ubuntu clang 18, -mcpu=mvp; check_wasm.py checks exports (gauge callback, SDK allocator hooks), imports (only env.fs* and WASI) and the feature set. panel.cfg VCockpit01 now loads the module (NO_TEXTURE).
  • Host removed from the product. Deleted LU737Host.exe and the Windows build, the WebSocket server, the launcher, the FMOD sound player, the XPanel gauge (interpreter, host link and SCI relay, MSFS bridge, fan integration) and its package step, the html_ui asset group (empty until the display units exist), xphost.zip. The stable cockpit interface stays: the model uses only SCI names and data/sci is the contract the rewrite implements; data/cockpit_io.json moved out of the gauge.
  • Oracle. xphost builds for Linux only and runs the original plugins headless with SCI: --selftest (5,088 plugin datarefs, 1,183 commands, ~2–3 ms per frame), --sci-test (elec_cold_start 54/54, elec_degraded 53/53, all_controls 2/2 pass) and the new --replay in.bin --out out.bin over the frame protocol (xphost/test/replay_smoke.py: the battery switch commands move the plugins' battery_pos 0 → 1).
  • Decompiler. Ghidra release downloads are blocked by the environment's network policy; tools/re/setup_decompiler.sh builds Ghidra's native decompiler (decomp_dbg with libbfd, sleigh x86-64) from the git sources. tools/re/decompile.py decompiles a zibomod function of the symbol-bearing Linux build with its constants resolved (checked on cfm56_7b_cn2_to_cn1: the N2/N1 tables read as numbers).
  • CI. build-wasm.yml (tests, build, checks, artifact, comparison with the committed module), oracle.yml (build, selftest, SCI scripts, replay smoke test).

Next: M1 in-sim check by the owner; M2 specifications (electrics, APU, pneumatics, engine start, CFM56 tables).

2026-11-01 — milestone 1 confirmed in MSFS 2024#

  • First loads failed with WASM: Error getting memory. The owner supplied the SDK's MSBuild files and its toolchain (Asobo's LLVM 15 fork, third_party/msfs2024-sdk/llvm); the module is now built with it and the SDK's exact Release options, including --stack-guard-page (data, guard page, stack), on a windows-latest CI job (identical to the committed module) or under Wine.
  • In the sim (owner, Behaviors → Variables): LU737_WASM_ALIVE 1, LU737_WASM_BUILD 3, LU737_WASM_FRAME 4352 at LU737_WASM_TIME 107.6 s. M1 done.
  • M2: CFM56-7B tables (wasm/src/core/engine/cfm56_7b.*) bit-exact against the original zibomod functions called directly (wasm/test/zibo_direct.*, ~60 M calls); spec docs/specs/cfm56_7b_tables.md. Electrical and engine start/dynamics specs done; APU and pneumatics specs in progress.

2026-11-01 — milestone 2 done (specifications and CFM56 tables)#

Specifications of Zibo 2.S1.50 in docs/specs/ (own words, constants and timings; no decompiled code), each with oracle scripts that pass and run in oracle.yml:

Spec Oracle scripts (checks)
electrics.md elec_spec_battery (22), elec_spec_sources (25)
engine_start_and_dynamics.md (+ per-frame call order) start_spec_eng1_ground (12)
apu.md apu_spec_start (27), apu_spec_delay_fault (12)
pneumatics.md pneu_spec_ground_air (21), pneu_spec_apu_bleed (17)
cfm56_7b_tables.md C++ implementation bit-exact vs. the original (~60 M calls)

Findings that shape M3: Zibo computes almost everything itself (buses, N1/N2, thrust, fuel flow, bleed/duct pressure, pressurization, APU controller); X-Plane supplies only engine light-off, oil temperature/ quantity, the APU spool and battery/generator electrics — the rewrite models these in the core (APU spool from the .acf; MSFS APU kept off). Open questions are listed per spec (§9).

Next: M3 — implement electrics, APU, pneumatics and engine start in wasm/src/core from these specs, with differential tests against the oracle, and route the cockpit's SCI events to the module.

2026-11-01 — M3a confirmed in MSFS; cockpit controls MSFS-style#

  • Owner: the electrical panel (build 4: battery, STBY PWR, ground power, meters, BUS TRANSFER) works in MSFS 2024 as described. M3a done.
  • All 991 click spots now use MSFS-style interactions (4d67342, docs/COCKPIT_CONTROLS.md): knobs wheel/drag/click, switches drag and click with spring positions held until mouse up, levers drag through detents, buttons and guards plain clicks. The 2123 SCI events are unchanged. To be tested in the sim.

2026-11-01 — M3b (APU, build 5) confirmed in MSFS#

  • Owner: the APU (build 5, 80ef492) works in MSFS 2024 as described in TEST_PLAN M3b (start, generator, bleed, shutdown/cool-down). M3b done. Next: M3c, pneumatics and engine start.

2026-11-01 — MSFS-style controls confirmed#

  • Owner: the cockpit controls with position-dependent cursors (78161da: knobs TurnLeft/TurnRight, switch arrows, lever grab; wheel/drag/click) work in MSFS 2024. Next: M4 display units, starting with the per-display materials and the upper EICAS.

2026-11-03 — EICAS: back to 752e471, Zibo's PANEL_3D EICAS redone (build 9)#

  • The canvas bitmap-interpreter EICAS and the dataref mirror (builds 7–8, f52b962..382b1fc) never showed reliably in MSFS. Owner's decision: restore 752e471 (1d51041) and redo the EICAS against the original PANEL_3D data, with Zibo's bitmaps.
  • New EICAS (docs/EICAS.md): PANEL_3D EICAS group extracted to panel.json; drawn as HTML/SVG elements (no canvas) in the BaseInstrument shell of 752e471; every one of the 103 datarefs has a documented source. Exact sources checked against Zibo in nine oracle snapshots; drawing checked pixel-for-pixel against a reference PANEL_3D interpreter.
  • Findings from the oracle: req_pie = commanded N1 − previous frame's N1; fuel quantities rounded up to 10 kg / 10 lb; LED fuel readouts need the low digits (the old interpreter clamped to 3999/6999).
  • Systems module build 9: Zibo's EICAS oil press/temp energise, start EGT redline and hot-start flags published per engine.
  • Owner test: TEST_PLAN M4a steps 2–3 (build 9); screenshot request for two X-Plane behaviours (docs/EICAS.md).

2026-11-03 — M4b (upper EICAS, build 9) confirmed in MSFS#

  • Owner: the PANEL_3D upper EICAS (524a73e) works in MSFS 2024. M4b done. Still open: the X-Plane screenshot check of the two assumed X-Plane behaviours (docs/EICAS.md). Next: rest of M4 (lower DU, PFD, ND, CDU, ISFD).

2026-11-03 — M4c: lower DU and MFD ENG / SYS (build 10)#

  • Lower DU from Zibo's PANEL_3D Lower DU group (ENG and SYS pages) with the EICAS method; the extractor (tools/msfs/build_du.py), drawing (panel3d, now with gen_pointer) and data sources (avionics/src/du) are shared by both units.
  • Systems module build 10: Zibo's page logic read from zibomod (CMD_mfd_eng/_sys, B738_eng_out, EICAS_warning, timers) — MFD buttons, forced ENG page without AC transfer bus 2, auto display, LevelUp page transition, compact-format warning, "was red" latches; checked against oracle traces (page sequence, transition timing, 10 s warning flash). The upper EICAS's compact format, warning box, ENG FAIL and was-red boxes are now driven too.
  • Pending on the lower DU: hydraulics, brake temperatures (their own subsystems); flight-control surface directions to be checked in the sim. Owner test: TEST_PLAN M4c.

2026-11-03 — DU page-build option#

  • Owner asked about the brief partial ENG page on MFD ENG: Zibo/LevelUp's page build (lu_init, three background stages over 1.2 s), as in X-Plane. Added L:LU737_DU_PAGE_BUILD_OFF (1 = full page at once; default 0 keeps Zibo's effect). Display only, systems module unchanged (build 10).

2026-11-03 — throttle jumping to idle (build 11)#

  • Owner: DUs fine (M4c and the page-build option confirmed). Old bug: after the engine start the throttle jumps back to idle now and then. The systems module writes the MSFS throttle only for cockpit-lever clicks; the likely cause is the engine coupling re-sending the MSFS start every 5 s when MSFS's combustion flag reads 0 for a running engine. Build 11: no re-start while MSFS N1 > 15 %, throttle put back after a real re-send, diagnostic counters (wasm/README.md, TEST_PLAN "Throttle jumps to idle").

2026-11-03 — EGT jump to ~1000 C during a start (build 12)#

  • Owner screenshots: EGT ~711 C at N1 10 % during a start, then a sudden 990 C. Reproduced natively (wet start, thrust lever slightly open). Original Zibo in the oracle does not jump: the EEC hot-start latch cuts the ignition at ~711 C and the start aborts. The rewrite evaluated the latch per frame (ignition back next frame, start EGT target re-based on the hot EGT → ~1100 C) and cleared hot_start_warn too early. Fixed as zibomod; the spec's §4.11.4 misreading corrected; old test expectation (EGT > 725 C) replaced by the oracle's values.

2026-11-03 — option: no EEC hot-start abort (build 13)#

  • Owner showed another simulator where a hot start runs into the red. Zibo (oracle) aborts at ~711 C. Added L:LU737_EEC_HOTSTART_OFF (default 0 = Zibo; 1 = no abort: Zibo's start EGT model then peaks near 980 C on the worst wet start, no jumps, the engine still reaches idle).

2026-11-03 — throttle guard reverted (build 14)#

  • Owner: the throttle still jumps back to idle with build 11's guard. Reverted (git revert b3ed459), the issue stays open and is to be fixed later. Hot-start fix (build 12) and the EEC option (build 13) kept. Next: M4d (PFD).

2026-11-03 — M4d started: PFD#

  • Renderer ready for the PFD (c09cc0d): group / instrument clips, needle deflect, gen_text; the captain PFD's PANEL_3D (LU_DU_CPT_OUTBD: PFD, RA/DH circles, HUD repeater) draws identically to the reference interpreter (Zibo's powered cold state from the oracle and an in-flight state). Zibo draws nothing on the PFD with OpenGL (oracle GL capture empty): the PFD is all PANEL_3D.
  • PFD datarefs (230): air data / attitude 19, radios (ILS, markers, RA) 31, IRS 4, AFDS / MCP / FMA 77, FMS 23, speed bands / flap speeds 27, EFIS control panel 21, TCAS / GPWS 3, HUD 7, other 17.
  • Owner's decisions: rewrite Zibo's IRS (alignment, lights, PFD/ND flags) first; M4d does what can be exact now (air data, IRS, radios, EFIS control panel, speed bands); FMA / FD / V-speeds / path with M5.
  • Zibo's IRS is Lua (B738.LATLON.lua: B738_irs_system, _annun, _electric, _source, pfd_nd, display data; about 2000 lines). Next: spec, systems-module subsystem, oracle differential tests.

2026-11-03 — IRS rewritten (build 15)#

  • wasm/src/core/irs/ ports Zibo's IRS (spec docs/specs/irs.md). Oracle differential trace xphost/test/sci/irs_trace.json (power-up at 22.31 N, both IRS to NAV, 333 s count, keypad position entry, source switch, left quick realign, left OFF): 0 mismatches over 14 445 frames x 21 values, timer edges within ±1 frame. Exactness needed XLua's timer model (float sim time) and Lua's double arithmetic: with float timers the selector delay came one frame late.
  • Found while testing: Zibo's flight start phase lasts 2 s (not 2 frames); during it the engines-running start state is re-applied every frame and the ADIRUs are powered (irs_enable starts at 1).
  • Owner's option L:LU737_IRS_ALIGN_TIME: 0 real (default), 1 fast 60 s, 2 instant, 3 long 150 s.
  • Temporary: position / ATT heading entered automatically until the IRS display unit and FMC POS INIT are rewritten (spec §6). Oracle host: new SCI step set_str (writable string datarefs).
  • Next: PFD data sources (air data, attitude, radios, EFIS control panel, speed bands, IRS flags).
  • Owner tested build 15 in MSFS: IRS works (alignment, lights, options).

2026-11-03 — PFD phase 1#

  • Captain PFD instrument (lu737-pfd, VCockpit LU_DU_CPT_OUTBD), whole PANEL_3D display (230 datarefs). Sources: air data / attitude / radios from MSFS, IRS flags from the systems module, Zibo's display formulas (drums, heading bug, track line, MCP bugs, RA steps and white box from zibomod via the decompiler and oracle sweeps); the rest pending at Zibo's ground values. Oracle trace pfd_trace.json; vitest: every exact source equals Zibo in three states (ground, cruise, approach).
  • Next: speed bands (B738.calc.lua), EFIS control panel, ILS source logic, FO PFD.

2026-11-03 — PFD speed bands (build 16)#

  • wasm/src/core/speeds/: port of B738_calc_min_max_spd (737-800 path), incl. zibomod's TAS / speed-of-sound filters. Oracle trace speeds_trace.json: 0 mismatches over 2550 frames. PFD reads L:LU737_PFD_*.
  • Open: FMS flight phase (Zibo's FMS sets approach phase 7 in descent even without a route) and approach flaps are 0 until M5; Zibo's AOA filter stands in by MSFS alpha.

2026-11-03 — EFIS control panel (build 17)#

  • wasm/src/core/efis/: both EFIS control panels' PFD part (MINS, BARO, IN/HPA, STD, MTRS, FPV, minimums alert, baro boxes); oracle trace efis_trace.json: 0 mismatches, 0 frame shifts. Found: Zibo resets the minimums every frame while DC standby and DC bus 2 are unpowered. Captain's setting → MSFS altimeter 1.

2026-11-03 — NAV radios and ILS on the PFD (build 18)#

  • wasm/src/core/nav/: classic NAV radios (Zibo's default, MMR hidden), MSFS receivers tuned from the panel, MMR mode, station identification with the 6.5 s decoding, VHF NAV source. PFD: ILS pointers, pfd_ils text lines (string datarefs now supported by DuSource). Identification uses MSFS reception (no X-Plane nav DB).

2026-11-03 — first officer PFD#

  • lu737-pfd-fo on LU_DU_FO_OUTBD: FO PANEL_3D, FO EFIS panel / IRS 2 / NAV 2 sources; oracle checks in three states pass. Next: the owner's two engine issues (no thrust in MSFS, throttle snapping back to idle).

2026-11-03 — engine attempts reverted; M4e ND (build 19)#

  • Owner: stop the engine work and go back to the last milestone: builds 19–22 reverted (d8794bc); the thrust / throttle issue stays open. Then "continue all M4": NDs for both sides (lu737-nd, lu737-nd-fo), the EFIS panel's ND part in the systems module (mode, range, CTR, VOR / ADF, map buttons), Zibo's ND heading / track / bug formulas, wind and pointers from MSFS; map content pending until the FMS (Zibo draws the route with OpenGL). Oracle checks pass in three states for both NDs.

2026-11-04 — M4f ISFD (build 20)#

  • ISFD instrument (lu737-isfd); systems module standby/ from zibomod's standby altimeter commands and altitude formula (constants read from the binary); the renderer gains X-Plane's rolling digits (gen_rolling). M4 displays left: the CDU, which comes with the FMS (M5).

2026-11-04 — M5a MCP dials (build 21)#

  • Systems module mcp/: speed (IAS / Mach), heading, altitude, both course knobs, bank angle, change-over; PFD and ND bugs and course read them. Mode buttons wait for the AFDS (zibomod: the buttons only set requests its mode logic processes each frame).

2026-11-04 — owner's decisions for M5; MSFS fuel feed (build 22)#

  • Owner: fix the thrust before the AFDS; AFDS = Zibo's logic with our own control laws flying MSFS's controls; FMS = port of Zibo's FMS on MSFS navigation data, in stages.
  • Build 22: MSFS's tank pumps / circuits kept on while Zibo's engine runs (fuel pressure was 0), engines.cfg without ignition / condition gates, engine diagnostics; K: table gets the altimeter / NAV events builds 17-18 missed.

2026-11-04 — MSFS engines burn (build 23); engines.cfg from Zibo's engine model#

  • Build 23 (SDK review): with DisableMixtureControls the condition lever is MSFS's fuel cutoff; a Zibo start now also sets condition lever k to low idle and ignition k on. Owner: MSFS's engines burn, N1 99.5 % at TO/GA, but net thrust ~2 400 lbf (gross 24 000, ram drag −21 500).
  • SDK engine notes read in full: airflow = corrected_airflow_table × inlet_area × δ_total / √θ_total, ram drag = TAS × airflow / g, gross thrust = corrected gross × δ_total (δ_total = δ (1 + 0.2 M²)^3.5). The airflow table held the whole engine's airflow (×20), and the thrust table ignored δ_total (cruise thrust ~1.8× too high).
  • engines.cfg's thrust, N1 schedule and fuel flow are now generated from Zibo's engine (docs/FDE_TUNING.md "Engine schedule from Zibo"): wasm/tools/engine_sweep.sh runs the rewrite's Zibo engine to steady state over lever × altitude × Mach (data/msfs/zibo_engine_sweep.json); tools/msfs/engine_schedule.py fits the tables. ISA match: N1 rms 0.9 % for levers ≥ 0.3, net thrust at Zibo's N1 within 1.5 %; idle within a few % (MSFS cannot tell ground from flight idle). The two commanded-N1 tables, which the SDK says must never be equal (the engine may drop to idle), crossed near lever 0.9 before; they are now ≥ 0.3 % apart everywhere. fde_check.py uses MSFS's net-thrust formula: cruise FL350 M.785 N1 87 %, approach flaps 30 N1 61 %.
  • WASM unchanged (build 23). Owner test: TEST_PLAN "Build 23 + engines.cfg from Zibo".

2026-11-04 — M5b-1 AFDS logic translated and verified#

  • Engine thrust: owner's own fix (44ad5ba, engines.cfg from Zibo's engine model) — "engine working now".
  • AFDS: zibomod's Glareshield / ap_speed_accel / flight_director_new and callees (189 functions) translated by tools/re/port_cpp.py from Ghidra listings (tools/re/decompile.py now with prototypes, constant sizes and symbol names) into wasm/src/core/afds/ (docs/AFDS.md).
  • Oracle: traces zibomod globals from memory (zibo:), samples inside zibomod's frame by call-site hooks (at:), long log lines no longer cut. afds_replay_matches_oracle: 730 state values identical on every frame through power-up, FD, A/T, HDG SEL, ALT HLD, SPEED, CMD A, V/S, LVL CHG, N1, VOR/LOC, CWS, CMD B, disengage.
  • Next: integration in Systems (inputs, events, L:vars), FD bars, our servo + A/T on MSFS controls.

2026-11-04 — M5b-1 AFDS in the systems module (build 24)#

  • systems_afds.cpp: AFDS inputs from MSFS and the modules, MainFlightLoopCallback's air data / start-up parts, MCP button and V/S wheel events, 168 outputs as L:vars (cockpit lights, PFD FMA / FD: 57 PFD sources now live). Hydraulics approximated (engine running) until rewritten. The autopilot does not fly yet (servo: M5b-3).
  • Found on the way: the FO PFD read element 0 of array datarefs (spd_ind_mode[1] etc.); fixed.

2026-11-04 — flight model research; CG frame fix#

  • Owner: the EFB weight & balance page shows the CG far aft. Research (reports/MSFS 2024 737 flight model CG.md, four research notes): MSFS 2024 measures aero_center_lift from the model origin but other positions from the datum (stations from the model centre in the W&B tutorial); wing_sweep is the leading-edge sweep; the EFB divides by wing_cg_refchord; passenger stations are capped at 220 lb; flap stage fields are drag then lift.
  • Generator: every position from the model origin (empty CG) with the datum at 0, 0, 0; aero_center_lift −0.51 (was −60.73 = 60 ft aft of the CG as MSFS reads it); wing_sweep 27.59; wing_cg_refchord 13.04; passenger zones type 0; CG limits 6–32 %; flap tables with the SDK column order and deflection compensation (ΔCLmax per detent from VREF ratios); dihedral 6°; empty inertia 2.6 / 1.0 / 3.3 M slug-ft²; SDK stall ratios. Expected EFB: 21.1 % empty, 22.1 % loaded. Next in the sim: htail_incidence for zero cruise trim, trim limits, drag polar.

2026-11-04 — M5b-3 servo and autothrottle (build 25)#

  • A/T: zibomod's thrust lever logic with MSFS's throttle as X-Plane's throttle ratio and the pilot's last lever as the joystick axis; levers moved by THROTTLE_SET; N1 limits via the engine module's n1-to-throttle-use; AFS speed limit from B738.calc.lua (the oracle default 235 kt was the gear-down limit: A/T reduced thrust before).
  • Servo (afds/servo): our pitch / roll control laws to the FD, trim offload, bumpless; sign L:vars for the field test. Build 24 also showed the levers at idle (zibomod's lever without its throttle input): fixed.

2026-11-04 — panel windows (build 26)#

  • Panel instrument on LU_PANEL: Zibo's PANEL_3D groups for the MCP, radio, clock, MMR, IRS display, electrical meter, pressurisation, slat and oxygen windows (docs/PANEL.md). MCP values from the MCP / AFDS L:vars (AFDS now also publishes the V/S dial, digits A / 8, show IAS), NAV from nav/, COM / ADF / squawk / time / position from MSFS, the meter from the electrics (amps, frequency, warm-up added). Panel lighting knobs at Zibo's defaults.

2026-11-04 — build 27: AP roll direction, ISFD scale#

  • Owner: AP engagement rolled the aircraft out of control; ISFD scale wrong. MSFS axis events are opposite to the control SimVars: aileron command negated (elevator was already), yoke / trim read physically, command limits. ISFD drawn at its VCockpit's 768 px (was 1024: cut off).

2026-11-04 — autopilot level-off and MCP speed (build 28)#

  • Owner (build 27): the autopilot does not level off at the MCP altitude (LVL CHG, V/S); the MCP speed sometimes jumps to 100. Both integration gaps (docs/AFDS.md "Level-off and the MCP speed"):
    • zibomod's MainFlightLoopCallback copies the altitude into altitude_pilot for the flight director; the port did not, so the FD held altitude 0 and kept climbing in ALT HOLD. Closed-loop tests now level off within 5–11 ft (V/S and LVL CHG, climb and descent, with the A/T).
    • Above the crossover altitude Zibo switches the MCP to Mach through sim/autopilot/knots_mach_toggle and reads X-Plane's flag back; X-Plane's own part of the command was missing, the MCP kept the Mach number as knots and was clamped to 100 kt. Emulated now; the C/O button goes through zibomod's handler.
    • Checked against the original plugin (oracle script afds_mcp_speed_ground.json): LVL CHG / V/S on the ground setting 100 kt is Zibo's own behaviour.
  • Systems module build 28, built with the SDK toolchain under Wine (reproduces the committed build 27 byte for byte). Owner test: TEST_PLAN "Build 28".

2026-11-05 — cold and dark lighting, yoke and CWS (build 29)#

  • Owner: the MCP and the clocks light before the battery is on. The panel windows' lit levels (instrument_brightness_ratio[n]) were constants from a powered oracle state; now the electrics compute them as zibomod's Systems / Chrono do (bus power, Zibo's lighting knob defaults, daylight): MCP, clocks (F/O clock on DC bus 2), radios, MMR, LE devices, pressurisation, meter. DU states (display_units/*) and the MCP button glow from the same code; NAV status from the AC buses (zibomod Comm); IRS display blank until the ISDU is rewritten. Cold-and-dark comparison with Zibo (battery off / on): every panel window and every 3D light the same.
  • Audit fixes: bank angle 25 deg at start (A1); A/P, A/T, FMC disconnect lights and the test switches (A2); the pilot's joystick axes observed by the gauge as zibomod's yoke (A3, A5: CMD to CWS on yoke input, CMD refused off centre, disconnect below 400 ft); CWS steering with attitude hold (A4); the pilot's throttle axis for the A/T (A6, partly: MSFS still applies the hardware lever's events).
  • Systems module build 29 (SDK toolchain under Wine). Owner test: TEST_PLAN "Build 29".

2026-11-05 — ND track, PFD heading rose (build 30)#

  • Owner: the ND map freezes; the PFD heading sometimes flashes.
    • PFD: Zibo's heading rose is three images selected by heading range (0-120 / 120-240 / 240-360). MSFS's PLANE HEADING DEGREES MAGNETIC (true heading - variation) can leave [0, 360) near north; X-Plane's never does, so no image was drawn for that frame. All DU and AFDS headings are wrapped now.
    • ND: in MAP mode above 45 m/s ground speed it turns with the track, which was MSFS's GPS GROUND MAGNETIC TRACK, a GPS-system variable that is not kept current without an MSFS GPS: the map stopped in flight. The track now comes from the aircraft's velocity (VELOCITY WORLD X / Z) and the heading's variation, in the DUs and the AFDS.
    • Not found before: the DU tests and the render / stress harness feed X-Plane-like inputs (headings in range, a GPS track that follows); MSFS's own value ranges and the GPS variables were never questioned.
  • Systems module build 30 (SDK toolchain under Wine). Owner test: TEST_PLAN "Build 30".

2026-11-05 — pressurization, yokes, surfaces, LODs (build 31)#

  • Pressurization: zibomod's Pressurisation_system, update_press_mode, the mode selector / manual outflow / FLT ALT / LAND ALT / cabin ready commands translated mechanically (tools/re/gen_press.sh, pneu/press_gen.*), the pressurization annunciators of zibomod's Annunciators by hand (pneu/press). Checked against the oracle: 3,076 sampled calls through a whole flight (ground, take-off thrust, climb, ALTN / MAN, FL350, descent, landing), all 102 globals equal (press_replay_matches_oracle; host call hooks press / press_out). Air start: cabin on Zibo's schedule, FLT ALT at the altitude. The digital outflow needle (AFDS) now gets the valve (it sat at -75).
  • Yokes were hidden (LevelUp shows them while X-Plane's hide_yoke is 1; nothing wrote it): shown, click toggles; 15 more cockpit variant switches set to Zibo's load values (HUD off, RAAS, RMI, INTV buttons, tablet parts). Yoke / rudder pedal / toe brake animations from MSFS's controls.
  • Exterior: aileron and elevator animations reversed (X-Plane positive trailing edge down, MSFS the opposite).
  • Exterior LODs: one 243k-vertex LOD was culled by MSFS's LOD limits at a distance (no lower LOD): three lower LODs (30 %, 10 %, 1.5 % of the triangles; gltfpack, every node kept), tools/msfs/build_lods.py.

2026-11-05 — hydraulics and flaps (build 32)#

  • Hydraulics and flaps: zibomod's Hydraulic_system, check_hyd_failure, B738_flaps_system, get_flap_time, B738_vib_flaps, the pump / FLT CONTROL / SPOILER / ALTERNATE FLAPS / flap lever commands translated mechanically (tools/re/gen_hyd.sh, hyd/hyd_gen.*), the hydraulic / LE annunciators by hand (hyd/hyd). Checked against the oracle: 4,527 sampled calls (host hooks hyd / flaps / vib), all globals equal except Zibo's rand() draw (hyd_replay_matches_oracle).
  • MSFS flaps: the systems module sets MSFS's handle (FLAPS_SET) to zibomod's flap target; the hardware lever becomes zibomod's lever. MSFS moves flaps at one rate, so the flight_model.cfg detent angles are Zibo's cumulative times (tools/msfs/generate_configs.py); the exterior flap / slat animations read zibomod's surface angles.
  • Load relief as Zibo's non-SFP -800 (flaps 40 / 30 only). Not yet: yaw damper (in Hydraulic_system, not sent to MSFS), the HYD six-pack light simplified, low-pressure (2.5x slower) flap motion not reflected in MSFS's flaps.

2026-11-05 — gear lever, Mach speed bug, IAS DISAGREE (build 33)#

  • Owner (build 32): LVL CHG climbs held 233–236 kt, the MCP's first digit flashed, the A/T went to idle at 260 kt with 280 set, the PFD speed bug sat at the bottom of the tape with Mach .82, IAS DISAGREE with equal speeds.
  • Gear lever: the cockpit lever did nothing (list B) and the AFDS read MSFS's gear handle; with the gear left down Zibo's AFS limit is 235 kt (B738.calc), so LVL CHG held 236, the MCP flashed its overspeed "8" and the A/T reduced to 236. Now Zibo's three-position lever (systems_gear.cpp): MSFS's gear follows it (UP needs system A, or B through the transfer unit), a G-key / hardware change moves it, Zibo's ground lock and LOCK OVERRIDE, the three green / red lights from zibomod Annunciators. zibomod's gear actuation itself is not translated (MSFS moves the gear).
  • PFD speed bug: B738_mcp_dial reads mcp_speed_dial_kts (zibomod converts Mach); it read the MCP value (0.82).
  • The F/O side of the AFDS read MSFS's airspeed indicator 2 (not the F/O's pitot here): IAS DISAGREE with both PFDs showing indicator 1. Both sides read indicator 1; the speed limits module too (it read index 0).

2026-11-05 — zibomod Systems: gear, speedbrake, brakes, autobrake, stab trim, yaw damper (build 34)#

  • zibomod's Systems (7,000 lines of pseudo-C: gear, brakes / autobrake / RTO, speedbrake lever and auto speedbrake, stab trim, steering, ...), Annun_timers, Systems_start_flight and the gear / speedbrake / brake / trim / yaw damper commands translated mechanically (tools/re/gen_sys.sh, sys/sys_gen.*; tools/re/sys_fixups.py for a stack array Ghidra split, the lost stab_trim_ratio results, the fuel selector types). Checked against the oracle: new host hooks around MainFlightLoopCallback -> Systems, its subsystems (Electric_bus, Hydraulic_system, Pressurisation_system, Gpws .. B738_autoland_fail: their results replayed) and Annunciators -> Annun_timers; sys_trace.json (gear, yaw damper, trim and cutouts, speedbrake, landing with auto speedbrake and autobrake, brakes, RTO, gear lock): 672 samples, every global equal (sys_replay_matches_oracle).
  • MSFS (systems_sys.cpp): gear handle follows zibomod's gear target, spoilers handle zibomod's speedbrake ratio (MSFS's arming off), brakes = zibomod's brake ratios and X-Plane's parking brake ratio zibomod brakes with (autobrake, RTO), MSFS parking brake = the lever, elevator trim when zibomod trims, yaw damper when zibomod's hydraulics engage it (the IRS inputs it needs were missing since build 32). The pilot's G key, spoiler axis / keys, parking brake key and brake pedals go to zibomod. Lights: SPEED BRAKE ARMED, SPEEDBRAKES EXTENDED, AUTO BRAKE DISARM, PARKING BRAKE, STAB OUT OF TRIM, YAW DAMPER (zibomod Annunciators by hand, sys.cpp).
  • A/P stab trim stops with the AUTOPILOT cutout switch.
  • Not yet: reversers (cockpit reverse levers; MSFS's throttle reverse range as before), aileron / rudder trim switches send MSFS's own trim, wheel speeds from ground speed (no skid model).

2026-11-05 — reversers, fuel system (build 35)#

  • Reversers: the cockpit reverse levers (engage = reverse idle, INC / DEC, disengage = stowed; interlock with the forward thrust lever at idle) set MSFS's throttle in its reverse range; the pilot's reverse range (F2, hardware) moves the lever. zibomod's Systems / AFDS get the reverse state (X-Plane prop mode) and MSFS's nozzle deploy ratio; the engine model's N1 follows the reverse lever; the EICAS REV indication reads the deploy ratio.
  • Fuel: zibomod's pump / crossfeed commands (translated with Systems), Systems' fuel feed (pumps running by switch and AC transfer bus, centre auto shutdown, suction feed above an N2 by altitude, crossfeed, spar / engine valves) gives the engines' fuel selectors (no fuel: the engine starves, as in X-Plane); MSFS's tank pumps and crossfeed valve follow zibomod's (the forced main tank pumps of the engine coupling removed); LOW PRESSURE and CROSSFEED VALVE OPEN lights from zibomod Annunciators by hand (sys.cpp, with the attitude-dependent unusable fuel).
  • Not yet: the FUEL six-pack light, fuel imbalance / LOW fuel indications, fuel temperature.

2026-11-05 — review fixes: CI, glideslope, left-click controls, hardware throttle (build 36)#

  • CI: the native test job's sparse checkout lacked xphost/ (the tests link xphost/src/json.cpp and read xphost/test/sci), so every build-wasm run since the SCI tests failed in "Native unit tests" while the SDK build passed. Checked out now.
  • Glideslope (owner: LOC right, GS not seen): the PFD / ND glideslope flag read MSFS's NAV GS FLAG, whose sense is unverified and which probably hid the pointer; it now comes from NAV HAS GLIDE SLOPE. The AFDS took NAV GSI (0 = centred) also without a glideslope, a false G/S capture risk: full scale then. An ILS tuned while only the localizer was received stayed LOC-only (Zibo knows the glideslope from its database): it becomes ILS when the glideslope comes in. Found while testing: zibomod's AFDS reads each side's active MMR mode, deviations-shown flag and deviations from Zibo's FMS Lua (mmr_active_mode), which nobody computed: mmr_act_mode stayed 0 (VOR) and nav_horz_dsp 0, so APP never armed and VOR LOC / APP never captured (the LOC the owner saw was the PFD's pointer). Ported, with the VHF NAV source switch (both on 1 / 2), and Zibo's rule that a deviation of exactly 0 is no glideslope. New closed-loop test: APP arms, LOC captures, G/S stays armed without a glideslope and captures when it comes in. The GS sign is still to be confirmed in the sim (docs/AFDS_AUDIT.md D).
  • Cockpit controls: left button and wheel only (owner: looking around with the right button moved controls). Right clicks removed from every MouseRect (knobs: left / right half, switches: upper / lower half, as before).
  • Hardware throttle against the A/T (owner: they fight): the gauge masks MSFS's throttle events (SimConnect, highest maskable priority: axes, THROTTLEn_SET, FULL / CUT / INCR / DECR) and the systems module places the levers (wasm/src/core/thr/thr_arbiter.h). L:LU737_THR_HW_MODE 0 last mover (default): the A/T's levers ignore axis noise; moving the hardware more than 3 % is a manual override relative to the A/T's lever (no thrust jump); 2 s after the hardware stops the A/T has the levers again; without the A/T the hardware takes the levers once it reaches them (pickup). 1 follow the A/T, 2 follow the hardware. Reverse from the hardware passes at once. L:LU737_THR_HW_STATE_1/2 shows the state. Without SimConnect the old observing path stays.
  • Aileron / rudder trim as zibomod: the cockpit switch / knob call zibomod's handlers of X-Plane's trim commands; the switch and dial animate to their target (zibomod Switches, Anim_value rate 10); the trim moves as X-Plane's own command does for this .acf (aileron -1..1 in 8 s, rudder in 30 s; bus 1 power) on MSFS's AILERON / RUDDER TRIM PCT (read first, so MSFS's trim keys still work). A click or a knob step trims 0.25 s (the cockpit sends press and release together; before, it trimmed nothing). Still open: the trim switch / dial / rudder trim indicator only animate to the right (the converter baked their 2-key animations over 0..1 only: data/xp_to_msfs_map.json "ranges", needs a model conversion run).

Tests: native 132 (new: G/S capture, NAV LOC->ILS, throttle arbiter 8, aileron / rudder trim), avionics 55.

2026-11-05 — options in one place, Zibo option defaults (build 37)#

  • docs/OPTIONS.md rewritten: the options settable now (with L:LU737_THR_HW_MODE), the troubleshooting sign switches, and every option of Zibo's config files (~270, from B738.a_fms.lua) with Zibo's default and its status here (L:var / fixed at Zibo's default / not yet / n/a in MSFS); diagnostics extended.
  • Found while listing them: options the ported zibomod code reads were 0 where Zibo's default is not: Systems parkbrake_remove_chock 1, tire_blown 1, pfd_fmc_flap_spd 1, the hardware flap axis detents (0 … 0.9375), ctr_fuel_pump_auto 1 (build 35's centre pump auto shutdown never ran); AFDS spd_intv_but 1 (the model shows the SPD INTV button), vnav_wakeup_idle 15, yoke_to_center 1 (zibomod's start of flight zeroes them; Zibo's Lua sets them). All set to Zibo's defaults (the oracle's values where it traces them).

2026-11-05 — ALT DISAGREE, panel backlights (build 38)#

  • ALT DISAGREE with both BARO knobs equal (owner): the AFDS compared the captain's altitude with MSFS's INDICATED ALTITUDE:2, whose altimeter (MSFS altimeter 2) nothing sets. The F/O altitude is now the captain's indicated altitude plus what the F/O's setting changes in the altimeter equation (equal settings: equal altitudes), in the AFDS and on the F/O PFD; the AFDS's F/O barometer setting is the F/O knob.
  • Radio / transponder backlights and the LE DEVICES panel (owner: the lit boxes are skewed): Zibo's PANEL_3D gives the radio backlight images CORNERS (corner offsets: bottom-left, top-left, top-right, bottom-right, panel pixels, y up), which the display compiler dropped, so the COM / NAV / ADF backlights sat 80 px left of their windows and bled into the neighbouring LE DEVICES windows. build_display.py keeps them (co), PanelView draws gen_rotary images onto the moved corners (CSS matrix3d). Checked by overlaying the cockpit model's panel-window UVs on a night render: every backlight inside its window. EICAS / lower DU boxes with CORNERS (a few pixels) follow too; gen_LED / gen_pie CORNERS (sub-pixel / N2 pie) not yet.

2026-11-05 — trim switch / dial / indicator animate both ways#

  • The aileron trim switch, the rudder trim dial and the rudder trim indicator only moved to the right: their OBJ animations are 2-key (X-Plane extrapolates below 0) and the converter authored keyframes over 0..1. data/xp_to_msfs_map.json "ranges" gives them -1..1; the cockpit model was reconverted (the converter reproduces the committed interior byte for byte without the change; with it exactly these three animations differ) and the behaviours regenerated. The exterior model was left as committed.

2026-11-05 — M5c: architecture decided, Lua 5.1 in the systems module (build 39)#

  • Decision with the owner: Zibo's original FMS Lua runs inside the systems module on PUC Lua 5.1 under a C XLua host; navdata from MSFS 2024 itself. Measurements and plan: docs/M5C_PLAN.md (survey: docs/M5C_FMS_SURVEY.md).
  • third_party/lua-5.1.5 (MIT): Lua errors without setjmp / exceptions through a Binaryen Asyncify unwind (wasm/tools/asyncify.py after the link: only Lua's functions instrumented, the SDK's StackSizesSection re-indexed and checked by function name, GuardPages kept). Lua is C in every build.
  • Spike (tools/fms_spike): the real a_fms runs in WASM Lua 5.1 + Asyncify: RTE, a 40-waypoint route (9 LEGS pages), PERF INIT; 3.8 ms per frame with the XLua glue in C (0.9 ms without a route).
  • Build 39 runs a Lua self-test at load (L:LU737_LUA_SELFTEST = 1): the check that MSFS runs the Asyncify-processed module. CI: the Lua sources in both jobs' checkout.

Tests: native 134 (new: Lua self-test). Module built with the SDK toolchain (3.5 MB).

2026-11-05 — build 40: the Asyncify step keeps the SDK module's form#

  • CI (Windows): the Asyncify step got MSYS paths (/d/a/..., read as D:\d\a\...) and needs npx.cmd: Windows paths and shutil.which.
  • Owner: build 39's module did not work in the sim. wasm-opt had changed the module's form: its target_features declared mutable-globals / sign-ext / bulk-memory-opt (the SDK toolchain declares only bulk-memory), it added a DataCount section and five asyncify_* exports. wasm/tools/asyncify.py now keeps the SDK build's own target_features, drops DataCount (no memory.init / data.drop in the module) and the exports, and enables only bulk-memory: the section layout equals build 38's.

2026-11-05 — build 41: Lua self-test on request#

  • Owner (build 40): BUILD 40, ALIVE 1, but the cockpit does nothing: the gauge most likely stopped in the Lua self-test at load. The self-test now runs only on request (L:LU737_LUA_SELFTEST_RUN = 1, in a frame), with L:LU737_LUA_SELFTEST_STEP and a console line before each step (checks without Lua errors first, then the error recovery), so a crash shows its step. If the cockpit still does nothing with no Lua running, the Asyncify-processed module itself is the problem.

2026-11-05 — build 42: 1 MB stack, Lua self-test one step per frame#

  • Owner (build 41): the cockpit works until L:LU737_LUA_SELFTEST_RUN = 1; then the gauge stops (frame counter stuck, nothing in the console). The module's stack was the wasm-ld default 64 KB (data, guard page at 0x80000, stack 0x90000–0xA0000): too small for Lua (parser, protected calls); an overflow hits the guard page and stops the gauge silently (offline tests used 1 MB). Linked with -z stack-size=1048576 now (stack 0x90000–0x190000). The self-test runs one step per frame: L:LU737_LUA_SELFTEST_NEXT (about to run) and L:LU737_LUA_SELFTEST_STEP (finished) survive a crash.

2026-11-05 — every display dark since the CDU commit: Object.fromEntries#

  • Owner: builds 39–42, all avionics dark (the systems module running). The CDU sources (shared by every display unit through DuSource) used Object.fromEntries and globalThis, which MSFS's JS engine lacks and esbuild (target es2017) does not polyfill: every display's script stopped at load. Replaced by plain loops / window; avionics/build.mjs now fails on these APIs in a bundle.
  • Lua self-test in build 42: steps 1–4 pass in MSFS (state, libraries, code without errors), the gauge stops at step 5, the first Lua error: the Asyncify unwind does not work in MSFS as built. Investigating offline with the SDK-built module itself.

2026-11-05 — build 43: Lua errors work in the shipped module#

  • Reproduced build 42's freeze offline: wasm/tools/fake_msfs_host.py runs the SDK-built module itself in wasmtime with every MSFS import provided (gauge API with an L:var store, WASI, commit_pages) and drives the gauge callback as the sim does. Step 5 trapped exactly as in MSFS: unreachable in lua_gettop, right after the first protected call returned.
  • Cause: luai_asyncify_catch (called by luaD_rawrunprotected to stop the unwind) was itself instrumented. An instrumented function entered while the Asyncify state is "unwinding" skips its body, so stop_unwind never ran and the next instrumented Lua function hit its unreachable guard. The clang 18 -O2 spike build had inlined the catch into luaD_rawrunprotected, which hid it; the SDK build calls it.
  • Fix (wasm/tools/asyncify.py): the catch stays uninstrumented with luaD_rawrunprotected; the build fails if either is missing from the Lua objects. --optimize-level=2 lets Asyncify optimise the functions it instruments (no module-wide passes, function indices unchanged): module 3.55 MB → 2.80 MB.
  • Build 43 in the fake host: self-test 10/10, frames continue. Timing loop (wasmtime): SDK module 580–660 ms, clang 18 -O3 module 460–540 ms. CI now runs the fake host on both builds (build-wasm.yml), so a module that would stop the gauge fails CI.

2026-11-05 — build 43 in MSFS: Lua self-test 10/10#

  • Owner: L:LU737_LUA_SELFTEST 1, 10 / 10, STEP / NEXT 13 / 13, timing loop 1565 ms. Lua errors (protected, nested, C, unprotected, 1,000 in a loop) are recovered in the sim: the Asyncify approach works in MSFS.
  • MSFS runs the module about 2.5× slower than wasmtime (580–660 ms there). Estimate for Zibo's FMS in MSFS: ~3 ms per frame without a route, ~12 ms with a 40-leg route; plan in docs/M5C_PLAN.md (measure in the sim first, run the FMS at its own rate).

2026-11-05 — build 44: Zibo's FMS runs in the systems module (M5c)#

  • B738.a_fms unmodified on a C XLua host inside the systems module (wasm/src/core/fms/, details in docs/M5C_PLAN.md "Build 44"): CDU pages on both CDU screens over the CommBus, the cockpit CDU keys, EXEC / MSG lights. No navdata yet (NAV DATA blank, NOT IN DATA BASE for any airport), no LNAV / VNAV, no keyboard.
  • Native: MENU → IDENT → POS INIT (the sim's zulu time and date), N1 LIMIT, RTE, typing KSEA; 0 Lua errors; about 3 ms per FMS run in the sanitizer build. Fake MSFS host, SDK module: load 395 ms, 4 ms per run (wasmtime; MSFS about 2.5× slower), 0 Lua errors.
  • Found on the way: with no scenery pack, Zibo's create_apt_rnw_dat2 calls :close() on a number when it cannot create its cache file. Its aircraft folder is now MSFS's \work\ (writable); the native tests used the working directory before this and left B738X_*.dat there (gitignored, removed).
  • Owner checks: L:LU737_FMS_STATE 1, _LOAD_MS, _MS / _MS_MAX (the FMS's cost in the sim; 20 Hz by L:LU737_FMS_HZ), _ERRORS 0; CDU MENU at power-up, <FMC → IDENT, the page keys; the EXEC light.

2026-11-06 — build 45: owner's build 44 test flight#

  • FMS in MSFS (owner): state 1, 0 Lua errors, load 920 ms, 6.9–7.4 ms per run (max 18 ms) at 20 Hz.
  • CDU keys did nothing (not even CLR): the cockpit interface named click spots after their 3-D nodes (CLR, A_9, PERIOD_2, ...); only the line-select keys, whose nodes carry the command name, reached the FMS. CDU keys are now named after their commands, 69 per CDU (tools/sci/generate_cockpit_sci.py; test fms_every_model_cdu_key_reaches_the_fms). The regeneration also kept a hand-written contract state (20_hyd ANNUNCIATOR_STD_RUD_ON) from being duplicated as a _2 nothing writes.
  • REV over the N1 limit on the EICAS: the limit digits' visibility (eng<n>_N1_bug_dig, AFDS) was a display placeholder fixed at 1; it reads the AFDS's value now (0 with the reverser deployed). Test with reverse thrust.
  • LOC S-turns on the A/P: deviation scaling by the measured beam width and a faster bank loop (docs/AFDS_AUDIT.md A9).
  • The owner's APU overspeed change (1680b1d: N1 climbs to 107 % before the trip, about 3.7 s) fails the native test apu_overspeed_and_low_oil_failures, which expects Zibo's trip at N1 > 99 within 3 frames: left for the owner to decide.

2026-11-06 — build 46: APU overspeed you can trigger, OVERSPEED / MAINT lamps#

  • Owner's APU overspeed model kept (N1 to 107 % before the trip); apu_overspeed_and_low_oil_failures follows it.
  • APU failures from the sim: L:LU737_FAIL_APU_* (docs/OPTIONS.md §2b); nothing set them before (only tests).
  • The cockpit's APU OVERSPEED and MAINT lamps never lit: Zibo's object (737_cockpit_switch.obj) lights them only in a shared lights-test batch (laminar/B738/annunciator/generic); zibomod's over_speed_apu / maint_apu had no lamp. data/light_splits.json (applied in tools/xplane/obj8.py) moves each lamp's triangles into a batch of its own, lit by that dataref (L:LU737_ANN_APU_OVERSPEED / _MAINT, which include the lights test).

2026-11-06 — build 47: owner's 17-item list (build 46)#

Fixed (offline checks; the sim check is the owner's):

  • 1 CDU text / F/O clock lit with the battery off: X-Plane's BUS_SRC (an element lights only with its power source) was ignored by the display builder; now a show condition (bus 3 DC power, bus 7 hot battery bus); the CDU screens have their own power gate (CDU1_PWR / CDU2_PWR, Zibo's mcdu1_on / mcdu2_on).
  • 2 yokes did not move: the yoke columns / wheels animate with zibomod's laminar/yoke/pitch, pitch2, roll, which our port never computed; now MSFS's yoke position (roll animation range widened to -1..1).
  • 3 LE DEVICES test did nothing: the panel's power_on_buses / annunciator/flaps_test were placeholders at 0; flaps_test = FLAP TEST held or the lights test (zibomod Annunciators), power_on_buses = DC power.
  • 4 speed brake / flap levers: the mouse wheel followed the lever's value (which grows aft); it follows the screen now, as the drag does (also gear, reversers, grab handles, tillers).
  • 6 stab trim wheel: zibomod's manual trim command wrote the trim and its X-Plane input together, so no change was seen and the next frame read MSFS's trim back; changes are tracked against the trim MSFS last gave. A wheel step is about 1 s of cranking (1 % of the range). Test stab_trim_wheel_moves_the_trim.
  • 7 tillers did not turn: laminar/B738/nosewheel_steer_ratio was never published; MSFS's nosewheel angle now.
  • 8 exterior left aileron reversed: MSFS's left / right aileron deflections have the same sign for a roll input.
  • 9 MCP lamps never lit: they are hidden while laminar/B738/brightness_level2 < 0.01 and nothing published it; zibomod's brightness2_export = battery bus x brightness x day (annunciator brightness), also the AFDS's lights test value.
  • 11 GPU / APU GEN / ENG GEN (spring switches) showed no movement: a click sent the position and its RELEASE at once; a spring position is now held from the press to mouse up, as X-Plane does.
  • 13 cockpit vanishing from outside: LOD0's glass is transparent and MSFS stops drawing the interior first; LOD0 now ends at 40 % of the screen (was 25 %), the opaque LOD1 takes over sooner. Tunable in build_lods.py.
  • 15 engines did not turn: the fan animations read L:LU737_fan_angle_1/2 (written by the removed JS gauge) and ENGINE_ROT_ANG0/1 over a 0..1 range; both are the fan angle in degrees now, 0..360.

Audit: of the 942 L:vars the cockpit model reads, 579 are not published by the systems module (model_lvars_the_systems_publish, LU737_MODEL_LVARS=<list>): mostly systems not rewritten yet (IRS display unit, audio panels, doors, lighting knobs, push-button press animations). Several reported items came from this.

Open: 5 (heavy controls without hydraulics: MSFS has no control-force output, needs a design), 10 (INTV buttons: small click spots behind the knob; with the button press animations), 12 (IRS display unit: not rewritten), 14 (flicker: needs where / what), 16 (start states: FLT files saved in the sim), 17 (cabin differential jumping after landing: needs the values).

2026-11-06 — build 48: manual reversion, EFIS knob centre buttons, cabin window flicker#

  • 5 heavy controls without hydraulics (owner: do it): manual reversion, L:LU737_MANUAL_REVERSION (docs/OPTIONS.md). The gauge masks AXIS_ELEVATOR_SET / AXIS_AILERONS_SET (SimConnect, added to a notification group only while reversion is on); the systems send ELEVATOR_SET / AILERON_SET at 60 % authority and 0.8 / s. Test manual_reversion_slows_the_stick_without_hydraulics.
  • 10 EFIS STD / RST / CTR / TFC (both sides) hard to click: the knob's click spot took the cursor. 12 ring / knob click spots (data/knob_center_buttons.json, tools/msfs/find_knob_buttons.py) push their centre button when pressed within 4 mm of the centre (Asobo's push knobs, CENTER_RADIUS 0.004).
  • 14 window flicker (owner's video): the cabin windows' inner panes (cabin/737_80NG_glass_interior.obj) lie 5.4 mm inside the outer panes; MSFS draws the interior from outside too and the two layers z-fought. The inner panes are left out of the MSFS interior (not visible from the flight deck).

2026-11-07 — build 49: 10 and 14 rolled back, manual reversion diagnostics#

Owner, build 48: 5 the stick still moves freely to full deflection, then jumps to a fraction of it; 10 and 14 unchanged; roll them back, the owner will look at them.

  • 10 and 14 rolled back to build 47: generate_behaviors.py without the centre zones, the cabin windows' inner panes back in the interior, LU738_INTERIOR.* and data/cockpit_io.json from build 47; data/knob_center_buttons.json and tools/msfs/find_knob_buttons.py removed.
  • 5: the stick unresisted, then the slowed value once the stick stops, is what the systems' output does when the mask does not hold: MSFS applies the stick's own events; ours (sent only when the slowed value changes) win when the stick stops sending. Why the mask does not hold is not known from here; the gauge now tells: L:LU737_SIMCONNECT, L:LU737_REV_MASKED, L:LU737_REV_SC_EVENTS, L:LU737_REV_KEY_EVENTS, L:LU737_REV_KEY_LAST, L:LU737_SIMCONNECT_EXCEPTION (docs/OPTIONS.md §2). Until it does, L:LU737_MANUAL_REVERSION 0 turns it off.

2026-11-07 — build 50: M5c navdata, design and the X-Plane folder#

  • docs/M5C_NAVDATA.md: how Zibo's FMS loads navdata (load order, every table's rows and indexes, the X-Plane file fields it reads, CIFP procedures on demand, AIRAC, magnetic variation, volumes) and the provider's design: a cache in X-Plane's formats written from MSFS's facility database, read by native loaders checked against Zibo's own; CIFP per airport on demand.
  • XLua host: Lua's io.open puts Zibo's relative paths (Custom Data/..., Output/FMS plans/...) under \work\xp\ and makes the folders of a written file (xlua_set_root).

2026-11-07 — build 51: native navdata loaders#

  • wasm/src/core/fms/xlua_nav.c: Zibo's read_navdata2, read_rnw_dat, read_apt_dat, read_awy_dat2 and create_idx_table in C, line for line (docs/M5C_NAVDATA.md design 3), replacing his in the script's namespace after it loads. Test navdata_native_loaders_equal_zibos: the 29 tables / indexes / globals equal to Zibo's loaders' on the same files; 3.4 s → 0.6 s on 20,000 fixes (test build). No navdata in the sim yet: the files come with the MSFS crawler (next).

2026-11-07 — build 52: navdata from MSFS's facility database#

  • fms/navdb.cpp: MSFS's records written in X-Plane's formats (earth_nav / fix / awy / hold) and Zibo's airport / runway caches (his header, his runway sort and measures), \work\xp\ and \work\.
  • fms/nav_crawl.cpp: the whole world asked for over frames (lists, then VOR / ILS, NDB, airport + runways, enroute waypoint + airways), 200 requests in flight; answers that never come are counted and skipped. Facility fields, layouts and units: docs/MSFS_FACILITY_DATA.md.
  • fms/nav_provider.cpp: the cache of the current AIRAC cycle, or a crawl → files → FMS reload. L:LU737_NAV_STATE / _PROGRESS / _REQUESTS / _LOST / _COUNTS, L:LU737_NAV_REBUILD, _BATCH (docs/OPTIONS.md).
  • Checked: native tests (writer files read by Zibo's loaders and ours equal; the crawler against a fake facility source; crawl → reload → cache hit); the shipped module in fake_msfs_host.py, whose SimConnect now answers facility requests as MSFS does (lists in parts, data items, data end through CallDispatch): it crawls, writes the files and reloads the FMS. Not yet seen in MSFS: the field names and layouts are from the SDK header and an open-source loader, a refusal shows as L:LU737_SIMCONNECT_EXCEPTION.

2026-11-07 — build 53: SID / STAR / approaches from MSFS#

  • fms/nav_procs.cpp: an airport's departures, arrivals and approaches from MSFS's facility data written as X-Plane's CIFP/<ICAO>.dat, which Zibo's FMS reads for the DEP / ARR pages (docs/M5C_NAVDATA.md, build 53).
  • When: the XLua host reports the files the scripts fail to open; a missing CIFP file (the pilot entered the airport) is fetched and written within a second or two, so the DEP / ARR page shows it when opened. The 3 airports within 25 nm of the aircraft are fetched once the navdata is ready. L:LU737_NAV_PROCS / _PROCS_FAILED.
  • Checked: the generated file read by Zibo's own read_ref_data / read_des_data (native test); the shipped module fetches and writes KSEA's procedures in fake_msfs_host.py, whose A:var reads now return values (the result pointer was in the wrong argument: every A:var had read 0 there).

2026-11-07 — build 54: owner's build-53 test#

  • Manual reversion: masked in cold & dark (L:LU737_REV_SC_EVENTS 1006, the key handler saw none), but after the hydraulics came on and went off again the stick moved freely: adding the axis events to the masked group again after removing them did not mask. The axes now stay masked from the first frame while L:LU737_MANUAL_REVERSION is 1; with hydraulic pressure the systems pass the stick on as it is (ELEVATOR_SET / AILERON_SET when it moves; test manual_reversion_passes_the_masked_stick_with_hydraulics).
  • EFIS knobs / window flicker: back as before (rollback confirmed).
  • Navdata: L:LU737_NAV_STATE -1 with nothing listed: the world list requests (SimConnect_RequestAllFacilities) were refused or not answered (no exception). Now: the older list messages are read too; if the world lists are refused or not answered within 20 s, SimConnect_RequestFacilitiesList (airports of the world; navaids and waypoints around the aircraft) and the airways followed from waypoint to waypoint, every facility met asked for (as Little Navmap reads MSFS); readouts L:LU737_NAV_FAIL, _FALLBACK, _LISTED, _FOUND, _RECV_LAST, the message kinds in the console. Tests navdata_crawler_falls_back_and_follows_the_airways, navdata_crawler_fails_without_lists.
  • M5 LNAV: zibomod's navigation translated (docs/LNAV.md), not wired yet.

2026-11-07 — build 55: navdata lists first, airways / runways on demand#

  • Owner, build 54: L:LU737_NAV_LISTED 349,087 (the world lists answer in seconds), _PROGRESS 4.4 % after minutes: asking for every facility's details one by one is far too slow. Now the lists are the files at once (the FMS reloaded within seconds of the load), the VORs' / NDBs' details follow in the background nearest first, airways and airport runways / localizers / transition levels on demand (docs/M5C_NAVDATA.md, build 55; docs/OPTIONS.md). The owner agreed to Zibo's NOT IN DATA BASE / INVALID ENTRY on the first entry.
  • XLua host: lu737_call(...) (the scripts' calls to the host), xlua_run (Lua in the script's namespace).
  • Fixed on the way: the crawler's request order for facilities found during the details indexed past its array (VOR lists).
  • Tests: navdata_provider_crawls_writes_and_reloads_the_fms (lists first, details, a VOR's frequency into the FMS's row, an airway Zibo's find_awy misses then finds); the fake MSFS host (one localizer, with its runway).

2026-11-07 — build 56: airports asked for by their ident#

  • Owner, build 55: the lists (message kinds 18–21) and the VOR / NDB details came in within seconds ("AIRAC 2611 … FMS reloaded"; "details done: 7808 navaids"), but every airport's procedures failed (L:LU737_NAV_PROCS 0, _PROCS_FAILED 4, L:LU737_SIMCONNECT_EXCEPTION 1; DEP page: SIDs / runways NONE). The airport requests used SimConnect_RequestFacilityData_EX1 with type 'A', which MSFS refused. Airports are now asked for with SimConnect_RequestFacilityData (ident alone, as Little Navmap asks for them); VORs / NDBs / waypoints keep _EX1.
  • New L:vars: LU737_SIMCONNECT_EXCEPTION_INDEX (the refused call's parameter), LU737_SIMCONNECT_EXCEPTIONS (count).
  • Fake MSFS host: answers the plain request and checks that the airports use it.

2026-11-07 — build 57: owner's build-56 test; ND range arcs as Zibo#

  • Owner, build 56: SIDs load (RTE LEGS of WMKK: KK720, OLKAN, KK721, MATPO), so airports by ident work. The ND showed no route, and in PLN the expanded map's three range arcs.
  • The arcs: EFIS_control/capt/rings was a constant 1 (the oracle's value in its only EFIS state, MAP expanded), never Zibo's glareshield formula. Now the systems module's (L:LU737_EFIS_RINGS_CAPT / _FO): expanded APP / VOR / MAP with WXR, TERR or TFC on; 0 in PLN and centred. TFC's toggle starts on as Zibo's cpt_tcas_enable (it started off). Test efis_nd_rings_as_zibo.
  • Why the tests missed it: the ND oracle (nd_trace.json) never turns the mode knob, so PLN / APP / VOR / CTR were never compared; a pending value copied from the oracle hides a mode dependence. Next ND oracle: the four modes and CTR, and a review of the other pending values that are not 0.
  • The route on the ND is not drawn yet (docs/ND.md: pending): zibomod (not Zibo's Lua) computes the symbols' positions and draws the lines, arcs and PLN circles with OpenGL. It is ported after LNAV, which shares its path.
  • Owner: the on-demand lookups (airways, an airport's procedures) took too long. Where the time went:
    • an airway's first fix is asked for as a waypoint, a VOR and an NDB; MSFS refuses the types it is not (an exception, no data end), and the job waited for the stall frames (300, about 5 s) before the airway counted;
    • the airway's segments reached the FMS only when the whole airway was followed, though the VIA entry needs the first one only;
    • the airport the FMS asked for queued behind the airports near the aircraft (prefetch), and a refused request waited 10 s. Now: the gauge keeps each facility request's packet id (SimConnect_GetLastSentPacketID); an exception naming it ends the request at once (NavProvider::onRefused, L:LU737_NAV_REFUSED; not counted as a SimConnect exception). Each airway segment goes into the FMS as it arrives. The FMS's airport goes first. Stall frames for airways 120. Tests: the provider test (a refused NDB request ends the airway job within 3 frames), the fake MSFS host (a refused NDB detail).

2026-11-07 — build 58: the FMS's inputs and outputs#

  • Owner, build 57: entries fail the first time (on demand, as agreed), TAKEOFF REF without N1 / V-speeds / TOW, no VNAV, ND MAP without range arcs, PLN without waypoints. Found: 380 of the scripts' inputs were X-Plane's cold & dark values, and B738.calc (N1 limits, V-speeds) never ran. docs/FMS_INPUTS.md.
  • B738.calc runs on the XLua host with B738.a_fms. The generator no longer takes a commented-out create_dataref for one the script creates (calc's ap_goaround, fd_goaround had no value at all).
  • Inputs: MSFS's A:vars (more: AP references, attitude, air data, NAV receivers), the L:var convention for sim/ too, and Systems::addFmsSources: zibomod's globals and X-Plane values from the ported modules' field tables, the EFIS / MCP / payload / reversers / ADF bearings. Unfilled: 85 read by the scripts, of them about 60 the LNAV path (pending), the station identifiers (strings), ACARS. wasm/out/fms_unbound.txt.
  • Outputs: 266 numbers the scripts create that the displays read, L:LU737_FMS_<NAME>; the displays' pending sources read them (V-speed bugs, flap bugs, FIX circles, ...).
  • Test fms_perf_init_gives_takeoff_speeds (Systems, GPU power, PERF INIT, TAKEOFF REF flaps 5): N1 98.7, QRH V1 134 / VR 136 / V2 146 selected, TOW, L:LU737_FMS_FMS_V1 134. Before: ---.-, ---.
  • Not done in this build: LNAV / VNAV (the path and the AFDS modes), the ND route, auto re-entry after NOT IN DATA BASE, the ND range arcs question (owner to read L:LU737_EFIS_RINGS_CAPT, _TFC_CAPT).

2026-11-07 — build 59: VIA entries wait for the airway#

  • Owner: load the airway first, wait, report an error only if there is none. A VIA entry (RTE page; Zibo's via_add / via_chg) for an airway the FMS's tables do not have is held (no INVALID ENTRY; the scratchpad keeps it) while the provider follows it from the fix (lu737_call("awy")). When the provider is done (lu737_awy_done, found or not; told at once when there is nothing to fetch) or after 8 s, the entry runs again if the scratchpad still holds it: taken, or Zibo's own INVALID ENTRY. Each airway is held once (lu737_awy_asked); lu737_awy_retried names the last one run again. Glue: fms.cpp kNavGlue.
  • Test (provider): RTE page as the pilot types it (KSEA / KSEA, TO ALPHA, VIA J1): held, then J1 on the row; VIA changed to J9 (none from ALPHA): held, then not taken, J1 kept.

2026-11-07 — build 60: LNAV in the systems module#

  • zibomod's navigation (translated, oracle-checked in build 59) runs each frame between the AFDS's Glareshield part and its flight director, as in zibomod (docs/LNAV.md "In the systems module"). Its links to the AFDS, the FMS's datarefs and MSFS are generated (tools/re/lnav_sync.py -> lnav/lnav_sync.inc): the copies between the ports, MainFlightLoopCallback's dataref reads before the block (from its disassembly), zibomod's registered datarefs of the navigation (both ways through the FMS's XLua store), and Read_B738_DR for the AFDS (145 inputs from the Lua FMS: legs_num, end_route, the courses to fly, N1 mode, ...).
  • Test lnav_flies_the_route_in_the_systems: airborne heading 180 near KSEA, the route on the CDU, DIRECT TO, FD, LNAV, CMD A: LNAV engaged, course to fly 200.6 towards the first fix, flight director 8 degrees right.
  • Fixed on the way: an AFDS translation fault the FMS's inputs reached (two stack floats passed as an array: tools/re/afds_fixups.py). The generators strip Lua comments as Lua does (tools/re/lua_strip.py; the same result for Zibo's scripts today).
  • Not yet: VNAV (zibomod's VNAV path is not translated), the ND route, the station identifiers.

2026-11-12 — build 61: the ND's route drawing#

  • Owner: the ND route first. zibomod draws the route on the NDs with OpenGL (NDRender_opengl: legs, turn arcs, RF / AF legs, holds, procedure turns, runway, PLN circles), not with the panel's gauges; translated with the LNAV (28 more functions: the drawing, NDRender_xplane / NDRender_xplane2, calc_panel_coord) and checked against the oracle: the oracle records zibomod's OpenGL calls (gl_capture, each 30th frame), the replay draws with lnav.h's recorder: 119 frames, every vertex and colour the same; the LNAV replay still 0 differences (strict too). docs/ND_ROUTE.md.
  • In the systems module in zibomod's order (NDRender_xplane, NDRender_xplane2 between the navigation block and fms_turn, with what MainFlightLoopCallback copies before them: EFIS mode / range, headings, power); the drawing goes to the NDs on the CommBus (LU737_ND_GL, 15 times a second) and avionics/src/du/NdDraw.ts draws it on a canvas over each ND. The EFIS's mode, range, DATA / POS / TFC now reach the FMS store as Zibo's glareshield datarefs. Test lnav_flies_the_route_in_the_systems: magenta route lines, every vertex inside the NDs.
  • Translation faults fixed in the tools (no hand edits): TransformX / TransformY return doubles (decompile.py); last_x a double, not a pointer (port_cpp.py); stack colour arrays, int pairs, short arrays with named tails, string + / += / to_string, glLineWidth's argument (lnav_fixups.py).
  • FMS bug fixed (also in build 60): the points the navigation inserts into the route (fms_add_data_2..6) were published as integers' bits; after a DIRECT TO the modified route could hold a point at latitude 0 (found when the ND drew a NaN line and the clipper hung). Clip_rect no longer loops on a NaN line.
  • Not yet: the waypoint names / altitudes / ETAs beside the route and the TOC / TOD / decel symbols (computed, not sent to the NDs yet), navaids / airports on the map, TCAS, terrain, weather; the NDs' BRT knobs (full); VOR / ADF bearing lines; the drawing's cost in MSFS not measured yet (the fake host does not fly a route).

2026-11-12 — build 62: the ND's labels and symbols beside the route#

  • Owner: the texts and symbols beside the route first. The waypoints' names, altitudes / FL, ETAs, RNP, their positions and kinds, T/C, T/D, S/C, E/D, DECEL, the GPS / IRS positions: zibomod's symbol datarefs, written by the translated ND parts; the labels were empty (Ghidra's C-string writes: sprintf / strcpy into buffers it typed float[4], argument order of the floating-point ones) — fixed in the tools (port_cpp.py, lnav_fixups.py c_strings, lnav.h str_sprintf / str_strcpy).
  • Oracle-checked: the 402 symbol datarefs the NDs read, after zibomod's drawing (hook nd_out), byte by byte for the labels: 119 frames, 0 differ (strict too); the drawing and the LNAV replay still 0.
  • To the NDs: CommBus LU737_ND_SYM (the changed values with the drawing, all each 2 s); the ND programs read them instead of the ground values (NdSources.ts). Test: in the systems module after LNAV engages, 516 values, the first waypoint labelled.
  • Caught by build.mjs's guard: a display change used Object.fromEntries (MSFS's JS engine lacks it): replaced.
  • Not yet: navaids / airports on the map, the FIX page labels, VNAV, TCAS / terrain / weather; the BRT knobs.

2026-11-12 — build 63: the ND map (stations, airports, fixes)#

  • Owner: navaids, airports, waypoints on the map next. zibomod's map (STA / ARPT / WPT: nd_earth_*, nd_apt_*) and the navdata loaders it runs at its start (read_earth_navdat, read_earth_fix, read_earth_fix2, read_apt) translated; in the systems module after the navigation block, the navdata read from the navigation provider's files each time the FMS loads them (docs/ND_ROUTE.md "The map").
  • Oracle-checked: the loaders' tables byte for byte as zibomod's after its start (the oracle dumps its memory); the replay with the map (its inputs at a new "map" hook, its symbols in nd_out, every element of nd/object_* and nd/apt_*): 0 differences after the map's warm-up (strict too); the drawing and the LNAV still 0.
  • The translation tools grew what zibomod's map code needs: arrays of records as bytes, a function's locals laid out as zibomod's stack frame where its code relies on it, Ghidra's CONCAT / 6-byte unknowns / float fields through 4-byte copies, C strings on stack addresses, std::ifstream on C stdio. The native and WASM builds use -fno-strict-aliasing; the native tests skip the alignment sanitizer (zibomod reads unaligned record pieces).
  • The EFIS's STA / ARPT / WPT / WXR in the FMS store (Zibo's glareshield datarefs); the test world starts with 85 far stations (zibomod's map skips earth_nav's first 80 rows).
  • Test lnav_flies_the_route_in_the_systems: SEA (STA) and an airport ahead (ARPT) on the captain's ND.
  • Not yet: TCAS, terrain, weather; the FIX page's circles (written, not checked); VNAV; the map's memory (68 MB of zibomod's tables) and the navdata load time in MSFS not measured yet.

2026-11-12 — build 64: the map's navdata near the aircraft#

  • Owner: avoid a load stall; plan 1 (only nearby, read in steps, again when far) + 4 (memory by the rows read). The map's navdata is read 2 ms a frame from the provider's files, only the rows within +-10 degrees (what zibomod's map can show is within 145 NM), loaded into zibomod's tables through its own loaders; again after 4 degrees. earth_nav: 0.4 MB instead of 50 MB. After a load, zibomod's first-frame full search, so the map fills at once (its 25-rows-a-frame search would take minutes for the world). docs/ND_ROUTE.md "Nearby navdata".
  • Tests: the oracle replay again with the map's navdata read this way: 0 differences; a 380,000-row synthetic world read in 60 frames of 2 ms, loaded in 9 ms (optimised native build).

2026-11-12 — build 65: VNAV#

  • Owner: VNAV now (the lights wait). zibomod's VNAV mode (ap_vnav, vnav_init, vnav_calc_speed, climb_perf, the restriction speeds) translated with the rest of the AFDS; before, they were empty stubs. docs/VNAV.md.
  • Oracle: a VNAV scenario (PERF INIT FL130, the LNAV route, MCP 13000, A/T, FD, LNAV, VNAV, CMD A, a scripted climb from 10000 ft and level-off): VNAV SPD with FMC N1, then VNAV PTH with FMC SPD and the FMS in cruise. The translation: identical on every frame (3760 frames, 734 values), and the first AFDS scenario too.
  • Fixed on the way (all modes): the LNAV heading was cut to whole degrees (an untyped local became an int); the PFD's GPS / ANP deviation scales were never computed (lnav_vnav_horiz_vert not called); the leg before the first leg read a neighbouring C++ member (zibomod reads an unused, empty leg).
  • Systems: the AFDS's writes to the FMS's datarefs (21: flight phase, descent mode, VNAV speeds, ap_on, ...) now reach the FMS's Lua with its handlers, as X-Plane does; the FMS's VNAV values reach the AFDS (161 inputs). Test vnav_climbs_and_levels_in_the_systems.
  • L:LU737_WASM_BUILD said 60 in builds 61-64 (the number was not bumped); now 65.

2026-11-12 — build 66: VNAV's descent, the IRS modes in the FMS#

  • Owner: the descent next. Oracle scenario afds_vnav_descent_trace (FL240 cruise, T/D, VNAV PTH down Zibo's path, VNAV ALT at the MCP altitude, the MCP lower): the translation is identical on every frame (4920 frames in two windows; the replay starts each window from the oracle's state). The oracle host: trace_restart (windows) and ramp (a held value moved in a straight line over the frames: smooth scripted flight). docs/VNAV.md.
  • Found by the systems test (vnav_descends_in_the_systems): the FMS never saw the IRSs in NAV. Zibo's IRS script (B738.LATLON, not run: ours is native) writes the IRS modes into datarefs the FMS creates; they stayed 0, so the FMS stopped the route's distances (to the next waypoint, to the destination, the T/D) and asked for LNAV / VNAV disconnect — since build 60. Now our IRS's modes, alignment, selectors go there (Fms::addOwnedSource). With it: the T/D at 43.3 nm (the oracle 43.5), VNAV PTH on 99 % of the descent, the path error within 5 ft.

2026-11-12 — build 67: VNAV's arrival and approach#

  • Owner: an arrival and a runway, for the speed limit, the altitude restrictions and the glide path. The test world gets KPDX's STAR LNVE1 and RNAV approach R10R (wasm/test/approach_world.h, written with our own CIFP writer): restrictions at or below / at or above, a 230 kt speed restriction, the FAF at 1700, the runway on a 3 degree path, a missed approach.
  • Oracle scenario afds_vnav_approach_trace: DEP/ARR on the CDU, the descent from FL240 to 500 ft on the final; the host now flies the altitude on the FMS's own path (follow) and keeps zibomod's engine N1 / N2 at idle (zibo_hold: the oracle has no engine model). Zibo's VNAV: 240 kt at 10000 ft, level-offs at the restrictions with FMC SPD, the approach phase, the 3 degree final. The translation: identical on every frame (13020).
  • Found by the systems test (vnav_approach_in_the_systems): VNAV dropped to SPEED after the FAF. zibomod reads some of the FMS's datarefs into globals the navigation and the AFDS share; the AFDS's copies were never filled (rnav_idx_last: the last RNAV approach leg). Now they are (kAfdsPreReads). With it: VNAV PTH down the final, the path error within 25 ft, to 500 ft.

2026-11-12 — build 68: PFD / ND small items#

  • PFD: VNAV's speed window (spd_wup / spd_wdn) and the A/T wake-up flag: the AFDS's own writes (it computed them all along; nothing carried them to the PFD). The SPD REF selector and its knob (zibomod's CMD_spd_ref_* added to the sys translation, the contract's SPEED_REFERENCE / _ADJUST events): the V-speed display (pfd/vspeed, its digit / REF / VREF flags, V1 off). Test spd_ref_knob_sets_the_pfd_vspeed.
  • ND: the green arc (Zibo's B738_draw_arc, efis/green_arc.h; checked in the VNAV climb test: 0.549 of the 10 nm map at 2000 ft to go); the POS button's VOR radial lines and R-xxx labels (NdSources.ts vorPos, unit test); the Lua FMS's strings the NDs read (96: selected VORs, runways, the next waypoint and its ETA, the FIX page's labels, VOR / ADF frequencies) over LU737_ND_SYM.
  • The sys translation regenerated (four commands added): zibomod's command handlers now return int (their real signature); no behaviour change (all tests).

2026-11-12 — build 69: the takeoff and the climb, DES NOW, SPD / ALT INTV#

  • Owner: the takeoff to the climb (SID, VNAV at 400 ft, thrust reduction and acceleration), DES NOW, SPD / ALT INTV. The test world gets KSEA's SID LNVS1 (approach_world.h: runway 16L, the runway heading to 1000 ft, LNVSA at or above 5000 and at or below 210 kt).
  • Oracle scenario afds_vnav_takeoff_trace: on the ground at KSEA, PERF INIT, the route with the runway and the SID, TAKEOFF REF (flaps 5, QRH V-speeds), the MCP speed to V2, FD, A/T, LNAV and VNAV armed, TO/GA, the roll and the climb (the host's new follow_spd: the airspeed follows the AFDS's speed); SPD INTV with the speed dialled, the MCP altitude above the cruise altitude and ALT INTV. Zibo: THR HLD at 84 kt, LNAV at 50 ft, VNAV SPD at 400 ft with V2 + 20, the SID's 210 kt, the climb phase and FMC N1 at 1500 ft; ALT INTV raises the cruise altitude. The translation: identical on every frame (5659 frames).
  • Oracle scenario afds_vnav_desnow_trace: FL240, DES NOW 10 nm before the T/D: -1000 ft/min (the host's follow now flies zibomod's commanded V/S while the FMS's descent_now is 2), the path captured at about 18000 ft, VNAV PTH, VNAV ALT at 16000. Identical on every frame (13836 frames).
  • Systems: the MCP's ALT INTV button now reaches Zibo's FMS command (laminar/B738/autopilot/alt_interv) and its light / position (L:LU737_AUTOPILOT_ALT_INTERV_POS); before, the button did nothing. Tests vnav_takeoff_in_the_systems, vnav_des_now_in_the_systems (the same steps through the cockpit's events).
  • The oracle host: zibomod writes the pilot's indicated airspeed itself (a filter), so the host keeps its own speed. eec_last_oat is zibomod's engine code's (Idle_mode, Thr_shift), not the AFDS's: no longer compared.
  • The tests' FMS error count no longer fails on one error of Zibo's own Lua (the ANP's random seed: an empty interval about once in 1000 seeds; in X-Plane too). It made the VNAV systems tests fail now and then.

2026-11-12 — build 70: ILS approach and autoland#

  • Owner: the ILS approach and the autoland. The test world gets KPDX's ILS IPDX 110.30 (runway 10R).
  • Oracle scenario afds_ils_trace (docs/AFDS.md "ILS approach and autoland"): a closed loop through zibomod's own laws. The oracle host now flies a kinematic aircraft on zibomod's flight director (fly) and computes the ILS receivers from the geometry (ils). Zibo: LOC and G/S capture and tracking, dual channel, FLARE armed, LAND 3, the flare, RETARD, the touchdown, the rollout. The translation: identical on every frame (11648 frames).
  • The oracle now prints int globals exactly (zibomod's PID states are floats in int arrays; the flare PID differed after a replay restart).
  • Systems test afds_ils_approach_and_autoland: the same approach through our servo and MSFS's CDI / GSI: LOC within 0.06 degree, G/S within 0.1 degree, flare at 63 ft, touchdown 1860 ft past the threshold at 200 ft/min, rollout on the centreline. MSFS's GSI sense (FSUIPC: positive with the aircraft above the glideslope) is what zibomod wants.

2026-11-13 — build 71: NAV receiver realism#

  • Owner: false glideslopes, a glideslope needle that jumps far out, and real-world small details (all four offered: false paths and range noise, beam bends, the back course and overflying, the DME's slant / lock-on), on by default with an option. docs/ILS_REALISM.md; L:LU737_ILS_REALISM_OFF = 1 for MSFS's clean signal.
  • One receiver model per NAV (nav/ils_receiver.h) read by zibomod's AFDS, Zibo's FMS scripts and the PFD / ND (they read MSFS's CDI / GSI / DME directly before; the PFD's localizer scale now matches the AFDS's).
  • Test afds_false_glideslope_capture: arriving high with APP armed, the G/S captures the 6° false path (reversed: the A/P then holds level), then the 9° one pulls it down; without the realism nothing is captured. The autoland and LOC tests keep their results with it on.
  • Found while building it: the receiver's needle noise must be smooth (zibomod differentiates the deviation each frame for its lead: frame-to-frame steps rocked the A/P), and a pegged needle must stay pegged under a bend (zibomod reads a needle leaving the stop as the beam coming alive: LOC captured 14 nm out).
  • The DME's slant range is MSFS's own (not recomputed).

2026-11-13 — build 72: PFD / ND, batch 1#

  • Owner: finish the PFD and the ND first, wiring what already exists; leave what is hard for later.
  • zibomod display datarefs that our translated code (Systems, AFDS, navigation) already computes but no display read: tools/re/du_exports.py finds them (a PFD / ND dataref still pending, zibomod's global computed by translated code, not only reset to one constant) and the systems module publishes them as L:LU737_DU_* (systems_du.cpp): the radio altitude dial, the TCAS RA pitch limits, the ND's FMC source, the ADF / VOR-DME fail flags. Test du_exports_publish_zibomod_values.
  • Zibo's display code in B738.glaresheild.lua (a script we do not run: its MCP / EFIS parts are native) written out in avionics/src/du/GlareDisplay.ts: the flight path vector, the PFD's runway symbol, the expanded localizer scale (hysteresis), the ND's heading-bug line (10 s timer), track-up active, the EFIS mode / MMR disagree (1.8 s), EFIS NAV, WX fail. Checked against the oracle's PFD / ND snapshots and unit-tested for the timers (glare_display.test.ts). The FPV's angle of attack is MSFS's INCIDENCE ALPHA.
  • Left pending: PFD 21 / 23 (FO) of about 230 (stall, bank angle and airspeed alerts, PULL UP / WINDSHEAR, DH minimum, display control panel, GLS / LOC failure flags, HUD); ND about 160 of 1050, nearly all TCAS (traffic, its flags) and terrain / weather radar, plus ANP, the destination runway ICAO flag, nd_refresh.

2026-11-13 — build 73: TCAS#

  • Owner: TCAS first (then the PFD's alerts, then terrain / weather radar); compare the displays with Zibo's.
  • zibomod's TCAS (Tcas_system and its functions, the transponder knob's commands) translated into the Systems loop. Oracle scenario tcas_trace: the TCAS test and five traffic targets (other, proximate, two head-on TA / RA, overtaking). The translation is identical on every sample (2543; tcas_replay_matches_oracle).
  • MSFS's traffic (SimConnect: aircraft and helicopters within 40 nm, about once a second) becomes X-Plane 12's TCAS targets, each aircraft keeping its slot, airborne first, moved on each frame with its velocity (tcas/traffic.h). The transponder knob turns (DEC held at STBY: the TCAS test); MSFS's transponder state follows zibomod's. docs/TCAS.md. Systems test tcas_traffic_in_the_systems.
  • The ND's traffic, its flags and altitude labels and the PFD's RA bands are now Zibo's (L:LU737_DU_*, labels over LU737_ND_SYM).
  • Displays compared with X-Plane's own render of Zibo's panel (cockpit_3d/-PANELS-/Panel_Preview.png): the layouts match. Its horizon colours are an older Zibo's bitmap, not ours. The VS needle matches in our renders; the owner's odd VS indicator in MSFS is still to look at, with a screenshot.
  • The clang build of the navigation's translation: its GCC-only pragmas guarded (lnav_fixups.py).

2026-11-13 — build 74: GPWS and the stall alert#

  • Owner: the PFD's alerts next.
  • zibomod's Gpws and PFD_stall_system translated into the Systems loop, with the GPWS SYS TEST and inhibit switches and the stall test buttons. Oracle scenario gpws_trace (GPWS test, approach callouts / minimums / below the glideslope, bank angle, sink rate, terrain closure, too low, windshear, low airspeed and stall, after takeoff): identical on every sample (13785; gpws_replay_matches_oracle). TOO LOW GEAR / FLAPS and DON'T SINK are not reached by the scenario (Zibo's takeoff / go-around state).
  • The PFD's PULL UP / WINDSHEAR, bank angle, low airspeed and stall band now come from Zibo's code. docs/GPWS.md; systems test gpws_alerts_in_the_systems.

2026-11-13 — build 75: ND terrain and weather radar (MSFS's map)#

  • Owner: MSFS's map engine for the terrain, MSFS's weather radar layer, the radar's tilt adjustable; asked about external data (lightning, turbulence, windshear).
  • Found: Zibo's ND terrain is the TerrainRadar plugin's (another author's X-Plane plugin; zibomod only reads it), and its weather radar X-Plane's own, set by zibomod's WXR() from the radar panel.
  • zibomod's WXR() and the radar panel's knobs translated (the panel turns; X-Plane's radar settings as L:LU737_WXR_*; the ND's mode label and the radar test flags). Systems test wxr_panel_in_the_systems.
  • avionics/src/du/NdMap.ts: MSFS's map (the SDK's BingComponent) in the ND's map slot with zibomod's ND geometry. TERR: EGPWS colours against the aircraft's altitude. WXR: MSFS's radar in 737 colours with gain, WX+A magenta cores (turbulence approximation), the ALT knob as a fade (approximation: MSFS has no cloud tops), MAP ground returns, zibomod's ±60 / ±90 degree sector. docs/TERRAIN_WXR.md. Not checked in MSFS yet.