Body To Wind Rotation Matrix — Robotics/Axes Transformations
Robotics/Axes_Transformations/Body_To_Wind_Rotation_Matrix · 1 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.
Body To Wind Rotation Matrix
Robotics / Axes Transformations
Builds the [3,3] direction cosine matrix that carries a vector from body axes into wind axes, from the angle of attack α and the sideslip angle β:
- DCM00 = cosβ·cosα, DCM01 = sinβ, DCM02 = sinα·cosβ
- DCM10 = −sinβ·cosα, DCM11 = cosβ, DCM12 = −sinα·sinβ
- DCM20 = −sinα, DCM21 = 0, DCM22 = cosα
Multiply a body-axis vector by it to read that vector in wind axes; transpose it for the other direction, the matrix being orthogonal for every α and β.
Ports
- ab – the two aerodynamic angles in radians, as a [2,1] column [α; β]. Row 0 is the angle of attack and row 1 the sideslip. Any value is accepted – the block computes rather than judges – and an angle outside ±π simply wraps, sines and cosines being periodic.
- DCM – the direction cosine matrix, [3,3]. Its size is fixed and does not follow the input's.
Parameters
- Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.
There are no others: both angles arrive on the port.
Code export
All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text.
The three hardware targets are simulation-only: a direction cosine matrix needs four sines and cosines, and no fixed-point sine is available to a block body, so those exports convert at the port boundary and compute in floating point. They simulate correctly and are not offered as synthesizable. The other seven are exact.
Simulink bridge
Import and export, mapped to Aerospace Blockset's
aerolibtransform2/Direction Cosine Matrix Body to Wind. That block
has no dialog parameters at all – measured in R2026a – so
nothing is mapped and the add_block carrying nothing is itself the
assertion. It defines no SampleTime either, so the rate stays
on the ICore side and a block given an explicit positive period reports that it
did not cross.
Notes
- Algebraic and stateless: the matrix depends on this sample alone.
- Not linear – every entry is a trigonometric function of the input – so the block carries no state space and model reduction correctly reports it as unmergeable.
- DCM21 is an exact zero. No angle can move it: rotating into wind axes leaves nothing of the angle of attack on the second axis.
- Verified against R2026a: over 200 random angle pairs, this block and the Simulink one agree in all 1800 entries to the last bit.
Code facts#
| Fact | Value |
|---|---|
| registered type | Robotics/Axes_Transformations/Body_To_Wind_Rotation_Matrix |
| family | Robotics/Axes_Transformations |
| solver environment class | ICoreBlock_0_Robotics_1_Axes_Transformations_2_Body_To_Wind_Rotation_Matrix |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Axes_Transformations/Body_To_Wind_Rotation_Matrix/ICoreBlock_0_Robotics_1_Axes_Transformations_2_Body_To_Wind_Rotation_Matrix.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Axes_Transformations/Body_To_Wind_Rotation_Matrix/ICoreBlock_0_Robotics_1_Axes_Transformations_2_Body_To_Wind_Rotation_Matrix.h |
| default size on canvas | 140 × 84 px |
| ports at insert | 1 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 | ab |
| 2 | out | ICoreDouble | DCM |
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#
No config variable beyond the Sampling Time (s) every block carries.
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 | aerolibtransform2/Direction Cosine Matrix\nBody to Wind |
| port-count rule | PortsParam::None |
SampleTime parameter | no — the counterpart defines none; the rate stays on the ICore side |
Caveat (shown to the user): the Aerospace Blockset block is a masked subsystem with no dialog parameters at all, so nothing but the signal crosses: the two angles arrive on one port as [alpha; beta] and the matrix leaves on one. It defines no SampleTime either, so an ICore rate set explicitly stays on this side and is reported
Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h
Description vs code#
The checker has a blind spot here — it could not resolve something (a grouped port bullet, a computed config name), which is reported and never counted as a pass. A reader has to settle it:
B0every stimulus in the sample errored — cross-checks skipped
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).
Body To Wind Rotation Matrix -- the two aerodynamic angles to the [3,3] matrix that takes a vector from body axes into wind axes DCM = | cb*ca sb sa*cb | ca = cos(alpha), sa = sin(alpha) | -sb*ca cb -sa*sb | cb = cos(beta), sb = sin(beta) | -sa 0 ca |
alpha is the angle of attack and beta the sideslip, both in radians and both arriving on ONE port as a [2,1] column -- the shape the Aerospace Blockset block uses, and the reason this block has a single input rather than two.
MEASURED, not transcribed: driven with 200 random (alpha, beta) pairs, R2026a's aerolibtransform2 block answers this matrix in every one of 1800 entries to the LAST BIT. The block carries no dialog parameters at all and no SampleTime, so the whole of its bridge is the library path and the port list.
⚠ THE MIDDLE COLUMN IS SIDESLIP ALONE, AND (2,1) IS AN EXACT ZERO. Rotating a vector into wind axes leaves nothing of the angle of attack on the second axis, so that entry is a constant no argument can move -- it is carried as a table entry rather than left as a gap, because a backend that quietly filled it would still produce a plausible-looking matrix.
ALGEBRAIC and STATELESS, and not linear in its input -- the entries are products of sines and cosines of it -- so the block carries no state space and model reduction correctly reports it as unmergeable.
Sample results#
No stimulus produced a sampled output in this rig — Invalid input size at Body To Wind Rotation Matrix block: ICore Blocks/Home/Body To Wind Rotation Matrix. That is a fact about the single-block rig, not a verdict on the block: an offline batch fit, a block whose output only appears at onSolverFinish, or one that needs a driven environment cannot be exercised alone.
Category unsampled · sample time 0.1 · 60 steps · commit d60dae4fbffb95396a5d3d2dc3582537a193a262 · produced by docsSample --out <folder> --blocks Body_To_Wind_Rotation_Matrix Wind_Angles_To_Rotation_Matrix ECEF_To_NED_Rotation_Matrix --steps 60
Sample data: docs/generated/samples/Robotics__Axes_Transformations__Body_To_Wind_Rotation_Matrix.json