""" A python API for interactions between a client and the MDS."""
import json
import math
import re
import time
import numpy as np
from enum import Enum
from mds_api import client
from mds_api.NetworkConfiguration import NetworkConfiguration
from mds_api.mds_utils import *
from mds_api.mds_io import *
ground_asset_types = {}
ground_asset_types['Unknown'] = 0
ground_asset_types['Station'] = 1
ground_asset_types['Ship'] = 2
ground_asset_types['Sumbarine'] = 3
ground_asset_types['GroundVehicle'] = 4
ground_asset_types['Plane'] = 5
pointing_modes = {}
pointing_modes['Idle'] = 0
pointing_modes['Nadir'] = 1
pointing_modes['Sun'] = 2
pointing_modes['Earth'] = 3
pointing_modes['CustomTarget'] = 4
pointing_modes['ClosestStation'] = 5
pointing_modes['CustomQuaternion'] = 6
comms_laws = {}
comms_laws['Nearest'] = 0
comms_laws['AllAvailable'] = 1
comms_laws['Friendly'] = 2
class AccelerationType(Enum):
PointMassGravity = 0
SphericalHarmonicGravity = 1
RadiationPressure = 2
Aerodynamic = 3
RelativisticCorrection = 4
class SphericalHarmonicGravityFieldModels(Enum):
egm96 = 1 # Earth
ggm02c = 2 # Earth
ggm02s = 3 # Earth
goco05c = 4 # Earth
glgm3150 = 5 # Moon
lpe200 = 6 # Moon
gggrx1200 = 7 # Moon
jgmro120d = 8 # Mars
jgmess160a = 9 # Mercury
shgj180u = 10 # Venus
class Integrators(Enum):
Euler = 0
RK4 = 1
RKF12 = 11
RKF45 = 12
RKF56 = 13
RKF78 = 14
RK87_DormandPrince = 15
RKF89 = 16
rungeKuttaVerner89 = 17
rungeKuttaFeagin108 = 18
rungeKuttaFeagin1210 = 19
rungeKuttaFeagin1412 = 20
class IntegratorOrderToUse(Enum):
lower = 0
higher = 1
class IAUConventions(Enum):
iau_2000_a = 0
iau_2000_b = 1
iau_2006 = 2
class ReferenceFrames(Enum):
J2000 = 0
ECLIPJ2000 = 1
class RadiationSourceModelTypes(Enum):
HighFidelity = 0
Constant = 1
class GravityFieldVariationsModelTypes(Enum):
Empty = -1
Default = 0
class PropagatorTypes(Enum):
cowell = 0
encke = 1
gauss_keplerian = 2
gauss_modified_equinoctial = 3
unified_state_model_quaternions = 4
unified_state_model_modified_rodrigues_parameters = 5
unified_state_model_exponential_map = 6
class PredefinedPropagationSettings(Enum):
VLEO = 0
LowLEO = 1
HighLEO = 2
MEO = 3
GEO = 4
AvailableSphericalHarmonicsModels = {
"Earth": {
"EGM96": SphericalHarmonicGravityFieldModels.egm96,
"GGM02c": SphericalHarmonicGravityFieldModels.ggm02c,
"GGM02s": SphericalHarmonicGravityFieldModels.ggm02s,
"GOCO05c": SphericalHarmonicGravityFieldModels.goco05c
},
"Moon": {
"GLGM3150": SphericalHarmonicGravityFieldModels.glgm3150,
"LPE200": SphericalHarmonicGravityFieldModels.lpe200,
"GGGRX1200": SphericalHarmonicGravityFieldModels.gggrx1200
},
"Mars": {
"JGMRO120d": SphericalHarmonicGravityFieldModels.jgmro120d
},
"Mercury": {
"JGMESS160a": SphericalHarmonicGravityFieldModels.jgmess160a
},
"Venus": {
"SHGJ180u": SphericalHarmonicGravityFieldModels.shgj180u
}
}
def enable_reentry_check():
data = {}
data['enableReentryCheck'] = True
json_data = json.dumps(data)
response = client.ToggleReentryCheck(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def disable_reentry_check():
data = {}
data['enableReentryCheck'] = False
json_data = json.dumps(data)
response = client.ToggleReentryCheck(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def modify_reentry_altitude(satName: str, altitude: float):
"""
Modify the re-entry altitude for all satellites.
Args:
satName (str): Name of the satellite.
altitude (float): Re-entry altitude in km.
Returns:
Response from the server
"""
data = {}
data['satName'] = satName
data['reentryAltitude'] = altitude
json_data = json.dumps(data)
response = client.ModifyReentryAltitude(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def setPredefinedPropagationSettings(satName: str, predefinedSettings: PredefinedPropagationSettings):
data = {}
data['satName'] = satName
data['predefinedSettings'] = str(predefinedSettings.value)
json_data = json.dumps(data)
response = client.setPredefinedPropagationSettings(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def modify_integrator_type(satName: str, useVariableStepIntegrator: bool):
data = {}
data['satName'] = satName
data['useVariableStepIntegrator'] = str(useVariableStepIntegrator)
json_data = json.dumps(data)
response = client.modifyIntegratorType(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def modify_integrator_numerical_scheme(satName: str, numericalScheme: Integrators, orderToUse: IntegratorOrderToUse = IntegratorOrderToUse.lower):
data = {}
data['satName'] = satName
data['numericalScheme'] = str(numericalScheme.value)
data['orderToUse'] = str(orderToUse.value)
json_data = json.dumps(data)
response = client.modifyIntegratorNumericalScheme(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def modify_variable_step_integrator_extra_parameters(satName: str, absoluteTolerance: float, relativeTolerance: float, minTimeStep: float, maxTimeStep: float, usePerElementStepSizeControl : bool = False):
data = {}
data['satName'] = satName
data['absTol'] = str(absoluteTolerance)
data['relTol'] = str(relativeTolerance)
data['minTimeStep'] = str(minTimeStep)
data['maxTimeStep'] = str(maxTimeStep)
data['usePerElementStepSizeControl'] = str(usePerElementStepSizeControl)
json_data = json.dumps(data)
response = client.modifyVariableStepIntegratorExtraParameters(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def modify_translational_propagator_type(satName: str, propagatorType: PropagatorTypes):
data = {}
data['satName'] = satName
data['propagatorType'] = str(propagatorType.value)
json_data = json.dumps(data)
response = client.modifyTranslationalPropagatorType(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def modify_simulation_reference_frame(satName: str, referenceFrame: ReferenceFrames):
data = {}
data['satName'] = satName
data['referenceFrame'] = str(referenceFrame.value)
json_data = json.dumps(data)
response = client.modifySimulationReferenceFrame(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def modify_gravity_field_variations_model(satName: str, bodyName: str, gravityFieldVariationModelType: GravityFieldVariationsModelTypes):
data = {}
data['satName'] = satName
data['bodyName'] = bodyName
data['gravityFieldVariationModelType'] = str(gravityFieldVariationModelType.value)
json_data = json.dumps(data)
response = client.modifyGravityFieldVariationsModel(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def modify_central_body_radiation_source_model(satName: str, bodyName: str, radiationSourceModelType: RadiationSourceModelTypes, includeAlbedoRadiation: bool = True, includeThermalRadiation: bool = True):
data = {}
data['satName'] = satName
data['bodyName'] = bodyName
data['radiationSourceModelType'] = str(radiationSourceModelType.value)
data['includeAlbedoRadiation'] = str(includeAlbedoRadiation)
data['includeThermalRadiation'] = str(includeThermalRadiation)
json_data = json.dumps(data)
response = client.modifyCentralBodyRadiationSourceModel(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def toggle_central_body_high_fidelity_atmosphere_model(satName: str, bodyName: str, useNRLMSISE: bool, spaceWeatherFilePath: str = ""):
data = {}
data['satName'] = satName
data['bodyName'] = bodyName
data['useNRLMSISE'] = useNRLMSISE
if spaceWeatherFilePath != "":
data['spaceWeatherFilePath'] = spaceWeatherFilePath
json_data = json.dumps(data)
response = client.toggleCentralBodyHighFidelityAtmosphereModel(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def toggle_central_body_high_fidelity_rotation_model(satName: str, bodyName: str, useHighFidelityRotationModel: bool, iauConvention: IAUConventions = IAUConventions.iau_2006):
data = {}
data['satName'] = satName
data['bodyName'] = bodyName
data['useHighFidelityRotationModel'] = useHighFidelityRotationModel
data['iauConvention'] = str(iauConvention.value)
json_data = json.dumps(data)
response = client.toggleCentralBodyHighFidelityRotationModel(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def modify_spherical_harmonics_model(satName: str, bodyName: str, sphericalHarmonicsModel: SphericalHarmonicGravityFieldModels):
data = {}
data['satName'] = satName
data['bodyName'] = bodyName
data['sphericalHarmonicsModel'] = str(sphericalHarmonicsModel.value)
json_data = json.dumps(data)
response = client.modifySphericalHarmonicsModel(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def add_acceleration(satName:str, bodyName:str, acceleration_type:AccelerationType, spherical_harmonic_degree:int = -1, spherical_harmonic_order:int = -1):
data = {}
data['satName'] = satName
data['bodyName'] = bodyName
data['accelerationType'] = acceleration_type.value
data['sphericalHarmonicDegree'] = spherical_harmonic_degree
data['sphericalHarmonicOrder'] = spherical_harmonic_order
json_data = json.dumps(data)
response = client.addAcceleration(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def load_custom_model_for_satellite(sat_name: str, custom_model_path: str):
data = {
"satelliteName": sat_name,
"customModelPath": custom_model_path
}
json_data = json.dumps(data)
response = client.load_custom_model_for_satellite(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def attach_torch(sat_name: str, range: float, spot_angle: float, intensity: float, color: list[float], position: list[float], rotation: list[float]):
data = {}
data['name'] = sat_name
data['range'] = range
data['spotAngle'] = spot_angle
data['intensity'] = intensity
data['color'] = vector_to_string(color)
data['position'] = vector_to_string(position)
data['rotation'] = '(' + str(rotation[0]) + ', ' + str(rotation[1]) + ', ' + str(rotation[2]) + ', ' + str(rotation[3]) + ')'
json_data = json.dumps(data)
response = client.attach_torch(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def calculate_intersat_metrics():
"""call to take fleet-data and compute relative pos/vel/etc in LVLH
Returns:
"""
data = {}
json_data = json.dumps(data)
response = client.calculate_intersat_metrics(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
[docs]
def calculate_coverage(resolution = 50000, data_index = 0):
"""call to take fleet-data, create an Earth-grid and perform
a coverage calculation.
Args:
resolution (int, optional): total propagation time. Defaults to 50000.
data_index (int, optional): the propagated data's time index to be used for the
instantaneous coverage calculation. Defaults to 0.
Returns:
"""
data = {}
data['resolution'] = resolution
data['dataIndex'] = data_index
# data['name'] = sat_name
json_data = json.dumps(data)
response = client.calculate_coverage(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
[docs]
def calculate_gs_pass(prop_time = 3600, prop_time_step = 60):
"""call to do an offline fleet propagation
and compute ground station passes between added satellites and available ground assests
Args:
prop_time (int, optional): total propagation time. Defaults to 3600.
prop_time_step (int, optional): propagation timestep. Defaults to 60.
Returns:
"""
data = {}
data['timeStep'] = prop_time_step
data['propTime'] = prop_time
# data['name'] = sat_name
json_data = json.dumps(data)
response = client.calculate_gs_pass(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def deattach_torch(sat_name: str):
data = {}
data['name'] = sat_name
json_data = json.dumps(data)
response = client.deattach_torch(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
[docs]
def check_constraint(sat_name: str):
"""Check constraints on satellite for monitoring
Args:
sat_name (str): sat name
"""
data = {}
data['name'] = sat_name
data['constraint'] = {}
data['constraint']['type'] = 1
json_data = json.dumps(data)
response = client.check_constraint(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
[docs]
def set_constraint(sat_name: str, constraint_type: int, limits: list[float], target: str = ""):
"""Check constraints on satellite for monitoring
Args:
sat_name (str): sat name
constraint_type (int ): enum type for constraint
1 - semi_major axis
2 - eccentricity
3 - inclination
4 - ltan
limits (list[float]): [min, max] limits for constraint
target (str, optional): when applicable - relative target. Defaults to "".
Returns:
"""
data = {}
data['name'] = sat_name
data['constraint'] = {}
data['constraint']['type'] = constraint_type
data['constraint']['minValue'] = min(limits)
data['constraint']['maxValue'] = max(limits)
data['constraint']['target'] = target
json_data = json.dumps(data)
response = client.set_constraint(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def remove_constraint(sat_name: str, constraint_type: int, target: str = ""):
"""Check constraints on satellite for monitoring
Args:
sat_name (str): sat name
constraint_type (int ): enum type for constraint
1 - semi_major axis
2 - eccentricity
3 - inclination
4 - ltan
limits (list[float]): [min, max] limits for constraint
target (str, optional): when applicable - relative target. Defaults to "".
Returns:
"""
data = {}
data['name'] = sat_name
data['constraint'] = {}
data['constraint']['type'] = constraint_type
data['constraint']['target'] = target
json_data = json.dumps(data)
response = client.remove_constraint(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
[docs]
def set_sat_propagation_attributes(sat_name: str, mass: float = 1, area: float = 1, c_d: float = 2.2, c_srp = 1.2):
"""Alter satellite mass, surface area expsoed to drag/srp and drag/srp coefficients
Args:
sat_name (str): satellite name to select
mass (float, optional): selected mass [kg]. Defaults to 1.
area (float, optional): selected surface area [m^2]. Defaults to 1.
c_d (float, optional): Drag coefficient [-]. Defaults to 2.2.
c_srp (float, optional): Solar radiation pressure coefficient [-]. Defaults to 1.2.
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['mass'] = mass
data['coefficientDrag'] = c_d
data['coefficientSRP'] = c_srp
data['surfaceArea'] = area
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
def set_earth_light_intensity(intensity: float):
'''
It's possible to set specificaly earth light intensity
:param intensity: float from 0 to n works as color multiplier
:return:
'''
data = {
'intensity': intensity
}
json_data = json.dumps(data)
response = client.set_earth_light_intensity(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def set_satellite_light_intensity(name: str, intensity: float):
'''
It's possible to set light intensity for specific satellite
param: name (str): satellite name
:param intensity: float from 0 to n works as color multiplier
:return:
'''
data = {
'name': name,
'intensity': intensity
}
json_data = json.dumps(data)
response = client.set_satellite_light_intensity(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def remove_entity(name: str):
'''
:param name: removes created entity from the scene
:return:
'''
data = {
'name': name
}
json_data = json.dumps(data)
response = client.remove_entity(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def add_update_entity(name: str, position_ECI:list[float], radius: float, r: int, g: int, b: int, central_body: str, update_entity: bool=False):
'''
Creates sphere in inertial coordinate system
:param name: entity name
:param position_ECI: position in intertial coordinate system
:param radius: sphere radius
:param r: color red
:param g: color green
:param b: color blue
:param central_body: central body "Earth", "Moon", "Sun", etc
:param update_entity: True or False. if True new entity will be added, if false existing entity will be updated
:return:
'''
data = {
'name': name,
'positionECI': vector_to_string(position_ECI),
'radius': radius,
'r': r,
'g': g,
'b': b,
'centralBody': central_body,
'mode': 'create_entity',
}
if (update_entity):
data['mode'] = 'updateEntity'
json_data = json.dumps(data)
response = client.add_update_entity(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def always_show_satellite_labels(show: bool, names: list[str]):
'''
Turn on of satellite labels for specific satellites
:param show: True or False, if true satellite label is always visible
:param names: satellite list of names which will be affected
:return:
'''
data = {}
data['show'] = show
n = ','.join(names)
data['names'] = n
json_data = json.dumps(data)
response = client.always_show_satellite_labels(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
[docs]
def set_mass(sat_name: str, mass: float):
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['mass'] = mass
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
def set_velocity(sat_name: str, local_velocity: list[float]):
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['localVelocity'] = vector_to_string(local_velocity)
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def set_simulation_timestep(time_step):
client.set_simulation_timestep(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, time_step)
[docs]
def enable_API_synchronization():
client.enable_API_synchronization(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
[docs]
def disable_API_synchronization():
client.disable_API_synchronization(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
def toggle_Tudat_Propagator( useTudatPropagator : bool ):
data = {}
data["handleTudatPropagator"] = useTudatPropagator
json_data = json.dumps(data)
response = client.toggle_Tudat_Propagator(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
[docs]
def clear_scene():
rval = client.clear_scene(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return rval.decode()
[docs]
def get_config() -> str:
""" Retrieve the full current simulation config from MDS.
"""
cfg = client.save_config(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return cfg.decode()
[docs]
def get_config_part(part_name: str) -> tuple[float, str]:
""" Retrieve a specified part of the simulation config from MDS.
Args:
part_name (str): the name of the CelestialBody, PropagatedBody or GroundStation to get the config of.
"""
jd, cfg_part = client.get_config_part([part_name], NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return jd, cfg_part
[docs]
def load_config(cfg_path: str) -> bytes:
""" Order MDS to load a new configuration. Loads configurationn from a specified file and sends to MDS.
Args:
cfg_path (str): path to config file (on client machine)
"""
with open(cfg_path, 'r', encoding='utf-8') as f:
json_str = f.read()
response = client.load_config(json_str, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def get_celestial_body_data(*names: str) -> tuple[float, dict]:
""" Get data about specified celestial bodies.
Args:
names (str): names of the celestial bodies.
"""
names = list(names)
jd, jsonStr = client.get_config_part(names, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
jsonDict = json.loads(jsonStr)
return jd, jsonDict
[docs]
def get_ground_asset_data(*names: str) -> tuple[float, dict]:
""" Get data about a specified ground station(s).
Args:
names (str): names of the ground stations.
"""
names = list(names)
jd, jsonStr = client.get_config_part(names, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
jsonDict = json.loads(jsonStr)
return jd, jsonDict
[docs]
def get_sat_data(*names: str) -> tuple[float, dict]:
""" Get data about a specified satellite/spacecraft.
Args:
names (str): names of the spacecraft
"""
names = list(names)
jd, jsonStr = client.get_config_part(names, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
jsonDict = { }
if jsonStr != '':
jsonDict = json.loads(jsonStr)
return jd, jsonDict
[docs]
def get_sat_names() -> list[str]:
jsonStr = get_config()
jsonDict = json.loads(jsonStr)
prop_bodies = jsonDict['propagatedBodies']
sat_names = []
for sat in prop_bodies:
sat_names.append(sat['name'])
return sat_names
[docs]
def get_sat_pos_vel(*names: str) -> tuple[float, list[float], list[float]]:
""" Get local position and velocity of a specified satellite/spacecraft.
Args:
names (str): names of the spacecraft
"""
names = list(names)
jd, jsonStr = client.get_config_part(names, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
if jsonStr == '':
return jd, [], []
jsonDict = json.loads(jsonStr)
positions = []
velocities = []
if len(names) > 1:
for dict in jsonDict:
name, pos, vel = extract_pos_vel(dict)
positions.append(pos)
velocities.append(vel)
else:
name, positions, velocities = extract_pos_vel(jsonDict)
return jd, positions, velocities
[docs]
def get_sat_elements(*names: str) -> tuple[float, list[float]]:
""" Get osculating Kepler elements of a specified satellite/spacecraft.
Args:
names (str): names of the spacecraft
"""
names = list(names)
jd, jsonStr = client.get_config_part(names, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
jsonDict = json.loads(jsonStr)
elements = []
if len(names) > 1:
for dict in jsonDict:
name, elems = extract_elements(dict)
elements.append(elems)
else:
elements = extract_elements(jsonDict)
return jd, elements
[docs]
def add_solar_sat(sat_name: str, mass: float, position: list[float], velocity: list[float]) -> bytes:
""" Instantiate a new spacecraft with specified mass, global position and velocity.
Args:
sat_name (str): name of the spnew spacecraft
mass (float): mass of the new spacecraft [kg].
position (list[float]): solar barycentric position of a spacecraft [m].
velocity (list[float]): velocity of the spacecraft in ICRF coordinates, [m/s]
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['mass'] = mass
# '(-149941742301.569, 1423841988.23641, 651518931.770543)'
data['solarBarycentricPosition'] = '(' + str(position[0]) + ', ' + str(position[1]) + ', ' + str(position[2]) + ')'
# '(505.265200853449, -26005.9285649072, -4509.1488673049)'
data['solarBarycentricVelocity'] = '(' + str(velocity[0]) + ', ' + str(velocity[1]) + ', ' + str(velocity[2]) + ')'
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def add_sat(sat_name: str, mass: float, local_pos: list[float], local_vel: list[float], central_body: str, referenceArea: float=1.0,
coefficientDrag: float=2.2, coefficientSRP: float=1.2, attitude: list[float]=[0, 0, 0, 1], angularVelocity: list[float]=[0, 0, 0],
max_ang_rate: float=2, max_ang_acc: float=0.1, rigidbody: bool=False,
custom_model_path: str=None, model_rotation_offset: list[float]=[0, 0, 0, 1], print_debug: bool=False) -> bytes:
""" Instantiate a satellite, orbiting around a specified celestial body.
Args:
sat_name (str): name of the new spacecraft to be instantiated.
mass (float): mass of the spacecraft [kg]
local_pos (list[float]): local position of the spacecraft, relative to the central body, in body centered inertial
frame (ICRF aligned) [m].
local_vel (list[float]): velocity relative to central body, in body centered inertial frame (ICRF aligned) [m/s].
central_body (str): name of the celestial body, around which the spacecraft will be rotating.
attitude (list[float], optional): attitude quaternion of the spacecraft, relative to ICRF.
angularVelocity (list[float], optional): angular velocity [rad/s] of the spacecraft, relative to ICRF.
max_ang_rate (float, optional): maximum angular rate for the spacecraft pointing maneuvers in degrees
per second. defaults to 2 [rad/s].
max_ang_acc (float, optional): maximum angular acceleration for the spacecraft pointing maneuvers
in degrees per second per second. defaults to 0.1 [rad/s/s].
rigidbody (bool, optional): flag to enable or disable rigidbody 6DOF simulation for this spacecraft.
defaults to False.
custom_model_path (str, optional): path to a custom .glb or .gltf 3D model. path can be absolute, or if
it is not absolute, it will be interpreted as relative to /MDS_Solaris_Data/StreamingAssets/.
model_rotation_offset (list[float], optional): orientation quaternion of the 3D model relative to the
spacecraft.
print_debug (bool, optional): flag to turn debug message printing on/off
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['mass'] = mass
data['localPosition'] = '(' + str(local_pos[0]) + ', ' + str(local_pos[1]) + ', ' + str(local_pos[2]) + ')'
data['localVelocity'] = '(' + str(local_vel[0]) + ', ' + str(local_vel[1]) + ', ' + str(local_vel[2]) + ')'
data['centralBody'] = central_body
data['attitudeState'] = {}
data['attitudeState']['attitude'] = '(' + str(attitude[0]) + ', ' + str(attitude[1]) + ', ' + str(
attitude[2]) + ', ' + str(attitude[3]) + ')'
data['attitudeState']['angularVelocity'] = vector_to_string(angularVelocity)
data['attitudeState']['maxAngularSpeed'] = max_ang_rate
data['attitudeState']['maxAngularAcceleration'] = max_ang_acc
data['rigidBodyAttached'] = rigidbody
data['modelRotation'] = '(' + str(model_rotation_offset[0]) + ', ' + str(model_rotation_offset[1]) + ', ' + str(
model_rotation_offset[2]) + ', ' + str(model_rotation_offset[3]) + ')'
data['referenceArea'] = referenceArea
data['coefficientDrag'] = coefficientDrag
data['coefficientSRP'] = coefficientSRP
if custom_model_path != None:
data['customModelPath'] = custom_model_path
json_data = json.dumps([data])
if print_debug:
print("updating config with:\n")
print(json_data)
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
def create_ground_asset_waypoint(name:str, longitudeDeg: float, latitudeDeg: float,
altitudeMeters:float, speed:float = -1, ascendAndHold:bool = True) -> dict:
""" Create a dict, representing a waypoint for a ground asset.
Args:
name: name for the waypoint (str).
latitudeDeg: latitude, in degrees. Can be given as a float, or a string of format [deg]o[minutes]'[seconds]''[N/S]. For example "55o32'29''N". (float or str)
longitudeDeg: longitude, in degrees. Can be given as float or a sting of format [deg]o[minutes]'[seconds]''[N/S]. For example "23o59'17''N". (float or str)
altitudeMeters: altitude above sea level, in meters. (float)
speed: maximum speed at which approacinh this waypoint is allowed. If set to -1, then the ground asset's own maximum speed is used. (float, default: -1);
ascendAndHold: manner, in which the ground asset ascends to the waypoin't altitude. only affects ground assets that can fly. If ascendAndHold is true,
then the asset ascends at maximum allowed ascent rate and continues to the waypoint at the waypoint's altitude. If this parameter is false,
then the asset ascends/descends gradually to the waypoint. (bool, default=True)
"""
deg2rad = math.pi / 180
waypoint_dict = {
"name": name,
'LonLatAltPosition':
'(' + str(get_degrees(longitudeDeg) * deg2rad) + ', ' + \
str(get_degrees(latitudeDeg) * deg2rad) + ', ' + \
str(altitudeMeters) + ')',
"speed": speed,
"ascentMode": 0 if ascendAndHold else 1
}
return waypoint_dict
[docs]
def add_ground_asset(asset_name: str, centralBody: str, longitudeDeg: float | str, latitudeDeg: float | str, altitudeMeters: float,
canFly: bool = True, maxSpeed:float = 250.0, maxAscentRate: float=12.0, loopWaypoints: bool=True, waypoints = None,
currentWaypointIndex: int | None = None,
tx_frequency: float=2047 * 1e6, rx_frequency: float=8250 * 1e6,
radius: float=10, r: int=255, g: int=255, b: int=255, assetType: int = 1,
print_debug:bool = False) -> bytes:
"""
Add ground asset
Args:
asset_name: name of your ground asset (string)
longitudeDeg: longitude, in dgrees (float)
latitudeDeg: latitude, in degrees (float)
altitudeMeters: altitude in meters, above sea level (float)
canFly: is the asset allowed to lift off above 0m altitude? (bool, default=True)
maxSpeed: maximum speed in m/s (float, default=250)
maxAscentRate: maximum speed in m/s, at which the asset can ascend/descend (float, default=12)
loopWaypoints: Whether to loop waypoints. If True, then upon reachng the last waypoint will redirect to the
first one. Otherwise will stop at the last waypoint. (bool, default=True)
waypoints: list of waypoints or None. If you provide a list, waypoints will be updated.
If you provide None, waypoints are not modified. Create each item in the list using
the function create_ground_asset_waypoint. (list or None, default=None)
currentWaypointIndex: index of waypoint that the ground asset is currently heading to. Place the index of waypoint,
-1 to make the asset stop or None to not modify current value. (int or None, default=None)
tx_frequency: Tranzsmission frequency in Hz (default = 2047 MHz)
rx_frequency: reception frequency, in Hz (default = 8520 MHz)
r,g,b: Color of the spherical marker, as byte from 0 to 255. (default = 255,255,255)
assetType: ground asset type. Only affects the icon displayed, does not affect behaviour. Please see
mds_api.ground_asset_types for possible values. (default = 1 (Station))
"""
deg2rad = math.pi / 180
data = {}
data['name'] = asset_name
data['mass'] = 10000 # default, unused number
data['bodyType'] = 2
data['centralBody'] = centralBody
data['canFly'] = canFly
data['maxSpeed'] = maxSpeed
data['maxAscentRate'] = maxAscentRate
data['loopWaypoints'] = loopWaypoints
data['txFrequency'] = tx_frequency
data['rxFrequency'] = rx_frequency
data['radius'] = radius
data['r'] = r
data['g'] = g
data['b'] = b
data['assetType'] = assetType
if currentWaypointIndex is not None:
data['currentWaypointIndex'] = currentWaypointIndex
if waypoints is not None:
data['waypoints'] = waypoints
data['positionLonLatAlt'] = '(' + str(get_degrees(longitudeDeg) * deg2rad) + \
', ' + str(get_degrees(latitudeDeg) * deg2rad) + \
', ' + str(altitudeMeters) + ')'
json_data = json.dumps([data])
if print_debug:
print("updating config with:\n")
print(json_data)
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def get_camera_frame(sat_name: str, delay : bool = True) -> bytes:
""" Get the current frame from a camera attached to satellite.
Args:
sat_name (str): name of the spacecraft with the camera of interest
delay (bool): workaround in MDS1.3.0 to make sure camera image matches camera UI
"""
response = client.get_camera_frame(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, sat_name)
if delay:
time.sleep(0.2)
response = client.get_camera_frame(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, sat_name)
return response
def get_camera_depth_frame(sat_name: str) -> bytes:
""" Get the current depth data frame from a camera attached to satellite.
Args:
sat_name (str): name of the spacecraft with the camera of interest
"""
response = client.get_camera_depth_frame(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, sat_name)
return response
[docs]
def add_sat_from_elements(sat_name: str, mass: float, a: float, e: float, i: float, Omega: float, omega: float,
nu: float, central_body: str, referenceArea: float=1.0,
coefficientDrag: float=2.2, coefficientSRP: float=1.2, attitude: list[float]=[0, 0, 0, 1],
angular_velocity: list[float]=[0, 0, 0],
max_ang_rate: float=2,
max_ang_acc: float=0.1, rigidbody: bool=False, model_rotation_offset: list[float]=[0,0,0,1],
custom_model_path: str=None) -> bytes:
""" Instantiate a satellite with a custom 3d model, orbiting around a specified celestial body.
Args:
sat_name (str): name of the new spacecraft to be instantiated
mass (float): mass of the spacecraft [kg]
a (float): semi-major axis [m]
e (float): eccentricity [-]
i (float): inclination [deg]
Omega (float): right ascension of the ascending node [deg]
omega (float): argument of periapsis [deg]
nu (float): true anomaly [deg]
central_body (str): name of central body
attitude (list[float], optional): attitude quaternion of the spacecraft, relative to ICRF.
max_ang_rate (float, optional): maximum angular rate for the spacecraft pointing maneuvers in degrees
per second. defaults to 2 [rad/s].
max_ang_acc (float, optional): maximum angular acceleration for the spacecraft pointing maneuvers
in degrees per second per second. defaults to 0.01 [rad/s/s].
rigidbody (bool, optional): flag to enable or disable rigidbody 6DOF simulation for this spacecraft.
defaults to False.
custom_model_path (str, optional): path to a custom .glb or .gltf 3D model. path can be absolute, or if
it is not absolute, it will be interpreted as relative to /MDS_Solaris_Data/StreamingAssets/.
model_rotation_offset (list[float], optional): orientation quaternion of the 3D model relative to the
spacecraft.
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['mass'] = mass
elements = {}
elements['semiMajorAxis'] = a
elements['eccentricity'] = e
elements['inclination'] = math.radians(i)
elements['ascendingNodeLongitude'] = math.radians(Omega)
elements['periapsisArgument'] = math.radians(omega)
elements['trueAnomaly'] = math.radians(nu)
data['orbitalElements'] = elements
data['centralBody'] = central_body
data['attitudeState'] = {}
data['attitudeState']['attitude'] = '(' + str(attitude[0]) + ', ' + str(attitude[1]) + ', ' + str(
attitude[2]) + ', ' + str(attitude[3]) + ')'
data['attitudeState']['angularVelocity'] = vector_to_string(angular_velocity)
data['attitudeState']['maxAngularSpeed'] = max_ang_rate
data['attitudeState']['maxAngularAcceleration'] = max_ang_acc
data['rigidBodyAttached'] = rigidbody
data['modelRotation'] = '(' + str(model_rotation_offset[0]) + ', ' + str(model_rotation_offset[1]) + ', ' + str(
model_rotation_offset[2]) + ', ' + str(model_rotation_offset[3]) + ')'
data['referenceArea'] = referenceArea
data['coefficientDrag'] = coefficientDrag
data['coefficientSRP'] = coefficientSRP
if custom_model_path != None:
data['customModelPath'] = custom_model_path
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
def add_battery(sat_name: str, charger_efficiency: float, capacity: float, charge_fraction: float) -> bytes:
""" Add a battery to a spacecraft.
Args:
sat_name (str): name of the spacecraft, for which to add the battery.
charger_efficiency (float): efficiency of the battery charging process [-].
capacity (float): maximum capacity of the battery [Wh].
charge_fraction (float): current charge level as a fraction of the maximum capacity [-].
"""
jd, jsonStr = client.get_config_part([sat_name], NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
jsonDict = json.loads(jsonStr)
battery_count = len(jsonDict['spacecraftData']['batteries']) + 1
# print(f"battery_count = {battery_count}")
solar_panel_count = len(jsonDict['spacecraftData']['solarPanels'])
# print(f"solar_panel_count = {solar_panel_count}")
if solar_panel_count < battery_count:
print("no solar panel to attach battery to")
return 0
else:
structure_id = jsonDict['spacecraftData']['solarPanels'][battery_count-1]['structureId']
battery = {}
battery['structureId'] = structure_id
battery['chargerEfficiency'] = charger_efficiency
battery['capacityWattHours'] = capacity
battery['chargeFloat'] = charge_fraction
jsonDict['spacecraftData']['batteries'].append(battery)
json_data = json.dumps([jsonDict])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def add_solar_panel(sat_name: str, position: list[float], rotation: list[float], area: float,
max_power: float, efficiency: float, display_axis: bool=True) -> bytes:
""" Add a solar panel to the spacecraft.
Args:
sat_name (str): name of the spacecraft to which the panel shall be added.
position (list): local position vector of the solar panel's center, w.r.t. spacecraft body frame origin [m].
rotation (list): local rotation quaternion of the solar panel w.r.t. spacecraft body z-axis.
max_power (float): maximum power that can be generated by this solar panel [W].
area (float): area of the solar panel [m^2].
efficiency (float): efficiency of the solar cells [-].
display_axis (bool, optional): flag to turn the visual aid axis on/off. default True.
"""
jd, jsonStr = client.get_config_part([sat_name], NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
jsonDict = json.loads(jsonStr)
structure_id = jsonDict['spacecraftData']['structure'][-1]['id'] + 1
structure = {}
structure['name'] = 'solar panel'
structure['id'] = structure_id
structure['parentID'] = 0
structure['localPosition'] = vector_to_string(position)
structure['localRotation'] = vector_to_string(rotation)
structure['size'] = vector_to_string([1, 1, 1])
panel = {}
panel['structureId'] = structure_id
panel['powerEfficiency'] = efficiency
panel['area'] = area
panel['maxPower'] = max_power
panel['displayAxis'] = display_axis
jsonDict['spacecraftData']['structure'].append(structure)
jsonDict['spacecraftData']['solarPanels'].append(panel)
json_data = json.dumps([jsonDict])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def add_sensor(sensor_name: str, sat_name: str, sensor_type: str, orientation: list[float], fov: list[float], pixel_dims: list[float],
range: float, line_color_r: int=0,line_color_g: int=0,line_color_b: int=255,line_color_a: int=255, cone_color_r:int=0,cone_color_g:int=0,cone_color_b:int=255,cone_color_a:int=60, replace:bool=False) -> bytes:
""" Add a sensor to the spacecraft.
Args:
sat_name (str): name of the spacecraft, for which to add the sensor.
sensor_type (str): type of the sensor. currently only 'Optical'.
orientation (list[float]): orientation quaternion w.r.t. the spacecraft body axes.
fov (list[float]): field of view angles [deg].
pixel_dims (list[int]): horizontal and vertical pixel dimensions.
range (float): range of the sensor [m].
"""
jd, jsonStr = client.get_config_part([sat_name], NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
jsonDict = json.loads(jsonStr)
sensor = {}
sensor['name'] = sensor_name
sensor['FoV'] = fov
sensor['PixelDimensions'] = pixel_dims
sensor['range'] = range
sensor['orientation'] = '(' + str(orientation[0]) + ', ' + str(orientation[1]) + ', ' + str(
orientation[2]) + ', ' + str(orientation[3]) + ')'
sensor['sensorType'] = sensor_type
sensor['Ranges'] = []
sensor['LinesOfSight'] = []
sensor['AngleMeasurements'] = []
sensor['PixelCoordinates'] = []
sensor['lineColorR'] = line_color_r
sensor['lineColorG'] = line_color_g
sensor['lineColorB'] = line_color_b
sensor['lineColorA'] = line_color_a
sensor['coneColorR'] = cone_color_r
sensor['coneColorG'] = cone_color_g
sensor['coneColorB'] = cone_color_b
sensor['coneColorA'] = cone_color_a
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['spacecraftData'] = jsonDict['spacecraftData']
if replace:
if "sensors" in data['spacecraftData']:
s = data['spacecraftData']['sensors']
count = len(s)
index = 0
while count > 0:
if sensor_name == data['spacecraftData']['sensors'][index]['name']:
data['spacecraftData']['sensors'][index] = sensor
break
count -= 1
index += 1
else:
data['spacecraftData']['sensors'].append(sensor)
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def add_radio(sat_name: str, orientation: list[float], antenna_input_power: float, tx_frequency: float,
rx_frequency: float, comms_law: str="Nearest", antenna_pattern_path: str="",
friendlyStations: list[str]=[], friendlySpacecraft: list[str]=[]) -> bytes:
""" Add a radio subsystem to a spacecraft.
Args:
sat_name (str): name of the spacecraft, for which to add the radio.
orientation (list[float]): orientation quaternion w.r.t. the spacecraft.
antenna_input_power (float): the power fed into the antenna input [W].
tx_frequency (float): transmission frequency [Hz].
rx_frequency (float): receive frequency [Hz].
antenna_pattern_path (str, optional): path to an antenna radiation pattern csv file. path can be absolute,
or if it is not absolute, it will be interpreted as relative to /MDS_Solaris_Data/StreamingAssets/.
friendlyStations (list[str], optional): list of friendly station names. is only relevant if comms_law is
"Friendly".
friendlySpacecraft (list[str], optional): list of friendly spacecraft names. is only relevant if comms_law
is "Friendly".
"""
jd, jsonStr = client.get_config_part([sat_name], NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
jsonDict = json.loads(jsonStr)
radio = {}
radio['antennaInputPower'] = antenna_input_power
radio['orientation'] = '(' + str(orientation[0]) + ', ' + str(orientation[1]) + ', ' + str(
orientation[2]) + ', ' + str(orientation[3]) + ')'
radio['txFrequency'] = tx_frequency
radio['rxFrequency'] = rx_frequency
radio['friendlyStations'] = friendlyStations
radio['friendlySpacecraft'] = friendlySpacecraft
radio['CommsLaw'] = comms_laws[comms_law]
if len(antenna_pattern_path) > 0:
radio['radiationPatternPath'] = antenna_pattern_path
jsonDict['spacecraftData']['radios'].append(radio)
json_data = json.dumps([jsonDict])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def add_polygons_from_csv(csv_path: str, r: float, g: float, b: float, a: float):
""" Add polygons from a CSV file.
Args:
csv_path (str): path to a csv file (on client computer).
r (float): red component of the polygon color.
g (float): green component of the polygon color.
b (float): blue component of the polygon color.
a (float): alpha component of the polygon color.
"""
file = open(csv_path, 'r')
content = file.read()
file.close()
response = client.load_polygons_from_csv(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, content,
r, g, b, a)
return response
[docs]
def toggle_celestial_vector(enabled: bool, celestial_body: str) -> bytes:
""" Toggle celestial vector for a specified celestial body.
CelestialBodies values: Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Moon, Sun
Args:
enabled (bool): flag that enables/disables celestial vector.
celestial_body (str): name of the celestial body that the celestial vector shall point to.
"""
response = client.toggle_celestial_vectors(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT,
enabled, celestial_body)
return response
[docs]
def clear_sensors(sat_name: str) -> bytes:
""" Remove all sensors from a satellite.
Args:
sat_name (str): name of the satellite, from which all sensors will be removed.
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data["spacecraftData"] = {}
data["spacecraftData"]["sensors"] = []
json_data = json.dumps([data])
print(json_data)
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def schedule_maneuver_at_date(sat_name: str, julian_date: float, delta_v: list[float], burn_time: float) -> bytes:
""" Schedule a maneuver for a specified date.
Args:
sat_name (str): name of the spacecraft, for which the maneuver will be scheduled.
julian_date (float): julian date, for which the maneuver is scheduled.
delta_v (list[float]): a vector of 3 components - delta V in inertial frame [m/s].
burn_time (float): time, which wil be taken to complete the maneuver [s]."""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
maneuver = {}
maneuver['julianDate'] = julian_date
maneuver['deltaV'] = '(' + str(delta_v[0]) + ', ' + str(delta_v[1]) + ', ' + str(delta_v[2]) + ')'
maneuver['burnDuration'] = burn_time
maneuver['useDate'] = True
data['maneuvers'] = [maneuver]
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def apply_torque(sat_name: str, torque: list[float], inertial: bool=False) -> bytes:
""" Apply a torque
Args:
sat_name (str): name of the spacecraft, for which the torque will be applied.
torque (list[float]): torque vector in the spacecraft body frame [Nm].
inertial (bool): if True, torque is applied in the inertial ICRF frame.
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
if inertial:
data['inertialTorque'] = "(" + str(torque[0]) + ", " + str(torque[1]) + ", " + str(torque[2]) + ")"
else:
data['torque'] = "(" + str(torque[0]) + ", " + str(torque[1]) + ", " + str(torque[2]) + ")"
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def apply_thrust(sat_name: str, thrust: list[float], inertial: bool=False) -> bytes:
""" Apply a thrust.
Args:
sat_name (str): name of the spacecraft to which the thrust should be applied.
thrust (list): thrust vector in ICRF, to be applied during the next simulation step [N].
inertial (bool): if True, thrust is applied in the inertial ICRF frame.
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
if inertial:
data['inertialThrust'] = "(" + str(thrust[0]) + ", " + str(thrust[1]) + ", " + str(thrust[2]) + ")"
else:
data['thrust'] = "(" + str(thrust[0]) + ", " + str(thrust[1]) + ", " + str(thrust[2]) + ")"
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def apply_thrust_torque(sat_name: str, thrust: list[float], torque: list[float], inertial: bool=False) -> bytes:
""" Apply a thrust and a torque.
Args:
sat_name (str): name of the spacecraft to which the thrust and torque should be applied.
thrust (list): thrust vector in spacecraft body frame, to be applied during the next simulation step [N].
torque (list): torque vector in spacecraft body frame, to be applied during the next simulation step [Nm].
inertial (bool): if True, thrust and torque are applied in the inertial ICRF frame.
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
if inertial:
data['inertialThrust'] = vector_to_string(thrust)
data['inertialTorque'] = vector_to_string(torque)
else:
data['thrust'] = vector_to_string(thrust)
data['torque'] = vector_to_string(torque)
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
def set_sat_velocity(sat_name: str, velocity: list[float]) -> bytes:
""" Hard set the position of a spacecraft.
Args:
sat_name (str): name of the spacecraft.
velocity (list[float]): the velocity vector, specifying spacecraft's velocity in body-centered inertial
frame, with origin at the central body's c.o.g. and axes aligned with ICRF.
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['localVelocity'] = vector_to_string(velocity)
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def set_sat_position(sat_name: str, position: list[float]) -> bytes:
""" Hard set the position of a spacecraft.
Args:
sat_name (str): name of the spacecraft.
position (list[float]): the position vector, specifying spacecraft's position in body-centered inertial
frame, with origin at the central body's c.o.g. and axes aligned with ICRF.
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['localPosition'] = vector_to_string(position)
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def set_sat_attitude(sat_name: str, quaternion: list[float]) -> bytes:
""" Hard set the attitude of a spacecraft.
Args:
sat_name (str): name of the spacecraft.
quaternion (list[float]): the attitude quaternion, specifying spacecraft's body frame
rotation from the inertial ICRF frame. in [x,y,z,w] format.
"""
q_norm = np.linalg.norm(quaternion)
if not np.isclose(q_norm, 1, rtol = 1e-4, atol = 1e-4):
quaternion = np.array(quaternion) / q_norm
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['attitudeState'] = {}
data['attitudeState']['attitude'] = '(' + str(quaternion[0]) + ', ' + str(quaternion[1]) + ', ' + str(
quaternion[2]) + ', ' + str(quaternion[3]) + ')'
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def set_sat_angular_velocity(sat_name: str, angular_velocity: list[float], attitude: list[float] = None, convert_to_world: bool = False) -> bytes:
""" Hard set the angular velocity of a spacecraft.
Args:
sat_name (str): name of the spacecraft.
angular_velocity (list[float]): rad/s the angular velocity vector, specifying the desired
angular velocity of the target in either its body frame (if convert_to_world is set to True,
default behaviour) or the ICRF world frame (if convert_to_world is set to False).
attitude (optional, list[float]): the attitude quaternion, specifying spacecraft's body frame
rotation from the inertial ICRF frame. in [x,y,z,w] format. Necessary only when
convert_to_world is set to True to convert from body frame to ICRF frame.
convert_to_world (optional, bool): Whether or not to convert the given angular velocity
from body frame to inertial ICRF frame. True by default because MDS expects the angular
velocity to be specified in the inertial ICRF frame. If set to False then the attitude
parameter can be left out also.
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['attitudeState'] = {}
if convert_to_world:
if attitude is None:
raise ValueError("If convert_to_world is set to True then the satellite attitude must be " \
"provided to convert the angular velocity from body to inertial ICRF world frame.\n" \
"Either convert the angular velocity yourself or provide the attitude quaternion to this method.")
# Using own implementation to rotate angular velocity vector into world frame
# (not relying on scipy)
angular_velocity_world = rotate_vector_by_quaternion(angular_velocity, attitude)
else:
angular_velocity_world = angular_velocity
data['attitudeState']['angularVelocity'] = vector_to_string(angular_velocity_world)
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def set_state_covariance(sat_name: str, covariance_matrix: list[list[float]]) -> bytes:
""" Set the state covariance matrix of a spacecraft.
Args:
sat_name (str): name of the spacecraft, for which the state covariance belongs.
covariance_matrix (list[list[float]]): 6x6 covariance matrix specified as list of lists.
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['stateCovariance'] = covariance_matrix
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
def set_inertia_tensor(sat_name: str, inertia_tensor: list[float]) -> bytes:
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['inertiaTensor'] = vector_to_string(inertia_tensor)
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def set_attitude_covariance(sat_name: str, covariance_matrix: list[list[float]]) -> bytes:
""" Set the attitude covariance matrix of a spacecraft.
Args:
sat_name (str): name of the spacecraft, for which the attitude covariance belongs.
covariance_matrix (list[list[float]]): 4x4 covariance matrix specified as list of lists.
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['attitudeState'] = {}
data['attitudeState']['attitudeCovariance'] = covariance_matrix
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def set_attitude_parameters(sat_name: str, max_rotation_rate: float, max_rotation_accel: float) -> bytes:
""" Set the attitude control constraints for a spacecraft.
Args:
sat_name (str): name of the spacecraft, for which to set the attitude parameters.
max_rotation_rate (float): maximum possible rotation rate magnitude in [deg/s].
max_rotation_accel (float): maximum possible angular acceleration magnitude in [deg/s/s].
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['attitudeState'] = {}
data['attitudeState']['maxAngularSpeed'] = max_rotation_rate
data['attitudeState']['maxAngularAcceleration'] = max_rotation_accel
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def set_pointing_mode(sat_name: str, mode_name: str) -> bytes:
""" Set the pointing mode of satellite.
Args:
sat_name (str): name of the spacecraft.
mode_name (str): a string that represents the pointing mode. available modes are: Idle, Nadir, Sun, Earth,
ClosestStation.
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['pointingMode'] = pointing_modes[mode_name]
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def attach_camera_to_sat(sat_name: str, attach: bool=True, focal_length: float=20.78461, distance_from_satellite: float=0.0,
near_plane: float=0.3, far_plane: float=1000000.0, sensor_size_x: float=36.0,
sensor_size_y: float=24.0, lens_shift_x: float=0.0, lens_shift_y: float=0.0,
gate_fit: str="Horizontal", iso: int=200, shutter_speed: float=100, focus_distance: float=5.0,
aperture: float=16, lens_distortion_intensity: float=0.0, bloom_intensity: float=0.0, bloom_scatter: float=0.0,
color_grain_intensity: float=0.0, color_grain_response: float=0.0, chromatic_aberration: float=0.0, enable_lens_flare: bool=False,
texture_width: int=1024, texture_height: int=1024,
enable_capture: bool=False, capture_interval: float=1.0,
camera_quaternion: list[float]=[0, 0, 0, 1],
python_server_ip: str=None, python_server_port: int=None, enable_depth_of_field: bool=True):
""" Attach a camera to a spacecraft.
Args:
sat_name (str): name of the spacecraft, to which the camera will be attached.
attach (bool, optional): True attaches the camera, False detaches it.
focal_length (float, optional): focal length of the camera [mm].
distance_from_satellite (float, optional): distance from the satellite [m].
near_plane (float, optional): near clipping plane of the camera [m].
far_plane (float, optional): far clipping plane of the camera [m].
sensor_size_x (float, optional): sensor size in the horizontal dimension [mm].
sensor_size_y (float, optional): sensor size in the vertical dimension [mm].
lens_shift_x (float, optional): The lens offset of the camera in the x direction. The lens shift is relative
to the sensor size. For example, a lens shift of 0.5 offsets the sensor by half its horizontal size.
lens_shift_y (float, optional): The lens offset of the camera in the y direction.
gate_fit (str, optional): Enum used to specify how the sensor gate (sensor frame) defined by sensor_size
fits into the resolution gate (render frame). Options are: "Vertical", "Horizontal", "Fill", "Overscan",
"None".
enable_capture (bool, optional): Flag to turn frame capture on and off. When True, frames are saved to a
local directory every capture_interval. Default is False.
camera_quaternion (list[float], optional): quaternion of the camera orientation relative to X axis
capture_interval (float, optional): interval at which to capture frames, if enable_capture is True [s].
python_server_ip (str, optional): IP of a python server to send the frames to. Default is None.
python_server_port (int, optional): port of a python server to send the frames to. Default is None.
"""
response = client.attach_camera_to_satellite(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT,
sat_name, attach, focal_length, distance_from_satellite, near_plane, far_plane,
sensor_size_x, sensor_size_y, lens_shift_x, lens_shift_y, gate_fit,
iso, shutter_speed, focus_distance, aperture, lens_distortion_intensity, bloom_intensity, bloom_scatter,
color_grain_intensity, color_grain_response,chromatic_aberration,enable_lens_flare,
texture_width, texture_height,
enable_capture,
capture_interval,
quaternion_to_string(camera_quaternion),
python_server_ip, python_server_port, enable_depth_of_field)
return response
[docs]
def set_pointing_target(sat_name: str, quaternion: list[float]) -> bytes:
""" Set a custom pointing target quaternion for a spacecraft.
Args:
sat_name (str): name of spacecraft
quaternion ([float]): list of [x,y,z,w] quaternion values representing ICRF to body frame rotation."""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
data['pointingMode'] = 'CustomQuaternion'
data['CustomPointingQuaternion'] = '(' + str(quaternion[0]) + ', ' + str(quaternion[1]) + ', ' + str(
quaternion[2]) + ', ' + str(quaternion[3]) + ')'
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
def set_power_consumption(sat_name: str, power_consumption: float):
""" Set spacecraft's power consumption.
Args:
sat_name (str): name of the spacecraft, for which to set the power consumption.
power_consumption (float): total power consumption of the spacecraft [W].
"""
data = {}
data['name'] = sat_name
data['bodyType'] = 1
scData = {}
scData['PowerConsumption'] = power_consumption
data['spacecraftData'] = scData
json_data = json.dumps([data])
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def set_sat_track(sat_name: str, enabled: bool, new_track_interval: int, r: float, g: float, b: float, a: float):
""" Attach a camera to a spacecraft.
Args:
sat_name (str): name of the spacecraft, to which the camera will be attached.
enabled (bool): True enables the sensor track, False disables it.
new_track_interval (int): simulation interval at which to produce track mark [s].
r,g,b,a (float): RGBA values ranging from 0 to 1 that set the color of the track."""
response = client.set_satellite_track(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, sat_name,
enabled, new_track_interval, r, g, b, a)
return response
[docs]
def clear_objects(object_name_list: list[str]):
""" Remove objects from the simulation.
Args:
object_name_list (list[str]): list of object names to be removed.
"""
response = client.delete_object(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, object_name_list)
return response
[docs]
def set_utc_date(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int):
""" Set the simulation date as UTC TDB date-time.
Args:
year (int): the year.
month (int): the month.
day (int): the day.
hour (int): the hour.
minute (int): the minute.
second (int): the second.
millisecond (int): the millisecond.
"""
utc_string = str(year).zfill(4) + "-" + str(month).zfill(2) + "-" + str(day).zfill(2) + "T" + str(hour).zfill(
2) + ":" + str(minute).zfill(2) + ":" + str(second).zfill(2) + "." + str(millisecond).zfill(3) + "Z"
print(utc_string)
response = client.set_simulation_date(utc_string, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def get_julian_date():
""" Get the current Julian date TDB of the simulation.
"""
response = client.get_config_part([], NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
jd = response[0]
return jd
[docs]
def get_utc_date():
""" Get the current UTC TDB date of the simulation.
"""
jd, utcdate = client.get_config_part(['utcDateTime'], NetworkConfiguration.MDS_HOST,
NetworkConfiguration.MDS_HOST_PORT)
# print(utcdate)
jsonDict = json.loads(utcdate)
return jsonDict
[docs]
def set_julian_date(julian_date: float):
""" Set the simulation date as Julian date TDB.
"""
response = client.set_simulation_date(str(julian_date), NetworkConfiguration.MDS_HOST,
NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def get_time_scale():
""" Get the timescale of simulation.
"""
jd, time_scale = get_config_part('timeScale')
return float(time_scale)
[docs]
def set_time_scale(scale: float):
""" Set the timescale of simulation.
Args:
scale (float): time multiplier
"""
response = client.set_simulation_timescale(str(scale), NetworkConfiguration.MDS_HOST,
NetworkConfiguration.MDS_HOST_PORT)
return response
[docs]
def step_sim(steps = 1, waitForPropagationEnd = False):
""" Step the simulation.
"""
response = client.step_simulation(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, steps, waitForPropagationEnd)
return response
[docs]
def select_satellite(name):
data = {
"name" : name
}
json_data = json.dumps(data)
response = client.select_satellite(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def disable_sat_thruster_effects(name):
data = {
"name" : name
}
json_data = json.dumps(data)
response = client.disable_sat_thruster_effects(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def enable_sat_thruster_effects(name):
data = {
"name" : name
}
json_data = json.dumps(data)
response = client.enable_sat_thruster_effects(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
[docs]
def load_orbits_from_csv(csv_path: str):
""" Load a list of spacecraft from a CSV file.
Args:
csv_path (str): a path to the csv file, on client computer.
"""
json_data = ""
with open(csv_path, 'r', encoding='utf-8') as f:
next(f)
for line in f:
elems = line.split(',')
data = {}
data['name'] = elems[0]
data['bodyType'] = 1
data['mass'] = float(elems[7])
elements = {}
rp = float(elems[1])
ra = float(elems[2])
elements['semiMajorAxis'] = (rp + ra) * 1000 / 2
elements['eccentricity'] = (ra - rp) / (ra + rp)
elements['inclination'] = float(elems[3]) * math.pi / 180.0
elements['ascendingNodeLongitude'] = float(elems[4]) * math.pi / 180.0
elements['periapsisArgument'] = float(elems[5]) * math.pi / 180.0
elements['trueAnomaly'] = float(elems[6]) * math.pi / 180.0
data['orbitalElements'] = elements
data['centralBody'] = elems[8]
json_data += "," + json.dumps(data)
json_data = json_data.lstrip(',')
json_data = "[" + json_data + "]"
response = client.update_config(json_data, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
# response = client.load_orbits_from_csv(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, content)
# return response
[docs]
def visualize_thrusters(sat_name, thrust_body, thrust_min=0):
"""visualizes the thrusters by converting body-frame thrust to booleans of whether
the positive/negative values exeed the thrust_min_shown threshold
Args:
thrust_body (array): body-frame thrust [N]
thrust_min (float): minimum thrust to visualize [N]
"""
response = client.set_manual_thrust_torque(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT,
sat_name,
thrustRight=bool(thrust_body[0]>thrust_min),
thrustLeft=bool(thrust_body[0]<-thrust_min),
thrustUp=bool(thrust_body[1]>thrust_min),
thrustDown=bool(thrust_body[1]<-thrust_min),
thrustForward=bool(thrust_body[2]>thrust_min),
thrustBackward=bool(thrust_body[2]<-thrust_min),
torqueLeft = False,
torqueRight = False,
torqueUp = False,
torqueDown = False,
torqueForward = False,
torqueBackward = False
)
return response
def set_manual_thrust_torque(name : str, thrustRight : bool = False, thrustLeft : bool = False, thrustUp : bool = False, thrustDown : bool = False, thrustForward : bool = False, thrustBackward : bool = False, torqueLeft :bool = False, torqueRight : bool = False, torqueUp : bool = False, torqueDown : bool = False, torqueForward : bool = False, torqueBackward : bool = False):
response = client.set_manual_thrust_torque(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, name, thrustRight, thrustLeft, thrustUp, thrustDown, thrustForward, thrustBackward, torqueLeft, torqueRight, torqueUp, torqueDown, torqueForward, torqueBackward)
return response
def enable_collisions():
response = client.enable_collisions(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
def disable_collisions():
response = client.disable_collisions(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
def attach_bodies(parent_sat_name, child_sat_name):
data = {}
data['parent'] = parent_sat_name
data['child'] = child_sat_name
_, sat_data = get_sat_data(parent_sat_name, child_sat_name)
r_parent = np.fromstring(sat_data[0]['localPosition'].strip('()'), sep=', ')
r_child = np.fromstring(sat_data[1]['localPosition'].strip('()'), sep=', ')
data['offsetVector'] = vector_to_string(r_child - r_parent)
json_data = json.dumps(data)
response = client.attach_bodies(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
def de_attach_bodies(parent_sat_name, child_sat_name):
data = {}
data['parent'] = parent_sat_name
data['child'] = child_sat_name
json_data = json.dumps(data)
response = client.de_attach_bodies(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
[docs]
def optimize_transfer(sat_name: str, a: float, e: float, i: float, Omega: float, omega: float, alpha1: float | None = None,
alpha2: float | None = None, mode: str='OPTIMIZE_DELTAV',
maxDeltaT: float | None = None, maxDeltaV: float | None = None):
"""Order an optimal orbit transfer.
Args:
sat_name (str): name of the spacecraft, which will perform the transfer.
a (float): desired semi-major axis in meters.
e (float): desired eccentricity.
i (float): desired inclination in degrees.
Omega (float): desired ascending node longitude in degrees.
omega (float): desired argument of periapsis in degrees.
alpha1 (float): initial orbit exit point true anomaly in degrees (TA in your first orbit where you apply maneuver)
alpha2 (float): target orbit entry point true anomaly in degrees (TA in transfer orbit, where the second maneuver will be applied)
mode (enum): desired optimization goal: (default) 'OPTIMIZE_DELTAV', 'OPTIMIZE_TRANSFER_TIME', 'OPTIMIZE_TIME'.
maxDeltaT (float): DeltaV constraint: Maximum allowed DeltaV. Used when you do time optimization.
maxDeltaT (float): Time constraint: Maximum allowed time for transfer, when you optimize for deltaV.
"""
data = {}
data['name'] = sat_name
target_elements = {}
target_elements['semiMajorAxis'] = a
target_elements['eccentricity'] = e
target_elements['inclination'] = i
target_elements['ascendingNodeLongitude'] = Omega
target_elements['periapsisArgument'] = omega
data['orbit'] = target_elements
data['mode'] = mode
data['alpha1'] = alpha1
data['alpha2'] = alpha2
data['maxDeltaT'] = maxDeltaT
data['maxDeltaV'] = maxDeltaV
json_data = json.dumps(data)
response = client.optimize_transfer(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response
[docs]
def send_log_message(message: str):
""" Send a message to be inserted to the mission log.
Args:
message (string): message to be added to the mission log.
"""
response = client.send_log_comment(message, NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT)
return response
def extract_elements(dict):
"""
:meta private:
"""
name = dict["name"]
elems = dict["orbitalElements"]
a = elems['semiMajorAxis']
e = elems['eccentricity']
i = elems["inclination"]
raan = elems['ascendingNodeLongitude']
omega = elems['periapsisArgument']
nu = elems["trueAnomaly"]
# print(name + ":")
# print("Semi-major axis = " + str(a) + " m")
# print("Eccentricity = " + str(e))
# print("Inclination = " + str(i * 180 / math.pi) + " deg")
# print("Ascending node longitude = " + str(raan * 180 / math.pi) + " deg")
# print("Periapsis argument = " + str(omega * 180 / math.pi) + " deg")
# print("True anomaly = " + str(nu * 180 / math.pi) + " deg")
return name, elems
def rename_spacecraft(name, rename_to):
"""
Rename a spacecraft.
:param name: original spacecraft name
:param rename_to: new spacecraft name
"""
data = {}
data['name'] = name
data['new_name'] = rename_to
json_data = json.dumps(data)
response = client.rename_spacecraft(NetworkConfiguration.MDS_HOST, NetworkConfiguration.MDS_HOST_PORT, json_data)
return response