Ideal Airspeed Correction — Robotics/Flight Parameters
Robotics/Flight_Parameters/Ideal_Airspeed_Correction · 3 input / 1 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text
Description#
The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.
Ideal Airspeed Correction
Robotics / Flight Parameters
Converts one of true (TAS), equivalent (EAS) and calibrated (CAS) airspeed into another, from the local speed of sound a and static pressure P, for dry air at γ = 1.4 measured against the sea-level a₀ = 340.2941 m/s and P₀ = 101325 Pa. With M = V/a:
- √σ = (a₀/a) · √(P/P₀), and EAS = TAS · √σ.
- Every leg through CAS goes out to the impact pressure and back: qc = P · ((1 + M²(γ−1)/2)γ/(γ−1) − 1) while V ≤ a, and the Rayleigh pitot relation qc = P · M²(γ+1)/2 · ((γ+1)²/(4γ − 2(γ−1)/M²))1/(γ−1) − P above it.
- Back from qc: V = a · √(2/(γ−1) · ((qc/P + 1)(γ−1)/γ − 1)); when that lands at or above a, the supersonic relation is solved by fixed-point iteration until two passes agree to 10−5 m/s, at most 30 passes.
TAS → CAS measures qc at (P, a) and reads it back at sea level; CAS → TAS the other way round; EAS → CAS goes through TAS first and CAS → EAS through TAS last.
Ports
- V_in – the input airspeed, of the kind Input Airspeed names, a [1,1] scalar in m/s.
- a – the local speed of sound, a [1,1] scalar in m/s.
- P – the local static pressure, a [1,1] scalar in pascal.
- V_out – the output airspeed, of the kind the live output selector names, a [1,1] scalar in m/s.
Every port is scalar. The units are SI and matter: the sea-level constants above are in SI, so a pressure in another unit gives a wrong answer rather than a rescaled one.
Parameters
- Input Airspeed – what arrives on V_in. This selects
which arithmetic executes:
- TAS – true airspeed. The default, as in Simulink.
- EAS – equivalent airspeed.
- CAS – calibrated airspeed.
- Output Airspeed (from TAS) – what V_out carries when the input is TAS: EAS (the default) or CAS.
- Output Airspeed (from EAS) – what V_out carries when the input is EAS: TAS (the default) or CAS.
- Output Airspeed (from CAS) – what V_out carries when the input is CAS: TAS (the default) or EAS.
- Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.
Only the output selector that matches Input Airspeed is live; the other two are kept, exactly as the Simulink dialog keeps them, so that a block switched back to another input finds its old choice. Together the four give the six conversions.
Code export
All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text.
The direction is baked in at export time: a core exports only the one conversion its configuration selects, and a TAS↔EAS core carries no iteration at all. Every constant is printed to 17 significant digits.
The three HDL targets are simulation-only: they divide by a
port, take roots and fractional powers, and run a loop whose trip count depends
on the data, so everything converts at the port boundary and runs in
real. VHDL declares the conversion as a function beside the block's
state, because a block body there has no real-valued variable of its own. The
cores simulate correctly and are not offered as synthesizable. They also test
the input domain first – a speed of sound or a pressure that is not
positive, or a negative airspeed, gives 0 – because VHDL's
math_real would otherwise abort the simulation on a root or a power
outside it; the seven software targets answer the IEEE value there instead.
Simulink bridge
Import and export, mapped to Aerospace Blockset's
aerolibasang/Ideal Airspeed Correction. The four parameters mirror
the Simulink dialog one for one, and each enum maps 1:1, so the mapping is
lossless both ways: Input Airspeed → vel_in,
Output Airspeed (from TAS) → vel_out_tas,
Output Airspeed (from EAS) → vel_out_eas and
Output Airspeed (from CAS) → vel_out_cas, with the
options spelled TAS, EAS and CAS on both sides.
Three Simulink parameters are always implied and carry no configuration
here: units is Metric (MKS), matching the SI constants;
method is Equation, because the other method is a
three-dimensional spline over a table shipped as a data file and is not
reproduced; and SubOnly ("Subsonic airspeeds only") is off,
because switching it on changes the Equation answer as well – by up to
174 m/s on the same supersonic inputs, measured.
The Simulink block defines no SampleTime, so the rate
stays on the ICore side.
Notes
- Stateful, with direct feedthrough. The output depends on the input at
the same instant, and on one held value: every supersonic solve starts from the
answer of the previous supersonic solve (340.2941 m/s for the first one in a
run). That is how the Simulink block iterates, and measured against it this
matters – starting from the subsonic estimate instead, as the MATLAB
function
correctairspeeddoes, lands up to 8.8×10−6 m/s away. A TAS↔EAS conversion never touches the held value. - A solve still moving after 30 passes stops the Simulink simulation with a convergence error; this block stops there too and reports the thirtieth pass. Negative or NaN inputs likewise stop Simulink with an assertion, and are simply computed here.
- Not linear, so the block carries no state space and model reduction correctly reports it as unmergeable.
Code facts#
| Fact | Value |
|---|---|
| registered type | Robotics/Flight_Parameters/Ideal_Airspeed_Correction |
| family | Robotics/Flight_Parameters |
| solver environment class | ICoreBlock_0_Robotics_1_Flight_Parameters_2_Ideal_Airspeed_Correction |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Flight_Parameters/Ideal_Airspeed_Correction/ICoreBlock_0_Robotics_1_Flight_Parameters_2_Ideal_Airspeed_Correction.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Flight_Parameters/Ideal_Airspeed_Correction/ICoreBlock_0_Robotics_1_Flight_Parameters_2_Ideal_Airspeed_Correction.h |
| default size on canvas | 144 × 112 px |
| ports at insert | 3 in, 1 out |
| code generators implemented | Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text |
Ports#
| # | Direction | Signal type | Description label |
|---|---|---|---|
| 1 | in | ICoreDouble | V_in |
| 2 | in | ICoreDouble | a |
| 3 | in | ICoreDouble | P |
| 4 | out | ICoreDouble | V_out |
Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.
Configuration variables#
| Config variable | Default | Simulink parameter |
|---|---|---|
Input Airspeed | TAS%~%EAS%~%CAS~~TAS | vel_in |
Output Airspeed (from TAS) | EAS%~%CAS~~EAS | vel_out_tas |
Output Airspeed (from EAS) | TAS%~%CAS~~TAS | vel_out_eas |
Output Airspeed (from CAS) | TAS%~%EAS~~TAS | vel_out_cas |
Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.
Simulink bridge#
| support | Support::Both |
| Simulink path | aerolibasang/Ideal Airspeed Correction |
| port-count rule | PortsParam::None |
SampleTime parameter | no — the counterpart defines none; the rate stays on the ICore side |
| always set | units = Metric (MKS), method = Equation, SubOnly = off |
| ICore config | Simulink parameter | Value translation |
|---|---|---|
Input Airspeed | vel_in | TAS → TAS, EAS → EAS, CAS → CAS |
Output Airspeed (from TAS) | vel_out_tas | EAS → EAS, CAS → CAS |
Output Airspeed (from EAS) | vel_out_eas | TAS → TAS, CAS → CAS |
Output Airspeed (from CAS) | vel_out_cas | TAS → TAS, EAS → EAS |
Caveat (shown to the user): the three ports are the airspeed, the speed of sound and the static pressure, all scalar and all SI. The four parameters are the dialog itself: vel_in, and one output selector per input value, of which vel_in decides which is live -- so each crosses to its own parameter and nothing is ever read into the wrong one. 'method' is always Equation (the Table Lookup method is a 3-D spline over a data file and is not reproduced), 'units' always Metric (MKS), and 'SubOnly' always off -- measured in R2026a, switching it on moves the Equation answer by up to 174 m/s. The Simulink block has no SampleTime, so the rate stays on the ICore side. ⚠ THE DIRECTION IS A MODE: the six conversions run different arithmetic, and four of them iterate
Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h
Description vs code#
The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.
The verdict above is
tools/docs/check_block_descriptions.py(P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.
File banner (developer view)#
The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).
Ideal Airspeed Correction -- true, equivalent and calibrated airspeed, each from another Dry air at gamma = 1.4, measured against the sea-level a0 = 340.2941 m/s and P0 = 101325 Pa. With M = V/a:
sqrt(sigma) = (a0 / a) * sqrt(P * (1/P0)) TAS <-> EAS, one multiply q_c = P * ((1 + M^2 (g-1)/2)^(g/(g-1)) - 1) V <= a (isentropic) q_c = P * M^2 (g+1)/2 * ((g+1)^2 / (4g - 2(g-1)/M^2))^(1/(g-1)) - P V > a (Rayleigh) V = sqrt(((q_c/P + 1)^((g-1)/g) - 1) / (g-1) * 2) * a and when that lands at or above a, the supersonic relation is solved by fixed-point iteration, V' = a * sqrt(((q_c/P + 1) * 2 / (g+1)) / ((g+1)^2 / (4g - (a/V)^2 * 2 (g-1)))^(1/(g-1))) until two passes agree to 1e-5 m/s, at most 30 passes.
Every leg through CAS goes out to an impact pressure and back: TAS -> CAS measures q_c at (P, a) and reads it back at sea level; CAS -> TAS measures it at sea level and reads it back at (P, a); EAS -> CAS goes through TAS first and CAS -> EAS through TAS last.
MEASURED AGAINST R2026a, and the measurement changed the design twice.
(1) THE ITERATION HOLDS ITS STATE BETWEEN SAMPLES. aerolibasang/Ideal Airspeed Correction is a masked subsystem, not a call to correctairspeed.m. Its supersonic solve is a do-while While Iterator (ResetStates = held) around a Memory whose initial condition is a0, inside an Enabled subsystem whose StatesWhenEnabling is also held. So every supersonic solve STARTS FROM THE PREVIOUS SUPERSONIC ANSWER -- a0 for the first one -- where correctairspeed.m starts from the subsonic estimate. Both stop within 1e-5 of the same fixed point, so they differ by up to 8.8e-6 m/s: over 60 samples mixing both regimes the .m function misses the real block by 8.7e-6 on every CAS leg, and a held start reproduces it to 2.8e-13. This block therefore carries one held value, seeded at a0 at the start of every run, and marks its output as having a side effect.
(2) THE ARITHMETIC IS WRITTEN IN THE MASK'S OWN ASSOCIATION ORDER -- (as/a)^2 * ((g-1)/2), P * (1/101325), ((q_c/P + 1) * 2 / (g+1)) / power -- read off the product blocks' input lists, so the ten exports and the C++ reference round the same way Simulink does. The subsonic test is the mask's too: q_c is isentropic while a >= V, the airspeed is subsonic while the estimate is < a.
At V = 180.5, a = 330.7, P = 70000.7 the six legs answer 154.37974384045492 (TAS->EAS), 156.0572861008105 (TAS->CAS), 211.03966873834395 (EAS->TAS), 183.12580360879352 (EAS->CAS), 208.09018528543956 (CAS->TAS) and 177.97733795057596 (CAS->EAS).
⚠ THE THREE HDL TARGETS ARE SIMULATION-ONLY: a division by a port, roots, powers with a
Sample results#
32 sample(s) were non-finite (nan/inf) and are absent from the plot; they are in the table below and in the JSON.
| t | in ICoreDouble-Out-0 | in ICoreDouble-Out-0 | in ICoreDouble-Out-0 | out ICoreDouble-Out-0 |
|---|---|---|---|---|
| 0 | -2 | -2 | -2 | nan |
| 0.4 | 0.5 | 0.5 | 0.5 | 0.7559 |
| 0.8 | -2 | -2 | -2 | nan |
| 1.2 | 0.5 | 0.5 | 0.5 | 0.7559 |
| 1.6 | -2 | -2 | -2 | nan |
| 2 | 0.5 | 0.5 | 0.5 | 0.7559 |
| 2.4 | -2 | -2 | -2 | nan |
| 2.8 | 0.5 | 0.5 | 0.5 | 0.7559 |
| 3.2 | -2 | -2 | -2 | nan |
| 3.6 | 0.5 | 0.5 | 0.5 | 0.7559 |
| 4 | -2 | -2 | -2 | nan |
| 4.4 | 0.5 | 0.5 | 0.5 | 0.7559 |
| 4.8 | -2 | -2 | -2 | nan |
| 5.2 | 0.5 | 0.5 | 0.5 | 0.7559 |
Every 4th of 60 samples, from the table stimulus.
The same rig also ran:
| Stimulus | What it is | Output range |
|---|---|---|
impulse | Impulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair) | 1.069 … 1.069 |
ramp | Ramp: slope 1 from t = 0 | 0.3381 … 2.597 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | 0.1683 … 1.069 |
step | Step: 0 -> 1 at t = 1 s | 1.069 … 1.069 |
Plotted: table — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample
Category static · sample time 0.1 · 60 steps · commit 383c0ecf1501cf1d43d89b8f688fc2fcad5e9b52 · produced by docsSample --out <folder> --blocks Ideal_Airspeed_Correction WGS84_Gravity_Model Linear_Regression_Predictor Linear_Classifier_Predictor Crossover_Pilot_Model Precision_Pilot_Model Tustin_Pilot_Model FIR_Least_Squares_Design FIR_Equiripple_Design Cartesian_To_Keplerian_Elements Keplerian_Elements_To_Cartesian --steps 60 · data docs/generated/samples/Robotics__Flight_Parameters__Ideal_Airspeed_Correction.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).