Polar To Cartesian — Robotics/Coordinate Transforms
Robotics/Coordinate_Transforms/Polar_To_Cartesian · 2 input / 2 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.
Polar To Cartesian
Robotics / Coordinate Transforms
Converts a planar polar pair to Cartesian form, entry by entry: x = r·cos θ and y = r·sin θ, with θ in radians.
The inverse of Cartesian To Polar. The two round-trip exactly for any θ already inside (−π, π] and any r ≥ 0.
Ports
- r – the radius, any size [m,n]. It may be negative – see Notes.
- theta – the angle in radians. It must be the same size as r; the two are paired entry by entry and neither is broadcast. It need not lie in any particular interval.
- x – r·cos θ, the same size as the inputs.
- y – r·sin θ, the same size as the inputs.
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. There is no tunable parameter, because the block has no parameter at all.
The three HDL targets are simulation-only: they carry the
arithmetic in real and quantize only at the port boundary, because
a sine and a cosine have no Q16.16 form to call.
Every backend needs only a multiply and the two trigonometric functions,
which each of the ten has by name – including PLC Structured Text,
where SIN and COS are standard. Nothing here is
guarded, because nothing here has a restricted domain.
Simulink bridge
Both directions, onto
simulink_extras/Transformations/Polar to Cartesian. That block has
no dialog parameters whatsoever (verified with
get_param(blk, 'DialogParameters'), which comes back empty), so
there is nothing to map: the bridge is the library path alone. It also has
no SampleTime parameter, so Sampling Time (s) does
not cross – the block runs at the surrounding Simulink rate.
Notes
- Algebraic and stateless: both outputs depend only on the current inputs.
- Nonlinear in θ, and deliberately carries no state space.
- A negative radius is passed through the formula, not corrected. r = −2 at θ = π/3 gives (−1, −1.732) – the point diametrically opposite. The block does not fold it to r > 0 with θ + π, and does not clamp it. This is measured against the Simulink block, and it is the useful behaviour: a signed range from a sensor stays meaningful.
- θ is not wrapped. Angles far outside (−π, π] are fine – sine and cosine are periodic. So this block accepts the raw, unwrapped angle that poses and headings carry in this library, with no Angle Wrap needed in front of it.
- Round-tripping is exact but not an identity in general. Feeding x and y back through Cartesian To Polar returns |r| and θ wrapped into (−π, π] – the same point, in canonical form. That is the polar representation being many-to-one, not a loss of precision: the measured error is exactly zero.
Code facts#
| Fact | Value |
|---|---|
| registered type | Robotics/Coordinate_Transforms/Polar_To_Cartesian |
| family | Robotics/Coordinate_Transforms |
| solver environment class | ICoreBlock_0_Robotics_1_Coordinate_Transforms_2_Polar_To_Cartesian |
| source | src/ICoreSDK/ICoreBlockLibrary/Blocks/Robotics/Coordinate_Transforms/Polar_To_Cartesian/ICoreBlock_0_Robotics_1_Coordinate_Transforms_2_Polar_To_Cartesian.cpp |
| header | src/ICoreSDK/ICoreBlockLibrary/Blocks/Robotics/Coordinate_Transforms/Polar_To_Cartesian/ICoreBlock_0_Robotics_1_Coordinate_Transforms_2_Polar_To_Cartesian.h |
| default size on canvas | 126 × 84 px |
| ports at insert | 2 in, 2 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 | r |
| 2 | in | ICoreDouble | theta |
| 3 | out | ICoreDouble | x |
| 4 | out | ICoreDouble | y |
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 | simulink_extras/Transformations/Polar to Cartesian |
| 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 Simulink block has no dialog parameters at all, so nothing but the block itself crosses; it also defines no SampleTime, so "Sampling Time (s)" does not cross and the block runs at the surrounding Simulink rate
Catalog contract: src/ICoreSDK/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).
Polar To Cartesian — the planar transform, elementwise x = r * cos(theta) y = r * sin(theta)
The inverse of Cartesian_To_Polar, measured WITH it in one MATLAB session against one shared probe table and then round-tripped through it -- 0.000e+00 error against the canonical polar form, and every point independently matched r*cos / r*sin. Measuring the two together is what the board row asked for, and it is the only way to catch a pair that is self-consistent and both wrong.
⚠ TWO MEASURED FACTS THAT MAKE THIS BLOCK TOTAL, and they are why it carries no guards where its inverse carries two:
- a NEGATIVE radius is NOT canonicalized -- r = -2 at theta = pi/3 gives (-1, -1.732),
the formula as written. No folding into r > 0, no clamp to zero.
- theta is NOT wrapped -- 5.5 and -4.2 rad were probed and simply go to sin/cos, which
are periodic. So this block takes the RAW unwrapped angle the convention ledger says poses carry, with no Angle_Wrap needed in front of it.
sin and cos are total in IEEE.MATH_REAL, in Verilog/SystemVerilog's real system functions and in IEC 61131-3 alike, so unlike Cartesian_To_Polar there is no domain to guard in any of the ten backends and PLC ST needs no reconstruction -- it has SIN and COS by name.
Sample results#
| t | in ICoreDouble-Out-0 | in ICoreDouble-Out-0 | out ICoreDouble-Out-0 | out ICoreDouble-Out-1 |
|---|---|---|---|---|
| 0 | -2 | -2 | 0.8323 | 1.819 |
| 0.4 | 0.5 | 0.5 | 0.4388 | 0.2397 |
| 0.8 | -2 | -2 | 0.8323 | 1.819 |
| 1.2 | 0.5 | 0.5 | 0.4388 | 0.2397 |
| 1.6 | -2 | -2 | 0.8323 | 1.819 |
| 2 | 0.5 | 0.5 | 0.4388 | 0.2397 |
| 2.4 | -2 | -2 | 0.8323 | 1.819 |
| 2.8 | 0.5 | 0.5 | 0.4388 | 0.2397 |
| 3.2 | -2 | -2 | 0.8323 | 1.819 |
| 3.6 | 0.5 | 0.5 | 0.4388 | 0.2397 |
| 4 | -2 | -2 | 0.8323 | 1.819 |
| 4.4 | 0.5 | 0.5 | 0.4388 | 0.2397 |
| 4.8 | -2 | -2 | 0.8323 | 1.819 |
| 5.2 | 0.5 | 0.5 | 0.4388 | 0.2397 |
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) | 0 … 0.5403 |
ramp | Ramp: slope 1 from t = 0 | -3.287 … 5.136 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | -0.561 … 0.5611 |
step | Step: 0 -> 1 at t = 1 s | 0 … 0.5403 |
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 ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/Robotics__Coordinate_Transforms__Polar_To_Cartesian.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).