Writing CPF: access to CPFHeader inside CPF class

Hi folks,

I am trying to write a ILRS CPF file containing position data generated via orbit determination.

My code is the following:

from org.orekit.files.ilrs import CPFHeader
cpf_header = CPFHeader()
cpf_header.setSource(ephemeris_source)
cpf_header.setStep(step_time)
cpf_header.setEndEpoch(date_end_cpf)
cpf_header.setIlrsSatelliteId(cospar_id)
cpf_header.setName(target_name)
cpf_header.setNoradId(str(norad_id))
cpf_header.setProductionEpoch(production_epoch)
cpf_header.setProductionHour(production_hour)
cpf_header.setSequenceNumber(ephemeris_sequence)
cpf_header.setSic(sic)
cpf_header.setStartEpoch(ephemeris_start_date)
cpf_header_v1 = cpf_header
cpf_header_v1.setVersion(1)

from org.orekit.files.ilrs import CPFWriter
cpf_writer_v1 = CPFWriter(cpf_header_v1, utc)

from org.orekit.files.ilrs import CPF
cpf = CPF()

# Add data to CPF file
date_current = ephemeris_start_date
leap = 0
while date_current.compareTo(date_end_cpf) <= 0:    
    pv_itrf = bounded_propagator.getPVCoordinates(date_current, itrf)
    coord = CPF.CPFCoordinate(date_current, pv_itrf.getPosition(), pv_itrf.getVelocity(), leap)
    cpf.addSatelliteCoordinate(coord)
    date_current = date_current.shiftedBy(dt_cpf)

from java.io import StringWriter
string_writer_v1 = StringWriter()
cpf_writer_v1.write(string_writer_v1, cpf)

It fails with the following:


JavaError Traceback (most recent call last)
in
11 from java.io import StringWriter
12 string_writer_v1 = StringWriter()
—> 13 cpf_writer_v1.write(string_writer_v1, cpf)

JavaError: <super: <class ‘JavaError’>, >
Java stacktrace:
java.lang.NullPointerException
at org.orekit.files.ilrs.CPFWriter.write(CPFWriter.java:83)

I tried with both Orekit 10.3 and 11.0 with no difference (apart from minor syntax changes I had to apply).

It seems to come from the Map get at CPFWriter.java:82 which returns a null pointer.

This is due to the satellite ID in the ephemeris map being null:

cpf.getSatellites()

returns:

<Map: {null=org.orekit.files.ilrs.CPF$CPFEphemeris@153cffb8}>

Because at CPF.java:81, ephemeris.getId() returns null. In turn, this comes from CPF.java:190: header.getIlrsSatelliteId() returns null.

I realized that the header member of cpf is an empty CPFHeader object created in the CPF constructor, and not the cpf_header_v1 variable that was defined above. Looking at CPF.java:69, it makes sense as there’s no way to pass the CPFHeader object to the CPF object (only the CPFWriter gets the CPFHeader object as an argument of its constructor).

In the Java unit tests it appears that the CPFWriter class is never tested to write (non-null) CPF files from scratch. Instead, existing files are loaded, written to another file and then both files are compared for consistency, which explains that this missing header issue doesn’t occur.

Is it a bug (or a feature actually not though of when developing the ILRS package) or am I using these classes wrong?

Cheers
Clément

Hi @yzokras

I’ll try to reproduce your configuration in Java and see if it’s a bug. But, it looks like.
I’ll go back to you soon.

Bryan

Sorry for the delay.
I confirm, it’s a bug. Could you open an issue on the Orekit issue tracker?
There is an easy way to fix it.

  1. Deprecate the addSatelliteCoordinate(CPFCoordinate coord) method
  2. Have a constructor CPFEphemeris(String id). The previous one must also be deprecated.
  3. Replace the deprecated method at 1. by addSatelliteCoordinate(String id, CPFCoordinate coord). It can be also useful to have the following signature: addSatelliteCoordinates(String id, List<CPFCoordinate> coord)

Before having the fix, there is a workaround. You can use the OrekitEphemerisFile class. For this, you need to create a list of spacecraft states based on the pv_itrf you extract. Then, the list of spacecraft states can be added as an ephemeris of the OrekitEphemerisFile.

cpf = new OrekitEphemerisFile()


# Add data to CPF file
date_current = ephemeris_start_date
states = []
while date_current.compareTo(date_end_cpf) <= 0:
    pv_itrf = bounded_propagator.getPVCoordinates(date_current, itrf)
    states.append(new SpacecraftState(...))
    date_current = date_current.shiftedBy(dt_cpf)

satelliteEphemeris = cpf.addSatellite(cpf_header_v1.getIlrsSatelliteId())
satelliteEphemeris.addNewSegment(states)

string_writer_v1 = StringWriter()
cpf_writer_v1.write(string_writer_v1, cpf)

Two things about the above example: (1) I wrote it directly in the forum so it may contain typos and (2) I’m a very bad Python programmer.

Thank you, I opened a bug ticket: CPF.addSatelliteCoordinate(CPFCoordinate coord) method writes null as satelliteId in ephemeris (#844) · Issues · Orekit / Orekit · GitLab

The workaround works, great! However, this only allows to use an inertial frame because OrekitEphemerisFile does not have the notion of frame, it only uses the frame of the input SpacecraftState, see OrekitEphemerisFile.java:265. Whereas I want to write the position data in ITRF frame to the CPF file as it’s the preferred frame for this file format.

Thank you for oppening the issue.

If you initialize the SpacecraftState using AbsolutePVCoordinate class, I think you will be able to write coordinates in ITRF. I recommend you to use the constructor with AbsolutePVCoordinate and AttitudeProvider. The only additional thing to do is to define the AttitudeProvider. Because you just want to write the coordinates, the AttitudeProvider will not impact the writing. Therefore, you can use a simple InertialProvider.

However, it is just a workaround and the best is to solve the issue for CPF.

Yes this is exactly what I did, but the SpacecraftState constructor won’t let me use a non-(pseudo)inertial frame, it gives an error message.

The issue was fixed by @bcazabonne in merge request Fixed null pointer exception when constructing CPF from coordinates. (!207) · Merge requests · Orekit / Orekit · GitLab , thanks a lot!

2 Likes