Skip to content

Commit

Permalink
Released v2.0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
Shiva-Iyer committed Sep 25, 2020
2 parents 00be9c8 + d7b1b66 commit 4a95b51
Show file tree
Hide file tree
Showing 20 changed files with 263 additions and 182 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Development

`mvn -e package`

2. Download and extract <https://github.com/ut-astria/orbdetpy/releases/download/2.0.2/orekit-data.tar.gz>
2. Download and extract <https://github.com/ut-astria/orbdetpy/releases/download/2.0.3/orekit-data.tar.gz>
under the `orbdetpy/` sub-folder.

Known Issues
Expand All @@ -79,3 +79,5 @@ Known Issues
2. If you use the `multiprocessing` Python package, imports and calls into
`orbdetpy` must not span `multiprocessing` function calls. That is, `orbdetpy`
can be used in the parent process or the spawned child processes, but not both.
A workaround is to run the `orbdetpy` RPC server using `orbdetpy/start_rpc_server.sh`
in a separate terminal window before running your Python code.
2 changes: 1 addition & 1 deletion orbdetpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def configure(**kwargs)->Settings:
estm_DMC_corr_time=40.0, estm_DMC_sigma_pert=5E-9,
estm_DMC_acceleration=Parameter(value=0.0, min=-1E-3, max=1E-3, estimation=EstimationType.ESTIMATE),
estm_smoother_iterations=10, estm_enable_PDAF=False, estm_detection_probability=0.99,
estm_gating_probability=0.99, estm_gating_threshold=5.0, **kwargs))
estm_gating_probability=0.99, estm_gating_threshold=5.0, estm_verbose_output=True, **kwargs))

def add_facet(cfg: Settings, normal: Tuple[float, float, float], area: float)->Facet:
"""Add a facet to a box-wing spacecraft model.
Expand Down
21 changes: 9 additions & 12 deletions orbdetpy/astro_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,18 @@ def format_weather(lines: str)->str:
"""Re-format space weather data into a more efficient form.
"""

output = []
c1 = [0, 5, 8, 11, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 47, 51, 55, 59, 63, 67,
71, 75, 79, 83, 87, 89, 93, 99, 101, 107, 113, 119, 125]
c2 = [5, 8, 11, 16, 19, 22, 25, 28, 31, 34, 37, 40, 43, 47, 51, 55, 59, 63, 67, 71,
75, 79, 83, 87, 89, 93, 99, 101, 107, 113, 119, 125, 131]

data = ""
for line in lines.splitlines():
if (line == "END DAILY_PREDICTED"):
break
if (len(line) > 0 and line[0].isnumeric()):
data += ",".join([line[i:j] for i, j in zip(c1, c2)]) + "\n"

return(data)
output.append(",".join([line[i:j] for i, j in zip(c1, c2)]))
return("\n".join(output))

def update_data()->None:
"""Download and re-format astrodynamics data from multiple sources.
Expand All @@ -48,15 +47,13 @@ def update_data()->None:
["http://maia.usno.navy.mil/ser7/tai-utc.dat", path.join(_data_dir, "tai-utc.dat"), None]]

for u in updates:
print("Updating {}".format(u[1]))
print(f"Updating {path.split(u[1])[-1]}")
try:
resp = requests.get(u[0], timeout=1.0)
if (resp.status_code != requests.codes.ok):
print("Error {} in {}".format(resp.status_code, u[0]))
continue
if (resp.status_code == requests.codes.ok):
with open(u[1], "w") as fp:
fp.write(u[2](resp.text) if (u[2] is not None) else resp.text)
else:
print(f"HTTP error: {resp.status_code}")
except Exception as exc:
print(exc)
continue

with open(u[1], "w") as f:
f.write(u[2](resp.text) if (u[2] is not None) else resp.text)
96 changes: 52 additions & 44 deletions orbdetpy/ccsds.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

_ccsds_stub = UtilitiesStub(RemoteServer.channel())

def export_OEM(cfg: Settings, obs, obj_id: str, obj_name: str)->str:
def export_OEM(cfg: Settings, obs, obj_id: str, obj_name: str, add_prop_cov: bool=False)->str:
"""Export ephemerides in CCSDS OEM format.
Parameters
Expand All @@ -33,49 +33,57 @@ def export_OEM(cfg: Settings, obs, obj_id: str, obj_name: str)->str:
obs : Measurements or estimation results to export.
obj_id : Object identifier.
obj_name : Object name.
add_prop_cov : Include propagated covariances if True; defaults to False.
Returns
-------
Ephemerides in OEM format.
"""

frame = Frame.ICRF if (cfg.prop_inertial_frame == Frame.GCRF) else cfg.prop_inertial_frame
oem_data = f"""CCSDS_OEM_VERS = 2.0
CREATION_DATE = {datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")}
ORIGINATOR = UTexas-Austin
oem_header = f"""CCSDS_OEM_VERS = 2.0
CREATION_DATE = {datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")}
ORIGINATOR = UT-Austin
META_START
OBJECT_NAME = {obj_name}
OBJECT_ID = {obj_id}
CENTER_NAME = EARTH
REF_FRAME = {frame}
TIME_SYSTEM = UTC
START_TIME = {get_UTC_string(obs[0].time)}
STOP_TIME = {get_UTC_string(obs[-1].time)}
START_TIME = {get_UTC_string(obs[0].time)[:-1]}
STOP_TIME = {get_UTC_string(obs[-1].time)[:-1]}
META_STOP
"""

cov_data, added = "", set()
state_key = "estimated_state" if (hasattr(obs[0], "estimated_state")) else "true_state"
eph_data, estm_cov, prop_cov, added = [], [], [], set()
is_estm = hasattr(obs[0], "estimated_state") and hasattr(obs[0], "estimated_covariance") and hasattr(obs[0], "propagated_covariance")
eph_key = "estimated_state" if (is_estm) else "true_state"
for o in obs:
if (o.time in added):
continue
added.add(o.time)
utc = get_UTC_string(o.time)
X=[x/1E3 for x in getattr(o, state_key)[:6]]
oem_data = f"{oem_data}\n{utc} {X[0]} {X[1]} {X[2]} {X[3]} {X[4]} {X[5]}"

cov = []
if (hasattr(o, "estimated_covariance")):
cov = o.estimated_covariance
elif (hasattr(o, "propagated_covariance")):
cov = o.propagated_covariance
if (len(cov) >= 21):
cov_data += f"EPOCH = {utc}\n"
cov_data += " ".join([str(c/1E6) for c in cov[:21]]) + "\n"

if (len(cov_data) > 0):
return(f"{oem_data}\n\nCOVARIANCE_START\n{cov_data}COVARIANCE_STOP")
utc = get_UTC_string(o.time)[:-1]
X = [x/1000.0 for x in getattr(o, eph_key)[:6]]
eph_data.append(f"{utc} {X[0]} {X[1]} {X[2]} {X[3]} {X[4]} {X[5]}")

if (is_estm and len(o.estimated_covariance) >= 21):
estm_cov.append(f"\nEPOCH = {utc}")
for m in range(6):
n = (m**2 + m)//2
estm_cov.append(" ".join([str(x/1E6) for x in o.estimated_covariance[n:m+n+1]]))
if (is_estm and add_prop_cov and len(o.propagated_covariance) >= 21):
prop_cov.append(f"\nEPOCH = {utc}")
for m in range(6):
n = (m**2 + m)//2
prop_cov.append(" ".join([str(x/1E6) for x in o.propagated_covariance[n:m+n+1]]))

oem_data = oem_header + "\n".join(eph_data)
if (len(estm_cov) > 0):
oem_data += "\n\nCOMMENT Updated covariance\nCOVARIANCE_START" + "\n".join(estm_cov) + "\nCOVARIANCE_STOP"
if (len(prop_cov) > 0):
oem_data += "\n\nCOMMENT Propagated covariance\nCOVARIANCE_START" + "\n".join(prop_cov) + "\nCOVARIANCE_STOP"
return(oem_data)

def export_TDM(cfg: Settings, obs, obj_id: str, station_list: Optional[List[str]]=None)->str:
Expand All @@ -101,49 +109,49 @@ def export_TDM(cfg: Settings, obs, obj_id: str, station_list: Optional[List[str]
if (MeasurementType.AZIMUTH in miter and MeasurementType.ELEVATION in miter):
obstype = "ANGLE_TYPE = AZEL"
obspath = "1,2"
if (MeasurementType.RANGE in miter):
if (MeasurementType.RANGE in miter or MeasurementType.RANGE_RATE in miter):
obspath = "2,1,2"
if (MeasurementType.AZIMUTH in miter and MeasurementType.ELEVATION in miter):
obstype = "RANGE_UNITS = km\nANGLE_TYPE = AZEL"
else:
obstype = "RANGE_UNITS = km"

tdm_data = f"""CCSDS_TDM_VERS = 1.0
CREATION_DATE = {datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")}
ORIGINATOR = UTexas-Austin
blocks = []
tdm_header = f"""CCSDS_TDM_VERS = 1.0
CREATION_DATE = {datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S")}
ORIGINATOR = UT-Austin
"""

for sname, sinfo in cfg.stations.items():
if (station_list is not None and sname not in station_list):
continue
sensor = f"{sname} (Lat:{sinfo.latitude/Constant.DEGREE_TO_RAD},Lon:{sinfo.longitude/Constant.DEGREE_TO_RAD},Alt:{sinfo.altitude/1E3}km)"
block_header = f"""
META_START
lat, lon, alt = sinfo.latitude/Constant.DEGREE_TO_RAD, sinfo.longitude/Constant.DEGREE_TO_RAD, sinfo.altitude/1000.0
blocks.append(f"""META_START
TIME_SYSTEM = UTC
PARTICIPANT_1 = {obj_id}
PARTICIPANT_2 = {sensor}
PARTICIPANT_2 = {sname} (WGS-84 Latitude: {lat} deg, Longitude: {lon} deg, Altitude: {alt} km)
MODE = SEQUENTIAL
PATH = {obspath}
{obstype}
META_STOP
"""
block_ent = "DATA_START\n"
""")
blocks.append("DATA_START")
for o in obs:
if (o.station != sname):
continue
utc = get_UTC_string(o.time)
if (MeasurementType.RANGE in miter):
block_ent = f"{block_ent}RANGE = {utc} {o.values[0]/1E3}\n"
if ("rangeRate" in o):
block_ent = f"{block_ent}DOPPLER_INSTANTANEOUS = {utc} {o.values[1]/1E3}\n"
utc = get_UTC_string(o.time)[:-1]
if (MeasurementType.RANGE in miter or MeasurementType.RANGE_RATE in miter):
if (MeasurementType.RANGE in miter):
blocks.append(f"RANGE = {utc} {o.values[0]/1000.0}")
if (MeasurementType.RANGE_RATE in miter):
blocks.append(f"DOPPLER_INSTANTANEOUS = {utc} {o.values[-1]/1000.0}")
if ((MeasurementType.AZIMUTH in miter and MeasurementType.ELEVATION in miter) or
(MeasurementType.RIGHT_ASCENSION in miter and MeasurementType.DECLINATION in miter)):
block_ent = f"""{block_ent}ANGLE_1 = {utc} {o.values[0]/Constant.DEGREE_TO_RAD}
ANGLE_2 = {utc} {o.values[1]/Constant.DEGREE_TO_RAD}
"""
tdm_data = f"{tdm_data}{block_header}{block_ent}DATA_STOP\n"
blocks.append(f"""ANGLE_1 = {utc} {o.values[0]/Constant.DEGREE_TO_RAD}\nANGLE_2 = {utc} {o.values[1]/Constant.DEGREE_TO_RAD}""")
blocks.append(f"DATA_STOP\n")

return(tdm_data)
return(tdm_header + "\n".join(blocks))

def import_TDM(file_name: str, file_format: int):
"""Import tracking data from CCSDS TDM file.
Expand Down
9 changes: 5 additions & 4 deletions orbdetpy/conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from typing import List, Tuple
from google.protobuf.wrappers_pb2 import DoubleValue, StringValue
from google.protobuf.wrappers_pb2 import StringValue
from orbdetpy.rpc.conversion_pb2_grpc import ConversionStub
from orbdetpy.rpc.messages_pb2 import AnglesInput, TransformFrameInput
from orbdetpy.rpc.messages_pb2 import AnglesInput, BoolDouble, TransformFrameInput
from orbdetpy.rpc.server import RemoteServer

_conversion_stub = ConversionStub(RemoteServer.channel())
Expand Down Expand Up @@ -161,19 +161,20 @@ def pv_to_elem(frame: int, time: float, pv: List[float])->List[float]:
resp = _conversion_stub.convertPvToElem(TransformFrameInput(src_frame=frame, UTC_time=time, pva=pv))
return(resp.array)

def get_UTC_string(j2000_offset: float)->str:
def get_UTC_string(j2000_offset: float, truncate: bool=True)->str:
"""Get ISO-8601 formatted UTC string corresponding to TT offset.
Parameters
----------
j2000_offset : Offset in TT from J2000 epoch [s].
truncate: Truncate to milliseconds level accuracy if True (default).
Returns
-------
ISO-8601 formatted UTC string.
"""

resp = _conversion_stub.getUTCString(DoubleValue(value=j2000_offset))
resp = _conversion_stub.getUTCString(BoolDouble(double_value=j2000_offset, bool_value=truncate))
return(resp.value)

def get_J2000_epoch_offset(utc_time: str)->float:
Expand Down
4 changes: 2 additions & 2 deletions orbdetpy/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.astria</groupId>
<artifactId>orbdetpy-server</artifactId>
<version>2.0.2</version>
<version>2.0.3</version>
<packaging>jar</packaging>

<properties>
<application.main.class>org.astria.rpc.RPCServer</application.main.class>
<os.detected.classifier>linux-x86_64</os.detected.classifier>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<grpc.version>1.31.1</grpc.version>
<grpc.version>1.32.1</grpc.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.javadoc.version>3.2.0</maven.javadoc.version>
Expand Down
8 changes: 4 additions & 4 deletions orbdetpy/rpc/conversion_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions orbdetpy/rpc/conversion_pb2_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def __init__(self, channel):
)
self.getUTCString = channel.unary_unary(
'/Conversion/getUTCString',
request_serializer=google_dot_protobuf_dot_wrappers__pb2.DoubleValue.SerializeToString,
request_serializer=messages__pb2.BoolDouble.SerializeToString,
response_deserializer=google_dot_protobuf_dot_wrappers__pb2.StringValue.FromString,
)
self.getJ2000EpochOffset = channel.unary_unary(
Expand Down Expand Up @@ -143,7 +143,7 @@ def add_ConversionServicer_to_server(servicer, server):
),
'getUTCString': grpc.unary_unary_rpc_method_handler(
servicer.getUTCString,
request_deserializer=google_dot_protobuf_dot_wrappers__pb2.DoubleValue.FromString,
request_deserializer=messages__pb2.BoolDouble.FromString,
response_serializer=google_dot_protobuf_dot_wrappers__pb2.StringValue.SerializeToString,
),
'getJ2000EpochOffset': grpc.unary_unary_rpc_method_handler(
Expand Down Expand Up @@ -275,7 +275,7 @@ def getUTCString(request,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/Conversion/getUTCString',
google_dot_protobuf_dot_wrappers__pb2.DoubleValue.SerializeToString,
messages__pb2.BoolDouble.SerializeToString,
google_dot_protobuf_dot_wrappers__pb2.StringValue.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
Expand Down
2 changes: 1 addition & 1 deletion orbdetpy/rpc/estimation_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4a95b51

Please sign in to comment.