orbitops 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- orbitops-0.1.0/PKG-INFO +227 -0
- orbitops-0.1.0/README.md +217 -0
- orbitops-0.1.0/pyproject.toml +25 -0
- orbitops-0.1.0/pyproject.toml.orig +25 -0
- orbitops-0.1.0/src/orbitops/__init__.py +0 -0
- orbitops-0.1.0/src/orbitops/api.py +190 -0
- orbitops-0.1.0/src/orbitops/cli.py +174 -0
- orbitops-0.1.0/src/orbitops/etc.py +10 -0
- orbitops-0.1.0/src/orbitops/propagation.py +51 -0
orbitops-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: orbitops
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A command-line toolkit for satellite tracking and orbital analysis.
|
|
5
|
+
Requires-Dist: requests>=2.34.2
|
|
6
|
+
Requires-Dist: sgp4>=2.27
|
|
7
|
+
Requires-Dist: skyfield>=1.55
|
|
8
|
+
Requires-Python: >=3.14
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# OrbitOps
|
|
12
|
+
|
|
13
|
+
OrbitOps is a Python command-line toolkit for satellite tracking and basic orbital analysis using publicly available CelesTrak data.
|
|
14
|
+
|
|
15
|
+
It uses Two-Line Element (TLE) data and the SGP4 propagation model to calculate spacecraft positions, velocities, and distances.
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- Calculate current latitude, longitude, and altitude
|
|
20
|
+
- Display TEME Cartesian position and velocity
|
|
21
|
+
- View satellite catalog information
|
|
22
|
+
- Search satellites by name
|
|
23
|
+
- Calculate 3D distance between two spacecraft
|
|
24
|
+
- Continuously monitor a spacecraft's calculated position
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
OrbitOps requires **Python 3.14 or later**.
|
|
29
|
+
|
|
30
|
+
Install from PyPI:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install orbitops
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then verify the installation:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
orbitops help
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
OrbitOps uses NORAD Catalog Numbers to identify spacecraft.
|
|
45
|
+
|
|
46
|
+
For example, the International Space Station (ISS) has catalog number `25544`.
|
|
47
|
+
|
|
48
|
+
### Position
|
|
49
|
+
|
|
50
|
+
Show the calculated latitude, longitude, and altitude of a spacecraft.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
orbitops position 25544
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Example:
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
Geographic position of ISS (ZARYA)
|
|
60
|
+
--------------------
|
|
61
|
+
Latitude: -32.7625°
|
|
62
|
+
Longitude: -54.1085°
|
|
63
|
+
Altitude: 432.53 km
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### TEME State
|
|
67
|
+
|
|
68
|
+
Show the spacecraft's TEME Cartesian position and velocity.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
orbitops teme 25544
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Position is reported in kilometers and velocity in kilometers per second.
|
|
75
|
+
|
|
76
|
+
### Satellite Information
|
|
77
|
+
|
|
78
|
+
View catalog information for a spacecraft.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
orbitops info 25544
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This may include information such as the spacecraft name, NORAD catalog number, launch date, owner, inclination, apogee, and perigee.
|
|
85
|
+
|
|
86
|
+
### Search
|
|
87
|
+
|
|
88
|
+
Search the satellite catalog by name.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
orbitops search ISS
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
You can also use broader searches:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
orbitops search STARLINK
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Broad searches may match many spacecraft. The current version of OrbitOps displays the first returned result.
|
|
101
|
+
|
|
102
|
+
### Distance
|
|
103
|
+
|
|
104
|
+
Calculate the current straight-line 3D distance between two spacecraft.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
orbitops distance 25544 69012
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The result is reported in kilometers.
|
|
111
|
+
|
|
112
|
+
The distance is calculated from both spacecraft's propagated TEME Cartesian positions and represents their instantaneous 3D separation, not distance along Earth's surface.
|
|
113
|
+
|
|
114
|
+
### Watch
|
|
115
|
+
|
|
116
|
+
Continuously monitor the calculated geographic position of a spacecraft.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
orbitops watch 25544
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Example:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
ISS (ZARYA) | Lat: 38.2841° | Lon: -72.1832° | Alt: 421.72 km
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Press `Ctrl+C` to stop.
|
|
129
|
+
|
|
130
|
+
## Command Reference
|
|
131
|
+
|
|
132
|
+
| Command | Usage | Description |
|
|
133
|
+
| --- | --- | --- |
|
|
134
|
+
| `position` | `orbitops position <CATNR>` | Show latitude, longitude, and altitude |
|
|
135
|
+
| `teme` | `orbitops teme <CATNR>` | Show TEME position and velocity |
|
|
136
|
+
| `info` | `orbitops info <CATNR>` | Show satellite catalog information |
|
|
137
|
+
| `search` | `orbitops search <name>` | Search satellites by name |
|
|
138
|
+
| `distance` | `orbitops distance <CATNR1> <CATNR2>` | Calculate 3D spacecraft separation |
|
|
139
|
+
| `watch` | `orbitops watch <CATNR>` | Continuously monitor spacecraft position |
|
|
140
|
+
| `help` | `orbitops help` | Display the help menu |
|
|
141
|
+
|
|
142
|
+
## How It Works
|
|
143
|
+
|
|
144
|
+
OrbitOps retrieves publicly available orbital data from CelesTrak.
|
|
145
|
+
|
|
146
|
+
For position calculations, OrbitOps retrieves a spacecraft's TLE and uses SGP4 to propagate its orbit to the current time.
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
CelesTrak
|
|
150
|
+
|
|
|
151
|
+
| TLE
|
|
152
|
+
v
|
|
153
|
+
OrbitOps
|
|
154
|
+
|
|
|
155
|
+
| SGP4
|
|
156
|
+
v
|
|
157
|
+
Calculated spacecraft state
|
|
158
|
+
|
|
|
159
|
+
+--> TEME position and velocity
|
|
160
|
+
|
|
|
161
|
+
+--> Latitude / Longitude / Altitude
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
For continuous tracking, the TLE does not need to be downloaded every second. OrbitOps retrieves the orbital elements and performs subsequent propagation locally.
|
|
165
|
+
|
|
166
|
+
## Data and Accuracy
|
|
167
|
+
|
|
168
|
+
OrbitOps does **not** receive live spacecraft telemetry.
|
|
169
|
+
|
|
170
|
+
Positions are calculated from publicly available orbital elements using SGP4. They should therefore be treated as **calculated or predicted positions**, not authoritative spacecraft positions.
|
|
171
|
+
|
|
172
|
+
Accuracy can be affected by factors including:
|
|
173
|
+
|
|
174
|
+
- Age of the orbital elements
|
|
175
|
+
- Atmospheric drag
|
|
176
|
+
- Spacecraft maneuvers
|
|
177
|
+
- Spacecraft orbit
|
|
178
|
+
- Time elapsed from the TLE epoch
|
|
179
|
+
|
|
180
|
+
OrbitOps is intended for educational, informational, satellite-tracking, visualization, and general orbital-analysis purposes.
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
Clone the repository:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
git clone <repository-url>
|
|
188
|
+
cd OrbitOps
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Install the development environment:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
uv sync
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Run OrbitOps:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
uv run orbitops position 25544
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Run tests and checks:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
uv run pytest
|
|
207
|
+
uv run ruff check .
|
|
208
|
+
uv run pyright
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
OrbitOps is licensed under the MIT License.
|
|
214
|
+
|
|
215
|
+
See the `LICENSE` file for the full license terms.
|
|
216
|
+
|
|
217
|
+
## Disclaimer
|
|
218
|
+
|
|
219
|
+
OrbitOps is provided for educational, informational, and general satellite tracking purposes only.
|
|
220
|
+
|
|
221
|
+
OrbitOps does not provide authoritative spacecraft telemetry, precision orbit determination, or mission-operational data. Satellite positions and other orbital information produced by OrbitOps are calculated from publicly available orbital elements using mathematical propagation models and may contain errors or become inaccurate over time.
|
|
222
|
+
|
|
223
|
+
OrbitOps is not intended for spacecraft navigation, collision avoidance, rendezvous operations, launch operations, flight safety, mission-critical decision-making, or any other safety-critical application.
|
|
224
|
+
|
|
225
|
+
Users are responsible for independently verifying any data produced by OrbitOps before relying on it for operational purposes.
|
|
226
|
+
|
|
227
|
+
THE SOFTWARE AND ALL OUTPUT GENERATED BY THE SOFTWARE ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. USE OF ORBITOPS AND RELIANCE ON ITS OUTPUT IS AT THE USER'S OWN RISK.
|
orbitops-0.1.0/README.md
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# OrbitOps
|
|
2
|
+
|
|
3
|
+
OrbitOps is a Python command-line toolkit for satellite tracking and basic orbital analysis using publicly available CelesTrak data.
|
|
4
|
+
|
|
5
|
+
It uses Two-Line Element (TLE) data and the SGP4 propagation model to calculate spacecraft positions, velocities, and distances.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Calculate current latitude, longitude, and altitude
|
|
10
|
+
- Display TEME Cartesian position and velocity
|
|
11
|
+
- View satellite catalog information
|
|
12
|
+
- Search satellites by name
|
|
13
|
+
- Calculate 3D distance between two spacecraft
|
|
14
|
+
- Continuously monitor a spacecraft's calculated position
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
OrbitOps requires **Python 3.14 or later**.
|
|
19
|
+
|
|
20
|
+
Install from PyPI:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install orbitops
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Then verify the installation:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
orbitops help
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
OrbitOps uses NORAD Catalog Numbers to identify spacecraft.
|
|
35
|
+
|
|
36
|
+
For example, the International Space Station (ISS) has catalog number `25544`.
|
|
37
|
+
|
|
38
|
+
### Position
|
|
39
|
+
|
|
40
|
+
Show the calculated latitude, longitude, and altitude of a spacecraft.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
orbitops position 25544
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
Geographic position of ISS (ZARYA)
|
|
50
|
+
--------------------
|
|
51
|
+
Latitude: -32.7625°
|
|
52
|
+
Longitude: -54.1085°
|
|
53
|
+
Altitude: 432.53 km
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### TEME State
|
|
57
|
+
|
|
58
|
+
Show the spacecraft's TEME Cartesian position and velocity.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
orbitops teme 25544
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Position is reported in kilometers and velocity in kilometers per second.
|
|
65
|
+
|
|
66
|
+
### Satellite Information
|
|
67
|
+
|
|
68
|
+
View catalog information for a spacecraft.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
orbitops info 25544
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This may include information such as the spacecraft name, NORAD catalog number, launch date, owner, inclination, apogee, and perigee.
|
|
75
|
+
|
|
76
|
+
### Search
|
|
77
|
+
|
|
78
|
+
Search the satellite catalog by name.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
orbitops search ISS
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
You can also use broader searches:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
orbitops search STARLINK
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Broad searches may match many spacecraft. The current version of OrbitOps displays the first returned result.
|
|
91
|
+
|
|
92
|
+
### Distance
|
|
93
|
+
|
|
94
|
+
Calculate the current straight-line 3D distance between two spacecraft.
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
orbitops distance 25544 69012
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The result is reported in kilometers.
|
|
101
|
+
|
|
102
|
+
The distance is calculated from both spacecraft's propagated TEME Cartesian positions and represents their instantaneous 3D separation, not distance along Earth's surface.
|
|
103
|
+
|
|
104
|
+
### Watch
|
|
105
|
+
|
|
106
|
+
Continuously monitor the calculated geographic position of a spacecraft.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
orbitops watch 25544
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Example:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
ISS (ZARYA) | Lat: 38.2841° | Lon: -72.1832° | Alt: 421.72 km
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Press `Ctrl+C` to stop.
|
|
119
|
+
|
|
120
|
+
## Command Reference
|
|
121
|
+
|
|
122
|
+
| Command | Usage | Description |
|
|
123
|
+
| --- | --- | --- |
|
|
124
|
+
| `position` | `orbitops position <CATNR>` | Show latitude, longitude, and altitude |
|
|
125
|
+
| `teme` | `orbitops teme <CATNR>` | Show TEME position and velocity |
|
|
126
|
+
| `info` | `orbitops info <CATNR>` | Show satellite catalog information |
|
|
127
|
+
| `search` | `orbitops search <name>` | Search satellites by name |
|
|
128
|
+
| `distance` | `orbitops distance <CATNR1> <CATNR2>` | Calculate 3D spacecraft separation |
|
|
129
|
+
| `watch` | `orbitops watch <CATNR>` | Continuously monitor spacecraft position |
|
|
130
|
+
| `help` | `orbitops help` | Display the help menu |
|
|
131
|
+
|
|
132
|
+
## How It Works
|
|
133
|
+
|
|
134
|
+
OrbitOps retrieves publicly available orbital data from CelesTrak.
|
|
135
|
+
|
|
136
|
+
For position calculations, OrbitOps retrieves a spacecraft's TLE and uses SGP4 to propagate its orbit to the current time.
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
CelesTrak
|
|
140
|
+
|
|
|
141
|
+
| TLE
|
|
142
|
+
v
|
|
143
|
+
OrbitOps
|
|
144
|
+
|
|
|
145
|
+
| SGP4
|
|
146
|
+
v
|
|
147
|
+
Calculated spacecraft state
|
|
148
|
+
|
|
|
149
|
+
+--> TEME position and velocity
|
|
150
|
+
|
|
|
151
|
+
+--> Latitude / Longitude / Altitude
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
For continuous tracking, the TLE does not need to be downloaded every second. OrbitOps retrieves the orbital elements and performs subsequent propagation locally.
|
|
155
|
+
|
|
156
|
+
## Data and Accuracy
|
|
157
|
+
|
|
158
|
+
OrbitOps does **not** receive live spacecraft telemetry.
|
|
159
|
+
|
|
160
|
+
Positions are calculated from publicly available orbital elements using SGP4. They should therefore be treated as **calculated or predicted positions**, not authoritative spacecraft positions.
|
|
161
|
+
|
|
162
|
+
Accuracy can be affected by factors including:
|
|
163
|
+
|
|
164
|
+
- Age of the orbital elements
|
|
165
|
+
- Atmospheric drag
|
|
166
|
+
- Spacecraft maneuvers
|
|
167
|
+
- Spacecraft orbit
|
|
168
|
+
- Time elapsed from the TLE epoch
|
|
169
|
+
|
|
170
|
+
OrbitOps is intended for educational, informational, satellite-tracking, visualization, and general orbital-analysis purposes.
|
|
171
|
+
|
|
172
|
+
## Development
|
|
173
|
+
|
|
174
|
+
Clone the repository:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
git clone <repository-url>
|
|
178
|
+
cd OrbitOps
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Install the development environment:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
uv sync
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Run OrbitOps:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
uv run orbitops position 25544
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Run tests and checks:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
uv run pytest
|
|
197
|
+
uv run ruff check .
|
|
198
|
+
uv run pyright
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
OrbitOps is licensed under the MIT License.
|
|
204
|
+
|
|
205
|
+
See the `LICENSE` file for the full license terms.
|
|
206
|
+
|
|
207
|
+
## Disclaimer
|
|
208
|
+
|
|
209
|
+
OrbitOps is provided for educational, informational, and general satellite tracking purposes only.
|
|
210
|
+
|
|
211
|
+
OrbitOps does not provide authoritative spacecraft telemetry, precision orbit determination, or mission-operational data. Satellite positions and other orbital information produced by OrbitOps are calculated from publicly available orbital elements using mathematical propagation models and may contain errors or become inaccurate over time.
|
|
212
|
+
|
|
213
|
+
OrbitOps is not intended for spacecraft navigation, collision avoidance, rendezvous operations, launch operations, flight safety, mission-critical decision-making, or any other safety-critical application.
|
|
214
|
+
|
|
215
|
+
Users are responsible for independently verifying any data produced by OrbitOps before relying on it for operational purposes.
|
|
216
|
+
|
|
217
|
+
THE SOFTWARE AND ALL OUTPUT GENERATED BY THE SOFTWARE ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. USE OF ORBITOPS AND RELIANCE ON ITS OUTPUT IS AT THE USER'S OWN RISK.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "orbitops"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A command-line toolkit for satellite tracking and orbital analysis."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.14"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"requests>=2.34.2",
|
|
9
|
+
"sgp4>=2.27",
|
|
10
|
+
"skyfield>=1.55",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
orbitops = "orbitops.cli:main"
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["uv_build>=0.12.5,<0.13.0"]
|
|
18
|
+
build-backend = "uv_build"
|
|
19
|
+
|
|
20
|
+
[dependency-groups]
|
|
21
|
+
dev = [
|
|
22
|
+
"pyright>=1.1.411",
|
|
23
|
+
"pytest>=9.1.1",
|
|
24
|
+
"ruff>=0.16.4",
|
|
25
|
+
]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "orbitops"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A command-line toolkit for satellite tracking and orbital analysis."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.14"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"requests>=2.34.2",
|
|
9
|
+
"sgp4>=2.27",
|
|
10
|
+
"skyfield>=1.55",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
orbitops = "orbitops.cli:main"
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["uv_build>=0.12.5,<0.13.0"]
|
|
18
|
+
build-backend = "uv_build"
|
|
19
|
+
|
|
20
|
+
[dependency-groups]
|
|
21
|
+
dev = [
|
|
22
|
+
"pyright>=1.1.411",
|
|
23
|
+
"pytest>=9.1.1",
|
|
24
|
+
"ruff>=0.16.4",
|
|
25
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
|
|
6
|
+
from . import propagation
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_sat_info_tle(catalog_number: int) -> list[str]:
|
|
10
|
+
"""Returns the cartesian state of the spacecraft in TLE format."""
|
|
11
|
+
|
|
12
|
+
returnArr = []
|
|
13
|
+
|
|
14
|
+
url = "https://celestrak.org/NORAD/elements/gp.php"
|
|
15
|
+
|
|
16
|
+
params = {
|
|
17
|
+
"CATNR": catalog_number,
|
|
18
|
+
"FORMAT": "TLE"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
response = requests.get(url, params=params)
|
|
23
|
+
response.raise_for_status()
|
|
24
|
+
|
|
25
|
+
data = response.text.splitlines()
|
|
26
|
+
|
|
27
|
+
if len(data) < 3:
|
|
28
|
+
return []
|
|
29
|
+
|
|
30
|
+
for line in data:
|
|
31
|
+
returnArr.append(line)
|
|
32
|
+
|
|
33
|
+
return returnArr
|
|
34
|
+
|
|
35
|
+
except requests.RequestException as error:
|
|
36
|
+
print(f"Failed to retrieve satellite data: {error}")
|
|
37
|
+
|
|
38
|
+
return []
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_satcat_data(catalog_number: int) -> dict:
|
|
42
|
+
"""Returns information about a given spacecraft."""
|
|
43
|
+
|
|
44
|
+
url = "https://celestrak.org/satcat/records.php"
|
|
45
|
+
|
|
46
|
+
params = {
|
|
47
|
+
"CATNR": catalog_number,
|
|
48
|
+
"FORMAT": "JSON"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
response = requests.get(url, params=params)
|
|
53
|
+
response.raise_for_status()
|
|
54
|
+
|
|
55
|
+
data = response.json()
|
|
56
|
+
|
|
57
|
+
if not data:
|
|
58
|
+
return {}
|
|
59
|
+
|
|
60
|
+
return data[0]
|
|
61
|
+
|
|
62
|
+
except requests.RequestException as error:
|
|
63
|
+
print(f"Failed to retrieve satellite data: {error}")
|
|
64
|
+
|
|
65
|
+
return {}
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def search_by_name(name: str) -> list[dict]:
|
|
69
|
+
"""Searches Celestrack by name, returns most relevant results."""
|
|
70
|
+
|
|
71
|
+
url = "https://celestrak.org/satcat/records.php"
|
|
72
|
+
|
|
73
|
+
params = {
|
|
74
|
+
"NAME": name,
|
|
75
|
+
"FORMAT": "JSON",
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
response = requests.get(url, params=params)
|
|
80
|
+
response.raise_for_status()
|
|
81
|
+
|
|
82
|
+
return response.json()
|
|
83
|
+
|
|
84
|
+
except requests.RequestException as error:
|
|
85
|
+
print(f"Failed to retrieve satellite data: {error}")
|
|
86
|
+
|
|
87
|
+
return []
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_distance_sats(catalog_num1: int, catalog_num2: int) -> None:
|
|
91
|
+
"""Returns the 3D Euclidean distance between two spacecraft."""
|
|
92
|
+
|
|
93
|
+
url = "https://celestrak.org/NORAD/elements/gp.php"
|
|
94
|
+
|
|
95
|
+
params = {
|
|
96
|
+
"CATNR": catalog_num1,
|
|
97
|
+
"FORMAT": "TLE"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
params2 = {
|
|
101
|
+
"CATNR": catalog_num2,
|
|
102
|
+
"FORMAT": "TLE"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
response = requests.get(url, params=params)
|
|
107
|
+
response.raise_for_status()
|
|
108
|
+
|
|
109
|
+
first_sat_data = response.text.splitlines()
|
|
110
|
+
|
|
111
|
+
if len(first_sat_data) < 3:
|
|
112
|
+
print(
|
|
113
|
+
f"No valid TLE found for catalog number "
|
|
114
|
+
f"{catalog_num1}."
|
|
115
|
+
)
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
tle_1_first_sat = first_sat_data[1]
|
|
119
|
+
tle_2_first_sat = first_sat_data[2]
|
|
120
|
+
|
|
121
|
+
teme_first_sat = propagation.get_teme_cartesian(
|
|
122
|
+
tle_1_first_sat,
|
|
123
|
+
tle_2_first_sat
|
|
124
|
+
)[0]
|
|
125
|
+
|
|
126
|
+
response2 = requests.get(url, params=params2)
|
|
127
|
+
response2.raise_for_status()
|
|
128
|
+
|
|
129
|
+
second_sat_data = response2.text.splitlines()
|
|
130
|
+
|
|
131
|
+
if len(second_sat_data) < 3:
|
|
132
|
+
print(
|
|
133
|
+
f"No valid TLE found for catalog number "
|
|
134
|
+
f"{catalog_num2}."
|
|
135
|
+
)
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
tle_1_second_sat = second_sat_data[1]
|
|
139
|
+
tle_2_second_sat = second_sat_data[2]
|
|
140
|
+
|
|
141
|
+
teme_second_sat = propagation.get_teme_cartesian(
|
|
142
|
+
tle_1_second_sat,
|
|
143
|
+
tle_2_second_sat
|
|
144
|
+
)[0]
|
|
145
|
+
|
|
146
|
+
print(
|
|
147
|
+
f"The distance between "
|
|
148
|
+
f"{first_sat_data[0].strip()} and "
|
|
149
|
+
f"{second_sat_data[0].strip()} is "
|
|
150
|
+
f"{math.dist(teme_first_sat, teme_second_sat):.3f}km."
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
except requests.RequestException as error:
|
|
154
|
+
print(f"Failed to retrieve satellite data: {error}")
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def watch(catalog_number: int) -> None:
|
|
158
|
+
"""Returns the latitude, longitude, and altitude of a spacecraft."""
|
|
159
|
+
|
|
160
|
+
sat_data = get_sat_info_tle(catalog_number)
|
|
161
|
+
|
|
162
|
+
if len(sat_data) < 3:
|
|
163
|
+
print(
|
|
164
|
+
f"No valid TLE found for catalog number "
|
|
165
|
+
f"{catalog_number}."
|
|
166
|
+
)
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
sat_name, tle_line1, tle_line2 = sat_data
|
|
170
|
+
|
|
171
|
+
print("Press 'Ctrl+C' to stop watching.")
|
|
172
|
+
|
|
173
|
+
while True:
|
|
174
|
+
latitude, longitude, altitude = (
|
|
175
|
+
propagation.get_geographic_position(
|
|
176
|
+
tle_line1,
|
|
177
|
+
tle_line2,
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
print(
|
|
182
|
+
f"\r{sat_name} | "
|
|
183
|
+
f"Lat: {latitude:.4f}° | "
|
|
184
|
+
f"Lon: {longitude:.4f}° | "
|
|
185
|
+
f"Alt: {altitude:.2f} km",
|
|
186
|
+
end="",
|
|
187
|
+
flush=True
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
time.sleep(1)
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from . import api, etc, propagation
|
|
4
|
+
|
|
5
|
+
VALID_COMMANDS = (
|
|
6
|
+
"help",
|
|
7
|
+
"--help",
|
|
8
|
+
"-h",
|
|
9
|
+
"teme",
|
|
10
|
+
"position",
|
|
11
|
+
"info",
|
|
12
|
+
"search",
|
|
13
|
+
"distance",
|
|
14
|
+
"watch",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def main() -> None:
|
|
19
|
+
"""Main CLI tool, accepts commands."""
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
# No command entered
|
|
23
|
+
if len(sys.argv) == 1:
|
|
24
|
+
etc.print_help_menu()
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
command = sys.argv[1]
|
|
28
|
+
|
|
29
|
+
# Help command
|
|
30
|
+
if command in ("help", "--help", "-h"):
|
|
31
|
+
etc.print_help_menu()
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
# Invalid command
|
|
35
|
+
if command not in VALID_COMMANDS:
|
|
36
|
+
print(f"Invalid command: {command}")
|
|
37
|
+
print("Use 'orbitops help' to view available commands.")
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
# Search requires a name
|
|
41
|
+
if command == "search" and len(sys.argv) < 3:
|
|
42
|
+
print("Missing satellite name.")
|
|
43
|
+
print("Usage: orbitops search <name>")
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
# Distance requires two catalog numbers
|
|
47
|
+
if command == "distance" and len(sys.argv) < 4:
|
|
48
|
+
print("Two satellite catalog numbers are required.")
|
|
49
|
+
print("Usage: orbitops distance <CATNR1> <CATNR2>")
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
# All remaining commands require one catalog number
|
|
53
|
+
if command not in ("search", "distance"):
|
|
54
|
+
|
|
55
|
+
if len(sys.argv) < 3:
|
|
56
|
+
print("Missing satellite catalog number.")
|
|
57
|
+
print(f"Usage: orbitops {command} <CATNR>")
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
catalog_number = int(sys.argv[2])
|
|
62
|
+
|
|
63
|
+
except ValueError:
|
|
64
|
+
print("Satellite catalog number must be an integer.")
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
sat_data = api.get_sat_info_tle(catalog_number)
|
|
68
|
+
|
|
69
|
+
if not sat_data:
|
|
70
|
+
print(
|
|
71
|
+
f"No satellite found with catalog number "
|
|
72
|
+
f"{catalog_number}."
|
|
73
|
+
)
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
sat_name, tle_line1, tle_line2 = sat_data
|
|
77
|
+
|
|
78
|
+
if command == "teme":
|
|
79
|
+
|
|
80
|
+
position, velocity = propagation.get_teme_cartesian(
|
|
81
|
+
tle_line1,
|
|
82
|
+
tle_line2
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
print(f"\nTEME Cartesian State of {sat_name}")
|
|
86
|
+
print("--------------------")
|
|
87
|
+
|
|
88
|
+
print("Position:")
|
|
89
|
+
print(f" X: {position[0]:10.2f} km")
|
|
90
|
+
print(f" Y: {position[1]:10.2f} km")
|
|
91
|
+
print(f" Z: {position[2]:10.2f} km")
|
|
92
|
+
|
|
93
|
+
print("Velocity:")
|
|
94
|
+
print(f" X: {velocity[0]:10.3f} km/s")
|
|
95
|
+
print(f" Y: {velocity[1]:10.3f} km/s")
|
|
96
|
+
print(f" Z: {velocity[2]:10.3f} km/s")
|
|
97
|
+
|
|
98
|
+
if command == "position":
|
|
99
|
+
|
|
100
|
+
print(f"\nGeographic position of {sat_name}")
|
|
101
|
+
print("--------------------")
|
|
102
|
+
|
|
103
|
+
latitude, longitude, altitude = (
|
|
104
|
+
propagation.get_geographic_position(
|
|
105
|
+
tle_line1,
|
|
106
|
+
tle_line2,
|
|
107
|
+
)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
print(f"Latitude: {latitude:.4f}°")
|
|
111
|
+
print(f"Longitude: {longitude:.4f}°")
|
|
112
|
+
print(f"Altitude: {altitude:.2f} km")
|
|
113
|
+
|
|
114
|
+
if command == "info":
|
|
115
|
+
|
|
116
|
+
data = api.get_satcat_data(catalog_number)
|
|
117
|
+
|
|
118
|
+
if not data:
|
|
119
|
+
print(
|
|
120
|
+
f"No satellite catalog information found for "
|
|
121
|
+
f"{catalog_number}."
|
|
122
|
+
)
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
for key, value in data.items():
|
|
126
|
+
print(f"{key}: {value}")
|
|
127
|
+
|
|
128
|
+
if command == "search":
|
|
129
|
+
|
|
130
|
+
results = api.search_by_name(sys.argv[2])
|
|
131
|
+
|
|
132
|
+
if not results:
|
|
133
|
+
print(
|
|
134
|
+
f"No satellites found matching "
|
|
135
|
+
f"'{sys.argv[2]}'."
|
|
136
|
+
)
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
data = results[0]
|
|
140
|
+
|
|
141
|
+
for key, value in data.items():
|
|
142
|
+
print(f"{key}: {value}")
|
|
143
|
+
|
|
144
|
+
if command == "distance":
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
catalog_number_1 = int(sys.argv[2])
|
|
148
|
+
catalog_number_2 = int(sys.argv[3])
|
|
149
|
+
|
|
150
|
+
except ValueError:
|
|
151
|
+
print("Satellite catalog numbers must be integers.")
|
|
152
|
+
return
|
|
153
|
+
|
|
154
|
+
api.get_distance_sats(
|
|
155
|
+
catalog_number_1,
|
|
156
|
+
catalog_number_2
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if command == "watch":
|
|
160
|
+
|
|
161
|
+
api.watch(catalog_number)
|
|
162
|
+
|
|
163
|
+
except IndexError as error:
|
|
164
|
+
print(f"Invalid satellite catalog number (CATNR): {error}")
|
|
165
|
+
|
|
166
|
+
except KeyboardInterrupt:
|
|
167
|
+
print("\nOrbitOps stopped.")
|
|
168
|
+
|
|
169
|
+
except Exception as error:
|
|
170
|
+
print(f"OrbitOps error: {error}")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
if __name__ == "__main__":
|
|
174
|
+
main()
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
def print_help_menu() -> None:
|
|
2
|
+
|
|
3
|
+
print("=== OrbitOps Help Menu ===")
|
|
4
|
+
print("position <CATNR> Show current latitude, longitude, and altitude")
|
|
5
|
+
print("teme <CATNR> Show current TEME position and velocity")
|
|
6
|
+
print("info <CATNR> Show satellite catalog information")
|
|
7
|
+
print("search <name> Search for a satellite by name")
|
|
8
|
+
print("distance <CATNR1> <CATNR2> Show distance between two satellites")
|
|
9
|
+
print("watch <CATNR> Continuously track a satellite's position")
|
|
10
|
+
print("help Show this help menu")
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from datetime import UTC, datetime
|
|
2
|
+
|
|
3
|
+
from sgp4.api import Satrec, jday
|
|
4
|
+
from skyfield.api import EarthSatellite, load, wgs84
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_teme_cartesian(tle_line_1: str, tle_line_2: str) -> tuple[
|
|
8
|
+
tuple[float, float, float],
|
|
9
|
+
tuple[float, float, float]
|
|
10
|
+
]:
|
|
11
|
+
|
|
12
|
+
satellite = Satrec.twoline2rv(tle_line_1, tle_line_2)
|
|
13
|
+
|
|
14
|
+
now = datetime.now(UTC)
|
|
15
|
+
|
|
16
|
+
jd, fr = jday(
|
|
17
|
+
now.year,
|
|
18
|
+
now.month,
|
|
19
|
+
now.day,
|
|
20
|
+
now.hour,
|
|
21
|
+
now.minute,
|
|
22
|
+
now.second + now.microsecond / 1_000_000,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
error, position, velocity = satellite.sgp4(jd, fr)
|
|
26
|
+
|
|
27
|
+
return (position, velocity)
|
|
28
|
+
|
|
29
|
+
def get_geographic_position(
|
|
30
|
+
tle_line_1: str,
|
|
31
|
+
tle_line_2: str,
|
|
32
|
+
) -> tuple[float, float, float]:
|
|
33
|
+
|
|
34
|
+
timescale = load.timescale()
|
|
35
|
+
current_time = timescale.now()
|
|
36
|
+
|
|
37
|
+
satellite = EarthSatellite(
|
|
38
|
+
tle_line_1,
|
|
39
|
+
tle_line_2,
|
|
40
|
+
ts=timescale,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
position = satellite.at(current_time)
|
|
44
|
+
|
|
45
|
+
geographic = wgs84.geographic_position_of(position)
|
|
46
|
+
|
|
47
|
+
latitude = geographic.latitude.degrees
|
|
48
|
+
longitude = geographic.longitude.degrees
|
|
49
|
+
altitude = geographic.elevation.km
|
|
50
|
+
|
|
51
|
+
return latitude, longitude, altitude
|