Skip to content

INS and GNSS#

This page gives the models of the GNSS receiver, the IMU and the INS, with the datum chain and the UTM projection. It also gives the INS of ACRES Core, the parameters and the seeds of the noise.

Scope and Assumptions#

The game has three position and motion sensors. Each sensor reads the state of the chassis at the physics step.

Sensor Model Output
GNSS receiver A receiver model in the position domain: sky view, fix states, correlated errors, datum chain. gnss.jsonl
IMU Specific force and angular rate with a constant bias and white noise. imu.jsonl
INS The OxTS-style unit of the Polaris: fix, IMU, velocity and UTM odometry at one point. ins.jsonl, the sensor stream

The models use these assumptions.

  • The GNSS receiver has no pseudoranges and no carrier phases. The model adds an error to the true antenna position.
  • The satellites are a synthetic constellation on circular orbits. The constellation is not a broadcast almanac.
  • The models apply the position errors in the grid ENU frame of the tile. The models ignore the convergence of that grid.
  • The IMU has no bias instability, no scale-factor error, no misalignment and no quantisation.
  • The INS is the truth with errors. It has no inertial mechanisation, no alignment and no GNSS outage.
  • The sensors do not apply forces to the vehicle.

The sensor stream and the ROS 2 topics carry the INS only. The GNSS receiver and the IMU write files only. The Sensor Stream page gives the binary frame of an INS epoch. The Camera page and the LiDAR page give the other sensors.

GNSS receiver model: the truth pose and the lever arm give the antenna position, the sky model and the fix state machine set the error terms, the datum chain gives the output row. POSITION PATH, ONE EPOCH AT gnss.hz (10 HZ) Truth pose chassis p and q physics step, 120 Hz Lever arm gnss.antenna_flu_m antenna position Add error Pos = Enu + Err ENU, metres Datum chain Geodetic() WGS84 or NAD83 gnss.jsonl row and GGA delay_s 0.05 s ERROR TERMS, EACH ENU AXIS Gm Gauss-Markov state U 30 s to 300 s Wh white noise new value each epoch Mp multipath state V 8 s Err, sigma SKY MODEL AND FIX STATE Constellation Sats 78 satellites 3 systems Sky obstruction Obstacles boxes, cylinders, tree crowns DOP and multipath Dop() HDOP, VDOP, PDOP, multipath index Fix state machine Fix no_fix, single, dgps, rtk_float, rtk_fixed Corrections CorrectionAge() 1 s interval, outages antenna position DOP ratio, index state parameters fix type, satellite counts RngGnss (PCG32, seed, sequence 1) gives 11 values for each epoch: 9 Gaussian values for the error terms, 1 uniform value for the cycle slip and 1 Gaussian value for the time to fix.
The GNSS receiver model. One call of FAcresGnssModel::Epoch computes all blocks at the GNSS rate. Open the diagram

Symbols#

Symbol Quantity Unit
\(t\) Episode time: the physics steps since the start of the recording, divided by 120 s
\(\Delta t\) Time since the previous epoch of the same sensor s
\(\mathbf{p}\), \(\mathbf{R}\) Position of the chassis origin and rotation from the body frame to the world frame m, -
\(\mathbf{v}\), \(\pmb{\omega}\) Linear velocity of the centre of mass and angular velocity of the chassis, world axes m/s, rad/s
\(\mathbf{r}_a\), \(\mathbf{r}_i\), \(\mathbf{r}_n\) Lever arm of the antenna, the IMU and the INS point, body FLU m
\(\mathbf{l}\) Unit line of sight to a satellite, ENU -
\(N_u\), \(N_c\), \(N_n\), \(N_f\) Satellites: used, clear, near an obstruction, through foliage -
\(H\), \(V\), \(P\) HDOP, VDOP and PDOP of the used satellites -
\(H_o\), \(V_o\) HDOP and VDOP of all satellites above the elevation mask -
\(\rho\), \(m\) Reflector index and multipath index -
\(a_c\) Age of the newest correction message s
\(\sigma_{gm}\), \(\sigma_w\), \(\sigma_{mp}\) Standard deviation of the Gauss-Markov, white and multipath error terms m
\(u\), \(z\) Unit Gauss-Markov states of the correlated error and of the multipath error -
\(g\) Standard normal value from the generator -
\(\tau\), \(\tau_m\) Correlation time of the fix state and of the multipath s
\(k_H\), \(k_V\) Horizontal and vertical DOP ratio -
\(\varphi\), \(\lambda\), \(h\) Latitude, longitude and ellipsoidal height rad, rad, m
\(N_g\) Geoid undulation m
\(\mathbf{f}\) Specific force m/s²
\(\mathbf{b}_a\), \(\mathbf{b}_g\) Bias of the accelerometer and of the gyroscope m/s², rad/s
\(E\), \(N\) UTM easting and northing m
\(\gamma\), \(k\) Meridian convergence and point scale factor of the UTM projection rad, -
\(\alpha\) Grid rotation: the UTM azimuth of the grid north of the simulator rad

Frames and Sample Times#

The recorder converts the Unreal frames to right-handed frames at its boundary. The function names are in AcresSensors.cpp.

Frame Axes Conversion from Unreal
ENU East, north, up, metres. The origin is the origin of the tile. Vector: \((x, -y, z)\). Angular rate: \((-x, y, -z)\). Functions Enu, EnuAngular.
Body FLU Forward, left, up, metres. The origin is the chassis origin. Vector: \((x, -y, z)\). Angular rate: \((-x, y, -z)\). Functions Flu, FluAngular.

The ENU axes are the grid axes of the survey grid EPSG:2968. They are not a geodetic tangent frame. The attitude in a row is the active rotation from FLU to ENU as a quaternion in the sequence x, y, z, w (EnuQuat). A mount in the frame base_footprint is a mount from the middle of the rear axle on the ground. The chassis origin is 0.4 of the wheelbase in front of that point, at the height of the centre of gravity.

A sensor with the rate \(f\) has its first sample at \(t = 0\). The function Due then adds \(1/f\) to the due time after each sample.

\[ \text{sample when } t + 10^{-8} \ge t_{due}, \qquad t_{due} \leftarrow t_{due} + 1/f \]

A rate that does not divide 120 Hz thus gives intervals of two different lengths. For example, the INS at 100 Hz gives intervals of one physics step and of two physics steps. The mean rate stays 100 Hz.

The rows of the GNSS receiver, the IMU and the INS have a modelled latency (Emit, Drain).

\[ t_{available} = t_{sample} + T_d \]

\(T_d\) is delay_s. The recorder writes a row at the first physics step with \(t \ge t_{available}\). The row contains sample_time_s, available_time_s and delivery_time_s. The sensor stream sends an INS epoch immediately, without this latency.

Seeds and Determinism#

Each noise source has its own generator FAcresPcg32 (PCG-XSH-RR, O'Neill 2014). The same seed gives the same noise.

\[ s \leftarrow 6364136223846793005\, s + c \pmod{2^{64}} \]
\[ x = \left( (s_{old} \gg 18) \oplus s_{old} \right) \gg 27, \qquad r = s_{old} \gg 59, \qquad \text{output} = \operatorname{rotr}_{32}(x, r) \]

The seed procedure is pcg32_srandom_r: set \(s = 0\) and \(c = 2q + 1\), do one step, add the seed to \(s\), do one step. \(q\) is the sequence number of the stream.

\[ U = \frac{\text{output} + 0.5}{2^{32}}, \qquad g = \sqrt{-2 \ln U_1}\, \cos(2 \pi U_2) \]

A uniform value uses one output. A Gaussian value uses two uniform values (Box-Muller, cosine branch).

Stream Seed Sequence Values
GNSS errors (RngGnss) seed 1 11 for each epoch
IMU (RngImu) seed + 1009 2 6 Gaussian values for the biases at the start, then 6 for each sample
GNSS constellation seed + 3027 4 1 uniform value, then 2 for each system and 1 for each satellite
INS (RngIns) seed + 5045 6 18 Gaussian values for each epoch

The LiDAR page and the Camera page give the streams of those sensors. A model uses the same number of values in each epoch, in all states. Thus the option -SensorNoNoise and a change of the fix state do not move the values of the subsequent epochs. seed is the key of sensors.json. The option -SensorSeed= replaces it.

Note

In a session with more than one vehicle, each vehicle in the game uses the same seeds. ACRES Core adds 7919 times the agent index to the seed of the INS stream.

GNSS Receiver#

The source is FAcresGnssModel in AcresGnssModel.cpp. The recorder calls Epoch at the rate gnss.hz on the physics thread.

Antenna Position#

The antenna is at the lever arm \(\mathbf{r}_a\) (gnss.antenna_flu_m) from the chassis origin.

\[ \mathbf{p}_a = \mathbf{p} + \mathbf{R}\, \mathbf{r}_a \]

The model uses \(\mathbf{p}_a\) in Unreal metres for the sky view and in ENU metres for the output.

Constellation#

Initialize builds the satellites one time. Each satellite has a circular orbit.

System Planes Satellites in a Plane Orbit Radius Inclination
GPS 6 5 26 559.7 km 55°
Galileo 3 8 29 599.8 km 56°
GLONASS 3 8 25 508.0 km 64.8°

The first uniform value gives a time offset \(t_0 = 86400\, U\). Each system then gets two offsets, \(\Omega_0 = 2\pi U\) and \(u_{00} = 2\pi U\). A system that is off uses its values also. Satellite \(j\) of plane \(i\) in a system with \(n_p\) planes and \(n_s\) satellites in a plane has these elements.

\[ \Omega = \Omega_0 + \frac{2\pi i}{n_p}, \qquad u_0 = u_{00} + \frac{2\pi j}{n_s} + \frac{2\pi i}{n_p n_s} + 0.2\,(U - 0.5), \qquad n = \sqrt{\mu / a^3} \]

\(\mu = 3.986004418 \times 10^{14}\) m³/s² and \(a\) is the orbit radius. At the time \(t\) the argument of latitude is \(u = u_0 + n t\).

\[ \mathbf{x}_i = a \begin{bmatrix} \cos\Omega \cos u - \sin\Omega \sin u \cos i_o \\ \sin\Omega \cos u + \cos\Omega \sin u \cos i_o \\ \sin u \sin i_o \end{bmatrix} \]
\[ \theta = \omega_E (t + t_0), \qquad \mathbf{x}_e = \begin{bmatrix} x_i \cos\theta + y_i \sin\theta \\ -x_i \sin\theta + y_i \cos\theta \\ z_i \end{bmatrix} - \mathbf{x}_{site} \]

\(\omega_E = 7.2921151467 \times 10^{-5}\) rad/s and \(i_o\) is the inclination. \(\mathbf{x}_{site}\) is the ECEF position of the origin of the tile on the WGS84 ellipsoid. The site latitude \(\varphi_s\) and longitude \(\lambda_s\) rotate the vector into ENU. The model then normalises it.

\[ \mathbf{l} \propto \begin{bmatrix} -\sin\lambda_s\, x_e + \cos\lambda_s\, y_e \\ -\sin\varphi_s \cos\lambda_s\, x_e - \sin\varphi_s \sin\lambda_s\, y_e + \cos\varphi_s\, z_e \\ \cos\varphi_s \cos\lambda_s\, x_e + \cos\varphi_s \sin\lambda_s\, y_e + \sin\varphi_s\, z_e \end{bmatrix} \]

The model keeps a satellite when its elevation \(\arcsin(l_u)\) is not less than elevation_mask_deg.

Sky Obstruction#

GatherStructures and the recorder collect the obstacles at the start of a recording.

Obstacle Shape Source
Building Box. The axis-aligned bounds of the actor. Actors with the tag ACREBuildingVisual
Grain bin Vertical cylinder. The radius is the smaller horizontal half extent. Actors with the tag ACRESiloVisual
Tree crown Sphere The tree crown proxies of the LiDAR

The box of a building that is not parallel to the grid is larger than the building. For each epoch, the model first keeps the obstacles that can come above the mask. \(D\) is the horizontal distance from the antenna to the obstacle, \(z_t\) is its top and \(d_n\) is near_margin_m.

\[ z_t + d_n - z_a > 0 \quad \text{and} \quad \mathrm{atan2}(z_t + d_n - z_a,\, D) \ge \text{mask} \]

The model then tests the ray from the antenna along \(\mathbf{l}\) against each kept obstacle.

Result Condition Effect
Blocked The ray hits a box or a cylinder. Or the path in a tree crown is foliage_block_path_m or longer. The receiver does not use the satellite.
Foliage The ray goes through a tree crown on a shorter path. Used. Counts in \(N_f\).
Near The ray hits the box or the cylinder that is larger by near_margin_m. Used. Counts in \(N_n\).
Clear No other result applies. Used. Counts in \(N_c\).

\(N_u = N_c + N_n + N_f\). The ray tests are RayBox (slab test), RayCylinder and SphereChord.

Dilution of Precision#

Dop builds the geometry matrix \(\mathbf{G}\) with one row \([-l_e, -l_n, -l_u, 1]\) for each satellite.

\[ \mathbf{Q} = (\mathbf{G}^T \mathbf{G})^{-1}, \qquad H = \sqrt{Q_{11} + Q_{22}}, \qquad V = \sqrt{Q_{33}}, \qquad P = \sqrt{Q_{11} + Q_{22} + Q_{33}} \]

The three values are 99.99 with less than 4 satellites or with a singular matrix. The model computes \(H, V, P\) from the used satellites and \(H_o, V_o\) from all satellites above the mask.

Multipath Index#

Each solid obstacle within reflector_range_m (\(d_r\)) adds to the reflector index \(\rho\). \(b_h\) is the half width of the obstacle.

\[ \rho = \operatorname{clamp}\!\left( \sum \frac{2 \mathrm{atan2}(b_h, \max(D, 1))}{2\pi}\; \operatorname{clamp}\!\left( \frac{\mathrm{atan2}(z_t - z_a, \max(D, 1))}{30^\circ}, 0, 1 \right) \left( 1 - \frac{D}{d_r} \right),\ 0,\ 1 \right) \]

\(b_h\) is the radius of a cylinder, or half of the horizontal diagonal of a box. The multipath index is as follows.

\[ m = \operatorname{clamp}\!\left( \frac{N_n + 0.5\, N_f}{\max(N_u, 1)} + 0.5\, \rho,\ 0,\ 1 \right) \]

Correction Stream#

The base station makes one correction message each interval_s (\(T_c\)). A message arrives latency_s (\(T_l\)) later. CorrectionAge finds the newest message that arrived.

\[ j = \left\lfloor \frac{t - T_l}{T_c} + 10^{-9} \right\rfloor, \qquad a_c = t - j\, T_c \]

The receiver does not get a message with its generation time \(j T_c\) in an outage interval \([t_1, t_2)\). The model then steps back to the last message before the outage. With the start mode cold, the first message is \(j = 0\). With no message, the age is not available and the row shows null.

Fix State Machine#

The model examines these conditions in sequence for each epoch.

Sequence Condition Fix State
1 Cold start with \(t\) less than cold_ttff_s, or \(N_u < 4\) no_fix
2 \(a_c\) not available or more than rtk_max_age_s, or \(N_u\) less than float_min_satellites dgps when \(a_c \le\) dgps_max_age_s, if not single
3 All other epochs rtk_float or rtk_fixed

States 1 and 2 end the RTK lock and record the time of the loss \(t_{loss}\). In state 3, the model does these steps.

  1. With no lock, start a lock. Set the lock time \(t_{lock} = t\) and the convergence time \(c = 0\). Draw a time to fix.
  2. With a lock, a cycle slip occurs when the uniform value is less than cycle_slip_rate_per_s \(\cdot\, m\, \Delta t\). Then set \(t_{loss} = t\), \(c = 0\) and draw a new time to fix.
  3. The epoch is capable when \(N_c \ge\) min_clear_satellites and \(P \le\) max_pdop. A capable epoch adds \(\Delta t\) to \(c\).
  4. The state is rtk_fixed when the epoch is capable and \(c \ge T_{fix}\). If not, the state is rtk_float.
  5. A change from rtk_fixed to rtk_float sets \(t_{loss} = t\) and \(c = 0\) and draws a new time to fix.

The time to fix uses the Gaussian value \(g_t\) of the epoch.

\[ T_{fix} = T_{med}\, e^{\sigma_T g_t}\, (1 + G_m m)\, r, \qquad r = \begin{cases} r_q & t - t_{loss} \le T_q \\ 1 & \text{if not} \end{cases} \]

\(T_{med}\) is ttf_median_s, \(\sigma_T\) is ttf_log_sigma, \(G_m\) is ttf_multipath_gain, \(r_q\) is reacquire_factor and \(T_q\) is reacquire_window_s. A cycle slip does not change \(t_{lock}\). Thus the float filter continues and only the ambiguity convergence starts again. With the start mode converged, the lock is 600 s old at \(t = 0\) and \(T_{fix} = 0\). The first capable epoch is then rtk_fixed.

Error Model#

Each fix state has its own parameters (states). The state no_fix uses the parameters of single. The base standard deviations are \(s_H\) (sigma_h_m) and \(s_V\) (sigma_v_m). In the state rtk_float they decrease from the initial values.

\[ s_H = \sigma_h + (\sigma_{h0} - \sigma_h)\, e^{-(t - t_{lock}) / \tau_c} \]

\(\sigma_{h0}\) is initial_sigma_h_m and \(\tau_c\) is convergence_tau_s. The vertical value uses the same equation. In the two RTK states, the baseline \(B\) (baseline_km) adds a term.

\[ s_H \leftarrow s_H + 10^{-3}\, \text{ppm}_h\, B, \qquad s_V \leftarrow s_V + 10^{-3}\, \text{ppm}_v\, B \]

The DOP ratios scale the errors with the geometry.

\[ k_H = \operatorname{clamp}\!\left( \frac{H}{\max(H_o, 10^{-3})}, 0.5, 20 \right), \qquad k_V = \operatorname{clamp}\!\left( \frac{V}{\max(V_o, 10^{-3})}, 0.5, 20 \right) \]

The standard deviations of the three terms for the axes east, north and up are as follows.

\[ \begin{aligned} \pmb{\sigma}_{gm} &= (s_H k_H,\ s_H k_H,\ s_V k_V) \\ \pmb{\sigma}_w &= (w_H k_H,\ w_H k_H,\ w_V k_V) \\ \pmb{\sigma}_{mp} &= m\, \mu_H\, (1,\ 1,\ r_V) \end{aligned} \]

\(w_H\), \(w_V\) are white_h_m, white_v_m, \(\mu_H\) is multipath_h_m and \(r_V\) is multipath_vertical_ratio. The two correlated terms are first-order Gauss-Markov processes with unit variance.

\[ \phi = e^{-\Delta t / \tau}, \qquad u_k = \phi\, u_{k-1} + \sqrt{1 - \phi^2}\; g_k \]
\[ \phi_m = e^{-\Delta t / \tau_m}, \qquad z_k = \phi_m\, z_{k-1} + \sqrt{1 - \phi_m^2}\; g'_k \]

\(\tau\) is tau_s of the current fix state and \(\tau_m\) is multipath_tau_s. In the first epoch, \(\phi = \phi_m = 0\) and \(\Delta t = 0.1\) s. The error and the reported standard deviation of each axis are as follows.

\[ e = \sigma_{gm}\, u + \sigma_w\, g'' + \sigma_{mp}\, z, \qquad \sigma = \sqrt{\sigma_{gm}^2 + \sigma_w^2 + \sigma_{mp}^2} \]
\[ \mathbf{p}_{out} = \mathbf{p}_a^{ENU} + \mathbf{e} \]

The reported \(\sigma\) is thus the true standard deviation of the model. The covariance in the row is diagonal. The 11 values of an epoch have this sequence: 3 for \(u\), 3 for the white term, 3 for \(z\), the uniform value, then \(g_t\). With -SensorNoNoise, all three standard deviations are zero.

Datum Chain#

Geodetic converts the ENU output position \((e, n, u)\) to geodetic coordinates in six steps.

  1. Grid coordinates in US survey feet: \(E_g = E_0 + e / c_f\) and \(N_g = N_0 + n / c_f\), with \(c_f = 1200/3937\) m.
  2. NAD83(HARN) latitude and longitude from the inverse projection EPSG:2968.
  3. NAD83(2011) latitude and longitude: add the constant shift harn_to_nad83_2011_shift_deg.
  4. Heights: \(H_{navd} = c_f Z_0 + u\), \(N_g = N_0^g + a_e\, e + a_n\, n\) and \(h_{2011} = H_{navd} + N_g\).
  5. ECEF on the GRS80 ellipsoid, then the inverse of the Helmert transformation at the epoch of the observation.
  6. Geodetic coordinates on the WGS84 ellipsoid. The frame is WGS84 (G2139), which the model sets equal to ITRF2014.

\((E_0, N_0, Z_0)\) is origin_source_ft of Acres/Content/Simulation/ACRE/site.json: 2 977 500 ft, 1 902 500 ft and 705 ft. \(N_0^g\), \(a_e\), \(a_n\) are n0_m, dn_de, dn_dn of the geoid plane.

Inverse projection. IndianaWestFeetToGeographic uses the series of Snyder (1987) on GRS80. The constants are \(\varphi_0 = 37.5°\), \(\lambda_0 = -87.08333°\), \(k_0 = 0.999966667\), false easting \(x_0 = 900\,000\) m and false northing \(y_0 = 250\,000\) m. With \(x = c_f E_g\) and \(y = c_f N_g\):

\[ M(\varphi) = a \left[ \left( 1 - \tfrac{e^2}{4} - \tfrac{3 e^4}{64} - \tfrac{5 e^6}{256} \right) \varphi - \left( \tfrac{3 e^2}{8} + \tfrac{3 e^4}{32} + \tfrac{45 e^6}{1024} \right) \sin 2\varphi + \left( \tfrac{15 e^4}{256} + \tfrac{45 e^6}{1024} \right) \sin 4\varphi - \tfrac{35 e^6}{3072} \sin 6\varphi \right] \]
\[ \mu = \frac{M(\varphi_0) + (y - y_0)/k_0}{a \left( 1 - \tfrac{e^2}{4} - \tfrac{3 e^4}{64} - \tfrac{5 e^6}{256} \right)}, \qquad e_1 = \frac{1 - \sqrt{1 - e^2}}{1 + \sqrt{1 - e^2}} \]
\[ \varphi_1 = \mu + \left( \tfrac{3 e_1}{2} - \tfrac{27 e_1^3}{32} \right) \sin 2\mu + \left( \tfrac{21 e_1^2}{16} - \tfrac{55 e_1^4}{32} \right) \sin 4\mu + \tfrac{151 e_1^3}{96} \sin 6\mu + \tfrac{1097 e_1^4}{512} \sin 8\mu \]
\[ C_1 = e'^2 \cos^2\varphi_1, \quad T_1 = \tan^2\varphi_1, \quad N_1 = \frac{a}{\sqrt{1 - e^2 \sin^2\varphi_1}}, \quad R_1 = \frac{a (1 - e^2)}{(1 - e^2 \sin^2\varphi_1)^{3/2}}, \quad D = \frac{x - x_0}{N_1 k_0} \]
\[ \begin{aligned} \varphi = \varphi_1 - \frac{N_1 \tan\varphi_1}{R_1} \Big[ & \tfrac{D^2}{2} - (5 + 3 T_1 + 10 C_1 - 4 C_1^2 - 9 e'^2) \tfrac{D^4}{24} \\ & + (61 + 90 T_1 + 298 C_1 + 45 T_1^2 - 252 e'^2 - 3 C_1^2) \tfrac{D^6}{720} \Big] \end{aligned} \]
\[ \lambda = \lambda_0 + \frac{1}{\cos\varphi_1} \left[ D - (1 + 2 T_1 + C_1) \tfrac{D^3}{6} + (5 - 2 C_1 + 28 T_1 - 3 C_1^2 + 8 e'^2 + 24 T_1^2) \tfrac{D^5}{120} \right] \]

\(e^2 = 2f - f^2\) and \(e'^2 = e^2 / (1 - e^2)\), with \(a = 6\,378\,137\) m and \(1/f = 298.257222101\).

ECEF. GeodeticToEcef and EcefToGeodetic use these equations. The inverse does 8 iterations of the latitude.

\[ N_e = \frac{a}{\sqrt{1 - e^2 \sin^2\varphi}}, \qquad \mathbf{X} = \begin{bmatrix} (N_e + h) \cos\varphi \cos\lambda \\ (N_e + h) \cos\varphi \sin\lambda \\ (N_e (1 - e^2) + h) \sin\varphi \end{bmatrix} \]
\[ p = \sqrt{X^2 + Y^2}, \qquad h = \frac{p}{\cos\varphi} - N_e, \qquad \varphi \leftarrow \mathrm{atan2}\!\left( Z,\ p \left( 1 - e^2 \frac{N_e}{N_e + h} \right) \right) \]

The first value of the iteration is \(\varphi = \mathrm{atan2}(Z, p (1 - e^2))\). The WGS84 ellipsoid has \(1/f = 298.257223563\).

Helmert transformation. The parameters are those of "ITRF2014 to NAD83(2011) (1)" in the coordinate-frame convention. With the epoch \(t_y\) in decimal years and \(\delta = t_y - 2010.0\):

\[ \mathbf{T} = \mathbf{T}_0 + \dot{\mathbf{T}} \delta, \qquad \mathbf{r} = -(\mathbf{r}_0 + \dot{\mathbf{r}} \delta)\, \frac{\pi}{648\,000}, \qquad s = 1 + (s_0 + \dot{s} \delta) \times 10^{-6} \]
\[ \mathbf{M} = \begin{bmatrix} 1 & -r_z & r_y \\ r_z & 1 & -r_x \\ -r_y & r_x & 1 \end{bmatrix}, \qquad \mathbf{X}_{2011} = \mathbf{T} + s\, \mathbf{M}\, \mathbf{X}_{ITRF}, \qquad \mathbf{X}_{ITRF} = \frac{1}{s}\, \mathbf{M}^T (\mathbf{X}_{2011} - \mathbf{T}) \]

HelmertForward and HelmertInverse are these two equations. The epoch is start_utc plus \(t\), as a decimal year of the correct year length (DecimalYear).

The key output_frame selects the values of latitude_deg, longitude_deg and ellipsoidal_height_m.

output_frame Latitude and Longitude Height
wgs84_g2139 WGS84 (G2139) at the epoch WGS84 ellipsoidal height
nad83 NAD83(HARN) NAD83(2011) ellipsoidal height

A row also contains the values of both frames with the prefixes nad83_harn_, nad83_2011_ and wgs84_. EnuFromWgs84 does the chain in the opposite direction.

Output Row#

A row of gnss.jsonl contains these groups of keys.

Keys Content
fix_type, gga_quality, solution The fix state and its NMEA quality: 4 rtk_fixed, 5 rtk_float, 2 dgps, 1 single, 0 no_fix.
satellites_used, satellites_clear, satellites_near_obstruction, satellites_foliage, satellites_blocked, satellites_above_mask The satellite counts.
hdop, vdop, pdop, hdop_open_sky, vdop_open_sky The DOP values.
multipath_index, reflector_index \(m\) and \(\rho\).
correction_age_s, rtk_lock_age_s, rtk_converge_s, rtk_time_to_fix_s \(a_c\), \(t - t_{lock}\), \(c\) and \(T_{fix}\).
model The standard deviations of the three terms, the correlation times, the DOP ratios and \(\Delta t\).
position_enu_m, sigma_enu_m, covariance_enu_m2, position_std_m The position with its error, \(\sigma\), \(\sigma^2\) and the horizontal standard deviation.
latitude_deg, longitude_deg, ellipsoidal_height_m, altitude_navd88_m, geoid_undulation_nad83_m The geodetic output.
nmea_gga The sentence $GNGGA with its checksum.

In the state no_fix, the position keys are null. Without site.json, a row has no geodetic keys and no sentence. The sentence contains the UTC time, the latitude and longitude in degrees and minutes with 7 decimals, the quality and the satellite count. It then contains HDOP, the NAVD88 altitude, the geoid separation, the correction age and the station 0001. The geoid separation is the ellipsoidal height of the output frame minus the NAVD88 altitude. The checksum is the exclusive OR of the characters between $ and *.

IMU#

IMU model: the chassis state gives the specific force and the angular rate, then a two-sample average, a constant bias and white noise give the output row. Chassis state velocity v angular rate ω attitude q physics step, 120 Hz ACCELEROMETER Lever arm imu.position_flu_m velocity of the IMU point Finite difference a = Δv / Δt Δt = 1/120 s Specific force f = a − g, body axes g = 9.80665 m/s² GYROSCOPE Body rate ω in body axes no lever arm BOTH SIGNALS, EACH AXIS Two-sample average ½ (x[k] + x[k−1]) zero at 60 Hz, then FLU Add bias AccelBias, GyroBias constant in an episode Add white noise accel_std_mps2 gyro_std_radps imu.jsonl imu.hz, 120 Hz delay_s 0.05 s specific force rate RngImu (PCG32, seed + 1009, sequence 2) gives the two bias vectors at the start, then 6 Gaussian values for each sample. The model has no quantisation, no scale-factor error and no misalignment.
The IMU model. The accelerometer and the gyroscope use the same average, bias and noise stages. Open the diagram

The source is FAcresSensorRecorder::PhysicsSample in AcresSensors.cpp. The IMU point is at the lever arm \(\mathbf{r}_i\) (imu.position_flu_m). The velocity of the IMU point uses the centre of mass \(\mathbf{p}_c\) of the chassis.

\[ \mathbf{v}_i = \mathbf{v} + \pmb{\omega} \times (\mathbf{p} + \mathbf{R}\, \mathbf{r}_i - \mathbf{p}_c) \]

The acceleration is the finite difference of this velocity across one physics step. The specific force subtracts the gravity vector.

\[ \mathbf{a}_k = \frac{\mathbf{v}_{i,k} - \mathbf{v}_{i,k-1}}{\Delta t}, \qquad \mathbf{f}^b_k = \mathbf{R}_k^T (\mathbf{a}_k - \mathbf{g}), \qquad \mathbf{g} = (0, 0, -9.80665) \text{ m/s}^2 \]
\[ \pmb{\omega}^b_k = \mathbf{R}_k^T \pmb{\omega}_k \]

A stationary IMU thus reads +9.80665 m/s² on the up axis. The output is the mean of two consecutive steps, in FLU axes, with a bias and noise.

\[ \mathbf{f}_{out} = \operatorname{FLU}\!\left( \tfrac{1}{2} (\mathbf{f}^b_k + \mathbf{f}^b_{k-1}) \right) + \mathbf{b}_a + \sigma_a \mathbf{g}_a \]
\[ \pmb{\omega}_{out} = \operatorname{FLU}_\omega\!\left( \tfrac{1}{2} (\pmb{\omega}^b_k + \pmb{\omega}^b_{k-1}) \right) + \mathbf{b}_g + \sigma_g \mathbf{g}_g \]

\(\sigma_a\) is accel_std_mps2 and \(\sigma_g\) is gyro_std_radps. \(\mathbf{g}_a\) and \(\mathbf{g}_g\) are three Gaussian values each, new for each sample. The mean of two samples is a filter with a zero at 60 Hz. It removes the 60 Hz velocity change of ±0.02 m/s that the brake solver gives to a parked vehicle. The biases are constant in an episode. Start draws them one time.

\[ \mathbf{b}_a = \sigma_{ba}\, \mathbf{g}, \qquad \mathbf{b}_g = \sigma_{bg}\, \mathbf{g} \]

\(\sigma_{ba}\) is accel_bias_mps2 and \(\sigma_{bg}\) is gyro_bias_radps. The file episode.json records the two vectors in imu_bias_flu. The first row is at the third physics step, because the average needs two differences. A reset of the vehicle starts the differences again. Thus the jump of the pose does not appear as an acceleration. A row of imu.jsonl contains specific_force_flu_mps2 with 6 decimals and angular_velocity_flu_radps with 8 decimals.

INS#

The source is FAcresSensorRecorder::InsEpoch. The INS reports one point of the vehicle at the rate ins.hz. The Polaris has the INS in its sensor overlay: an OxTS AV200 that reports base_footprint at 100 Hz. The Maxxum gets the same INS at base_footprint when a sensor stream is open, with the defaults of the code.

Truth at the INS Point#

\[ \mathbf{p}_n = \mathbf{p} + \mathbf{R}\, \mathbf{r}_n, \qquad \mathbf{v}_n = \mathbf{v} + \pmb{\omega} \times (\mathbf{p}_n - \mathbf{p}_c) \]

Errors#

Six unit Gauss-Markov states \(x_1 \dots x_6\) give the errors of east, north, up, heading, roll and pitch.

\[ a = e^{-\Delta t / \tau_p}, \qquad x_{j,k} = a\, x_{j,k-1} + \sqrt{1 - a^2}\; g_{j,k} \]

\(\tau_p\) is position_tau_s. In the first epoch, \(x_j = g_j\) and \(\Delta t = 1/f\). The position output is as follows.

\[ \mathbf{p}_{ins} = \mathbf{p}_n^{ENU} + \sigma_p\, (x_1,\ x_2,\ 1.5\, x_3) + \sigma_{pw}\, \mathbf{g}_p \]

\(\sigma_p\) is position_sigma_m and \(\sigma_{pw}\) is position_white_m. The attitude errors are small rotations.

\[ \delta\psi = \sigma_\psi\, x_4, \qquad \delta\phi = \sigma_\theta\, x_5, \qquad \delta\theta = \sigma_\theta\, x_6 \]
\[ \mathbf{q}_{ins} = \mathbf{q}_z(\delta\psi) \otimes \mathbf{q}_{true} \otimes \mathbf{q}_y(\delta\theta) \otimes \mathbf{q}_x(\delta\phi) \]

\(\sigma_\psi\) is heading_sigma_deg and \(\sigma_\theta\) is attitude_sigma_deg. \(\mathbf{q}_{true}\) is the rotation from FLU to ENU. The heading error turns about the ENU up axis. The roll and pitch errors turn about the body axes. The velocity, the angular rate and the specific force are in FLU axes with white noise.

\[ \mathbf{v}_{ins} = \operatorname{FLU}(\mathbf{R}^T \mathbf{v}_n) + \sigma_v \mathbf{g}_v, \qquad \pmb{\omega}_{ins} = \operatorname{FLU}_\omega(\mathbf{R}^T \pmb{\omega}) + \sigma_\omega \mathbf{g}_\omega \]
\[ \mathbf{f}_{ins} = \operatorname{FLU}\!\left( \mathbf{R}^T \left( \frac{\mathbf{v}_{n,k} - \mathbf{v}_{n,k-1}}{\Delta t} + (0, 0, 9.80665) \right) \right) + \sigma_f \mathbf{g}_f \]

\(\sigma_v\), \(\sigma_f\), \(\sigma_\omega\) are velocity_white_mps, accel_white_mps2 and gyro_white_radps. Here \(\Delta t\) is the time since the previous INS epoch. The first epoch and the first epoch after a reset report gravity only. The 18 values of an epoch have this sequence: 6 for the states and 3 for the position noise. Then 3 for the velocity, 3 for the specific force and 3 for the angular rate.

Fix and UTM Odometry#

The datum chain of the GNSS receiver converts \(\mathbf{p}_{ins}\) to latitude, longitude and height. The key ins.frame selects the frame.

ins.frame Latitude and Longitude Height
nad83 NAD83(2011) NAD83(2011) ellipsoidal height
wgs84_g2139 WGS84 (G2139) at the epoch WGS84 ellipsoidal height

GeographicToUtm projects the latitude and longitude to the zone ins.utm_zone. The orientation of the odometry is in the UTM grid. The model finds the grid rotation \(\alpha\) from a second point that is 10 m to the grid north of the first point.

\[ \alpha = \mathrm{atan2}(E_2 - E_1,\ N_2 - N_1), \qquad \mathbf{q}_{utm} = \mathbf{q}_z(-\alpha) \otimes \mathbf{q}_{ins} \]

The model computes \(\alpha\) again in each epoch. At the origin of the tile \(\alpha\) is 0.054°. The fix has the status 2 (RTK) and a diagonal covariance.

\[ \sigma^2 = \sigma_p^2 + \sigma_{pw}^2, \qquad \operatorname{cov} = \operatorname{diag}(\sigma^2,\ \sigma^2,\ 2.25\, \sigma^2) \]

Outputs#

A row of ins.jsonl has the frame name oxts_link and these objects.

Object Content
fix Latitude, longitude, altitude, datum, status 2, service 1, the diagonal of the covariance.
imu orientation_enu_xyzw, angular_velocity_flu_radps, linear_acceleration_flu_mps2.
velocity linear_flu_mps, angular_flu_radps.
position_enu_m \(\mathbf{p}_{ins}\) in the grid ENU frame.
odom Frame utm, child frame base_footprint, the UTM position, \(\mathbf{q}_{utm}\), the twist, grid_rotation_deg.

The sensor stream sends the same epoch as 40 values of 64 bits, with the truth of the point for the ROS 2 bridge. The function ins_to_messages of the ROS 2 bridge converts an epoch to these messages.

Topic, Polaris Topic, Maxxum Message Content
vehicle/odom odom nav_msgs/Odometry UTM pose with \(\mathbf{q}_{utm}\), covariance diagonal, FLU twist. Frame utm.
oxts/fix gnss/fix sensor_msgs/NavSatFix The fix. Frame navsat_link or gnss_link.
oxts/imu imu/data sensor_msgs/Imu Attitude, angular rate and specific force.
oxts/velocity - geometry_msgs/TwistStamped FLU velocity in m/s and angular rate in degrees per second. Frame oxts_link.
sim/ground_truth/odom ground_truth/odom nav_msgs/Odometry The pose without errors in the same UTM frame.

The bridge also sends the transform from utm to base_footprint. The IMU message of the Polaris uses the mounting axes of the OxTS unit: x left, y forward, z down.

\[ \mathbf{q}_{imu} = \mathbf{q}_{ins} \otimes \left( \tfrac{1}{\sqrt{2}}, \tfrac{1}{\sqrt{2}}, 0, 0 \right)_{xyzw}, \qquad (x, y, z)_{imu} = (y, x, -z)_{FLU} \]

The IMU message of the Maxxum stays in the body FLU axes. The Topics page lists all topics.

UTM Projection#

The source is AcresGeoUtm.h. It is a header without Unreal types, and the game and ACRES Core use the same file. The method is the series of Krüger to the sixth order in the third flattening \(n\) (Karney 2011). The ellipsoid is WGS84, the scale on the central meridian is \(k_0 = 0.9996\) and the false easting is \(E_0 = 500\,000\) m.

The functions are for the northern hemisphere. The central meridian of the zone \(z\) is \(\lambda_c = -183° + 6° z\). Zone 16 has \(\lambda_c = -87°\).

\[ n = \frac{f}{2 - f}, \qquad A = \frac{a}{1 + n} \left( 1 + \frac{n^2}{4} + \frac{n^4}{64} + \frac{n^6}{256} \right), \qquad e = \sqrt{f (2 - f)} \]

Forward (GeographicToUtm). With \(\tau = \tan\varphi\) and \(\lambda\) measured from \(\lambda_c\):

\[ \sigma = \sinh\!\left( e \operatorname{atanh} \frac{e \tau}{\sqrt{1 + \tau^2}} \right), \qquad \tau' = \tau \sqrt{1 + \sigma^2} - \sigma \sqrt{1 + \tau^2} \]
\[ \xi' = \mathrm{atan2}(\tau', \cos\lambda), \qquad \eta' = \operatorname{asinh} \frac{\sin\lambda}{\sqrt{\tau'^2 + \cos^2\lambda}} \]
\[ \xi = \xi' + \sum_{j=1}^{6} \alpha_j \sin(2 j \xi') \cosh(2 j \eta'), \qquad \eta = \eta' + \sum_{j=1}^{6} \alpha_j \cos(2 j \xi') \sinh(2 j \eta') \]
\[ E = E_0 + k_0 A\, \eta, \qquad N = k_0 A\, \xi \]

The coefficients are as follows.

\[ \begin{aligned} \alpha_1 &= \tfrac{n}{2} - \tfrac{2 n^2}{3} + \tfrac{5 n^3}{16} + \tfrac{41 n^4}{180} - \tfrac{127 n^5}{288} + \tfrac{7891 n^6}{37800} \\ \alpha_2 &= \tfrac{13 n^2}{48} - \tfrac{3 n^3}{5} + \tfrac{557 n^4}{1440} + \tfrac{281 n^5}{630} - \tfrac{1983433 n^6}{1935360} \\ \alpha_3 &= \tfrac{61 n^3}{240} - \tfrac{103 n^4}{140} + \tfrac{15061 n^5}{26880} + \tfrac{167603 n^6}{181440} \\ \alpha_4 &= \tfrac{49561 n^4}{161280} - \tfrac{179 n^5}{168} + \tfrac{6601661 n^6}{7257600} \\ \alpha_5 &= \tfrac{34729 n^5}{80640} - \tfrac{3418889 n^6}{1995840} \\ \alpha_6 &= \tfrac{212378941 n^6}{319334400} \end{aligned} \]

Convergence and scale. With \(p = 1 + \sum 2 j \alpha_j \cos(2 j \xi') \cosh(2 j \eta')\) and \(q = \sum 2 j \alpha_j \sin(2 j \xi') \sinh(2 j \eta')\):

\[ \gamma = \arctan\!\left( \frac{\tau'}{\sqrt{1 + \tau'^2}} \tan\lambda \right) + \mathrm{atan2}(q, p) \]
\[ k = \frac{k_0 A}{a} \sqrt{p^2 + q^2}\; \frac{\sqrt{1 - e^2 \sin^2\varphi}\, \sqrt{1 + \tau^2}}{\sqrt{\tau'^2 + \cos^2\lambda}} \]

\(\gamma\) is grid north minus true north. It is positive to the east of the central meridian.

Inverse (UtmToGeographic). With \(\xi = N / (k_0 A)\) and \(\eta = (E - E_0) / (k_0 A)\):

\[ \xi' = \xi - \sum_{j=1}^{6} \beta_j \sin(2 j \xi) \cosh(2 j \eta), \qquad \eta' = \eta - \sum_{j=1}^{6} \beta_j \cos(2 j \xi) \sinh(2 j \eta) \]
\[ \tau' = \frac{\sin\xi'}{\sqrt{\sinh^2\eta' + \cos^2\xi'}}, \qquad \lambda = \mathrm{atan2}(\sinh\eta', \cos\xi') \]

Newton iterations find \(\tau\) from \(\tau'\). The first value is \(\tau = \tau'\). The loop stops after 6 iterations or at a step below \(10^{-14}\).

\[ \tau \leftarrow \tau + \frac{\tau' - \tau'_i}{\sqrt{1 + \tau_i'^2}}\; \frac{1 + (1 - e^2) \tau^2}{(1 - e^2) \sqrt{1 + \tau^2}}, \qquad \varphi = \arctan\tau \]

\(\tau'_i\) is \(\tau'\) of the current \(\tau\).

\[ \begin{aligned} \beta_1 &= \tfrac{n}{2} - \tfrac{2 n^2}{3} + \tfrac{37 n^3}{96} - \tfrac{n^4}{360} - \tfrac{81 n^5}{512} + \tfrac{96199 n^6}{604800} \\ \beta_2 &= \tfrac{n^2}{48} + \tfrac{n^3}{15} - \tfrac{437 n^4}{1440} + \tfrac{46 n^5}{105} - \tfrac{1118711 n^6}{3870720} \\ \beta_3 &= \tfrac{17 n^3}{480} - \tfrac{37 n^4}{840} - \tfrac{209 n^5}{4480} + \tfrac{5569 n^6}{90720} \\ \beta_4 &= \tfrac{4397 n^4}{161280} - \tfrac{11 n^5}{504} - \tfrac{830251 n^6}{7257600} \\ \beta_5 &= \tfrac{4583 n^5}{161280} - \tfrac{108847 n^6}{3991680} \\ \beta_6 &= \tfrac{20648693 n^6}{638668800} \end{aligned} \]

The table gives the values at the origin of the tile for the NAD83(2011) coordinates of the datum chain.

Quantity Value
Latitude, longitude 40.4715765° N, 86.9943737° W
Easting, northing, zone 16 500 476.935 m, 4 480 099.755 m
Meridian convergence \(\gamma\) 0.00365°
Point scale factor \(k\) 0.99960000
Grid rotation \(\alpha\) 0.0541°

An independent implementation and pyproj give the same easting and northing to less than 0.001 mm.

Georeference Caveats#

The georeference of the simulator has these known limits.

  • Datum of a fix. A fix of the real vehicle does not state its datum. An RTK fix from the Indiana network is in NAD83(2011). A fix without corrections is in WGS84 (G2139) at the epoch of the observation. The two frames differ by 1.30 m on the tile at the epoch 2026.73: 0.97 m to the west and 0.87 m to the north. The WGS84 height is 1.13 m lower. Thus a tool that converts a fix to the grid must know the datum of the fix.
  • Different defaults. The INS uses nad83 and the GNSS receiver uses wgs84_g2139. The two outputs thus differ by 1.30 m.
  • Two meanings of nad83. The GNSS receiver gives NAD83(HARN) latitude and longitude. The INS gives NAD83(2011). The difference is 2.1 cm.
  • Ellipsoid of the UTM projection. The projection uses the WGS84 ellipsoid for NAD83(2011) coordinates also, as the converter of the vehicle does.
  • False northing. The projection EPSG:2968 in the code uses 250 000 m. The EPSG value is 0.1 mm smaller.
  • Geoid. The geoid is a plane fit to the GEOID12B grid. The residual of the fit is less than 0.3 mm on the tile.
  • Landmark. episode.json reports one landmark that the project supplied. No survey confirmed this landmark, and the model does not use it.

The Python module Learning/acres_learn/deploy/georef.py does the same chain from UTM to the grid for the real vehicle.

ACRES Core INS#

Of these three sensors, ACRES Core has the INS only. The source is FInsModel in ROS/acres_core_sim (CoreSimIns.h), with the georeference in CoreSimGeo.h. The node acres_core_sim sends the same 40 values on its sensor stream.

Item Game ACRES Core
Error model, sequence of the 18 values InsEpoch The same equations in FInsModel::Epoch
Generator FAcresPcg32 FPcg32, the same algorithm
Seed of the stream seed + 5045, sequence 6 seed + 5045 + 7919 × agent index, sequence 6
Configuration sensors.json with the vehicle overlay The block ins of sensors_polaris.json and the seed of sensors.json
Sample times First epoch at \(t = 0\) of the recording First epoch at the first sample
Frames nad83 and wgs84_g2139 nad83 only
Datum chain FAcresGnssModel::Geodetic FSiteGeoreference::EnuToNad83, steps 1 to 4 of the chain
UTM projection AcresGeoUtm.h The same header
Noise off -SensorNoNoise --no-noise, or noise:=false of the launch file
GNSS receiver and IMU models Yes No

Agent 0 of ACRES Core thus uses the same stream of values as the game. FSiteGeoreference also gives an affine map between the grid and UTM for the simulator control channel. The map uses the UTM position of the origin and one grid rotation. It is correct to a few millimetres across the tile.

Parameters#

The defaults are the values of Acres/Content/Simulation/sensors.json. The Polaris column is the value after the overlay sensors_polaris.json.

Common Keys#

Name Type Unit Default Description
seed integer 42 The seed of all noise streams.
delay_s number s 0.05 The modelled latency of the GNSS, IMU and INS rows. Range 0 to 10.
mount_frame string chassis The origin of the mounts: chassis or base_footprint. The Polaris overlay sets base_footprint.

GNSS Receiver#

Name Type Unit Default Description
gnss.enabled boolean true The receiver is on.
gnss.hz number Hz 10 The epoch rate. Range 1 to 120.
gnss.antenna_flu_m array of 3 m [0, 0, 1.7] The lever arm of the antenna. Polaris: [1.1, 0, 2.15].
gnss.std_m number m 0.02 Not used. The error model sets the noise.
gnss.start string converged converged or cold.
gnss.cold_ttff_s number s 20 The time to the first fix after a cold start.
gnss.elevation_mask_deg number deg 10 The elevation mask. Range 0 to 45.
gnss.constellations.gps, .galileo, .glonass boolean true The systems that the receiver uses. One system must be on.
gnss.multipath_tau_s number s 8 The correlation time of the multipath error.
gnss.multipath_vertical_ratio number 1.5 The vertical multipath error divided by the horizontal error.

The block gnss.states has one object for each fix state.

Name Type Unit rtk_fixed rtk_float dgps single
sigma_h_m number m 0.008 0.10 0.35 1.0
sigma_v_m number m 0.015 0.20 0.70 2.0
initial_sigma_h_m number m 0 0.40 0 0
initial_sigma_v_m number m 0 0.80 0 0
convergence_tau_s number s 30 30 30 30
white_h_m number m 0.003 0.02 0.10 0.30
white_v_m number m 0.006 0.04 0.20 0.60
tau_s number s 60 30 120 300
multipath_h_m number m 0.015 0.15 0.50 1.50
Name Type Unit Default Description
gnss.rtk.ttf_median_s number s 10 The median time to fix.
gnss.rtk.ttf_log_sigma number 0.5 The standard deviation of the logarithm of the time to fix.
gnss.rtk.ttf_multipath_gain number 2 The increase of the time to fix at the multipath index 1.
gnss.rtk.reacquire_factor number 0.3 The factor of the time to fix after a recent loss. Range 0 to 1.
gnss.rtk.reacquire_window_s number s 10 The time after a loss in which the factor applies.
gnss.rtk.min_clear_satellites integer 6 The clear satellites that the convergence needs. Minimum 4.
gnss.rtk.float_min_satellites integer 5 The used satellites that an RTK solution needs. Minimum 4.
gnss.rtk.max_pdop number 5 The PDOP limit of the convergence.
gnss.rtk.cycle_slip_rate_per_s number 1/s 0.3 The cycle slip rate at the multipath index 1.
gnss.corrections.interval_s number s 1 The interval of the correction messages.
gnss.corrections.latency_s number s 0.3 The delay of a correction message.
gnss.corrections.rtk_max_age_s number s 10 The maximum correction age for RTK.
gnss.corrections.dgps_max_age_s number s 60 The maximum correction age for DGPS.
gnss.corrections.baseline_km number km 5 The distance to the base station. Range 0 to 200.
gnss.corrections.ppm_h, ppm_v number ppm 1, 1.5 The error for each kilometre of baseline, in parts per million.
gnss.corrections.outages_s array s [] Correction outages as pairs [start, end).
gnss.obstruction.near_margin_m number m 1.5 The margin that makes a line of sight "near".
gnss.obstruction.foliage_block_path_m number m 8 The path in a tree crown that blocks a satellite.
gnss.obstruction.reflector_range_m number m 60 The range of the reflector index.
gnss.datum.output_frame string wgs84_g2139 wgs84_g2139 or nad83.
gnss.datum.start_utc string 2026-09-23T15:00:00Z The UTC time at \(t = 0\), in the ISO 8601 format.
gnss.datum.harn_to_nad83_2011_shift_deg array of 2 deg [-7.742258e-09, 2.4261007e-07] The shift of latitude and longitude from NAD83(HARN) to NAD83(2011).
gnss.datum.geoid.n0_m number m -33.6268735 The geoid undulation at the origin.
gnss.datum.geoid.dn_de, dn_dn number m/m -1.07779725e-05, -6.19783884e-06 The slope of the geoid plane to the east and to the north.

The block gnss.datum.helmert_itrf2014_to_nad83_2011 has the 14 parameters of the Helmert transformation.

Name Type Unit Default Description
t_m array of 3 m [1.00530, -1.90921, -0.54157] The translation \(\mathbf{T}_0\).
r_arcsec array of 3 arcsec [0.02678138, -0.00042027, 0.01093206] The rotation \(\mathbf{r}_0\).
s_ppm number ppm 0.00036891 The scale \(s_0\).
dt_m_per_yr array of 3 m/yr [0.00079, -0.00060, -0.00144] The rate of the translation.
dr_arcsec_per_yr array of 3 arcsec/yr [0.00006667, -0.00075744, -0.00005133] The rate of the rotation.
ds_ppm_per_yr number ppm/yr -0.00007201 The rate of the scale.
reference_epoch number yr 2010.0 The reference epoch.

IMU#

Name Type Unit Default Description
imu.enabled boolean true The IMU is on.
imu.hz number Hz 120 The sample rate. Range 1 to 120.
imu.accel_std_mps2 number m/s² 0.03 The white noise of the specific force, each axis and sample.
imu.gyro_std_radps number rad/s 0.002 The white noise of the angular rate, each axis and sample.
imu.accel_bias_mps2 number m/s² 0.01 The standard deviation of the constant accelerometer bias.
imu.gyro_bias_radps number rad/s 0.001 The standard deviation of the constant gyroscope bias.
imu.position_flu_m array of 3 m [0, 0, 0] The lever arm of the IMU. Polaris: [0.95, -0.45, 0.7].

INS#

The block ins is in sensors_polaris.json only. The column "Code" gives the default without the block.

Name Type Unit Default Code Description
ins.enabled boolean true false The INS is on. An open sensor stream sets it on.
ins.hz number Hz 100 100 The epoch rate. Range 1 to 120.
ins.position_flu_m array of 3 m [0, 0, 0] [0, 0, 0] The INS point. The Polaris value is base_footprint.
ins.position_sigma_m number m 0.01 0.01 The Gauss-Markov position error. The up axis uses 1.5 times this value.
ins.position_tau_s number s 30 30 The correlation time of the six states.
ins.position_white_m number m 0.003 0.003 The white position noise.
ins.heading_sigma_deg number deg 0.1 0.1 The heading error.
ins.attitude_sigma_deg number deg 0.03 0.03 The roll error and the pitch error.
ins.velocity_white_mps number m/s 0.02 0.01 The white velocity noise.
ins.accel_white_mps2 number m/s² 0.02 0.02 The white noise of the specific force.
ins.gyro_white_radps number rad/s 0.0005 0.0005 The white noise of the angular rate.
ins.frame string nad83 nad83 nad83 or wgs84_g2139.
ins.utm_zone integer 16 16 The UTM zone of the odometry. Range 1 to 60.

Command-Line Options#

Name Type Unit Default Description
-SensorSeed= integer seed Replaces the seed of all noise streams.
-SensorDelay= number s delay_s Replaces the modelled latency.
-SensorGnssHz= number Hz gnss.hz Replaces the GNSS rate.
-SensorImuHz= number Hz imu.hz Replaces the IMU rate.
-SensorNoGnss, -SensorNoImu, -SensorNoIns flag Sets one sensor off.
-SensorNoNoise flag Sets all errors, biases and noise of all sensors to zero.
-SensorGnssStart= string gnss.start converged or cold.
-SensorGnssFrame= string gnss.datum.output_frame wgs84_g2139 or nad83.
-SensorGnssBaselineKm= number km gnss.corrections.baseline_km Replaces the baseline.
-SensorGnssOutage= string s Adds one correction outage as <start>:<end>.
-SensorConfig= path sensors.json Uses a different complete configuration file.
-PolarisSensorConfig= path sensors_polaris.json Uses a different overlay for the Polaris.

The INS has no rate option. Change ins.hz in the overlay. The game stops with a message when a value is out of its range. The Sensors and Rigs tutorial shows how to use these keys and options.

Code Map#

Item File Function
Receiver epoch, state machine, error model, row Acres/Source/Acres/AcresGnssModel.cpp FAcresGnssModel::Epoch
Constellation AcresGnssModel.cpp FAcresGnssModel::Initialize
Obstacles AcresGnssModel.cpp, AcresSensors.cpp GatherStructures, FAcresSensorRecorder::Start
Ray tests, DOP AcresGnssModel.cpp RayBox, RayCylinder, SphereChord, Dop
Correction age AcresGnssModel.cpp FAcresGnssModel::CorrectionAge
Datum chain AcresGnssModel.cpp Geodetic, EnuFromWgs84, HelmertForward, HelmertInverse, GeodeticToEcef, EcefToGeodetic
Projection EPSG:2968 AcresSensors.cpp IndianaWestFeetToGeographic, GeographicToIndianaWestFeet
UTM projection Acres/Source/Acres/AcresGeoUtm.h GeographicToUtm, UtmToGeographic
GNSS configuration AcresGnssModel.cpp FAcresGnssModelConfig::Parse, ApplyCommandLine, Validate
IMU AcresSensors.cpp FAcresSensorRecorder::PhysicsSample
INS AcresSensors.cpp FAcresSensorRecorder::InsEpoch
Sample times, latency AcresSensors.cpp Due, Emit, Drain
Generator AcresSensors.cpp FAcresPcg32::Seed, Next, Gaussian
Configuration and options AcresSensors.cpp LoadSensorConfig, FAcresSensorConfig::Load
INS to ROS 2 messages ROS/acres_sim/src/conversions.cpp ins_to_messages
ACRES Core INS ROS/acres_core_sim/src/CoreSimIns.cpp FInsModel::Epoch, FInsConfig::Load
ACRES Core georeference ROS/acres_core_sim/src/CoreSimGeo.cpp FSiteGeoreference::EnuToNad83, EnuToUtm, WorldToUtm
Tests ROS/acres_core_sim/test/test_core_sim_unit.cpp, Tools/PolarisModel/polaris_tests.cpp Geo, Ins and UTM tests

Limitations#

  • The parameters of the GNSS receiver and of the IMU are typical datasheet values. They are not a calibration of a real receiver.
  • The GNSS receiver has no ionosphere, no troposphere, no receiver clock and no velocity output.
  • The multipath index is a heuristic. The obstacles are boxes, cylinders and spheres.
  • At the default spawn, the antenna of a vehicle is in the box of the ICSC garage. The receiver has no fix until the vehicle moves away from the garage.
  • The IMU reads the velocity of the physics solver. A parked vehicle shows the small velocity changes of the brake solver in the truth rows.
  • The INS does not use the GNSS receiver model. Its fix status is always 2, also below obstacles.
  • The lever arms of the Polaris INS are estimates. The project has no record of the real values of the OxTS configuration.
  • The grid ENU frame is not a tangent frame. The grid north differs from the true north by 0.058° at the origin.

References#

  • Karney, C. F. F. (2011). Transverse Mercator with an accuracy of a few nanometers. Journal of Geodesy, 85(8), 475-485.
  • Snyder, J. P. (1987). Map Projections: A Working Manual. U.S. Geological Survey Professional Paper 1395.
  • O'Neill, M. E. (2014). PCG: A Family of Simple Fast Space-Efficient Statistically Good Algorithms for Random Number Generation. Technical report HMC-CS-2014-0905, Harvey Mudd College.
  • Box, G. E. P., and Muller, M. E. (1958). A note on the generation of random normal deviates. The Annals of Mathematical Statistics, 29(2), 610-611.
  • National Geodetic Survey. HTDP: Horizontal Time-Dependent Positioning, transformation "ITRF2014 to NAD83(2011) (1)" of the EPSG registry.
  • National Geodetic Survey. GEOID12B and NADCON5 grids.
  • National Marine Electronics Association. NMEA 0183 Standard for Interfacing Marine Electronic Devices, sentence GGA.