FLIGHT LAB / METHODS AND CASE STUDY ·

From a classic Learjet to an open flight lab

A plain account of what this lab is made of: which software is NASA’s and which is ours, where the data comes from, how the two views talk to each other, and how you can rebuild every file from the repository. Written for engineers, instructors and anyone who wants to check the claims rather than take them.

What was actually built

  1. A telemetry console on NASA’s Open MCT. /lab/console runs NASA Ames’ open-source Open MCT framework, version 4.3.1, unmodified, inside a same-origin frame that is created only when the page opens. Open MCT is visualisation software: it draws whatever telemetry a provider hands it. It does not simulate an aircraft and it is not a source of flight data.
  2. One authored synthetic dataset. lp-ft-001: six minutes at 1 Hz of a research-style scenario (a climb, a level-off) with explicit units, ISO 8601 UTC timestamps on a fixed synthetic epoch, a schema version, a scenario id, a provenance block and the label Synthetic educational data. It is generated by a seeded script, so two runs are byte-identical. It contains one hidden discrepancy whose explanation lives in a separate truth file.
  3. A typed adapter. lab/adapter.js (with adapter.d.ts) owns dataset selection, the shared time cursor, the time window, event selection and Lear assembly ids, validating every id against the dataset and the Aircraft Atlas pack. The console page is glue around it.
  4. The Lear assembly view, embedded. The Aircraft Atlas gained an embed=1 mode (no header, rail or footer) and a small postMessage bridge with an explicit schema. Selecting an event frames its window in Open MCT and selects the assembly it concerns; picking a part in the assembly selects the event that concerns it and moves the window. Both directions are exercised by the end-to-end test.
  5. Static fallbacks. A chart (SVG) and a table, both written by the same generator, are on the console page before any script runs and are shown automatically if Open MCT cannot be loaded. The authored explanation is in the truth file and, as a spoiler, at the end of this page.
  6. A dated research brief and an explainer. The brief reads NASA’s 4 September 2026 X-59 report precisely; the explainer draws shock formation and coalescence as original illustrative artwork.

Which NASA code is used, and which is not

Used: Open MCT 4.3.1

The npm package openmct@4.3.1 (Apache-2.0; “Open MCT, Copyright (c) 2014-2024, United States Government as represented by the Administrator of the National Aeronautics and Space Administration”), pinned exactly; the build fails if any other version is installed. Only the files the shell loads at runtime ship, at /lab/openmct/4.3.1/, 14 files, about 5.4 MB uncompressed (openmct.js 5.17 MB, about 1.28 MB gzipped): openmct.js and its bundled third-party notices, the Espresso theme stylesheet, two workers (search, historical telemetry), four symbol fonts, four theme images, and LICENSE.md. No source maps, no unused themes, no example plugins.

Integration uses documented extension points only, no patched internals: setAssetPath; install of LocalStorage, MyItems, UTCTimeSystem, Espresso and Conductor (fixed time only, no clock); types.addType; objects.addProvider and addRoot; telemetry.addProvider (request only, no subscriptions); time.setTimeSystem, setMode, setBounds, getBounds and the boundsChanged event; start(element). Plots, the telemetry table and the flexible layout are Open MCT’s own built-in object types, created as domain objects by the provider.

Not used

  • No NASA data. No X-59, Learjet or any other NASA flight-test dataset; the console shows synthetic data only. Nothing on this site implies that NASA’s digital-twin code or datasets are public.
  • No NASA model or twin. The “expected” channels come from a two-line teaching model written for the scenario.
  • OpenVSP was not used. It is native desktop software under the NASA Open Source Agreement 1.3; the Lear training model is original procedural geometry proportioned from NASA’s published Learjet three-view. No OpenVSP analysis badge exists here because no analysis was run.
  • Not used either: NASA-AMMOS 3D Tiles Renderer (a possible later terrain option) and NASA Aviary (a Python design tool). They are noted as future options, not promised.
  • No insignia, no affiliation. Software attribution and research citations only. The NASA logo inside Open MCT’s own About screen is that software’s, not this site’s.

Why the console runs in its own frame

The documented way to start Open MCT is a script tag and openmct.start(element). Tried directly on a page with the site’s header (an experiment kept as a screenshot in the engineering notes), the shell mounts but escapes its container and overlays the page, and Open MCT’s theme stylesheet, written for a page it owns, restyles html, body (measured: 12 px Helvetica), every a (grey), div (position: relative), p, li, button, select and the headings, so the site’s header, footer and authored panel are redrawn in NASA’s theme. Rather than patch a 320 KB stylesheet or the framework, the console gives Open MCT a same-origin page of its own (/lab/telemetry) inside an iframe with an accessible title, exactly the “supported integration boundary” the framework expects; the host page drives it through the public time API. Open MCT owns that frame’s URL hash; the console page uses none.

The dataset, field by field

File: /lab/data/lp-ft-001.json (about 78 KB). Generator: scripts/gen-lab-dataset.mjs, seed 20260901, PRNG mulberry32, Box–Muller noise, rounding per channel, no clock reads. The values are generic research-scenario values chosen to be readable; they are not Learjet 24 or N24SA parameters, limits or procedures, and the file says so in provenance.valuesNote.

Top-level schema, version 1.0
KeyTypeMeaning
schemaVersion, id, title, labelstrings"1.0", "lp-ft-001", the title, and always "Synthetic educational data".
provenanceobjectauthor, authored date, generator (name, version, prng, seed), kind, statement, isNot (a flight by Lloyd; NASA telemetry; real-time data; a certified model; a digital twin), valuesNote, inspiredBy.
timeobjectepoch 2026-09-01T14:00:00Z (synthetic), stepSeconds 1, count 361, start, end, timezone UTC, note.
timestampsstring[361]ISO 8601 UTC, one per sample.
channelsobject[]id, name, unit, class (measured, expected, derived; simulated is reserved and unused), decimals, group, the Lear component ids the channel is read from (schematic), plus formula for derived channels and model for expected ones.
series{ id: number[361] }The values. Measured: pressure altitude (ft), indicated airspeed (kt), pitch (°), N₁ left/right (%), EGT left/right (°C), fuel flow left/right (lb/h), cabin altitude (ft). Expected: EGT and fuel flow per engine. Derived: EGT observed − expected per engine, EGT split, vertical speed.
thresholds, comparisonsobject, object[]Review thresholds for the exercise (30 °C absolute for EGT, 4 % of expected for fuel flow; “not operating limits”) and the observed/expected pairs the console summarises.
eventsobject[6]id, t (s), time (ISO), label, description, componentIds, primaryComponentId, window (start/end in s and ISO), hypotheses (id, text). Chronological.
componentsobjectaircraft lear-24xr, its route, and the referenced ids with their names and systems, copied from the Atlas pack at generation time.

Companions: lp-ft-001.truth.json (the authored answer: fault channel, start, rate, explanation, discriminating evidence, helpful observation, and a verdict per event; fetched only when a visitor asks for the reveal and frozen in memory), lp-ft-001.svg (the static chart), the table stamped into the console page between markers, and lear-24xr-components.json (the pack’s component ids, so the adapter can refuse unknown ids without importing the pack).

The evidence arithmetic

For the current window the console reports, per comparison, the mean of observed − expected, the largest absolute difference and when it occurred, and the first sample above the threshold. These come from lab/evidence.js, pure functions pinned by unit tests on a five-sample fixture; the console renders those numbers and nothing else. No model arithmetic is involved anywhere.

The adapter, with an example

Framework-free, DOM-free, and unit-tested in Node. It validates dataset ids against an allow-list, event ids against the dataset, and component ids against both the dataset and the Lear pack; unknown ids throw and emit error, and the state is left untouched.

import { createAdapter } from '/lab/adapter.js';

const adapter = createAdapter({ load: (url) => fetch(url).then((r) => r.json()) });
adapter.on('window', ({ start, end }) => openmct.time.setBounds({ start: Date.parse(start), end: Date.parse(end) }));
adapter.on('component', (id) => assemblyFrame.contentWindow.postMessage({ v: 1, type: 'atlas:select', componentId: id }, location.origin));

await adapter.loadDataset('lp-ft-001');      // fetches, validates (schema + Lear ids), emits dataset/window/cursor
adapter.selectEvent('egt-diverge');          // window 14:02:50Z to 14:06:00Z, cursor 14:03:20Z, component right-exhaust
adapter.selectComponent('outflow-valve');    // → event cabin-settle, its window and cursor
adapter.setCursor('2026-09-01T14:04:46Z');   // nearest sample; readouts follow
adapter.getState();                          // { datasetId, dataset, cursor, window, eventId, componentId, componentIds }
adapter.selectComponent('flux-capacitor');   // throws: Unknown Lear component id

Contract: loadDataset(id), setCursor(tISO), setWindow(startISO, endISO), selectEvent(id), selectComponent(id), eventForComponent(id), getState(), on(type, fn) with events dataset, cursor, window, event, component, error. Types in lab/adapter.d.ts.

The embed protocol

The assembly view is <iframe title="Lear 24XR assembly view" src="/atlas/lear-24xr?embed=1">. Messages are same-origin only, checked on both sides for origin and source window, and parsed strictly (version, type, exact keys, slug ids up to 64 characters); anything else is dropped:

{ v: 1, type: 'atlas:ready' }                                // atlas → console: listening
{ v: 1, type: 'atlas:selected', componentId: string | null } // atlas → console: the visitor picked or cleared a part
{ v: 1, type: 'atlas:select',   componentId: string }        // console → atlas: select this part (unknown ids ignored)

The sync demonstration

The end-to-end test (tests/e2e/lab.spec.ts, Chromium with software WebGL, desktop and phone viewports) asserts the whole chain on the built site: the landing page shows the static chart and the label without any request to /lab/openmct/; the console requests openmct.js and Open MCT’s shell mounts inside the frame; clicking event 3 moves Open MCT’s bounds to 14:02:50 to 14:06:00 (read back from openmct.time.getBounds()) and the assembly frame’s URL gains component=right-exhaust; picking the outflow valve inside the assembly frame selects event 6 and moves the bounds to 14:01:00 to 14:06:00; the label stays visible in every state; blocking the script route shows the static fallback; and the brief, explainer and this page pass an axe audit.

Reproducible commands

git clone <repository> && cd lloydpilot-home && npm ci
node scripts/gen-lab-dataset.mjs            # writes lab/data/* and the static table; run twice, diff nothing
node scripts/gen-lab-dataset.mjs --check    # exit 1 if the committed files differ from the generator
npm run check && npm test                   # syntax/type checks; vitest incl. tests/unit/lab.test.ts
npm run build                               # copies the pinned Open MCT runtime to dist/lab/openmct/4.3.1/
npm run preview                             # http://127.0.0.1:4173/lab/console
./node_modules/.bin/playwright test --project=desktop-1440 tests/e2e/lab.spec.ts
./node_modules/.bin/playwright test --project=phone-390 tests/e2e/lab.spec.ts

What visitors learn

  • That “expected” and “observed” are different kinds of number, and that the interesting information is in their difference over time, not in either alone.
  • That a discrepancy has to be explained against alternatives: a hotter engine, a wrong indication, a fuel-scheduling difference, a wrong model. Each predicts something different in the other channels.
  • That one sensor disagreeing while everything it should move with stays put points at the measurement chain, and that the next useful step is an independent observation rather than a conclusion.
  • That a real telemetry tool (Open MCT) is unglamorous and precise: fixed bounds, a time conductor, a plot per quantity, a table for the numbers. The framework does not know or care that the data is synthetic; the labels beside it are what keep the exercise honest.
  • Where the channels live on the aircraft, schematically, through the Aircraft Atlas assembly the console points at for each event.

Spoiler: the authored answer

Show the hidden discrepancy of lp-ft-001

From 200 s the right EGT indication drifts upward at a steady 0.35 °C per second, regardless of what the engine is doing. Right N₁ and right fuel flow stay on their expected values and match the left engine throughout, including the thrust reduction at 250 s; the left EGT keeps tracking its expected value, so the model is not the problem; the discrepancy grows linearly with time and does not respond to the power change. The observed-minus-expected difference passes the 30 °C review threshold at 286 s. The authored answer is hypothesis B at event 3: the indication is wrong, the engine is not. What would help most is an independent measurement of the same quantity and a look at whether the discrepancy persists at idle on the ground. Source: lab/data/lp-ft-001.truth.json.

Sources and notices

  1. NASA, Open MCT: repository github.com/nasa/openmct; API.md sections “Starting an Open MCT application”, “Domain Objects and Identifiers”, “Telemetry Providers”, “Telemetry Metadata”, “The Time Conductor”, “Time API”; the packaged index.html bootstrap; npm package openmct@4.3.1, license Apache-2.0, published August 2026. Checked 7 September 2026.
  2. Apache License, Version 2.0, §4 (redistribution: licence copy, notices) and §6 (trademarks). apache.org
  3. OpenVSP licence: NASA Open Source Agreement v1.3, clauses 3.A to 3.F and 4.A (recorded to explain why the tool was not used). github.com/OpenVSP
  4. NASA media usage guidelines, “Images and media”, page updated 13 August 2026: NASA insignia not in the public domain; no implied endorsement. nasa.gov
  5. The Lear training model and its sources: the Aircraft Atlas provenance drawer at /atlas/lear-24xr.

Software notices (Open MCT’s licence text and copyright line, and every other library) · Open MCT LICENSE.md · Sources / suggest a correction. Educational; nothing here is operating guidance for any airplane. This independent site is not affiliated with or endorsed by NASA or its Administrator.