Zum Inhalt

Migration

Diese Seite wurde aus der AirSimTech MediaWiki migriert.

PERF1 Performance Engine

Overview

Perf1Engine is a direct port of the VB6 PREF.ctl COM control. It provides MDB-based performance data (vertical speed and fuel flow) for trajectory computation. The engine decrypts the PERF table from FMGCPERF1.MDB, builds 5 in-memory 3D grids (Weight x IAS x Altitude), and provides tri-linear interpolated lookups matching VB6 behavior exactly.

Before Phase 20, PerfDataStore used a hardcoded linear approximation (e.g., climb V/S = 2000 - altitude/1000 * 40). Now, LoadFromDatabase instantiates Perf1Engine, wires a lambda to _lookupFunction, and all trajectory computation uses real MDB-derived data.

Data Flow

FMGCPERF1.MDB
  |
  v
Perf1Engine.LoadAndDecrypt()
  |-- reads EXTENDED table -> PRO or ADV version (PublicKey)
  |-- reads PERF table -> decrypts each row
  |-- routes by Mode field -> 5 grids
  v
PerfDataStore.LoadFromDatabase()
  |-- creates Perf1Engine, calls LoadAndDecrypt
  |-- wires _lookupFunction lambda (phase -> VSCLB/FFCLB/FFCRZ/VSDES/FFDES)
  |-- updates PerfLimits from actual grid bounds
  v
TrajectoryActor.ComputeClimbProfile / ComputeDescentProfile / ComputeCruiseProfile
  |-- calls _perfData.GetPerformanceCached(alt, weight, phase, ias)
  |-- receives PerfResult(FuelFlow, VerticalSpeed) from tri-linear interpolation

PERF Table Field Mapping

The PERF table uses string-encoded columns N1 through N18:

Column Field Description
N1 GW Gross Weight (kg)
N2 IAS Indicated Airspeed (kt)
N3 ALT Altitude (ft)
N4 VS Vertical Speed (ft/min)
N5 FF Fuel Flow (kg/h)
N9 Mode Flight phase (determines grid assignment)
N13 Stime Time field
N16 CODECHECK Checksum (sum of N1-N12)
N17 Keynum Decryption key number (1, 2, 3, or 4)
N18 ID Sequential ID (ORDER BY)

Decryption Algorithm

Each string-encoded field value follows the format EEMMMMMMMMMMM where:

  • First 2 characters = exponent (E)
  • Remaining characters = mantissa (M)

Step 1: Decode

temp2 = Val(Left(fieldValue, 2))           ' exponent
temp1 = CDbl(Right(fieldValue, Len - 2))   ' mantissa
decodedValue = temp1 / (10 ^ (Len - 2 - temp2))

Step 2: Key-based scaling

PublicKey = 4532535  ' PRO version (or 4465230 for ADV)
cKey = PublicKey / (10000000 * Keynum)
finalValue = Int(decodedValue * cKey + 0.5)

The version (PRO vs ADV) is determined by the EXTENDED table: value 1 = PRO, value 2 = ADV.

The DecodeField and CALC_AVERAGE functions are exposed as Friend Shared for direct unit testing.

Grid Assignment

After decryption, each PERF row is routed to a grid based on the Mode field (N9):

Grid Index Grid Name Mode Values Data Field
0 VSCLB 3 or 7 VS (Vertical Speed)
1 VSDES 4 or 8 VS (Vertical Speed)
2 FFCLB 3 or 7 FF (Fuel Flow)
3 FFCRZ 1 or 5 FF (Fuel Flow)
4 FFDES 4 or 8 FF (Fuel Flow)

Each grid type holds a List(Of PerfGrid). Each PerfGrid represents one weight page:

  • Weight — the gross weight this page covers
  • IasRows — sorted list of IAS values (row headers)
  • AltCols — sorted list of altitude values (column headers)
  • Data(iasIndex, altIndex) — the performance value; -99999 marks missing cells

Rows and columns are inserted in sorted order via InsertPoint, expanding the data matrix as needed.

Tri-Linear Interpolation (Calcit)

GetData(tableName, gw, ias, alt) dispatches to the correct grid list and calls Calcit.

The Calcit function performs 3D interpolation in three stages:

  1. Find weight pages — locate LOW_GRID and HIGH_GRID bracketing the target GW
  2. Find altitude columns — in each weight page, find ALT_MIN and ALT_MAX bracketing the target altitude
  3. Find IAS rows — for each (weight page, altitude column) pair, find IAS_MIN and IAS_MAX
  4. Collect 8 corner values — VS/FF at each combination of (low/high weight, low/high altitude, low/high IAS)
  5. 3-stage CALC_AVERAGE interpolation:
    • Stage 1 (IAS): 4 results — interpolate along IAS axis at each (weight, altitude) corner
    • Stage 2 (ALT): 2 results — interpolate along altitude axis at each weight
    • Stage 3 (GW): 1 final result — interpolate along weight axis

CALC_AVERAGE is standard linear interpolation:

result = calcValue1 + (calcValue2 - calcValue1) * ((origValue - value1) / (value2 - value1))

Boundary clamping: The engine always returns the nearest valid data instead of falling back to hardcoded approximations. Fallback is only used when the MDB file is missing or unreadable. Clamping operates at three levels:

Calcit level (GW, ALT, IAS dimensions)

  • GW: If target weight is below/above all grid pages, the lowest/highest page is used (both low and high grid point to the same edge page)
  • ALT: If target altitude is below/above all column headers, both low and high column indices point to the nearest edge column
  • IAS: If target IAS is below/above all row headers, both low and high row indices point to the nearest edge row
  • Zero corner fill: Grid cells containing 0 (no data at edge) are replaced by the nearest non-zero neighbor value from the same GW side, preventing spurious fallback at grid boundaries
  • FindAltCol (high): returns last column; (low): returns first column
  • FindIasRow: VB6 fallback scans for last valid row from top
  • When HIGH_GRID value is the missing sentinel (-99999), LOW_GRID value is substituted

PerfDataStore lambda level

  • Before calling engine.GetData, IAS and GW are clamped to the per-phase limits (MinIASDES..MaxIASDES, etc.) tracked from actual grid data during LoadAndDecrypt

VSDES sign handling

  • The VSDES grid returns negative values (descent rate in ft/min). These are valid, not errors. The lambda takes Math.Abs(vs) for descent phase. The Calcit error sentinel (-1.0) is distinguished by checking vs > -2.0 AndAlso vs < 0.

PerfDataStore Wiring

In PerfDataStore.LoadFromDatabase, after CI_IAS and CI_MACH tables are loaded:

  1. Perf1Engine is instantiated and LoadAndDecrypt(dbPath) is called
  2. If IsLoaded is true, a lambda is assigned to _lookupFunction:
    • Phase 0 (Climb): GetData("VSCLB") + GetData("FFCLB")
    • Phase 1 (Cruise): VS = 0, GetData("FFCRZ")
    • Phase 2 (Descent): GetData("VSDES") + GetData("FFDES")
  3. IAS and GW are clamped to per-phase grid limits before calling GetData, ensuring the engine always returns the nearest valid value
  4. For descent (phase 2), negative VS values are treated as valid descent rates (Math.Abs); only the Calcit error sentinel (-1.0, detected by vs > -2.0 AndAlso vs < 0) triggers fallback
  5. If GetData still returns -1 after clamping, fallback functions GetFallbackVs/GetFallbackFf provide the old linear approximation and PerfResult.PerfSourceMdb is set to False
  6. PerfLimits are updated from actual grid bounds (MaxWeightCLB, MinIASCLB, etc.)

The _lookupFunction signature includes an IAS parameter: Func(Of Long, Long, Integer, Long, PerfResult) — (altitude, grossWeight, phase, ias). TrajectoryActor passes the current managed IAS at each computation step.

Performance Cache

GetPerformanceCached uses a single-entry cache with 4-dimensional key: altitude (within threshold), weight (within 1,000 kg), phase, and IAS (within 10 kt). The IAS dimension was added to prevent stale cache hits when the approach deceleration zone produces rapidly changing IAS values at similar altitudes. Cache is reset at the start of each trajectory computation.

Performance Source Tracking

Each PerfResult carries a PerfSourceMdb As Boolean flag. The flag is True when both V/S and FF came from the Perf1Engine grids, and False when either value fell back to the hardcoded approximation (or when no _lookupFunction is wired at all). This flag propagates through ProfilePoint.PerfSourceMdb and ComputedFlightPlanLeg.PerfSourceMdb to the diagnostic popup (frmPointInfo), which displays "Source: MDB" or "Source: Fallback".

Verified Reference Data

The VSCLB grid data has been verified against the pre-computed VSCLB table in the MDB:

Weight (kg) IAS (kt) 5,000 ft 20,000 ft 35,000 ft
42,000 200 4,500 1,988 878
42,000 270 3,635 1,606 709
42,000 340 2,937 1,297 573
57,000 200 2,084 921 406
57,000 270 1,684 744 328
57,000 340 1,361 601 265
72,000 200 965 426 188
72,000 270 780 344 152
72,000 340 630 278 123

All values are vertical speed in ft/min. Unit tests validate exact grid point lookups and interpolated values.

Test Coverage

32 tests in Perf1EngineTests.vb (Category: Phase20):

  • DecodeField — 5 tests verifying exponent/mantissa decoding
  • CALC_AVERAGE — 5 tests covering equal values, bounds, midpoint, zero-diff
  • PerfGrid — 3 tests for grid construction and sorted insertion
  • GetData — 11 tests covering exact grid points, interpolation, boundary clamping, out-of-range
  • PerfDataStore backward compatibility — 8 tests verifying existing cache, fallback, and optional IAS parameter

Source Files

  • A320_FMGC/Trajectory/Perf1Engine.vb — Decryption, grid build, Calcit interpolation, GetData dispatch (~770 LOC)
  • A320_FMGC/Trajectory/PerfDataStore.vb — _lookupFunction wiring, cache, fallback (~795 LOC)
  • A320_FMGC.Tests/Trajectory/Perf1EngineTests.vb — 32 unit tests (~450 LOC)
  • VB6 reference: /c/AST-Projects/A320_VB6/ASTServer/PREF.ctl — original COM control source