International Geomagnetic Reference Field — Robotics/Gravity Models
Robotics/Gravity_Models/International_Geomagnetic_Reference_Field · 0 input / 0 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.
International Geomagnetic Reference Field
Robotics / Gravity Models
The Earth's main magnetic field and its secular variation from the IAGA's International Geomagnetic Reference Field, a spherical-harmonic expansion of degree 13 whose Gauss coefficients are published for every fifth year since 1900. At a geodetic position and a decimal year t the block reports the field vector in the local north-east-down frame, the four readings derived from it, and the change each undergoes in a year:
- B = [X; Y; Z] nT, from the potential V = a·Σn=1..13(a/r)n+1Σm=0..n (gnmcos mλ + hnmsin mλ)·Pnm(cosθ), evaluated in geocentric spherical coordinates and rotated into the geodetic frame.
- H = √(X² + Y²), F = √(H² + Z²), D = atan2(Y, X) and I = atan2(Z, H), the last two in degrees.
- The coefficients at t: linear between two five-year epochs, g(t) = g(tk) + (t − tk)·(g(tk+1) − g(tk))/5, and after the last epoch along the published secular-variation column. Their rate is the model's dg/dt, and the field synthesised from it is dB.
- dH, dD, dI and dF are what a year changes: the readings of (B + dB) less the readings of B, with the two angles in minutes per year.
Ports
- h – height above the WGS84 ellipsoid in metres, [1,1].
- lat – geodetic latitude in degrees, north positive, [1,1]. A latitude past a pole is folded back (±180 − φ) and its longitude turned half a revolution, as the reference does.
- lon – geodetic longitude in degrees, east positive, [1,1]; wrapped to [−180, 180].
- dyear – the time as a decimal year, [1,1]. Outside the generation's range the coefficients are extrapolated rather than clamped.
- B – the field vector [X; Y; Z] in nT, a [3,1]: north, east and down.
- H – horizontal intensity in nT, [1,1].
- D – declination in degrees, east positive, [1,1].
- I – inclination in degrees, down positive, [1,1].
- F – total intensity in nT, [1,1].
- dB – the secular variation of the field vector in nT/year, a [3,1].
- dH – secular variation of the horizontal intensity in nT/year, [1,1].
- dD – secular variation of the declination in minutes/year, [1,1].
- dI – secular variation of the inclination in minutes/year, [1,1].
- dF – secular variation of the total intensity in nT/year, [1,1].
Parameters
- Generation – which revision of the model runs. This selects a
whole table of coefficients, not a value, and each generation extends the
last by one epoch and revises the one before it:
- IGRF-11 – epochs 1900 to 2010, valid to 2015.
- IGRF-12 – to 2015, valid to 2020.
- IGRF-13 – to 2020, valid to 2025.
- IGRF-14 – to 2025, valid to 2030. The default, as in Simulink.
- 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 chosen generation's whole table is inlined at export time – 195 coefficients for each of its epochs plus the secular-variation column, about 4000 numbers – because the decimal year stays a port and the interval it falls in is only known at run time. The epoch interpolation, the Schmidt normalisation and the synthesis are emitted once, as loops, so all ten targets run the same statements in the same order.
The three hardware targets are simulation-only: sines,
arctangents, square roots and a degree-13 recursion do not belong in a Q16.16
datapath, so the arithmetic runs in real and only the ports are
fixed point. Q16.16 also bounds every port to about ±32767, which for this
block means a height below about 32.7 km and a field below 32767 nT – the
Earth's field exceeds that over most of the globe, so a hardware core is honest
only where the field is weak (the South Atlantic, where it falls to ~22000 nT).
PLC Structured Text has neither floor nor atan2
and rebuilds both from TRUNC and ATAN.
Simulink bridge
Import and export, mapped to Aerospace Blockset's
aerolibgravity2/International Geomagnetic Reference Field.
Generation → generation, value for value
(IGRF-11 → IGRF-11 and so on), which is 1:1 and lossless
in both directions.
Four Simulink parameters are always implied and carry no configuration here:
units is always Metric (MKS) (English units – feet and
nanogauss – do not cross), time_in is always on so the
decimal year arrives on the fourth port rather than as a month/day/year dialog
(no configuration here can add or remove a port), sv_out is always
on so the five secular-variation outputs exist, and action is
always Warning. An imported block holding any other value of the four is
reported rather than silently accepted. action only decides whether
Simulink reports a position or date outside the model's range; the values are the
same either way, and this block computes them without a report. The Simulink
block defines no SampleTime (measured), so the rate stays on
the ICore side.
Notes
- Algebraic and stateless: the output depends only on this sample's inputs.
- Not linear, so the block carries no state space and model reduction correctly reports it as unmergeable.
- The secular variation of H, D, I and F is a difference, not a derivative: it is what the next year brings, computed as the readings of (B + dB) less the readings of B. Measured against R2026a, the analytic derivative form is wrong by up to 4 % and the difference form by 1.3×10−11.
- Before 2000 the field is degree 10: the coefficients of degrees 11 to 13 are zero in every epoch column up to 1995, so a date in the twentieth century runs the same loops over a shorter model, and a date between 1995 and 2000 interpolates those degrees up from zero.
- At a geographic pole the east component is a limit rather than a quotient, and the block takes the reference's own special case for it.
- Verified against R2026a: over 400 positions and dates – both poles exactly, latitudes of 95, 181 and 270, dates from 1850 to 2035 – the Simulink block and this arithmetic agree to 1.5×10−12 relative on the field (and 1.3×10−11 on dD), the worst of it within a few metres of a pole; away from the poles it is ~10−15.
Code facts#
| Fact | Value |
|---|---|
| registered type | Robotics/Gravity_Models/International_Geomagnetic_Reference_Field |
| family | Robotics/Gravity_Models |
| solver environment class | ICoreBlock_0_Robotics_1_Gravity_Models_2_International_Geomagnetic_Reference_Field |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Gravity_Models/International_Geomagnetic_Reference_Field/ICoreBlock_0_Robotics_1_Gravity_Models_2_International_Geomagnetic_Reference_Field.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Gravity_Models/International_Geomagnetic_Reference_Field/ICoreBlock_0_Robotics_1_Gravity_Models_2_International_Geomagnetic_Reference_Field.h |
| default size on canvas | 180 × 220 px |
| ports at insert | ? in, ? 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 | h |
| 2 | in | ICoreDouble | lat |
| 3 | in | ICoreDouble | lon |
| 4 | in | ICoreDouble | dyear |
| 5 | out | ICoreDouble | B |
| 6 | out | ICoreDouble | H |
| 7 | out | ICoreDouble | D |
| 8 | out | ICoreDouble | I |
| 9 | out | ICoreDouble | F |
| 10 | out | ICoreDouble | dB |
| 11 | out | ICoreDouble | dH |
| 12 | out | ICoreDouble | dD |
| 13 | out | ICoreDouble | dI |
| 14 | out | ICoreDouble | dF |
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 |
|---|---|---|
Generation | options~~GEN_OPTIONS[GEN_COUNT - 1] | generation |
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 | aerolibgravity2/International Geomagnetic Reference Field |
| port-count rule | PortsParam::None |
SampleTime parameter | no — the counterpart defines none; the rate stays on the ICore side |
| always set | units = Metric (MKS), time_in = on, action = Warning, sv_out = on |
| ICore config | Simulink parameter | Value translation |
|---|---|---|
Generation | generation | IGRF-11 → IGRF-11, IGRF-12 → IGRF-12, IGRF-13 → IGRF-13, IGRF-14 → IGRF-14 |
Caveat (shown to the user): the position crosses on three scalar ports (height in metres, latitude and longitude in degrees) and the time on a fourth as a decimal year; the field comes back as a [3,1] [X; Y; Z] in nT plus H, D, I and F, and then the secular variation of each (the two angles in minutes per year). 'units' is always Metric (MKS): English units (feet, nanogauss) do not cross. 'time_in' is always on -- the month/day/year dialog would remove the fourth input port, and no configuration here can move a port. 'sv_out' is always on, so the five secular-variation outputs exist. 'action' is always Warning; it only decides whether Simulink reports a position or a date outside the model's range, and the values are identical either way. The Simulink block has no SampleTime, so the rate stays on the ICore side. ⚠ Generation IS A MODE: each is a table of its own and each is rigged separately
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).
International Geomagnetic Reference Field -- the IAGA main-field model, IGRF-11 .. IGRF-14 Inputs h (metres), latitude and longitude (degrees), the time as a decimal year Outputs [X; Y; Z] nT in north-east-down, H nT, D degrees, I degrees, F nT, and the secular variation of each: [dX; dY; dZ] nT/yr, dH nT/yr, dD min/yr, dI min/yr, dF nT/yr
The arithmetic is shared with the World Magnetic Model and lives in ICoreGeomagneticSupport: one spherical-harmonic synthesis, ten export targets, and a coefficient set per model. This file is the block around it -- the ports, the generation, and the Simulink mapping.
MEASURED against R2026a's
aerolibgravity2/International Geomagnetic Reference Fieldover 400 positions and dates (both poles exactly, latitudes of 95, 181, 270, a longitude of -370, heights from -1 km to 609 km, dates from 1900 to 2030 and outside both ends): this arithmetic reproduces the block to <= 1.5e-12 relative on the field and <= 1.3e-11 on dD -- the worst of both within a few metres of a pole, where the block's own geocentric conversion rounds differently; away from the poles it is ~1e-15.⚠ THE DECIMAL YEAR IS A PORT, not a date parameter. The Simulink block offers both (
time_in); the port is its default, and it is the only form in which the epoch interpolation is exercised at run time, so it is what crosses. Outside 1900..last epoch + 5 the coefficients are EXTRAPOLATED along the nearest interval's slope, which is what the Simulink block does (measured at 1850, 1899, 2031 and 2035 against IGRF-14).⚠ EVERY GENERATION IS A MODE: the four tables differ in their last epoch column and their secular-variation column, and every earlier column is identical (measured bit for bit).
ALGEBRAIC and STATELESS.
Sample results#
| t | in ICoreDouble-Out-0 | in ICoreDouble-Out-0 | in ICoreDouble-Out-0 | out ICoreDouble-Out-0 [3x1] entry 0 | out ICoreDouble-Out-1 | out ICoreDouble-Out-2 |
|---|---|---|---|---|---|---|
| 0 | -2 | -2 | -2 | [2.812e4, -4.309e4, 2.513e5] | 5.145e4 | -56.87 |
| 0.4 | 0.5 | 0.5 | 0.5 | [1.705e4, -5.55e4, 2.437e5] | 5.806e4 | -72.92 |
| 0.8 | -2 | -2 | -2 | [2.812e4, -4.309e4, 2.513e5] | 5.145e4 | -56.87 |
| 1.2 | 0.5 | 0.5 | 0.5 | [1.705e4, -5.55e4, 2.437e5] | 5.806e4 | -72.92 |
| 1.6 | -2 | -2 | -2 | [2.812e4, -4.309e4, 2.513e5] | 5.145e4 | -56.87 |
| 2 | 0.5 | 0.5 | 0.5 | [1.705e4, -5.55e4, 2.437e5] | 5.806e4 | -72.92 |
| 2.4 | -2 | -2 | -2 | [2.812e4, -4.309e4, 2.513e5] | 5.145e4 | -56.87 |
| 2.8 | 0.5 | 0.5 | 0.5 | [1.705e4, -5.55e4, 2.437e5] | 5.806e4 | -72.92 |
| 3.2 | -2 | -2 | -2 | [2.812e4, -4.309e4, 2.513e5] | 5.145e4 | -56.87 |
| 3.6 | 0.5 | 0.5 | 0.5 | [1.705e4, -5.55e4, 2.437e5] | 5.806e4 | -72.92 |
| 4 | -2 | -2 | -2 | [2.812e4, -4.309e4, 2.513e5] | 5.145e4 | -56.87 |
| 4.4 | 0.5 | 0.5 | 0.5 | [1.705e4, -5.55e4, 2.437e5] | 5.806e4 | -72.92 |
| 4.8 | -2 | -2 | -2 | [2.812e4, -4.309e4, 2.513e5] | 5.145e4 | -56.87 |
| 5.2 | 0.5 | 0.5 | 0.5 | [1.705e4, -5.55e4, 2.437e5] | 5.806e4 | -72.92 |
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.495e4 … 1.919e4 |
ramp | Ramp: slope 1 from t = 0 | -2997 … 1.919e4 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | 1.495e4 … 2.359e4 |
step | Step: 0 -> 1 at t = 1 s | 1.495e4 … 1.919e4 |
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 cdc0c690bf9b47670fe4cf40a3a20ef35334348c · produced by docsSample --out <folder> --blocks International_Geomagnetic_Reference_Field --steps 60 · data docs/generated/samples/Robotics__Gravity_Models__International_Geomagnetic_Reference_Field.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).