astrox-python 1.0.0__py3-none-any.whl → 1.2.0__py3-none-any.whl

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.
astrox/celestial.py CHANGED
@@ -74,6 +74,9 @@ class MpcOrbitalElements:
74
74
  raan_deg: float | None = None
75
75
  argument_of_periapsis_deg: float | None = None
76
76
  mean_anomaly_deg: float | None = None
77
+ reference_frame: str | None = None
78
+ """Heliocentric mean-ecliptic variant: ``MeanEclpJ2000`` (JPL) or
79
+ ``EclpJ2000ICRF`` (MPC, server default)."""
77
80
 
78
81
  def __post_init__(self) -> None:
79
82
  for field_name in (
@@ -95,9 +98,14 @@ class MpcOrbitalElements:
95
98
  parameter=field_name,
96
99
  ),
97
100
  )
101
+ object.__setattr__(
102
+ self,
103
+ "reference_frame",
104
+ _optional_string(self.reference_frame, parameter="reference_frame"),
105
+ )
98
106
 
99
- def to_wire(self) -> dict[str, float]:
100
- payload: dict[str, float] = {}
107
+ def to_wire(self) -> dict[str, Any]:
108
+ payload: dict[str, Any] = {}
101
109
  _include_if_supplied(payload, "EpochMjdTdt", self.epoch_mjd_tdt)
102
110
  _include_if_supplied(payload, "PeriTimeMjdTdt", self.periapsis_time_mjd_tdt)
103
111
  _include_if_supplied(payload, "Q", self.periapsis_distance_au)
@@ -111,6 +119,7 @@ class MpcOrbitalElements:
111
119
  self.argument_of_periapsis_deg,
112
120
  )
113
121
  _include_if_supplied(payload, "MeanAnomaly", self.mean_anomaly_deg)
122
+ _include_if_supplied(payload, "ReferenceFrame", self.reference_frame)
114
123
  return payload
115
124
 
116
125
 
@@ -125,6 +134,7 @@ def mpc_orbital_elements(
125
134
  raan_deg: float | None = None,
126
135
  argument_of_periapsis_deg: float | None = None,
127
136
  mean_anomaly_deg: float | None = None,
137
+ reference_frame: str | None = None,
128
138
  ) -> MpcOrbitalElements:
129
139
  """Build an MPC orbital-element fragment without adding physics policy."""
130
140
  return MpcOrbitalElements(
@@ -137,6 +147,7 @@ def mpc_orbital_elements(
137
147
  raan_deg=raan_deg,
138
148
  argument_of_periapsis_deg=argument_of_periapsis_deg,
139
149
  mean_anomaly_deg=mean_anomaly_deg,
150
+ reference_frame=reference_frame,
140
151
  )
141
152
 
142
153
 
@@ -153,7 +164,7 @@ def _elements_to_wire(
153
164
  value: MpcOrbitalElements | None,
154
165
  *,
155
166
  parameter: str,
156
- ) -> dict[str, float] | None:
167
+ ) -> dict[str, Any] | None:
157
168
  if value is None:
158
169
  return None
159
170
  if not isinstance(value, MpcOrbitalElements):
@@ -175,6 +186,9 @@ def lambert_transfer_window(
175
186
  arrival_step_days: float | None = None,
176
187
  departure_elements: MpcOrbitalElements | None = None,
177
188
  arrival_elements: MpcOrbitalElements | None = None,
189
+ max_departure_delta_v_m_s: int | None = None,
190
+ max_arrival_delta_v_m_s: int | None = None,
191
+ max_time_of_flight_days: int | None = None,
178
192
  ) -> dict[str, Any]:
179
193
  """Return a Lambert transfer-window scan from ASTROX.
180
194
 
@@ -236,6 +250,30 @@ def lambert_transfer_window(
236
250
  "ArrivalElements",
237
251
  _elements_to_wire(arrival_elements, parameter="arrival_elements"),
238
252
  )
253
+ _include_if_supplied(
254
+ payload,
255
+ "MaxDepartureDV",
256
+ _optional_integer(
257
+ max_departure_delta_v_m_s,
258
+ parameter="max_departure_delta_v_m_s",
259
+ ),
260
+ )
261
+ _include_if_supplied(
262
+ payload,
263
+ "MaxArrivalDV",
264
+ _optional_integer(
265
+ max_arrival_delta_v_m_s,
266
+ parameter="max_arrival_delta_v_m_s",
267
+ ),
268
+ )
269
+ _include_if_supplied(
270
+ payload,
271
+ "MaxTofDays",
272
+ _optional_integer(
273
+ max_time_of_flight_days,
274
+ parameter="max_time_of_flight_days",
275
+ ),
276
+ )
239
277
  return _without_status_fields(
240
278
  raw.post("/celestial/transfer", json=payload),
241
279
  endpoint="/celestial/transfer",
@@ -331,8 +369,16 @@ def mpc_ephemeris(
331
369
  observer_frame: str | None = None,
332
370
  start: str | None = None,
333
371
  stop: str | None = None,
372
+ step_s: float | None = None,
373
+ target_elements: MpcOrbitalElements | None = None,
334
374
  ) -> dict[str, Any]:
335
- """Return ASTROX minor-planet ephemeris output from its MPC-backed route."""
375
+ """Return ASTROX minor-planet ephemeris output from its MPC-backed route.
376
+
377
+ When ``target_elements`` is supplied, the server integrates those MPC
378
+ orbital elements directly instead of resolving ``target_name`` through the
379
+ MPC network query. ``step_s`` controls the output sampling cadence; zero
380
+ requests the server's internal integration grid.
381
+ """
336
382
  payload: dict[str, Any] = {
337
383
  "TargetName": _string(target_name, parameter="target_name"),
338
384
  }
@@ -351,6 +397,16 @@ def mpc_ephemeris(
351
397
  "Stop",
352
398
  _optional_string(stop, parameter="stop"),
353
399
  )
400
+ _include_if_supplied(
401
+ payload,
402
+ "Step",
403
+ _optional_number(step_s, parameter="step_s"),
404
+ )
405
+ _include_if_supplied(
406
+ payload,
407
+ "TargetElements",
408
+ _elements_to_wire(target_elements, parameter="target_elements"),
409
+ )
354
410
  return _without_status_fields(
355
411
  raw.post("/celestial/mpc", json=payload),
356
412
  endpoint="/celestial/mpc",
astrox/orbits.py CHANGED
@@ -11,6 +11,7 @@ from astrox._http import raw
11
11
  __all__ = [
12
12
  "CartesianState",
13
13
  "KeplerianElements",
14
+ "LambertResult",
14
15
  "MeanKeplerianElements",
15
16
  "Tle",
16
17
  "cartesian_state",
@@ -517,14 +518,37 @@ def geo_ym_lambert_delta_v(
517
518
  )
518
519
 
519
520
 
521
+ @dataclass(frozen=True, kw_only=True)
522
+ class LambertResult:
523
+ """Single-case Lambert solution with the sampled transfer positions."""
524
+
525
+ departure_delta_v_m_s: tuple[float, float, float]
526
+ arrival_delta_v_m_s: tuple[float, float, float]
527
+ positions: tuple[float, ...]
528
+ """Flat ``(x, y, z, ...)`` transfer position sequence in meters.
529
+
530
+ The server samples ``path_point_count`` points including the departure and
531
+ arrival positions, so the sequence length is ``path_point_count * 3``.
532
+ """
533
+
534
+
520
535
  def lambert_delta_v(
521
536
  *,
522
537
  departure_state: CartesianState,
523
538
  arrival_state: CartesianState,
524
539
  time_of_flight_s: float,
525
540
  gravitational_parameter_m3_s2: float | None = None,
526
- ) -> tuple[tuple[float, float, float], tuple[float, float, float]]:
527
- """Return single-revolution Lambert delta-v between two Cartesian states."""
541
+ get_path_points: bool = False,
542
+ path_point_count: int | None = None,
543
+ ) -> tuple[tuple[float, float, float], tuple[float, float, float]] | LambertResult:
544
+ """Return single-revolution Lambert delta-v between two Cartesian states.
545
+
546
+ By default returns ``(departure_delta_v_m_s, arrival_delta_v_m_s)``. When
547
+ ``get_path_points`` is true, the server also samples the transfer orbit and
548
+ the return is a :class:`LambertResult` carrying the delta-v pair plus the
549
+ flat position sequence; ``path_point_count`` sets the sample count
550
+ (server default 100, including both endpoints).
551
+ """
528
552
  if not isinstance(departure_state, CartesianState):
529
553
  raise TypeError("departure_state must be a CartesianState instance")
530
554
  if not isinstance(arrival_state, CartesianState):
@@ -536,12 +560,20 @@ def lambert_delta_v(
536
560
  "TOF": [time_of_flight_s],
537
561
  }
538
562
  _include_if_supplied(payload, "Gm", gravitational_parameter_m3_s2)
563
+ if get_path_points:
564
+ payload["GetPathPoints"] = True
565
+ _include_if_supplied(payload, "NumberOfPathPoints", path_point_count)
539
566
 
540
567
  result = raw.post("/orbit/lambert", json=payload)
541
- return (
542
- (result["DV1"][0], result["DV1"][1], result["DV1"][2]),
543
- (result["DV2"][0], result["DV2"][1], result["DV2"][2]),
544
- )
568
+ departure_delta_v = (result["DV1"][0], result["DV1"][1], result["DV1"][2])
569
+ arrival_delta_v = (result["DV2"][0], result["DV2"][1], result["DV2"][2])
570
+ if get_path_points:
571
+ return LambertResult(
572
+ departure_delta_v_m_s=departure_delta_v,
573
+ arrival_delta_v_m_s=arrival_delta_v,
574
+ positions=tuple(result["Positions"]),
575
+ )
576
+ return departure_delta_v, arrival_delta_v
545
577
 
546
578
 
547
579
  def convert_czml_position(
astrox/propagator.py CHANGED
@@ -42,6 +42,7 @@ __all__ = [
42
42
  "sgp4",
43
43
  "simple_ascent",
44
44
  "two_body",
45
+ "two_body_rv",
45
46
  ]
46
47
 
47
48
 
@@ -672,6 +673,36 @@ def two_body(
672
673
  return _success_path(result)
673
674
 
674
675
 
676
+ def two_body_rv(
677
+ *,
678
+ state: CartesianState,
679
+ time_of_flight_s: float,
680
+ gravitational_parameter_m3_s2: float | None = None,
681
+ step_s: float | None = None,
682
+ ) -> tuple[float, ...]:
683
+ """Propagate a Cartesian state with two-body dynamics and return the sampled ephemeris.
684
+
685
+ Returns the flat ``[t, x, y, z, vx, vy, vz, ...]`` sequence, where ``t`` is
686
+ seconds relative to the initial epoch, positions are in meters, and
687
+ velocities are in m/s. The sequence includes the start and stop samples;
688
+ intermediate samples follow ``step_s`` (server default 60 s).
689
+ """
690
+ if not isinstance(state, CartesianState):
691
+ raise TypeError("state must be a CartesianState instance")
692
+
693
+ payload: dict[str, Any] = {
694
+ "RV0": state.to_wire(),
695
+ "TimeOfFlight": time_of_flight_s,
696
+ }
697
+ if gravitational_parameter_m3_s2 is not None:
698
+ payload["Gm"] = gravitational_parameter_m3_s2
699
+ if step_s is not None:
700
+ payload["Step"] = step_s
701
+
702
+ result = raw.post("/Propagator/TwoBodyRV", json=payload)
703
+ return tuple(result["Positions"])
704
+
705
+
675
706
  def j2(
676
707
  *,
677
708
  start: str,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: astrox-python
3
- Version: 1.0.0
3
+ Version: 1.2.0
4
4
  Summary: Python SDK for the ASTROX Web API.
5
5
  Project-URL: Homepage, https://github.com/Mtrya/astrox-python
6
6
  Project-URL: Repository, https://github.com/Mtrya/astrox-python.git
@@ -3,13 +3,13 @@ astrox/_http.py,sha256=ZZDkJ6qPG4VbMDRZO7xonIib88aYS6PPTVRxRYwXU3A,15606
3
3
  astrox/access.py,sha256=pjyCher_yQNmkoojf2sWpG1HRDnLLK9gI90jM1iEoTo,5817
4
4
  astrox/cat.py,sha256=doZqy6lGDtgrVe9QlU57l1lccmzirc14RDSR41tyGUU,15057
5
5
  astrox/catalog.py,sha256=WDb3adiDO41YY6IdivjN3ri1vIQMHi6DPI3dBa8W1Gc,5255
6
- astrox/celestial.py,sha256=Mh8VChM-YWGAM4Jn2fs09SxfOuV4kR56j36LmKv6MP0,10840
6
+ astrox/celestial.py,sha256=EFuKRyZ2uMsjqdRCDTdwDsA3QuzstIU98DwWj8t-Btk,12703
7
7
  astrox/conjunction.py,sha256=sXhTNAlMai27ejuJekksaAw-850nHPimo21YdFpuzQo,9108
8
8
  astrox/exceptions.py,sha256=apClRT1r-N3XY1ibXxkW3oxpsJgiHos4tC0cMJ8eBRk,2592
9
9
  astrox/libration.py,sha256=YBgcZi2pTAjL5VUlE8y6jLhVbYnwRxv-PIUxJQmFBSg,13454
10
10
  astrox/lighting.py,sha256=aWMty64rU3wv1H1Q5q-yXG5mINbIY31omyd-pjdNaVs,3876
11
- astrox/orbits.py,sha256=JiSkCyWqYmeV-yX5WBL9554_JNhec1Li8EdmkZmzXQc,17823
12
- astrox/propagator.py,sha256=kiW4kFSBy2_quZPZ2VqP49vfSYDKkJ1dna5rM5bo-9M,37334
11
+ astrox/orbits.py,sha256=SdRE7f9W9zO4N6Xxjg5quHK--gsVh1E5qdvJigpwxLA,19223
12
+ astrox/propagator.py,sha256=GPcg5fpra_jsof4ipxgtaeJNof2PrWBvUTnDG1aH940,38434
13
13
  astrox/rocket.py,sha256=tb6wXc4bZ0EMAd9bVzva_7k3I9S2lsYA7Q2RD6byqfk,2755
14
14
  astrox/terrain.py,sha256=2v_Ca5OnXjmMtIKNFQJ_Kbdsh42ZuIc4ZDe-0aWnpdg,6146
15
15
  astrox/astrogator/__init__.py,sha256=rgIHos9rHjQ3i-qkpLeLRwZL6zsVthc2bOVbAdwHWHU,267
@@ -33,7 +33,7 @@ astrox/coverage/number_of_assets.py,sha256=Bf1OQRu1r9lunyARyQoV4x7KetkgY_7OMNcEc
33
33
  astrox/coverage/response_time.py,sha256=yLnv8eEDL6CS0PJHC1JIKBCoOachOwo3m8dNoHH3HfQ,5164
34
34
  astrox/coverage/revisit_time.py,sha256=R5vF2egixCF9elvsGYXzpgWbD5lfcQW8uZqi1ifIDX8,5155
35
35
  astrox/coverage/simple_coverage.py,sha256=ZPj3HrP3M59rG47pz4eHt1rq-vTlk-oZ7c2ZBmFsz1U,4992
36
- astrox_python-1.0.0.dist-info/METADATA,sha256=zlaPEYsPsqHp6bnpp655SzjcopUFyKAAK-sC1-Z7r80,2520
37
- astrox_python-1.0.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
38
- astrox_python-1.0.0.dist-info/licenses/LICENSE,sha256=Fbt55lCQnv_n5yGhbG_VDlowjaV_FPHBQWsJIyuBpDk,1062
39
- astrox_python-1.0.0.dist-info/RECORD,,
36
+ astrox_python-1.2.0.dist-info/METADATA,sha256=wh8FRFOydoqxg-BeW5pnZZj1Pz5i_zaC_TxamnoA5mw,2520
37
+ astrox_python-1.2.0.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
38
+ astrox_python-1.2.0.dist-info/licenses/LICENSE,sha256=Fbt55lCQnv_n5yGhbG_VDlowjaV_FPHBQWsJIyuBpDk,1062
39
+ astrox_python-1.2.0.dist-info/RECORD,,