Generated reference › Direction Cosine Matrix To Quaternion — Robotics/Orientation 3D
kind: generated#block#robotics-orientation-3d

Direction Cosine Matrix To Quaternion — Robotics/Orientation 3D

Robotics/Orientation_3D/Direction_Cosine_Matrix_To_Quaternion · 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.

Direction Cosine Matrix To Quaternion

Robotics / Orientation 3D

Converts a [3,3] passive direction cosine matrix D into the scalar-first [4,1] quaternion [w x y z]T that represents the same rotation. It takes the same four-way branch MATLAB's dcm2quat takes: whichever of four square roots is largest is the one computed, and the other three components are formed from it. Writing tr for the trace and d for the diagonal:

  • tr > 0 – s = √(tr+1), w = s/2, and each of x, y, z is an antisymmetric pair of entries divided by 2s.
  • d₁ largest – s = √(d₁−d₀−d₂+1), y = s/2, the rest scaled by 0.5/s.
  • d₂ largest – the same shape with z = s/2.
  • otherwise – the same shape with x = s/2.

Branching on the largest root is what keeps the answer accurate near a half turn, where the trace formula divides by something close to zero. Quaternion To Rotation Matrix is the map back – through a transpose, see Notes.

Ports

  • D – the direction cosine matrix, a [3,3]. The size is fixed. It is the passive matrix, the aerospace convention.
  • q – the quaternion, a [4,1] column [w x y z]T, scalar first. 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.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text.

The branch is chosen by the data rather than by a setting, so every generated body carries all four arms; there is nothing here for a tunable parameter to hold.

On the three HDL targets the block is offered as simulation-only: a Q16.16 datapath has neither a square root nor a reciprocal, so all of it goes through real arithmetic and only the ports quantize. They also guard the root against a negative radicand and answer zero there, because VHDL's MATH_REAL SQRT asserts and an assertion aborts the whole simulation rather than producing one bad sample – see Notes for what the other targets do.

Simulink bridge

Both directions, onto aerolibtransform2/Direction Cosine Matrix to Quaternions in the Aerospace Blockset. No parameter crosses. The Simulink block's two dialog parameters, action and tolerance, decide what it does when the matrix it is handed is not orthonormal; this block validates nothing and computes the same arithmetic on whatever arrives, so both are listed as ignored rather than mapped – the same choice Direction Cosine Matrix To Rodrigues makes.

The block defines no SampleTime parameter, so the rate stays on this side and a block configured with an explicit positive rate reports that the rate did not cross.

The Simulink block uses a row at its quaternion port; this one uses a column, as every vector port in this family does.

Notes

  • Algebraic, with no state: the output depends only on the current input.
  • Feed the PASSIVE matrix. This tree's Quaternion To Rotation Matrix produces the active rotation, which is this matrix's transpose – so a round trip through the two blocks needs a Matrix Operations / Transpose between them. Measured: transposed, the pair returns the original quaternion to the last bit; untransposed, it returns the conjugate, which is a perfectly valid quaternion of the wrong rotation and so cannot be caught by anything downstream.
  • No normalization, and no validation. The arithmetic runs on whatever arrives. A matrix that is not orthonormal produces a quaternion that is not a unit one, and no warning is raised – that is what the Simulink block's action and tolerance are for, and they do not cross.
  • The sign is not unique, and the branch decides it. q and −q name the same rotation; this block returns whichever one the largest-root arm produces, exactly as dcm2quat does.
  • A vanishing root gives an all-zero quaternion, not an infinity. The reciprocal is guarded, so the multiplier stays at zero when the root does. Measured: a half turn about y answers [0 0 1 0] exactly.
  • A NEGATIVE radicand is not a rotation matrix. The six software targets answer NaN there (MATLAB, a complex number) and the three HDL ones answer zero, for the assertion reason under Code export. It cannot arise from an orthonormal matrix.
  • Bit-identical to the reference, deliberately. The trace arm divides by 2s and the other three multiply by 0.5/s, which are different doubles; dcm2quat writes it that way and so does this block.
  • Deliberately no state space – the map is not linear.

Code facts#

FactValue
registered typeRobotics/Orientation_3D/Direction_Cosine_Matrix_To_Quaternion
familyRobotics/Orientation_3D
solver environment classICoreBlock_0_Robotics_1_Orientation_3D_2_Direction_Cosine_Matrix_To_Quaternion
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Orientation_3D/Direction_Cosine_Matrix_To_Quaternion/ICoreBlock_0_Robotics_1_Orientation_3D_2_Direction_Cosine_Matrix_To_Quaternion.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Orientation_3D/Direction_Cosine_Matrix_To_Quaternion/ICoreBlock_0_Robotics_1_Orientation_3D_2_Direction_Cosine_Matrix_To_Quaternion.h
default size on canvas150 × 76 px
ports at insert1 in, 1 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleD
2outICoreDoubleq

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.

supportSupport::Both
Simulink pathaerolibtransform2/Direction Cosine Matrix \nto Quaternions
port-count rulePortsParam::None
SampleTime parameterno — the counterpart defines none; the rate stays on the ICore side
deliberately not crossedaction, tolerance

Caveat (shown to the user): MEASURED 2026-09-10: the Simulink block's dialog carries action (None | Warning | Error) and tolerance (default eps(2)) and nothing else. Neither crosses -- they govern an orthonormality CHECK this block does not perform, exactly as with Direction Cosine Matrix To Rodrigues. It defines no SampleTime parameter, so an ICore rate set explicitly stays on this side and is reported, and it uses a ROW at its quaternion port where this block uses a column

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:

  • B0 every 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).

Direction Cosine Matrix To Quaternion — a [3,3] passive DCM to its [4,1] scalar-first quaternion, by the four-way branch MATLAB's dcm2quat takes Whichever of the four square roots is largest is the one computed; the other three components follow from it. Writing tr for the trace and d for the diagonal:

tr > 0 s = sqrt(tr + 1) q0 = s/2, and q1..q3 = (antisymmetric pair) / (2s) d1 > d0 and d1 > d2 s = sqrt(d1 - d0 - d2 + 1), q2 = s/2, k = 0.5/s d2 > d0 s = sqrt(d2 - d0 - d1 + 1), q3 = s/2, k = 0.5/s otherwise s = sqrt(d0 - d1 - d2 + 1), q1 = s/2, k = 0.5/s

⚠ THE FOUR ARMS ARE WRITTEN ONCE, in branchAt() below, and the C++ reference and all TEN generators read that one table. Four arms times ten backends would otherwise be forty transcriptions of a sign-and-index table, which is exactly the shape of thing that goes wrong in one cell and nowhere else.

⚠ THREE DETAILS TAKEN OFF dcm2quat.m RATHER THAN OFF A TEXTBOOK (R2026a, 2026-09-10):

  1. THE INDEX PAIRS ARE TRANSPOSED from the familiar ones -- the trace arm forms q1 from

D(1,2) - D(2,1), not D(2,1) - D(1,2) -- because the aerospace DCM is the PASSIVE matrix, the transpose of the active rotation this tree's Quaternion To Rotation Matrix produces. MEASURED: quat(R') returns q exactly, and quat(R) returns the conjugate, which is a valid quaternion of the wrong rotation and cannot be caught downstream.

  1. THE TRACE ARM DIVIDES BY 2s; THE OTHER THREE MULTIPLY BY 0.5/s. Those are different

doubles, and the asymmetry is reproduced on purpose: it is what makes this block bit-identical to the reference rather than merely close to it.

  1. THE RECIPROCAL IS GUARDED -- if (s ~= 0) s = 0.5/s; end -- so a vanishing root leaves

the multiplier at ZERO and the answer is an all-zero quaternion rather than an infinity.

MEASURED against aerolibtransform2/Direction Cosine Matrix \nto Quaternions (R2026a, 2026-09-10) through its own dcm2quat, on FIVE matrices chosen so that each of the four arms runs at least once -- traces 2.42, -0.967, -0.979, -0.992 and -0.992, largest diagonal at index 1, 1, 2 and 0 respectively. All five agree to the LAST BIT (worst difference exactly 0), and the degenerate half turn about y answers [0 0 1 0].

⚠ SIMULATION-ONLY on the three HDL targets: a square root and a reciprocal per sample. They additionally guard the root against a negative radicand and answer zero there -- VHDL's MATH_REAL SQRT asserts, and an assertion aborts the whole simulation rather than producing one bad sample. A negative radicand means the matrix is not a rotation at all; the six

Sample results#

No stimulus produced a sampled output in this rig — Invalid input size at Direction Cosine Matrix To Quaternion block: ICore Blocks/Home/Direction Cosine Matrix To Quaternion. 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 3c100aff6f27235305db4ad4d572f32e342718ad · produced by docsSample --out <folder> --blocks Direction_Cosine_Matrix_To_Quaternion --steps 60

Sample data: docs/generated/samples/Robotics__Orientation_3D__Direction_Cosine_Matrix_To_Quaternion.json