Skip to content

Commit 7b20c62

Browse files
authored
add multithreading (#61)
* add multithreading * memory optimzation * tolerance test
1 parent 0944faf commit 7b20c62

18 files changed

Lines changed: 1309 additions & 1201 deletions

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ jobs:
9494
9595
- name: Test import and basic functionality
9696
run: |
97-
python -c "from astroz import Tle, Sgp4, version; print(f'astroz {version()}')"
97+
python -c "from astroz import Tle, Sgp4, __version__; print(f'astroz {__version__}')"
9898
python -c "
9999
from astroz import Tle, Sgp4
100100
import numpy as np

.github/workflows/python.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ jobs:
111111
- name: Test wheel
112112
run: |
113113
pip install bindings/python/dist/*.whl
114-
python -c "from astroz import Tle, Sgp4, version; print(f'astroz {version()}')"
114+
python -c "from astroz import Tle, Sgp4, __version__; print(f'astroz {__version__}')"
115115
python -c "
116116
from astroz import Tle, Sgp4
117117
tle = Tle('1 25544U 98067A 24127.82853009 .00015698 00000+0 27310-3 0 9995\n2 25544 51.6393 160.4574 0003580 140.6673 205.7250 15.50957674452123')

README.md

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
## Astronomical and Spacecraft Toolkit Written in Zig
1010

11-
**Featuring the fastest open-source SGP4 propagator.**
11+
**Featuring the fastest CPU based open source SGP4 propagator**
1212

1313
| Orbital Mechanics | Spacecraft Ops | Astronomy |
1414
|-------------------|----------------|-----------|
@@ -19,7 +19,7 @@
1919

2020
### Performance
2121

22-
Sub-meter accuracy validated against reference implementations. Uses SIMD (AVX2/SSE) to process 4 satellites simultaneously.
22+
Sub-meter accuracy validated against reference implementations. Uses SIMD (AVX2/SSE) to process 4 satellites simultaneously, with multithreaded constellation propagation across all available cores.
2323

2424
#### Single Satellite (Python)
2525

@@ -33,10 +33,12 @@ Sub-meter accuracy validated against reference implementations. Uses SIMD (AVX2/
3333

3434
#### Multi-Satellite Constellation
3535

36-
| Satellites | Time Points | Total Props | Throughput |
37-
|------------|-------------|-------------|------------|
38-
| 100 | 10,080 | 1M | **7.9M props/sec** |
39-
| 13,000+ | 120 | 1.5M | **6M+ props/sec** |
36+
| Mode | Throughput |
37+
|------|------------|
38+
| Single-core (SIMD) | **22M props/sec** |
39+
| Multithreaded | **200M+ props/sec** |
40+
41+
Uses SIMD (AVX2/SSE) to process 4 satellites per batch with optional multithreaded time-major iteration. Validated against Vallado AIAA 2006-6753 reference vectors (< 10m position error, < 1µm/s velocity error). Set `ASTROZ_THREADS` environment variable to control thread count (defaults to all available cores).
4042

4143
The [Cesium visualization example](examples/README.md) propagates the entire active satellite catalog (~13,000 satellites) at interactive rates. **[Try the live demo →](https://attron.github.io/astroz-demo/)**
4244

@@ -47,7 +49,7 @@ pip install astroz
4749
```
4850

4951
```python
50-
from astroz import Tle, Sgp4, Sgp4Constellation
52+
from astroz import Tle, Sgp4
5153
import numpy as np
5254

5355
tle = Tle("1 25544U 98067A 24127.82853009 ...\n2 25544 51.6393 ...")
@@ -65,12 +67,25 @@ positions = np.empty((len(times), 3), dtype=np.float64)
6567
velocities = np.empty((len(times), 3), dtype=np.float64)
6668
sgp4.propagate_into(times, positions, velocities)
6769

68-
# Multi-satellite constellation (SIMD-accelerated)
69-
tles = [Tle(tle_str) for tle_str in tle_strings]
70-
constellation = Sgp4Constellation(tles)
71-
times = np.arange(1440, dtype=np.float64) # 1 day in minutes
72-
out = np.empty((len(times) * constellation.num_batches * 4 * 6,), dtype=np.float64)
73-
constellation.propagate_into(times, out) # ~6M propagations/sec
70+
# Multi-satellite constellation (SIMD + multithreaded)
71+
from astroz import load_constellation, propagate_constellation
72+
73+
# Load from CelesTrak group, file, URL, or TLE string
74+
constellation = load_constellation("starlink") # or load_constellation(norad_id=25544)
75+
76+
# Propagate for 1 day at 1-minute intervals (defaults to current UTC time)
77+
times = np.arange(1440, dtype=np.float64)
78+
positions = propagate_constellation(constellation, times, output="ecef")
79+
# shape: (num_times, num_sats, 3)
80+
81+
# With velocities and custom start time
82+
from datetime import datetime, timezone
83+
positions, velocities = propagate_constellation(
84+
constellation, times,
85+
start_time=datetime(2024, 6, 15, tzinfo=timezone.utc),
86+
output="ecef",
87+
velocities=True,
88+
)
7489
```
7590

7691
### Usage
@@ -113,7 +128,7 @@ exe.root_module.addImport("astroz", astroz_mod);
113128

114129
- #### [Cesium Satellite Visualization](examples/README.md)**[Live Demo](https://attron.github.io/astroz-demo/)**
115130

116-
Interactive 3D visualization of the entire near-earth satellite catalog (~13,000 satellites) using Cesium. Features real-time SGP4 propagation at ~6M props/sec, constellation filtering, search, and satellite tracking.
131+
Interactive 3D visualization of the entire near-earth satellite catalog (~13,000 satellites) using Cesium. Features multithreaded SGP4 propagation at ~190M props/sec, constellation filtering, search, and satellite tracking.
117132

118133
#### Spacecraft Operations
119134

bindings/python/README.md

Lines changed: 87 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,120 @@
11
# astroz Python Bindings
22

3-
High-performance SGP4 satellite orbit propagation for Python, powered by Zig with SIMD acceleration.
3+
High-performance SGP4 satellite propagation, powered by Zig with SIMD acceleration.
44

5-
**Supported platforms:** macOS and Linux
6-
**Requires:** Python 3.10+
5+
**Platforms:** macOS, Linux | **Requires:** Python 3.10+
76

87
## Quick Start
98

109
```python
11-
from astroz import Tle, Sgp4
10+
from astroz import load_constellation, propagate_constellation
1211
import numpy as np
1312

14-
# Parse TLE
15-
tle = Tle("""1 25544U 98067A 24127.82853009 .00015698 00000+0 27310-3 0 9995
16-
2 25544 51.6393 160.4574 0003580 140.6673 205.7250 15.50957674452123""")
17-
18-
# Single propagation
19-
sgp4 = Sgp4(tle)
20-
pos, vel = sgp4.propagate(30.0) # 30 minutes after TLE epoch
13+
# Load Starlink satellites and propagate for 1 day
14+
constellation = load_constellation("starlink")
15+
positions = propagate_constellation(constellation, np.arange(1440))
16+
# positions: (1440, num_satellites, 3) in km, ECEF coordinates
17+
```
2118

22-
# Batch propagation (convenience method)
23-
times = np.arange(0, 1440, 1.0, dtype=np.float64) # 1 day, 1-min intervals
24-
positions, velocities = sgp4.propagate_batch(times)
19+
## Loading TLEs
2520

26-
# Or use propagate_into for zero-copy into pre-allocated arrays
27-
positions = np.empty((len(times), 3), dtype=np.float64)
28-
velocities = np.empty((len(times), 3), dtype=np.float64)
29-
sgp4.propagate_into(times, positions, velocities)
21+
```python
22+
from astroz import load_constellation
23+
24+
# CelesTrak groups
25+
constellation = load_constellation("starlink")
26+
constellation = load_constellation("iss")
27+
constellation = load_constellation("gps")
28+
constellation = load_constellation("all") # ~10k active satellites
29+
30+
# By NORAD ID
31+
constellation = load_constellation(norad_id=25544) # ISS
32+
constellation = load_constellation(norad_id=[25544, 48274]) # Multiple
33+
34+
# Local file or URL
35+
constellation = load_constellation("satellites.tle")
36+
constellation = load_constellation("https://example.com/tles.txt")
37+
38+
# With metadata (name, norad_id, inclination, period, etc.)
39+
constellation, metadata = load_constellation("starlink", with_metadata=True)
40+
for sat in metadata:
41+
print(f"{sat['name']}: {sat['inclination']:.1f}° inc, {sat['period']:.1f} min period")
3042
```
3143

32-
## Performance
44+
Groups: `all`, `starlink`, `oneweb`, `planet`, `spire`, `gps`, `glonass`, `galileo`, `beidou`, `stations`/`iss`, `weather`, `geo`
45+
46+
## Propagation
47+
48+
```python
49+
from astroz import load_constellation, propagate_constellation
50+
from datetime import datetime, timezone
51+
import numpy as np
52+
53+
constellation = load_constellation("starlink")
3354

34-
**1.3-2.9x faster** than python-sgp4:
55+
# Simple (defaults: now, ECEF)
56+
positions = propagate_constellation(constellation, np.arange(1440))
3557

36-
| Scenario | astroz | python-sgp4 | Speedup |
37-
|----------|--------|-------------|---------|
38-
| 2 weeks (second intervals) | 160 ms | 464 ms | **2.9x** |
39-
| 1 month (minute intervals) | 5.9 ms | 16.1 ms | **2.7x** |
58+
# With options
59+
positions = propagate_constellation(
60+
constellation,
61+
np.arange(14 * 1440), # 2 weeks
62+
start_time=datetime(2024, 6, 1, tzinfo=timezone.utc),
63+
output="geodetic", # "ecef" (default), "teme", or "geodetic"
64+
)
4065

41-
## API
66+
# With velocities
67+
positions, velocities = propagate_constellation(
68+
constellation, np.arange(1440), velocities=True
69+
)
70+
```
4271

43-
### Tle
72+
## Single Satellite
4473

4574
```python
46-
tle = Tle(tle_string)
47-
tle.satellite_number # NORAD catalog number
48-
tle.epoch # Epoch (J2000 seconds)
49-
tle.inclination # Degrees
50-
tle.eccentricity
51-
tle.mean_motion # Rev/day
75+
from astroz import Tle, Sgp4
76+
import numpy as np
77+
78+
tle = Tle("""1 25544U 98067A 24127.82853009 .00015698 00000+0 27310-3 0 9995
79+
2 25544 51.6393 160.4574 0003580 140.6673 205.7250 15.50957674452123""")
80+
81+
sgp4 = Sgp4(tle)
82+
pos, vel = sgp4.propagate(30.0) # 30 min after epoch
83+
positions, velocities = sgp4.propagate_batch(np.arange(1440))
5284
```
5385

54-
### Sgp4
86+
## Collision Screening
5587

5688
```python
57-
sgp4 = Sgp4(tle, gravity_model=WGS84) # WGS84 (default) or WGS72
89+
from astroz import load_constellation, propagate_constellation, coarse_screen, min_distances
90+
import numpy as np
5891

59-
# Single point
60-
pos, vel = sgp4.propagate(tsince) # tsince in minutes
61-
# Returns ((x,y,z), (vx,vy,vz)) in km and km/s (TEME frame)
92+
constellation = load_constellation("starlink")
93+
positions = propagate_constellation(
94+
constellation, np.arange(1440),
95+
output="teme", layout="satellite_major"
96+
)
6297

63-
# Batch (convenience) - returns allocated arrays
64-
positions, velocities = sgp4.propagate_batch(times)
98+
# Find pairs within 10km
99+
pairs, t_indices = coarse_screen(positions, threshold=10.0)
65100

66-
# Batch (zero-copy) - writes directly to pre-allocated arrays
67-
sgp4.propagate_into(times, positions, velocities)
101+
# Get exact minimum distances
102+
pairs_array = np.array(pairs, dtype=np.uint32)
103+
min_dists, min_times = min_distances(positions, pairs_array)
68104
```
69105

106+
## Performance
107+
108+
| Constellation (13,448 sats × 1,440 steps) | Throughput |
109+
|--------------------------------------------|------------|
110+
| 1 thread | 7.7M props/sec |
111+
| 16 threads | 56M props/sec |
112+
113+
Set `ASTROZ_THREADS` to control thread count.
114+
70115
## Building
71116

72-
Requires [Zig](https://ziglang.org/) and Python 3.12+.
117+
Requires [Zig](https://ziglang.org/) and Python 3.10+.
73118

74119
```bash
75120
cd bindings/python

0 commit comments

Comments
 (0)