GeoSim (Geometric Simulation)
A Python library for geometric simulation of geolocation scenarios. GeoSim models platforms (satellites, ground stations, aircraft) with attached sensors that observe emitters and generate measurements (AOA, TDOA, FDOA) for testing geolocation algorithms. Requires Python 3.12+.
Mathematical Background
The sensor models compute measurements from geometry:
AOA (Angle of Arrival). Direction cosines (u, v) from the collector-to-emitter unit vector projected into the sensor's local coordinate frame. For ENU-based sensors, u = east component, v = north component of the unit direction.
TDOA (Time Difference of Arrival). The difference in propagation time between two collectors:
dt = (||x - c1|| - ||x - c2||) / c
where x is the emitter position, c1 and c2 are collector positions, and c is the speed of light. The iso-surface is a hyperboloid with foci at the two collectors.
FDOA (Frequency Difference of Arrival). The differential Doppler shift observed between two collectors:
df = (f / c) * (v1 . r1_hat - v2 . r2_hat)
where f is the carrier frequency, v1, v2 are collector velocities, and r1_hat, r2_hat are unit vectors from each collector to the emitter.
References: Torrieri (1984), "Statistical Theory of Passive Location Systems"; Ho & Chan (1997), "Geolocation of a Known Altitude Object from TDOA and FDOA Measurements."
Key Features
- Platform modeling: Stationary, TLE (SGP4), Keplerian orbit, and waypoint trajectories
- Sensor types: AOA (Angle of Arrival), TDOA (Time Difference of Arrival), FDOA (Frequency Difference of Arrival)
- FOV constraints: Elevation masks, conical FOV, Earth occlusion
- Observation generation: Self-contained measurements with embedded collector state
- Geolocation pipeline: Observation -> Ell (ellipsoid) conversion and fusion
- Monte Carlo support: Statistical analysis via multiple simulation iterations
- Atmospheric corrections: Integration with gri-tropo/gri-iono for delay modeling
Core Classes
| Class | Purpose |
|---|---|
Scenario |
Orchestrates simulation timeline and all participants |
Platform |
Moving or stationary observer (satellite, ground station, aircraft) |
Emitter |
Target being observed (stationary or moving) |
AoaSensor |
Angle of Arrival measurements (azimuth/elevation or direction cosines) |
TdoaSensor |
Time Difference of Arrival measurements (paired platforms) |
FdoaSensor |
Frequency Difference of Arrival measurements (Doppler-based) |
LocatorResult |
Geolocation solution with position, covariance, and chi-squared |
Documentation
The wheel ships its documentation inside the package, in gri_geosim/docs/, so
it is available wherever the package is installed:
overview.md-- building a scenario, simulating, locating, Monte Carlo, atmosphere, fields of view, the array-level locators, and the geometry trapsapi_summary.md-- every public class, function, and signature (generated)
Print the directory with
python -c "import gri_geosim, pathlib; print(pathlib.Path(gri_geosim.__file__).parent / 'docs')".
Every example in those files is run by the test suite.
Installation
pip install gri-geosim
All ecosystem dependencies (ellipsoids, fusion, atmospheric corrections, motion models) are installed automatically; there are no optional extras to select.
Quick Start
from gri_geosim import Scenario, Platform, Emitter, filter_aoa
from gri_geosim.sensors import AoaSensor
from gri_pos import Pos
from gri_nsepoch import Time
# Create a ground-based emitter (target)
emitter = Emitter(Pos.LLA(39.0, -76.5, 0.0), frequency_hz=1.5e9, name="TARGET")
# Create sensor platform
ground = Platform(Pos.LLA(38.0, -77.0, 100.0), name="GROUND_STATION")
ground.add_sensor(AoaSensor("aoa", azimuth_std_rad=0.001, elevation_std_rad=0.001))
# Run single-instant simulation
scenario = Scenario.for_instant(platforms=[ground], emitters=[emitter])
observations = scenario.simulate_at(Time.from_str("2024-01-15T12:00:00Z"))
# Access observations
for obs in filter_aoa(observations):
print(f"Azimuth: {obs.azimuth:.4f} rad, Elevation: {obs.elevation:.4f} rad")
Trajectories
Motion models live in the standalone gri-trajectory package (a dependency
of gri-geosim). Import them from there and pass to Platform/Emitter, which
also accept a bare Pos (wrapped in a StationaryTrajectory internally):
from gri_trajectory import Sgp4Trajectory, WaypointTrajectory
| Trajectory | Description |
|---|---|
StationaryTrajectory |
Fixed position, zero velocity |
Sgp4Trajectory |
SGP4/SDP4 TLE propagation (python-sgp4 backend) |
KeplerianTrajectory |
Two-body orbital mechanics (gri-utils backend) |
WaypointTrajectory |
Interpolated path through waypoints (cubic/linear) |
See the gri-trajectory README for the full set (LinearTrajectory,
CoordinatedTurnTrajectory, GreatCircleTrajectory, CircularOrbitTrajectory,
HermiteTrajectory, SegmentedTrajectory, plus the legs and placement
submodules).
FOV Constraints
Sensors support pluggable field-of-view constraints:
| Constraint | Description |
|---|---|
NoFov |
No restriction (always visible) |
ConeFov |
Conical field of view with half-angle |
ElevationMaskFov |
Elevation angle constraints (auto-detects ground reference) |
EarthOcclusionFov |
Earth line-of-sight blockage check |
from gri_geosim.sensors import AoaSensor, ElevationMaskFov
# Ground station with 10-degree elevation mask
sensor = AoaSensor(
"ground_aoa",
azimuth_std_rad=0.001,
fov=ElevationMaskFov(min_elev_deg=10.0),
)
Geolocation Pipeline
Convert observations to position estimates with covariance:
from gri_geosim import locate
from gri_convolve import smart_convolve
# Generate observations
observations = scenario.simulate_at(observation_time)
# Convert to position estimate with covariance ellipsoid (needs at least two
# collectors; raises ValueError where the solution leaves position unobservable).
# A ground altitude prior (0 m, 5 km sigma) is on by default: pass
# altitude_prior=None for an airborne emitter. locate_candidates returns every
# solution, such as both roots of the mirror ambiguity of level collectors.
ell = locate(observations)
print(f"Position: {ell.lla}")
print(f"95% ellipse: {ell.ellipse}")
# Combine multiple ellipsoids (with outlier rejection)
combined, used, discarded = smart_convolve([ell1, ell2, ell3])
Dependencies
This is a Layer 3 application package in the GRI FOSS ecosystem. All of the following are installed automatically:
gri-utils: Coordinate conversions, orbit propagation, observable calculationsgri-pos: Position objects (Pos class) with LLA/XYZ supportgri-nsepoch: Nanosecond-accurate timestamps (Time, Delta classes)gri-trajectory: Platform/emitter motion models (also supplies thesgp4backend)gri-obs: Typed observation containers re-exported by gri-geosimgri-ell: Covariance ellipsoid objects forlocate()gri-convolve: Ellipsoid combination (smart_convolve,convolve,cluster_convolve)gri-tropo: Tropospheric delay modeling (ITU-R 2019)gri-iono: Ionospheric delay modeling (Bent, IRI, NeQuick)numpy,scipy: Numerical computing
Examples
See examples/README.md for detailed examples:
basic_scenario.py- Multi-platform AOA with ground/aircraft/satellite sensorstdoa_scenario.py- TDOA sensor pairs with paired platformsmonte_carlo.py- Statistical analysis via multiple simulation runslocate_and_fuse.py- Full geolocation pipeline with ellipsoid fusionatmosphere_effects.py- Tropospheric and ionospheric delay demonstrationmulti_platform_geo.py- Complex hybrid AOA/TDOA/FDOA scenario
Other Projects
Current list of other GRI FOSS Projects we are building and maintaining.
License
MIT License. See LICENSE for details.
Metadata
Release files for gri-geosim 0.4.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gri_geosim-0.4.3.tar.gz | 112.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gri_geosim-0.4.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 198.4 kB
Release files / gri_geosim-0.4.3.tar.gz
| Download URL | gri_geosim-0.4.3.tar.gz |
|---|---|
| Size | 112.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
49d211026cca2b56d39aaacdff1a854c3fd24471ff5a01d63b70c7af25ea726a
|
|
BLAKE2b-256 checksum How to use checksums |
090c79cc5dbe8993a9674c3cb3e8c613afca0f73bfdec462621a575fda4812a3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / gri_geosim-0.4.3-py3-none-any.whl
| Download URL | gri_geosim-0.4.3-py3-none-any.whl |
|---|---|
| Size | 85.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
75c05d14f04436742a93c56b3606ade4fc66cae65dd9ab0c217fcb4e8e50cf01
|
|
BLAKE2b-256 checksum How to use checksums |
b4166b58978442f1a7427064acf62bb706b5ffa9a528c532fce37c21bb902fb5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|