Geodetic To Geocentric Latitude — Robotics/Axes Transformations
Robotics/Axes_Transformations/Geodetic_To_Geocentric_Latitude · 2 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.
Geodetic To Geocentric Latitude
Robotics / Axes Transformations
Converts the geodetic latitude a map uses – the angle between the equatorial plane and the ellipsoid's surface normal – into the geocentric latitude, the angle at the planet's centre. With e² = f(2−f):
- N = R / √(1 − e²·sin²φ), the prime vertical radius of curvature
- ρ = (N + h)·cosφ, the distance from the spin axis
- z = (N(1−e²) + h)·sinφ, the height above the equatorial plane
- λ = atan2(z, ρ)
The two definitions differ because an ellipsoid's surface normal does not point at its centre. On Earth the gap peaks near 45° at about 0.19°, and it is exactly zero at the equator and at the poles. Its inverse is Geocentric To Geodetic Latitude.
Ports
- gd – the geodetic latitude, in DEGREES. Any size [m,n]; the block works entry by entry.
- h – the altitude above the ellipsoid, in R's length unit. The same size as gd – there is no scalar expansion here, because a latitude and an altitude are a position rather than a signal, and a broadcast would more often be a mistake than a convenience.
- gc – the geocentric latitude, in DEGREES. Same size as the inputs.
Parameters
- Flattening – the ellipsoid's flattening f, a dimensionless scalar. Defaults to 0.0033528106647474805, which is WGS84's 1/298.257223563. Zero gives a sphere, on which the two latitudes are equal at every point. A value of exactly 1 is refused: it is a degenerate ellipsoid with no polar extent.
- Equatorial Radius – the ellipsoid's equatorial radius R, a scalar. Defaults to WGS84's 6378137 metres, and its unit is the unit h is read in.
- 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.
R and e² are baked into the arithmetic at export time rather than exposed as tunables: they say which planet this is, which is structure and not a knob. Both are printed at 17 significant digits, so every backend computes the same double.
The three hardware targets are simulation-only: a sine, a
square root, a division and an arctangent have no Q16.16 form, so values convert
at the port boundary and the arithmetic runs in floating point. And a
fixed-point port cannot carry an Earth-sized radius or altitude at all
– Q16.16 saturates past about 32767 – so a hardware export of this
block is for a small custom body, or for lengths expressed in a larger unit.
PLC Structured Text has no ATAN2 in IEC 61131-3, so it is
rebuilt from ATAN with the quadrant tests written out.
Simulink bridge
Import and export, mapped to Aerospace Blockset's
aerolibtransform2/Geodetic to Geocentric Latitude.
Flattening → F and Equatorial Radius →
R, values passing straight through.
Three Simulink parameters are always implied and carry no configuration here:
ptype is always Custom, units always
Metric (MKS), and outputRadius always off. Custom is
not cosmetic – a block left on its Earth (WGS84) setting accepts
and discards a written F or R, so the pair only takes effect alongside it, and
Custom carrying the WGS84 numbers is bit-identical to the Earth setting.
outputRadius would add a second output port carrying the
radius to the point; this block has one output, so it is pinned off. The
Simulink block defines no SampleTime, so the rate stays on
the ICore side.
Notes
- Algebraic and stateless: the output depends on this sample alone.
- Not linear, so the block carries no state space and model reduction correctly reports it as unmergeable.
- Nothing checks that h and R share a unit. Feed metres against an R in feet and the answer is still a latitude, wrong by an amount no diagnostic can see.
- Verified against R2026a: over 200 random latitude/altitude pairs the arrangement above reproduces the Simulink block in all 200 samples to the last bit. Driven end to end through the parity rig – where the exported MATLAB core meets Simulink's own Gain shapers rather than a transcription of the formula – the two agree to 7.1×10−15 degrees.
Code facts#
| Fact | Value |
|---|---|
| registered type | Robotics/Axes_Transformations/Geodetic_To_Geocentric_Latitude |
| family | Robotics/Axes_Transformations |
| solver environment class | ICoreBlock_0_Robotics_1_Axes_Transformations_2_Geodetic_To_Geocentric_Latitude |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Axes_Transformations/Geodetic_To_Geocentric_Latitude/ICoreBlock_0_Robotics_1_Axes_Transformations_2_Geodetic_To_Geocentric_Latitude.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Axes_Transformations/Geodetic_To_Geocentric_Latitude/ICoreBlock_0_Robotics_1_Axes_Transformations_2_Geodetic_To_Geocentric_Latitude.h |
| default size on canvas | 150 × 78 px |
| ports at insert | 2 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 | gd |
| 2 | in | ICoreDouble | h |
| 3 | out | ICoreDouble | gc |
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 |
|---|---|---|
Flattening | gFmt(WGS84_F) | — |
Equatorial Radius | gFmt(WGS84_R) | — |
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/Geodetic to \nGeocentric Latitude |
| port-count rule | PortsParam::None |
SampleTime parameter | no — the counterpart defines none; the rate stays on the ICore side |
| always set | ptype = Custom, units = Metric (MKS), outputRadius = off |
| ICore config | Simulink parameter | Value translation |
|---|---|---|
CONFIG_F.c_str() | F | passes through |
CONFIG_R.c_str() | R | passes through |
Caveat (shown to the user): both latitudes are in DEGREES and the altitude carries R's length unit. 'ptype' is always written as Custom, because a block left on Earth (WGS84) accepts a written F or R and discards it -- Custom carrying the WGS84 numbers is bit-identical to the Earth setting, so nothing is lost. 'outputRadius' is always off: switching it on adds a SECOND output port carrying the radius to the point, and this block has one output. The Simulink block has no SampleTime, so the rate stays on the ICore side
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:
B02 Simulink params rule(s) this tool cannot resolve
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).
Geodetic To Geocentric Latitude -- the map latitude to the angle at the planet's centre e2 = f * (2 - f) N = R / sqrt(1 - e2 * sin(gd)^2) rho = (N + h) * cos(gd) z = (N * (1 - e2) + h) * sin(gd) gc = atan2(z, rho)
Both latitudes in DEGREES, as the Aerospace Blockset block takes them; the altitude carries R's length unit. The two definitions differ because the surface normal of an ellipsoid does not point at its centre -- on Earth the gap peaks near 45 degrees at about 0.19 degrees, and it vanishes at the equator and at the poles.
⚠ THE ARRANGEMENT ABOVE WAS MEASURED, NOT DERIVED. Over 200 random (latitude, altitude) pairs it reproduces R2026a's block in all 200 samples TO THE LAST BIT. An algebraically identical spelling does not:
gd * 0.017453292519943295in place ofgd * pi / 180moves 74 of the 200 by one unit in the last place, and the same on the way out. Every backend therefore writes both conversions the same way round, and this is why the two blocks of this family differ on the point -- ECEF To NED's Simulink counterpart really does multiply by a pre-divided slope, and this one really does not.⚠ THE TWO CONSTANTS ARE STRUCTURE, NOT TUNING. R and f say which planet this is, so they are baked into every export rather than exposed as tunable parameters.
ALGEBRAIC and STATELESS, and nonlinear in its input, so no state space.
Sample results#
| t | in ICoreDouble-Out-0 | in ICoreDouble-Out-0 | out ICoreDouble-Out-0 |
|---|---|---|---|
| 0 | -2 | -2 | -1.987 |
| 0.4 | 0.5 | 0.5 | 0.4967 |
| 0.8 | -2 | -2 | -1.987 |
| 1.2 | 0.5 | 0.5 | 0.4967 |
| 1.6 | -2 | -2 | -1.987 |
| 2 | 0.5 | 0.5 | 0.4967 |
| 2.4 | -2 | -2 | -1.987 |
| 2.8 | 0.5 | 0.5 | 0.4967 |
| 3.2 | -2 | -2 | -1.987 |
| 3.6 | 0.5 | 0.5 | 0.4967 |
| 4 | -2 | -2 | -1.987 |
| 4.4 | 0.5 | 0.5 | 0.4967 |
| 4.8 | -2 | -2 | -1.987 |
| 5.2 | 0.5 | 0.5 | 0.4967 |
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.9933 |
ramp | Ramp: slope 1 from t = 0 | 0 … 5.761 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | -0.9933 … 0.9929 |
step | Step: 0 -> 1 at t = 1 s | 0 … 0.9933 |
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 d541288cd5ad49da343fa0a3f9debc142164f671 · produced by docsSample --out <folder> --blocks Geodetic_To_Geocentric_Latitude Geocentric_To_Geodetic_Latitude --steps 60 · data docs/generated/samples/Robotics__Axes_Transformations__Geodetic_To_Geocentric_Latitude.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).