OEM file position Values differ from TLE estmation

Dear all,
We are working on a satellite communication project and using TLE data to estimate satellite position. However, we recognized that the TLE is not accurate for our application. Thus, we want to use the OEM file to extract the satellite’s position. Here is the corresponding position from the OEM file, which gives the position of the W-Cube at a specific time.
2023-12-04T17:21:21.000 [ 5277.5283809 -4021.94223396 1807.27277294] [ 0.66139832 -2.42748985 -7.27157065] None
There is also an estimation of the position using TLE data for the same time as follows.
[2343226.0597383264 -6053264.475372732 -2294752.278416445]
The bolded values show the positions, but they are completely different. Could anybody explain why this is the case? Are the positions defined in different coordinate systems in TLE and OEM? How can I convert them to each other? How can I convert the position values given in the OEM file to their corresponding Azimuth and Elevation values using Orekit in Python?
Thank you very much for your time and consideration.

Hi @Erfan

Welcome to the Orekit forum!

I think that the answer to the second question is yes.
Positions from a TLE propagator are usually expressed in TEME frame.
However, I don’t know what is the frame used in your OEM file. This information shall be in the metadata with the REF_FRAME key.

To convert frames you can do

position_epoch = AbsoluteDate("2023-12-04T17:21:21.000", TimeScaleFactory.getUTC())
oem_frame = ... # the frame of you OEM file
tle_frame = FramesFactory.getTEME()
teme_to_oem = tle_frame.getTransformTo(oem_frame, position_epoch)
position_in_oem_frame = teme_to_oem.transformPosition(Vector3D(2343226.0597383264, -6053264.475372732, -2294752.278416445))

Just note that the position in the OEM is expressed in kilometers and position_in_oem_frame will probably be in meters.

Best regards,
Bryan

Hello Bryan,
Many thanks for your response. I want to convert the position values in the OEM file to the TLE frame. I do not know how to get the header and metadata from the OEM file in Orekit. I am using the OEM library to do so, and I can only extract segments and states of the OEM. To my knowledge, the parser is only used when we want to create an OEM file, not read it. Could you please provide me with some information about how to read the OEM file in Orekit and convert its frame to TLE?
Thank you very much.

Just to double-check, did you convert your km values to meters? Orekit works entirely in meters and seconds.

Hi @Erfan ,

Can’t you open the file in plain text and find the reference frame? That would be the quickest way to determine the reference frame.

The parser is used to read OEM file too :wink:
See this example in the test suite: OEMParserTest.
In testParseOEM2() the reference frame is accessed in the metadata of the first segment with:
file.getSegments().get(0).getMetadata().getReferenceFrame().asFrame().

Also, as @baubin said, beware that Orekit works with SI units, so positions should be in meters and velocities in meters per second.

Cheers,
Maxime

Hi @MaximeJ,

Many thanks for your response. I opened the OEM file using NotePad. I realized that the values have been defined in ECEF coordinates. I cannot open the OEM file using the Orekit library for further data processing. I am using Python right now. I checked the link you proposed, and it was in Java. I used a similar approach in Python, but I got an error. Here is my Python program:

with resource_stream(__name__, 'WCUBE.oem') as file_stream:
         source = file_stream.read().decode()
file = OemParser.parseMessage(source)

The error I get is:

file = OemParser.parseMessage(source)
TypeError: descriptor 'parseMessage' for 'AbstractMessageParser' objects doesn't apply to a 'str' object

I do not know what is the input type of the ParseMessage class. Do you have any idea how I can import the file to parseMessage?
Furthermore, I am using the MATLAB function ‘ecef2aer’ to convert my ECEF position values to the corresponding azimuth and elevations. However, I want to do it using the Orekit. Is there any function to do this conversion?

Best wishes,
Erfan

Hi @Erfan,

I wasn’t sure how to do it so I tried it myself.
Here is a sample code, tell me if it’s what you’re looking for:

from org.hipparchus.util import FastMath
from org.orekit.files.ccsds.ndm import ParserBuilder
from org.orekit.files.ccsds.ndm.odm.oem import OemSegment
from org.orekit.data import DataSource
from org.orekit.bodies import GeodeticPoint
from org.orekit.models.earth import ReferenceEllipsoid
from org.orekit.frames import FramesFactory, TopocentricFrame
from org.orekit.utils import TimeStampedPVCoordinates, IERSConventions

# Define observer point on ground
# -------------------------------

# Earth frame (ITRF = ECEF)
itrf = FramesFactory.getITRF(IERSConventions.IERS_2010, True)

# Reference ellipsoid
earth_shape = ReferenceEllipsoid.getWgs84(itrf)

# Observer on ground
lat = FastMath.toRadians(0.)  # Latitude in radians
lon = FastMath.toRadians(0.)  # Longitude in radians
alt = 0.  # Altitude above ellipsoid in degrees
observer_point = GeodeticPoint(lat, lon, alt)

# Observer frame
observer_frame = TopocentricFrame(earth_shape, observer_point, "observer")

# Read OEM
# --------

# Path to OEM
oem_file_path = Path.home() / 'git/orekit/src/test/resources/ccsds/odm/oem/test.oem'

# Convert to DataSource
oem_file_source = DataSource(oem_file_path.as_uri().replace("file:///", ""))

# Build OEM parser
oem_parser = ParserBuilder().buildOemParser()

# Parse OEM
oem = oem_parser.parse(oem_file_source)

# Loop on segments
for segment in oem.getSegments():
    oem_segment = OemSegment.cast_(segment)
    
    # Loop on data points in segment
    for pv in oem_segment.getCoordinates():
        date = pv.getDate()
        pos = pv.getPosition()
        # Get AZEL in degrees and range in km
        print(f"Date = {date} / "
              f"az = {FastMath.toDegrees(observer_frame.getAzimuth(pos, itrf, date))} ° / "
              f"el = {FastMath.toDegrees(observer_frame.getElevation(pos, itrf, date))} ° / "
              f"range = {1.E-03 * observer_frame.getRange(pos, itrf, date)} km")

Cheers,
Maxime

1 Like

Hi there,

Check out this post to read your file in python.

You can convert ECEF position into AzElAlt with Orekit yes. Maxime gave you an example. If you’re using version 12, there is also a getTrackingCoordinates method in TopocentricFrame that will compute everything at once.

Cheers,
Romain

2 Likes

Thank you @Serrof for the getTrackingCoordinates method. I wasn’t aware of that one and it’s very helpful!

Cheers,
Maxime

Hi @MaximeJ,
Many thanks for your complete answer. Your proposed code works perfectly fine. I also made a minor change in the path to OEM in the following form.
oem_file_path = Path(__file__).parent.absolute() / 'my_OEM.oem'
This refers directly to the path where the code is located.
It is also worth mentioning that if there is a folder in the path having a name with two separate parts, such as ‘myfile file’, it should be renamed to ‘myfile_file’ without any spacing; otherwise, it causes an error in the data source reader.
All the best,
Erfan

1 Like