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.
@@ -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.
@@ -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