WGS84 Gravity Model — Robotics/Gravity Models
Robotics/Gravity_Models/WGS84_Gravity_Model · 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.
WGS84 Gravity Model
Robotics / Gravity Models
Normal gravity of the 1984 World Geodetic System's equipotential ellipsoid (NIMA TR8350.2) at a geodetic position, in the local north-east-down frame: g = [gN; 0; gD], with gD positive downward and gN positive northward. With the geodetic latitude φ, the height h and the ellipsoid's constants a, e², f, γe, k:
- Taylor Series – gD = γ(φ)·(1 − 2(1 + f + m − 2f·sin²φ)·h/a + 3h²/a²), with γ(φ) = γe(1 + k·sin²φ) / √(1 − e²sin²φ); gN = 0.
- Close Approximation – the point is taken to ellipsoidal-harmonic coordinates (u, β), the two components γu, γβ of the normal field are evaluated there, and gD = √(γu² + γβ²); gN = 0.
- Exact – the same two components rotated through the geocentric latitude ψ and the deflection α = φ − ψ into the geodetic vertical (gD) and the north tangent (gN), so the north component is real.
Ports
- lla – the position as one [3,1], [φ; λ; h]: geodetic latitude and longitude in degrees, then height above the ellipsoid in metres, in that order. A latitude past a pole is folded back and its longitude turned half a revolution, as the reference does.
- g – the gravity vector as one [3,1], [gN; 0; gD] in m/s². The east entry is always 0, and so is the north entry except under Exact.
Parameters
- Gravity Model – which method runs. This selects which
arithmetic executes, not just a value:
- Taylor Series – the closed-form surface gravity expanded to second order in h. The default, and Simulink's. Meant for low heights; the three switches below do not apply to it.
- Close Approximation – the ellipsoidal-harmonic field at the point, as a magnitude. Sub-microgal below about 20000 m.
- Exact – the same field resolved into down and north.
- Exclude Atmosphere – on (the default) uses GM without the atmosphere's mass, 3.9860009×1014; off uses the WGS84 value including it, 3.986004418×1014 m³/s². Harmonic methods only.
- Precessing Reference Frame – on (the default) takes Earth's rate as the IAU value plus the precession in right ascension at the date below, ω = 7.2921151467×10−5 + 7.086×10−12 + 4.3×10−15·T, T in Julian centuries from J2000; off takes the WGS84 rate 7.292115×10−5 rad/s. Harmonic methods only.
- No Centrifugal Effects – on (the default) removes the centrifugal term, leaving pure attraction; off folds it in, which is what an instrument at rest on the Earth feels. Harmonic methods only.
- Month – the date's month, January to December. Defaults to October. Read only with a precessing frame.
- Day – the date's day, a whole number from 1 to 31. Defaults to 10. A day past the month's end is taken as its last day (February 30 as February 28), as Simulink does.
- Year – the date's year, a whole number of 1 or more. Defaults to 2004. A year of 99 is special, exactly as in the reference: the Julian date is then the Day itself rather than a date in the year 99.
- 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 method and the three switches are baked in at export time: a core built for Taylor Series contains no harmonic arithmetic at all, and one built without the centrifugal term contains no centrifugal term. Earth's rate is computed from the date once, when the block's configuration is loaded, and emitted as a constant. Every target emits the reference's association operation for operation, because two of the intermediate quantities are small differences of large ones and a reordered product moves the last three digits; the C and C++ bodies also switch off fused multiply-add for the same reason.
The three hardware targets are simulation-only: sines, cosines,
arctangents, square roots and divisions of a planet-sized geometry 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 what a port can carry to about
±32767, which keeps a hardware core below about 32 km of height.
VHDL carries the body as a function with its own real variables;
PLC Structured Text has no floor and rebuilds it from TRUNC.
How closely a target agrees depends on its math library, because two of the
intermediate quantities are small differences of large ones and magnify a
rounding about a hundred thousand times. Measured over 16 configurations: the
generated MATLAB reproduces the Simulink block exactly, and C, Python, Verilog
and SystemVerilog (under Icarus) agree to about 5×10−15
m/s². A library that differs in the last place moves the answer by up to
about 2×10−12: Java's does (7×10−13),
and so does an optimising C or C++ compiler that fuses a sine and a cosine of
the same angle into one call. VHDL under GHDL uses the IEEE
reference MATH_REAL, whose sine and arctangent are good to about
10−8, and there agrees to about 5×10−4
m/s²; its cosine of π/2 is exactly zero, which would divide by zero at a
latitude of exactly ±90°, so the VHDL body replaces that zero with
the correctly rounded value.
Simulink bridge
Import and export, mapped to Aerospace Blockset's
aerolibgravity2/WGS84 Gravity Model – the library
name really ends in two spaces. Gravity Model →
model (Taylor Series/Close Approximation/Exact
→ WGS84 Taylor Series/WGS84 Close
Approximation/WGS84 Exact), Exclude Atmosphere
→ no_atmos, Precessing Reference Frame →
precessing, No Centrifugal Effects →
no_centrifugal (all three on/off 1:1), Month →
month (1:1), Day → day and Year
→ year. Every mapping is 1:1 and lossless in both
directions.
Three Simulink parameters are always implied and carry no configuration here:
units is always Metric (MKS) (English units do not cross),
jd_loc is always off – Simulink's option of taking the
Julian date on a second input port does not cross, because no
configuration here can add a port, and a Julian date near 2.46×106
could not ride a hardware port in any case – and action is
always Warning. That last one only chooses whether Simulink reports a
height below 0 or above 20000 m, or a folded latitude; the values are the same
either way, and this block computes them without a report. An imported block
holding any other value of the three is reported rather than silently
accepted. Simulink refuses a [3,1] MATRIX on this port: it takes a
1-D 3-element vector (or an [m,3] array of points), so feed the exported block
from something that produces a vector. The Simulink block defines no
SampleTime, so the rate stays on the ICore side.
Notes
- Algebraic and stateless: the output depends only on this sample's input.
- Not linear, so the block carries no state space and model reduction correctly reports it as unmergeable.
- The longitude does not change the answer – the ellipsoid is symmetric about the spin axis – except in its last digits, where the reference's own rounding of it shows. It is wrapped and folded exactly as the reference does so that those digits agree too.
- A latitude past a pole is folded in radians (π − φ across the north pole, −π − φ across the south), with the longitude turned by +π and −π respectively. Only Exact can tell the difference, and without the fold a latitude of 181 is wrong by 19.6 m/s² there.
- Do not check Close Approximation or Exact against MATLAB's
gravitywgs84at its defaults: the function includes the centrifugal term and the atmosphere by default and the Simulink block (and this one) excludes both, so the two disagree by about 0.017 m/s² at 45°. - Verified against R2026a: over 16 configurations and 25 points (both hemispheres, latitudes of 95, 181, 270 and 450, heights from −500 to 32000 m, the leap-day, invalid-day and year-99 dates) the Simulink block and this arithmetic agree to 5.3×10−15.
Code facts#
| Fact | Value |
|---|---|
| registered type | Robotics/Gravity_Models/WGS84_Gravity_Model |
| family | Robotics/Gravity_Models |
| solver environment class | ICoreBlock_0_Robotics_1_Gravity_Models_2_WGS84_Gravity_Model |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Gravity_Models/WGS84_Gravity_Model/ICoreBlock_0_Robotics_1_Gravity_Models_2_WGS84_Gravity_Model.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Gravity_Models/WGS84_Gravity_Model/ICoreBlock_0_Robotics_1_Gravity_Models_2_WGS84_Gravity_Model.h |
| default size on canvas | 170 × 80 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 | lla |
| 2 | out | ICoreDouble | g |
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 |
|---|---|---|
Gravity Model | std::string(MODEL_TAYLOR)%~%MODEL_CLOSE%~%MODEL_EXACT~~MO… | model |
Exclude Atmosphere | onOff | no_atmos |
Precessing Reference Frame | onOff | precessing |
No Centrifugal Effects | onOff | no_centrifugal |
Month | months~~MONTHS[9] | month |
Day | 10 | day |
Year | 2004 | year |
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/WGS84 Gravity Model |
| port-count rule | PortsParam::None |
SampleTime parameter | no — the counterpart defines none; the rate stays on the ICore side |
| always set | units = Metric (MKS), jd_loc = off, action = Warning |
| ICore config | Simulink parameter | Value translation |
|---|---|---|
Gravity Model | model | Taylor Series → WGS84 Taylor Series, Close Approximation → WGS84 Close Approximation, Exact → WGS84 Exact |
Exclude Atmosphere | no_atmos | on → on, off → off |
Precessing Reference Frame | precessing | on → on, off → off |
No Centrifugal Effects | no_centrifugal | on → on, off → off |
Month | month | January → January, February → February, March → March, April → April, May → May, June → June, July → July, August → August, September → September, October → October, November → November, December → December |
Day | day | passes through |
Year | year | passes through |
Caveat (shown to the user): the position crosses as [lat; lon; h] in degrees, degrees and metres, and gravity comes back as [north; 0; down] in m/s^2. ⚠ The Simulink block REFUSES a [3x1] matrix signal -- it takes a 1-D 3-element vector or an [m x 3] array of points -- so feed the exported block from something that produces a vector. 'units' is always Metric (MKS): English units do not cross. 'jd_loc' is always off: taking the Julian date on a second input port does not cross, because no configuration here adds a port. 'action' is always Warning; it only decides whether Simulink reports a height outside [0, 20000] m or a folded latitude, and the values are identical either way. The Simulink block has no SampleTime, so the rate stays on the ICore side. ⚠ Gravity Model IS A MODE: the three methods run different arithmetic 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).
WGS84 Gravity Model -- normal gravity of the 1984 World Geodetic System's ellipsoid Input [lat; lon; h] geodetic latitude and longitude in DEGREES, height in METRES Output [g_north; 0; g_down] in the local north-east-down frame, m/s^2
Transcribed from MathWorks' reference C, shared_aero_gravity/src/aerogravitywgs84.c (NIMA TR8350.2 eqs. 4-1..4-24), the same source the MATLAB function gravitywgs84 runs:
Taylor Series gamma(phi)*(1 - 2(1 + f + m - 2f sin^2 phi) h/a + 3 h^2/a^2), gamma(phi) = gamma_e (1 + k sin^2 phi)/sqrt(1 - e^2 sin^2 phi) Close Approximation |(gamma_u, gamma_beta)| at the point, in ellipsoidal-harmonic coordinates (u, beta), eqs. 4-5 and 4-6 Exact (gamma_u, gamma_beta) rotated through the geocentric latitude psi and the deflection alpha = phi - psi into the geodetic vertical and the north tangent, eqs. 4-16 and 4-23
MEASURED against R2026a's
aerolibgravity2/WGS84 Gravity Model(the name really ends in two spaces) over 16 configurations x 25 points, including latitudes past both poles and past a full half-turn: this arithmetic reproduces the block to <= 5.3e-15 everywhere.⚠ THE MULTIPLY ORDER IS THE REFERENCE'S, AND HERE THAT IS NOT PEDANTRY. q and q' (eqs. 4-11, 4-13) are differences of two terms that agree to five or six digits, so the arithmetic amplifies a rounding by ~1e5. Measured: one unit in the last place of the latitude IN RADIANS moves the answer by 6.2e-13, and letting the compiler fuse a multiply-add moves it by 7.1e-13 -- so this file turns contraction off (below) and every generator emits the same association as the reference.
⚠ A LATITUDE PAST A POLE IS FOLDED IN RADIANS, NOT DEGREES, AND THE LONGITUDE TURNS BY +pi ACROSS THE NORTH POLE AND -pi ACROSS THE SOUTH ONE. Both are measured: folding in degrees (90 - |mod(lat + 90, 360) - 180|, which is exact for the flat-Earth blocks) is off by 6.2e-13 at a latitude of 95, and a +pi at the south pole by 3.2e-13 at 181. The Exact method is not even symmetric under the fold -- without it a latitude of 181 is wrong by 19.6.
⚠ A YEAR OF 99 IS A SENTINEL in the reference: the Julian date is then the DAY itself, not a date in the year 99. Measured, and reproduced. An invalid day is clamped to the month's last (February 30 -> 28, April 31 -> 30), as the Simulink block does with a warning.
ALGEBRAIC and STATELESS.
Sample results#
Plotted: vector — Sine Wave, [3,1]: amplitudes 1/2/3 at 2 rad/s (tried only because every scalar stimulus was refused)
Category dynamic · sample time 0.1 · 60 steps · commit 383c0ecf1501cf1d43d89b8f688fc2fcad5e9b52 · produced by docsSample --out <folder> --blocks Ideal_Airspeed_Correction WGS84_Gravity_Model Linear_Regression_Predictor Linear_Classifier_Predictor Crossover_Pilot_Model Precision_Pilot_Model Tustin_Pilot_Model FIR_Least_Squares_Design FIR_Equiripple_Design Cartesian_To_Keplerian_Elements Keplerian_Elements_To_Cartesian --steps 60 · data docs/generated/samples/Robotics__Gravity_Models__WGS84_Gravity_Model.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).