Journey CD-AMD API Demonstration¶
This notebook demonstrates using the JOE-Postgrest API to create a Constellation Mission Design, trigger the Constellation Design, and follow it's completion with a Mission Propagation for each satellite defined in the Constellation Design.
The Semi-major Axis, Inclination, and Eccentricity are then graphed for all satellites.
In [1]:
import os
import json
import requests
from uuid import uuid4
from time import sleep
from datetime import datetime, timedelta
from io import StringIO
import hashlib
import matplotlib.pyplot as plt
import pandas as pd
import jwt
import joe.model as joe
In [2]:
JOE_URL = "http://localhost:3001"
USERNAME = os.getenv("USER_NAME", "user@user.user")
PASSWORD = os.getenv("USER_PASSWORD", "m0rpheus")
In [3]:
TOKEN = None
def refresh_token():
global TOKEN
try:
resp = requests.post(f"{JOE_URL}/api/auth/login/", json={"email": USERNAME, "password": PASSWORD})
TOKEN = resp.json()['access']
except:
print(f"Failed: {resp.status_code} -- {resp.text}")
refresh_token()
In [4]:
company_id = jwt.decode(TOKEN, options={"verify_signature": False})['company']
Define Mission Context¶
In [5]:
joes = []
# Physics
physics = joe.Physics(
start_time=(datetime.utcnow() + timedelta(days=365)).isoformat(),
gravity_model="EIGEN-6S",
atmospheric_model="NRLMSISE-00",
third_body_model="all",
solar_radiation_model=True,
harmonic_degree=4,
harmonic_order=4
)
morpheus_office_target = joe.Target(
name="morpeus-ofice-target",
latitude=33.920466,
longitude=-118.390008,
minimum_elevation=45.0,
altitude=1000.0,
shapefile=None
)
# Mission
mission = joe.Mission(
id=str(uuid4()),
creation_date=datetime.utcnow().isoformat(),
update_date=datetime.utcnow().isoformat(),
owned_by=company_id,
name=f"CD-WD-TEST",
launch_date=(datetime.utcnow() + timedelta(days=365)).isoformat(),
amd_enabled=True,
physics=physics,
targets=[morpheus_office_target],
groundstations=[],
maneuvers=[])
joes.append(mission)
# Bus
bus = joe.Bus(**joe.Bus.BUS_TEMPLATES['6U'])
payload = joe.Payload(
fov_cross=40.0,
fov_along=20.0
)
thruster = joe.Propulsion(
type="Custom",
prop_mass=10.0,
isp=[float(200)],
thrust=[float(0.3)]
)
battery = joe.Battery(
voltage=float(12.7),
capacity=float(30000),
maximum_discharge_rate=float(30),
recommended_discharge_rate=float(30)
)
solar_panels = [joe.SolarPanel(
mount_type="Body",
cell_efficiency=float(0.8),
surface_area=0.04,
voltage=12.0,
) for i in range(3)]
power_budget = joe.PowerBudget(
budget={
"propulsion_active_power_draw": float(1),
"adcs_active_power_draw": float(1)
}
)
# Satellite
satellite = joe.Satellite(
id=str(uuid4()),
creation_date=datetime.utcnow().isoformat(),
update_date=datetime.utcnow().isoformat(),
owned_by=company_id,
mission=mission.id,
name=f"CD-WD-TEST",
bus=bus,
payload=payload,
battery=battery,
solar_panels=solar_panels,
power_budget=power_budget,
propulsion=thruster)
joes.append(satellite)
In [6]:
constellationDesign = joe.ConstellationDesign(
id=str(uuid4()),
creation_date=datetime.utcnow().isoformat(),
update_date=datetime.utcnow().isoformat(),
owned_by=company_id,
mission=mission.id,
type="Walker Delta",
number_of_planes=5,
inter_plane_spacing=1.0,
satellite_count=15.0,
elevation_angle=5.0,
desired_coverage_fold=5.0,
end_date_hours=5.0,
end_date_days=5.0,
spatial_coverage=5.0,
spatial_uniformity=5.0,
temporal_uniformity=5.0,
temporal_average_gap=5.0,
maximum_gap=5.0,
initial_orbit_type="LEO",
deployment_orbit_template="MEO",
semi_major_axis_value=8000.0,
semi_major_axis_type="???",
eccentricity=0.0005,
inclination=55.0,
argument_of_perigee=0.05,
raan=0.01,
true_anomaly=30.0,
targets=[morpheus_office_target],
generate=False
)
joes.append(constellationDesign)
/Users/lutherblackwood/prj/qajourney/venv/lib/python3.11/site-packages/joe/model/base.py:104: UserWarning: Casting number_of_planes from <class 'int'> to <class 'float'>. warnings.warn(f"Casting {varname} from {intype} to {outtype}.")
In [7]:
refresh_token()
for obj in joes:
obj_type = type(obj).__name__.lower()
resp = requests.post(
f"{JOE_URL}/{obj_type}",
json=obj.to_json(to_obj=True),
headers={"Authorization": f"Bearer {TOKEN}"})
assert resp.status_code == 201, f"Failed to create {type(obj).__name__}: {resp.status_code}--{resp.text}"
print(f"Created {type(obj).__name__} in Journey")
Created Mission in Journey Created Satellite in Journey Created ConstellationDesign in Journey
In [8]:
resp = requests.patch(
f"{JOE_URL}/constellationdesign?id=eq.{constellationDesign.id}",
json={
"generate": True
},
headers={"Authorization": f"Bearer {TOKEN}"})
assert resp.status_code == 200, f"Failed to create {type(obj).__name__}: {resp.status_code}--{resp.text}"
Poll for CD Completion¶
In [9]:
completed = False
while not completed:
refresh_token()
resp = requests.get(
f"{JOE_URL}/constellationdesignticket?mission=eq.{mission.id}",
headers={"Authorization": f"Bearer {TOKEN}"})
if resp.status_code == 200:
cd_ticket = resp.json()[0]
print(f"{datetime.utcnow()} -- {cd_ticket['status']}")
completed = cd_ticket['status'] == "COMPLETED"
if not completed:
sleep(30)
print(f"CD Completed at {datetime.utcnow()}")
2024-07-16 22:04:54.843277 -- STARTED 2024-07-16 22:05:25.957908 -- STARTED 2024-07-16 22:05:56.971390 -- STARTED 2024-07-16 22:06:28.006017 -- STARTED 2024-07-16 22:06:59.037996 -- STARTED 2024-07-16 22:07:31.415950 -- COMPLETED CD Completed at 2024-07-16 22:07:31.416685
Poll for MD Completion¶
In [10]:
completed = False
while not completed:
refresh_token()
resp = requests.get(
f"{JOE_URL}/missiondesignticket?constellation=eq.{cd_ticket['id']}",
headers={"Authorization": f"Bearer {TOKEN}"})
if resp.status_code == 200:
md_tickets = resp.json()
stati = [md['status'] for md in md_tickets]
num_completed = sum([s == "COMPLETED" for s in stati])
print(f"{datetime.utcnow()} -- {num_completed}/{len(stati)}")
completed = num_completed == len(stati)
if not completed:
sleep(30)
print(f"MD Runs Completed at {datetime.utcnow()}")
2024-07-16 22:07:32.629678 -- 0/12 2024-07-16 22:08:03.653984 -- 0/15 2024-07-16 22:08:34.722374 -- 0/15 2024-07-16 22:09:07.025833 -- 9/15 2024-07-16 22:09:38.135924 -- 15/15 MD Runs Completed at 2024-07-16 22:09:38.136333
Plot Mission-Design Orbital Elements Results¶
In [11]:
data = {}
for md in md_tickets:
print(f"{datetime.utcnow()} -- Retrieving {md['id']}")
refresh_token()
data[md['id']] = {}
for data_type in ["orbital_elements", "power", "propulsion"]:
resp = requests.get(
f"{JOE_URL}/missiondesignresult?mission_design_ticket=eq.{md['id']}&key=eq.{data_type}",
headers={"Authorization": f"Bearer {TOKEN}"})
assert resp.status_code == 200, f"Failed to retrieve {data_type} for {md['id']} -- {resp.status_code} - {resp.text}"
data[md['id']][data_type] = pd.read_csv(StringIO(resp.text))
2024-07-16 22:09:38.148310 -- Retrieving 2f4bfccb-01b7-4092-9e62-f93a4b65a4c1 2024-07-16 22:09:40.273927 -- Retrieving 038b4788-8e2b-47af-963c-bd4b7088fb51 2024-07-16 22:09:42.388060 -- Retrieving 7cab8c28-4a45-4684-a6fe-734b78ecc580 2024-07-16 22:09:44.468958 -- Retrieving 51cc85ab-cfa4-4473-89e4-b69f1244b53d 2024-07-16 22:09:46.783017 -- Retrieving 2ead524d-90e1-4785-bb1e-8dcd333e34f3 2024-07-16 22:09:48.798417 -- Retrieving ae46d5f4-ab30-440e-89c6-acfb080e21ef 2024-07-16 22:09:50.905317 -- Retrieving 14076502-a3b9-4763-8562-41275d39808c 2024-07-16 22:09:53.030853 -- Retrieving 7b88dfc0-b656-4e4d-869c-b107a8cf1413 2024-07-16 22:09:55.104722 -- Retrieving 230b4c0b-9831-43ed-9325-fa4e943825fe 2024-07-16 22:09:57.252665 -- Retrieving 2b89e2b5-e63d-4fa7-9d2d-5d542558fa64 2024-07-16 22:09:59.282198 -- Retrieving bc1c360f-58b0-48a1-ad38-3e6c2a3341d3 2024-07-16 22:10:01.637163 -- Retrieving edebbd0a-706a-47e1-b70e-57b59cd5afd7 2024-07-16 22:10:03.750377 -- Retrieving e6dc4d61-8a98-415c-8e5e-16d0508d2de7 2024-07-16 22:10:05.873848 -- Retrieving 1b3d4c3a-65c8-47e6-b801-a59e447e44f9 2024-07-16 22:10:08.014623 -- Retrieving 52117054-8771-418f-9dc3-5011d23056ad
In [12]:
fig, axes = plt.subplots(nrows=3, ncols=len(data), sharey='row', sharex='col')
for m_idx, (md, md_data) in enumerate(data.items()):
for e_idx, elem in enumerate(['Semi-major Axis [km]', 'Inclination [degrees]', 'Eccentricity']):
ax = axes[e_idx][m_idx]
df = md_data['orbital_elements']
df['Time [UTC]'] = pd.to_datetime(df['Time [UTC]'], format='mixed')
x = df['Time [UTC]']
y = df[elem]
ax.plot(x, y, label=md)
ax.set_title(f"Sat_{m_idx}-{elem}")
fig.set_size_inches(48, 10.5, forward=True)
fig.autofmt_xdate()
In [ ]: