Calling AbstractMeasurement's addParameterDriver() using the Orekit Python Wrapper

Hi,

I have a question regarding the Orekit’s Python wrapper.

The aim of my code is to solve an orbit determination problem making use of range measurements. In my code I have extended the class PythonAbstractMeasurement to create a new class (NewRange) which is very similar to the Range one and everything is working fine (i.e. the orbit determination problem is carried out without errors).

Now, I would like to estimate also the clock offset and the clock drift (now I am estimating just the 6 orbital parameters) and I have followed the code written in the Range class. Therefore, I have added the parameter drivers for the clock offset and the clock drift in the constructor of my class, but I get a “AttributeError: ‘NewRange’ object has no attribute ‘addParameterDriver’” when calling the method addParameterDriver. I guess it is due to the fact that it is a protected method and hence not wrapped, is it right? And if so, is there a way to overcome this problem?

Thank you in advance.

Best,
Samuele

Hi @samuele

Unfortunately, I can’t help you for your Python issues. Howerver, I have a small question. Why did you extend the Range measurement class?

Estimating the clock offset is straightforward using the current implementation of the Range measurement. After initialing the ObservableSatellite object used in the Range measurement’s constructor, you can set the clock estimation like that:

satellite = ObservableSatellite(0)
satellite.getClockOffsetDriver().setSelected(true)

For the GroundStation, you can do the same thing:

station = GroundStation(...)
station.getClockOffsetDriver().setSelected(true)

Please note that the spacecraft and station clock drifts are not used in Range measurement, they are only used for the RangeRate measurement (that’s maybe the reason why you extended the class).

Best regards,
Bryan

Hi @bcazabonne,

thank you for your reply! I have extended the Range class because my range is not computed as a satellite-station range but it is instead a range between the satellite and the Solar System Barycentre. You can see the detailed description of my problem here. So I have re-written the theoreticalEvaluation() method, but I can’t call the addParameterDriver() method…

Best regards,
Samuele

Hi @samuele

Thank you for your answer. Therefore, it’s probably a Python issue, and unfortunately I cannot help you for that…
I hope our Orekit-Python experts will help you.

Best regards,
Bryan

Hi Samuele,

Yes, the addParameterDriver is private in the AbstractMeasurement class and is not modified in the PythonAbstractMeasurements so that is why.

It seems to have a quite trivial function, so you could just do a “self.supportedParameters.add(driver)”.

Otherwise if this is a reasonable method to open, I can add this method to next release?

Regards
/Petrus

Hi Petrus,

thank you for your reply!

if I just do a “self.supportedParameters.add(driver)” I get a ‘NewRange’ object has no attribute ‘supportedParameters’. So I thought to just create the attribute ‘supportedParameters’ in my class and it seems to work.

However, it is still not clear to me how to get the estimated values for the satellite clock offset and drift starting from the method ‘BatchLSEstimator.estimate()’, that I run in my main code after adding N ‘NewRange’ measurements. Should I select (with the method setSelected()) the two clock ParameterDrivers before calling the estimate method? The only part of my code which deals with the two clock ParameterDrivers is contained in the ‘NewRange’ class, as it is done in the ‘Range’ class. So I thought it was sufficient to just add the ‘NewRange’ measurements to the estimator, but I don’t see any estimated values of the clock parameters…

Thank you so much!
Samuele

Hi @samuele,

You need indeed to select the drivers to estimate them with the batch LS.
Orbital parameter drivers (the state vector) are automatically selected while other drivers (propagation and measurement drivers) are un-selected by default.
It is the up to the user to set their selection to true if they must be estimated.

Hi @MaximeJ,

many thanks! Sure, it makes sense.

My question is: where and how should I select the drivers? The code of interest is:

estimator = BatchLSEstimator(optimizer, propagatorBuilder)

estimator.setParametersConvergenceThreshold(estimator_convergence_thres)
estimator.setMaxIterations(estimator_max_iterations)
estimator.setMaxEvaluations(estimator_max_evaluations)

observableSatellite = ObservableSatellite(0)

for i in range(0, number_of_measurements):

    sat = ArrayList()
    sat.add(observableSatellite)
    
    psr_range = NewRange(date[i], float(0.0), float(sigma), float(range_weight), sat, .....)
    
    estimator.addMeasurement(psr_range)

while the ‘NewRange’ class manages the clock parameters exactly as done in the ‘Range’ class, as you suggested me some time ago :slight_smile:

Best,
Samuele

Hi @samuele,

If I understood well you added the two ParameterDrivers to your NewRange class, along with a new attribute supportedParameters that somehow replaces the one you don’t have access to in the AbstractMeasurement class. Is that correct ?

First, do you have access to these new drivers so that you can use the method setSelected(true) on them ?

Then, the batch LS gets the estimable measurements parameters in BatchLSEstimator. getMeasurementsParametersDrivers method.
You will note that it calls the ObservedMeasurement.getParametersDrivers() method which should be defined in your case in AbstractMeasurement.getParametersDrivers.
This last method refers to the attribute supportedParameters from the AbstractMeasurement class and not the attribute supportedParameters that you added to your NewRange class.
So you probably need to overwrite the method getParametersDrivers in your NewRange class so that the batch LS can see your new drivers.

I’m not proficient enough in Python to tell you if it will work. But this looks a bit sketchy,I think the best way would be to open the method addParameterDriver in the PythonAbstractMeasurement class as @petrus.hyvonen suggested.

To check if your drivers are visible from the batch LS point of view, once you’ve added your measurement you can call the BatchLSEstimator.getMeasurementsParametersDrivers method and see if you drivers’ names in the ParamaterDriversList returned.

Hope this will help you,
Maxime

Hi @MaximeJ,

Yes, exactly. Here it is my class with the constructor and the getParametersDrivers method.

class NewRange(PythonAbstractMeasurement):

supportedParametersNew = []

def __init__(self, date, observed, sigma, base_weight, satellite):

    super().__init__(date, observed, sigma, base_weight, satellite)
    
    # Select the ParameterDrivers
    self.getSatellites().get(0).getClockOffsetDriver().setSelected(True)
    self.getSatellites().get(0).getClockDriftDriver().setSelected(True)
    
    # add the selected ParameterDrivers to the attribute supportedParametersNew. 
    self.supportedParametersNew.append(self.getSatellites().get(0).getClockOffsetDriver())
    self.supportedParametersNew.append(self.getSatellites().get(0).getClockDriftDriver())
    

def getParametersDrivers(self):
    
    array_list = ArrayList()
    array_list.add(self.supportedParametersNew[0])
    array_list.add(self.supportedParametersNew[1])

    return Collections.unmodifiableList(array_list)

When I print the results of the method BatchLSEstimator.getMeasurementsParametersDrivers I get just “org.orekit.utils.ParameterDriversList@5905d963” so I can’t understand if the drivers are visible…

It would be great indeed!

Thank you so much,
Samuele

I have tried to print something inside the overwritten getParametersDrivers method and it is not visualized during the execution of the code. I guess this means that the NewRange object still calls the getParametersDrivers method which refers to the supportedParameters attribute I can’t touch…