Core API¶
Top-level environment, configuration, and rollout primitives.
OrbitalGymEnv¶
orbitalgym.env.core.OrbitalGymEnv ¶
Composed environment. reset / step are pure functions under the hood.
reset ¶
Reset returns (env_state, BySide(guard=SideOutput, bandit=SideOutput)).
reset_from_state ¶
Start an episode from a stored state instead of sampling one.
Returns the state with the clock zeroed, derived frame views
re-materialized from the truth frame, and the initial observations
computed with key. The reference orbit carried by state is
kept so a bank sampled under one epoch replays under the same one.
step ¶
Symmetric step. Both sides run through identical machinery.
EnvState¶
orbitalgym.env.core.EnvState ¶
SingleAgentView¶
orbitalgym.env.single_agent.SingleAgentView ¶
Projects the symmetric OrbitalGymEnv core to a single-agent view.
Caller provides actions for the controlled side only. The opponent
is driven by the policy declared in cfg.{side}_policy, which
ScenarioConfig.__post_init__ defaults to ZeroControl() when the
builder leaves it unset.
ScenarioConfig¶
orbitalgym.config.ScenarioConfig
dataclass
¶
ScenarioConfig(
n_guards: int,
n_bandits: int,
epoch_mjd_utc: float,
reference_orbit: ReferenceOrbitState,
guard_components: tuple[StateComponentKey, ...],
bandit_components: tuple[StateComponentKey, ...],
guard_params: VehicleParamsSpec,
bandit_params: VehicleParamsSpec,
ic_sampler: ICSpec,
dt: float,
max_horizon_s: float,
seed: int,
truth_dynamics: Any = DynamicsKey.HCW_RTN,
policy_dynamics: Any = DynamicsKey.HCW_RTN,
belief_dynamics: Any = None,
belief_dynamics_resolved: Any = DynamicsKey.HCW_RTN,
reference_orbit_dynamics: Any = None,
reference_orbit_dynamics_resolved: Any = DynamicsKey.KEPLERIAN_ECI,
guard_action_components: tuple[
ActionComponentKey, ...
] = (ActionComponentKey.IMPULSIVE_MANEUVER,),
bandit_action_components: tuple[
ActionComponentKey, ...
] = (ActionComponentKey.IMPULSIVE_MANEUVER,),
action_frame: Frame | None = None,
guard_components_extended: tuple[
StateComponentKey, ...
] = (),
bandit_components_extended: tuple[
StateComponentKey, ...
] = (),
guard_observation_fn: Any = None,
bandit_observation_fn: Any = None,
guard_ground_station_network: Any = None,
bandit_ground_station_network: Any = None,
reward_fn: Any = None,
termination_fn: Any = None,
guard_belief_initializer: Any = None,
guard_belief_updater: Any = None,
bandit_belief_initializer: Any = None,
bandit_belief_updater: Any = None,
controlled_side: Any = "guard",
guard_policy: Any = None,
bandit_policy: Any = None,
game: Any = None,
attitude_dynamics_key: AttitudeDynamicsKey
| None = None,
guard_attitude_params: Any = None,
bandit_attitude_params: Any = None,
attitude_control_torque_max: tuple[float, float, float]
| None = None,
pointing_boresight_body: tuple[float, float, float] = (
1.0,
0.0,
0.0,
),
layout: Any = None,
)
VehicleParamsSpec¶
orbitalgym.config.VehicleParamsSpec
dataclass
¶
VehicleParamsSpec(
dry_mass_kg: float,
isp_s: float,
max_thrust_n: float,
slew_rate_rad_s: float = 0.0,
)
ReferenceOrbitState¶
orbitalgym.reference_orbit.ReferenceOrbitState ¶
Reference orbit 6D state in Earth-Centered Inertial frame at the scenario epoch.
The dynamical anchor that HCW linearizes about. Canonical representation; osculating elements derived on demand via astrojax helpers.
from_keplerian
classmethod
¶
from_keplerian(
semi_major_axis_m: float,
eccentricity: float,
inclination: float,
raan: float,
argument_of_perigee: float,
mean_anomaly: float,
as_degrees: bool = True,
) -> ReferenceOrbitState
Build a ReferenceOrbitState from classical Keplerian elements.
Element convention matches astrojax / sampling.side.RelativeKeplerian:
[a, e, i, RAAN, omega, M] with M the mean anomaly. Semi-major
axis is in meters; angle units are controlled by as_degrees.
Conversion delegates to astrojax.state_koe_to_eci (Montenbruck &
Gill Satellite Orbits, Eq. 2.43–2.44).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
semi_major_axis_m
|
float
|
Semi-major axis |
required |
eccentricity
|
float
|
Eccentricity |
required |
inclination
|
float
|
Inclination |
required |
raan
|
float
|
Right ascension of ascending node |
required |
argument_of_perigee
|
float
|
Argument of perigee |
required |
mean_anomaly
|
float
|
Mean anomaly |
required |
as_degrees
|
bool
|
If |
True
|
Returns:
| Type | Description |
|---|---|
ReferenceOrbitState
|
ReferenceOrbitState with |
ReferenceOrbitState
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
The canonical representation is Cartesian ECI (position_eci, velocity_eci).
You can construct it either directly with those arrays, or from classical
Keplerian elements via ReferenceOrbitState.from_keplerian(...) — angles
default to degrees; pass as_degrees=False for radians. The Keplerian
constructor delegates to astrojax.state_koe_to_eci and validates that
semi_major_axis_m > 0 and 0 ≤ eccentricity < 1.
from orbitalgym.reference_orbit import ReferenceOrbitState
# Cartesian (canonical):
ref_cart = ReferenceOrbitState(
position_eci=jnp.array([7000e3, 0.0, 0.0]),
velocity_eci=jnp.array([0.0, 7546.05, 0.0]),
)
# Keplerian (degrees by default):
ref_kep = ReferenceOrbitState.from_keplerian(
semi_major_axis_m=7000e3,
eccentricity=0.001,
inclination=51.6,
raan=0.0,
argument_of_perigee=0.0,
mean_anomaly=0.0,
)
Rollout¶
Two drivers: rollout is obs-only (agent_view = obs); belief_rollout
threads per-side beliefs (agent_view = belief). Both compile to a
single lax.scan-based JIT region.
orbitalgym.rollout.rollout ¶
rollout(
env,
policies: BySide,
init_policy_state_fns: BySide,
key: Array,
n_steps: int,
*,
initial_state: Any = None,
) -> Trajectory
Symmetric obs-only rollout. Both sides driven by their own policies.
agent_view passed to each policy is the flattened per-side observation.
Returns a Trajectory with sides.guard / sides.bandit each carrying
(T, N_side, ...) leading-axis arrays, plus episode_done (T,) latched.
initial_state starts the episode from a stored EnvState (see
orbitalgym.eval.bank) instead of sampling.
orbitalgym.rollout.rollout_single_agent ¶
rollout_single_agent(
view,
controlled_policy: Callable,
init_controlled_ps_fn: Callable,
key: Array,
n_steps: int,
) -> Trajectory
Convenience wrapper for SingleAgentView-style runs.
Pulls the opponent's scripted policy from view.opponent_policy and
threads only the controlled side's policy state externally.
orbitalgym.rollout.belief_rollout ¶
belief_rollout(
env,
policies: BySide,
init_policy_state_fns: BySide,
belief_initializers: BySide,
belief_updaters: BySide,
key: Array,
n_steps: int,
*,
guard_ground_station_network: Any = None,
bandit_ground_station_network: Any = None,
team_sync_fns: Any = None,
guard_link: Any = None,
bandit_link: Any = None,
initial_state: Any = None,
) -> tuple[Trajectory, BySide]
Belief-aware rollout: agent_view = post-update Belief.
Each tick:
- For each side, compute a per-tick contact mask of shape
(n_side,)from the side's link predicate when given, else from its ground-station network. When neither is given the mask is all-False. - Optionally fuse the side's belief across in-contact teammates via
team_sync_fns.X(belief_X, contact_X). - Wrap the (possibly synced) belief as a
:class:
~orbitalgym.belief.contact_aware.ContactAwareBeliefand pass it topolicies.X(ps_X, agent_view, key, t). env.stepadvances the world.- Each side's
belief_updaterfolds the new per-side observation channels into the synced belief (so team fusion persists into the next tick's prior). The control it predicts under is the Δv the env actually imparted, fromStepOutput.info["applied_dv"], not the commanded Δv: a command clipped by thrust or an empty tank would otherwise leave the own-state belief drifting off truth. - Freeze-on-done as in :func:
rollout.
The synced belief — not the wrapped ContactAwareBelief — is what
flows forward in the carry: wrapping is a per-tick view, fusion is a
persistent state update.
Returns (Trajectory, BySide[belief_history]). belief_history is a
BySide whose leaves are belief leaves with a leading time axis — what
feeds :class:orbitalgym.viz.animation.RolloutScene's
belief_history parameter for animated 2σ ellipsoids.
initial_state starts the episode from a stored EnvState (see
orbitalgym.eval.bank) instead of sampling.
orbitalgym.rollout.episode_mask ¶
Boolean mask True up to and including the terminating step.
Same semantics as Phase 0's episode_mask but reads from the new
Trajectory shape's episode_done field.
Precision¶
orbitalgym.set_precision ¶
Set both astrojax's internal dtype and JAX's jax_enable_x64 flag.
astrojax.config.set_dtype(jnp.float64) enables jax_enable_x64 as a side
effect, but set_dtype(jnp.float32) does NOT toggle it back. This helper
keeps the two settings consistent so callers can flip precision with a
single call. Call set_precision(jnp.float64) for sub-mm orbit precision
(required by KOE/ECI conversions inside KEPLERIAN_ECI and full-force
astrojax dynamics); the package default is jnp.float32 for backend
portability.
No package-level cache invalidation is needed: typed-instance dynamics
(KeplerianEciDynamics, AstrojaxOrbitDynamics) build their Epoch / RHS
state in __post_init__, so a fresh dtype is picked up the next time
a new env is constructed. Existing instances retain their original dtype
— flip the dtype before building the env you want to run on the new
precision.
Logging¶
orbitalgym.logging.writer.save_run ¶
Save (config, trajectory) to a single HDF5 file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path | str
|
destination file path (created or overwritten) |
required |
config
|
ScenarioConfig
|
the ScenarioConfig used to produce the trajectory (round-tripped via JSON) |
required |
trajectory
|
Any
|
any pytree (typically a Trajectory) whose leaves are jax.Array |
required |
The config is stored as a JSON string in a root-level attribute so an
inspector (e.g., h5py.File(path).attrs['config']) can see the scenario
without loading the arrays.
orbitalgym.logging.reader.load_run ¶
Load a saved run from HDF5.
Returns:
| Type | Description |
|---|---|
ScenarioConfig
|
(config, trajectory_dict) where trajectory_dict maps dotted-path strings |
dict[str, Any]
|
to jax.Arrays (e.g., "reward", "action", "env_state.guards.rtn"). |
Raises ValueError if the file is missing required structure (config attr, schema_version attr, /trajectory group) or if schema_version doesn't match what this reader understands.