How to access BatchLSEstimator estimated range rate measurements?

Hi all,

I’m trying to figure out how to use the Batch Least Squares Estimator in Orekit to determine how range rate measurements impact my uncertainty in the spacecraft location. I’ve already generated ‘synthetic’ range rate measurements by simulating an orbit and calculating the range rate when it’s in view of a ground station.
this.GROUNDSTATION_GEO = new GeodeticPoint(FastMath.toRadians(lat), FastMath.toRadians(lon), alt_m);
this.TOPOCENTRIC_FRAME = new TopocentricFrame(earth, this.GROUNDSTATION_GEO, "Ground Station Frame");
this.GROUND_STATION = new GroundStation(TOPOCENTRIC_FRAME);

Then, as I propagate:
AbsoluteDate date = state.getDate();
Frame earthFrame = FramesFactory.getITRF(IERSConventions.IERS_2010, true);
PVCoordinates scPV_earth = state.getPVCoordinates(earthFrame);
double rangeRate = this.TOPOCENTRIC_FRAME.getRangeRate(scPV_earth, earthFrame, date);

I output these ‘synthetic’ range rates to a file along with the date/time and LLA position of the spacecraft. Now I’m trying to use the BatchLSEstimator to track my uncertainty and error in the initial state estimate using the synthetic measurements I just generated. The general hope is that by increasing the number of measurements (to a certain point), I can minimize the uncertainty in my initial state.

Currently, I’m just trying to verify everything is working so I’m using the truth state for my a priori. As I add the range rate measurements to the estimator my error (difference between true and estimated initial state) is growing quite rapidly. I’m assuming that the BatchLSEstimator has a different measurement model for the range rate than what I’m calculating but I can’t seem to access it. I’m curious, how are the RangeRate measurements used in the BatchLSEstimator? It seems like the estimator calculates EstimatedMeasurements and compares those to the ObservedMeasurements. If so, how are the estimated RangeRates calculated?

// ----- Initialize Orekit data
initOrekit.initializeOrekitData();

// ----- Initialize any frames used 
Frame earthFrame = FramesFactory.getITRF(IERSConventions.IERS_2010, true);

// ----- 1) Build ground stations
GroundStation GS1 = buildStation(GS1_LAT, GS1_LON, GS1_ALT, GS1_NAME);

// ----- 2) Satellite ID wrapper
ObservableSatellite sat = new ObservableSatellite(0);

// ----- 3) Load + build synthetic measurement objects
File gs1CSV = new File(<PathToFile);
List<ObservedMeasurement<?>> measurements_gs1 = loadRangeRateFile(gs1CSV, GS1, sat, SIGMA_RR);
List<List<ObservedMeasurement<?>>> allPasses = detectPasses(measurements_gs1);

int i = 1; // number of passes to use
List<ObservedMeasurement<?>> selectedGS1 = new ArrayList<>();
for (int j = 0; j < Math.min(i, allPasses.size()); j++) {
    selectedGS1.addAll(allPasses.get(j)); // flatten each pass
}

// ----- 4) Guess the initial orbit
Orbit trueOrbit = initialOrbitTLE();
SpacecraftState offsetState = new SpacecraftState(trueOrbit).shiftedBy(0.0); // eventually change the offset in time to make the initial guess worse
Orbit guessOrbit = offsetState.getOrbit();
System.out.println("Initial Orbit Created.");

// ----- 5) Build the Numerical Propagator Builder
NumericalPropagatorBuilder builder = buildNumericalPropagatorBuilder(guessOrbit);
System.out.println("Propagator Builder Created.");

// ----- 6) Build the LS Estimator
LevenbergMarquardtOptimizer optimizer = new LevenbergMarquardtOptimizer(
   100,  // initialStepBoundFactor
   1e-6,  // costRelativeTolerance
   1e-6,   // parRelativeTolerance
   1e-6,         // orthoTolerance
   1e-8      // QR ranking threshold
);

BatchLSEstimator estimator = new BatchLSEstimator(optimizer, builder);
estimator.setMaxEvaluations(MAX_EVALUATIONS);
estimator.setMaxIterations(MAX_ITERATIONS);

// Add measurements
int iMeas = 3;
for (int idx = 0; idx < Math.min(iMeas, selectedGS1.size()); idx++) {
   RangeRate meas = (RangeRate) selectedGS1.get(idx);
   estimator.addMeasurement(meas);
   System.out.println("Measurement " + idx + " added: " + meas.getObservedValue()[0] + " m/s");
}

// ----- 7) Run the estimator and extract the error
Propagator estimated = estimator.estimate()[0];
System.out.println("Estimator Run.");
SpacecraftState initState = estimated.getInitialState();
Orbit initOrbit = initState.getOrbit();

for (idx = 0; idx < iMeas; idx++) {
   RangeRate meas = (RangeRate) selectedGS1.get(idx);
   AbsoluteDate measurementDate = meas.getDate();  // Use the date from the measurement for prediction
   PVCoordinates predictedPV = initOrbit.getPVCoordinates(measurementDate, earthFrame);

   double predictedRangeRate = GS1.getBaseFrame().getRangeRate(predictedPV, earthFrame, measurementDate);
   System.out.println("Predicted Range Rate " + idx + " : " + predictedRangeRate + " m/s");
}

PVCoordinates estimatedPV = initOrbit.getPVCoordinates();
PVCoordinates truePV = trueOrbit.getPVCoordinates();

double dx = estimatedPV.getPosition().getX() - truePV.getPosition().getX();
double dy = estimatedPV.getPosition().getY() - truePV.getPosition().getY();
double dz = estimatedPV.getPosition().getZ() - truePV.getPosition().getZ();

double positionError = Math.sqrt(dx*dx + dy*dy + dz*dz);
System.out.println("Position error (m): " + positionError);

Hi @sswen20,

TopocentricFrame.getRangeRate is a purely geometrical calculation, it doesn’t take into account light time delay.

RangeRate, on the other hand, takes that delay into account. I think that can explain your discrepancy.

If you want to simulate measurements for an OD, you should use the measurements’ generation package of Orekit.
Here are some links to help you do so:

  • At the beginning of the “estimation” package architecture explanation
  • In the tutorials related to the performance of the estimation classes, there’s a MeasurementGenerator class.

Hope this helps,
Maxime

@MaximeJ thanks for answering so quickly!

So just to confirm, rather than outputting the geometric range rate (TopocentricFrame.getRangeRate), I would need to wrap the value in the measurement generation package? Would something like this work?


private static SortedSet<ObservedMeasurement<?>> generateRangeRateAboveElevation(

        Propagator propagator,

        GroundStation station,

        ObservableSatellite sat,

        double sigmaRR,

        double step,

        AbsoluteDate start,

        AbsoluteDate end,

        double elevation_deg) {




        // --- Noise generator

        double[][] cov = { { sigmaRR * sigmaRR } };

        CorrelatedRandomVectorGenerator noise =

                new CorrelatedRandomVectorGenerator(

                        MatrixUtils.createRealMatrix(cov),

                        1e-10,

                        new GaussianRandomGenerator(new Well19937a(1234)));




        // --- RangeRate builder

        RangeRateBuilder builder = new RangeRateBuilder(noise, station, false, sigmaRR, 1.0, sat);



        // --- Generator

        Generator gen = new Generator();

        gen.addPropagator(propagator);



        // Create the elevation detector

        TopocentricFrame gsTopo = station.getBaseFrame();

        ElevationDetector elevDetector = new ElevationDetector(gsTopo)

            .withConstantElevation(FastMath.toRadians(elevation_deg))

            .withMaxCheck(1.0)

            .withThreshold(1e-6);



        // ---- Scheduler automatically enforces elevation mask and step

        EventBasedScheduler<RangeRate> scheduler = new EventBasedScheduler<>(

            builder, 

            new FixedStepSelector(step, TimeScalesFactory.getUTC()),

            propagator,

            elevDetector, 

            SignSemantic.FEASIBLE_MEASUREMENT_WHEN_POSITIVE);



        gen.addScheduler(scheduler);



        // generate measurements between start and end

        SortedSet<ObservedMeasurement<?>> measurements = gen.generate(start, end);




        return measurements;

    }

With this, I seem to only be getting range rate measurements from a single pass. Is there something within the scheduler that stops once a negative value has been detected?

Hi @sswen20,

This is due to the ElevationDetector default constructor, which has a built-in StopOnDecreasing() event handler.
You should add withHandler(new ContinueOnEvent()) when building your ElevationDetector.

1 Like

I’m including my code for completeness below, but for the first iteration/evaluation of the BatchLSEstimator if I’m starting with my apriori state as the truth state (same orbit that I used to generate measurements), shouldn’t the residuals be the same order of magnitude as my sigma in the measurements (range rate)? I’m getting residuals that are the same order of magnitude as my measurements. I’m using the same force/drag/atmosphere models when generating my ‘data’ and when running the estimator.

Residual[0] = +25.1879
Residual[1] = +54.8990
Residual[2] = +131.704
Residual[3] = +367.654
Residual[4] = +1026.94
Residual[5] = +1159.12
Residual[6] = +455.001
Residual[7] = +163.948
Residual[8] = +73.1320
Residual[9] = +36.6805
Residual[10] = +359.574
Residual[11] = +611.428
Residual[12] = +1081.79
Residual[13] = +1905.81
Residual[14] = +2929.49

public static void main(String[] args) {




        // ===== Setup Orekit

        initOrekit.initializeOrekitData();




        // ===== Generate initial state from TLE

        TLE tle = new TLE(TLE_LINE1, TLE_LINE2);

        // Eventually, shift the trueInitialState slightly forward or backward in time from the TLE

        SpacecraftState trueInitialState = eme2000InitialStateFromTLE(tle);

        AbsoluteDate startDate = trueInitialState.getDate();

        AbsoluteDate endDate = startDate.shiftedBy(PROP_TIME_S);




        // ===== Define the ground stations

        GroundStation gs1 = createGroundStation(GS1_LAT, GS1_LON, GS1_ALT_M, GS1_NAME);

        

        // ===== Create the "TRUE" numerical propagator

        // This includes gravity (16,16) and a simplified model for drag (flat plate?)

        NumericalPropagator trueProp = buildNumericalPropagator(trueInitialState);




        // ===== Generate true range rate measurements

        // This ObservableSatellite MUST be reused for the estimator

        ObservableSatellite sat = new ObservableSatellite(0);

        SortedSet<ObservedMeasurement<?>> measurements = generateRangeRateAboveElevation(

            trueProp, gs1, sat, SIGMA_RR, MEASUREMENT_DT, startDate, endDate, GS_ELEV_DEG);

        // optional: limit how many you want for OD

        List<ObservedMeasurement<?>> selectedMeasurements =

                pickFirstN(measurements, 15);




        // ===== Create the estimation propagator builder

        NumericalPropagatorBuilder builder = buildPropagatorBuilder(trueInitialState);




        // ===== Create least squares estimator, add measurements

        BatchLSEstimator estimator = initializeEstimator(builder, selectedMeasurements);

        BatchLSObserver observer = createObserver(); // output the residual with the measurement and covariance matrix diagonal at each iteration

        estimator.setObserver(observer);





        // ===== Run estimation

        Propagator estimated = estimator.estimate()[0];

        SpacecraftState estimatedInitialState = estimated.getInitialState();




        // output result

        Vector3D deltaPos = trueInitialState.getPVCoordinates().getPosition()

                   .subtract(estimatedInitialState.getPVCoordinates().getPosition());




        Vector3D deltaVel = trueInitialState.getPVCoordinates().getVelocity()

                        .subtract(estimatedInitialState.getPVCoordinates().getVelocity());




        System.out.println("Position difference [m]: " + deltaPos.getNorm());

        System.out.println("Velocity difference [m/s]: " + deltaVel.getNorm());




    }




    /**

     * Generate the initial spacecraft state based on feeding the TLE through the TLE propagator.

     * 

     * @param tle

     * @return

     */

    private static SpacecraftState eme2000InitialStateFromTLE(TLE tle) {




        // ---- Create the TLE propagator using the TLE lines

        Frames frames = DataContext.getDefault().getFrames();

        TLEPropagator SGP4propagator = TLEPropagator.selectExtrapolator(

            tle, 

            InertialProvider.of(frames.getTEME()),

            SPACECRAFT_MASS_KG,

            frames.getTEME());




        // ---- Get the initial state SGP4 osculating state in TEME

        AbsoluteDate epoch = tle.getDate();

        SpacecraftState initialState_TEME = SGP4propagator.getInitialState();




        // ---- Convert the initial state to the EME (Inertial) reference frame

        Frame emeFrame = FramesFactory.getEME2000();

        Frame teme = initialState_TEME.getFrame();

        Transform toEME = teme.getTransformTo(emeFrame, epoch);




        PVCoordinates pvEME = toEME.transformPVCoordinates(initialState_TEME.getPVCoordinates());

        

        // ---- Build an orbit with this data

        double mu = Constants.EGM96_EARTH_MU;

        Orbit initialOrbit = new CartesianOrbit(pvEME, emeFrame, epoch, mu);




        // ---- use that orbit to determine the initial state

        SpacecraftState initialState_EME = new SpacecraftState(initialOrbit, SPACECRAFT_MASS_KG);




        return initialState_EME;

    }




    /**

     * Create the ground station based on the given LLA (degree, degree, m). This object is used to determine

     * the range rate.

     * 

     * @param lat_deg

     * @param lon_deg

     * @param alt_m

     * @param name

     * @return

     */

    private static GroundStation createGroundStation(double lat_deg, double lon_deg, double alt_m, String name) {




        // create the ground station geodetic point

        GeodeticPoint geo = new GeodeticPoint(FastMath.toRadians(lat_deg), FastMath.toRadians(lon_deg), alt_m);




        // Earth ellipsoid in ITRF frame

        Frame earthFrame = FramesFactory.getITRF(IERSConventions.IERS_2010, true); // Earth-centered Earth-fixed (geodesy)

        BodyShape earth = new OneAxisEllipsoid(

            Constants.WGS84_EARTH_EQUATORIAL_RADIUS,

            Constants.WGS84_EARTH_FLATTENING, 

            earthFrame);




        // create the topocentric frame for the ground station

        TopocentricFrame topo = new TopocentricFrame(earth, geo, name);




        // create the ground station object

        return new GroundStation(topo);

    }




    /**

     * Generate the range rate measurements.

     * This uses the numerical propagator to propagate the spacecraft state forward in time. It then uses the 

     * RangeRateBuilder to determine the range rate at the propagated state. It only generates measurements

     * when the spacecraft is above the specified elevation from the ground station.

     * 

     * @param propagator propagator for propagating the orbit/state in time

     * @param station ground station

     * @param sat satellite that is being propagated

     * @param sigmaRR range rate estimated 1 sigma bound noise

     * @param step time step

     * @param start starting time

     * @param end ending time

     * @param elevation_deg elevation threshold

     * @return measurements

     */

    private static SortedSet<ObservedMeasurement<?>> generateRangeRateAboveElevation(

        Propagator propagator,

        GroundStation station,

        ObservableSatellite sat,

        double sigmaRR,

        double step,

        AbsoluteDate start,

        AbsoluteDate end,

        double elevation_deg) {




        // --- Noise generator

        double[][] cov = { { sigmaRR * sigmaRR } };

        CorrelatedRandomVectorGenerator noise =

                new CorrelatedRandomVectorGenerator(

                        MatrixUtils.createRealMatrix(cov),

                        1e-10,

                        new GaussianRandomGenerator(new Well19937a(1234)));




        // --- RangeRate builder

        // for now, assume perfect measurements to verify estimator

        RangeRateBuilder builder = new RangeRateBuilder(noise, station, false, sigmaRR, 1.0, sat);




        // --- Generator

        Generator gen = new Generator();

        gen.addPropagator(propagator);




        // Create the elevation detector

        TopocentricFrame gsTopo = station.getBaseFrame();

        ElevationDetector elevDetector = new ElevationDetector(gsTopo)

            .withConstantElevation(FastMath.toRadians(elevation_deg))

            .withMaxCheck(1.0)

            .withThreshold(1e-6)

            .withHandler(new ContinueOnEvent<>());




        // ---- Scheduler automatically enforces elevation mask and step

        EventBasedScheduler<RangeRate> scheduler = new EventBasedScheduler<>(

            builder, 

            new FixedStepSelector(step, TimeScalesFactory.getUTC()),

            propagator,

            elevDetector, 

            SignSemantic.FEASIBLE_MEASUREMENT_WHEN_POSITIVE);




        gen.addScheduler(scheduler);




        // generate measurements between start and end

        SortedSet<ObservedMeasurement<?>> measurements = gen.generate(start, end);




        return measurements;

    }




    /**

     * Utility function to select the first N range-rate measurements. For use alongside

     * an estimator to determine impact of number of measurements.

     * 

     * @param all SortedSet<ObservedMeasurement<?>> list of full measurements

     * @param N number of measurements to pull

     * @return first N measurements

     */

    private static List<ObservedMeasurement<?>> pickFirstN(

        SortedSet<ObservedMeasurement<?>> all, int N) {




        List<ObservedMeasurement<?>> out = new ArrayList<>(N);

        int i = 0;

        for (ObservedMeasurement<?> m : all) {

            if (i >= N) break;

            out.add(m);

            i++;

        }

        return out;

    }




    private static NumericalPropagator buildNumericalPropagator(SpacecraftState initState) {




        DormandPrince54Integrator integrator = 

            new DormandPrince54Integrator(MIN_STEP, MAX_STEP, POSITION_TOLERANCE, VELOCITY_TOLERANCE);




        NumericalPropagator propagator =

            new NumericalPropagator(integrator);




        // ---- Reference frames

        Frame emeFrame = FramesFactory.getEME2000(); // Inertial Earth-centered, Earth-fixed aligned with ICRF (satellite tracking)

        Frame earthFrame = FramesFactory.getITRF(IERSConventions.IERS_2010, true); // Earth-centered Earth-fixed (geodesy)




        // ---- Earth Shape and Sun PVC

        OneAxisEllipsoid earth = new OneAxisEllipsoid(

            Constants.WGS84_EARTH_EQUATORIAL_RADIUS,

            Constants.WGS84_EARTH_FLATTENING, 

            earthFrame);

        PVCoordinatesProvider sun = CelestialBodyFactory.getSun();




        // ---- Use Earth gravity truncated to J2, J3, J4 only to align with SGP4

        NormalizedSphericalHarmonicsProvider gravity =

            GravityFieldFactory.getNormalizedProvider(16, 16);

        ForceModel gravityModel = new HolmesFeatherstoneAttractionModel(earthFrame, gravity);




        // ---- Account for drag with Cd, A, and real density

        NRLMSISE00InputParameters weatherData = new CssiSpaceWeatherData("SpaceWeather-All.*\\.txt");

        Atmosphere nrlAtmosphere = new NRLMSISE00(weatherData, sun, earth);

        DragSensitive spacecraft = new IsotropicDrag(SPACECRAFT_CROSSSECTION_M2, SPACECRAFT_CD);

        DragForce dragModel = new DragForce(nrlAtmosphere, spacecraft);




        // ---- Optional, lock nadir pointing

        NadirPointing nadirLaw = new NadirPointing(emeFrame, earth);




        propagator.setAttitudeProvider(nadirLaw);

        propagator.addForceModel(gravityModel);

        propagator.addForceModel(dragModel);




        propagator.setInitialState(initState);




        return propagator;

    }




    public static NumericalPropagatorBuilder buildPropagatorBuilder(SpacecraftState initState) {




        Orbit initOrbit = initState.getOrbit();




        ODEIntegratorBuilder integratorBuilder =

                new DormandPrince54IntegratorBuilder(MIN_STEP, MAX_STEP, POSITION_TOLERANCE);




        NumericalPropagatorBuilder propagatorBuilder = new NumericalPropagatorBuilder(

                initOrbit,

                integratorBuilder,

                PositionAngle.MEAN,

                1.0);




        // ---- Reference frames

        Frame emeFrame = FramesFactory.getEME2000(); // Inertial Earth-centered, Earth-fixed aligned with ICRF (satellite tracking)

        Frame earthFrame = FramesFactory.getITRF(IERSConventions.IERS_2010, true); // Earth-centered Earth-fixed (geodesy)




        // ---- Earth Shape and Sun PVC

        OneAxisEllipsoid earth = new OneAxisEllipsoid(

            Constants.WGS84_EARTH_EQUATORIAL_RADIUS,

            Constants.WGS84_EARTH_FLATTENING, 

            earthFrame);

        PVCoordinatesProvider sun = CelestialBodyFactory.getSun();




        // ---- Use Earth gravity truncated to J2, J3, J4 only to align with SGP4

        NormalizedSphericalHarmonicsProvider gravity =

            GravityFieldFactory.getNormalizedProvider(16, 16);

        ForceModel gravityModel = new HolmesFeatherstoneAttractionModel(earthFrame, gravity);




        // ---- Account for drag with Cd, A, and real density

        NRLMSISE00InputParameters weatherData = new CssiSpaceWeatherData("SpaceWeather-All.*\\.txt");

        Atmosphere nrlAtmosphere = new NRLMSISE00(weatherData, sun, earth);

        DragSensitive spacecraft = new IsotropicDrag(SPACECRAFT_CROSSSECTION_M2, SPACECRAFT_CD);

        DragForce dragModel = new DragForce(nrlAtmosphere, spacecraft);




        // ---- Optional, lock nadir pointing

        NadirPointing nadirLaw = new NadirPointing(emeFrame, earth);




        propagatorBuilder.setAttitudeProvider(nadirLaw);

        propagatorBuilder.addForceModel(gravityModel);

        propagatorBuilder.addForceModel(dragModel);




        return propagatorBuilder;

    }




    /**

     * Initialize the estimator used for the orbit determination.

     * @param inputData input data

     * @param propagator orbit propagator

     * @param initialGuess intial guess of the orbit determination

     * @param measurements measurements

     * @return a configured estimator

     */

    private static BatchLSEstimator initializeEstimator(final NumericalPropagatorBuilder propagator,

                                                        final List<ObservedMeasurement<?>> measurements) {




        // Optimizer

        GaussNewtonOptimizer optimizer = new GaussNewtonOptimizer(new QRDecomposer(1e-11), false);




        // Estimator

        BatchLSEstimator estimator = new BatchLSEstimator(optimizer, propagator);




        double convergenceThreshold = 1.0e-3;

        int maxIterations = 10;

        int maxEvaluations = 20;




        estimator.setParametersConvergenceThreshold(convergenceThreshold);

        estimator.setMaxIterations(maxIterations);

        estimator.setMaxEvaluations(maxEvaluations);




        // Add the measurements to the estimator

        for (ObservedMeasurement<?> measurement : measurements) {

            estimator.addMeasurement(measurement);

        }




        // Return the configured estimator

        return estimator;




    }




    private static BatchLSObserver createObserver() {

        BatchLSObserver observer = new BatchLSObserver() {

            @Override

            public void evaluationPerformed(int iteration,

                                            int evalCount,

                                            Orbit[] estimatedOrbits,

                                            ParameterDriversList orbitalParams,

                                            ParameterDriversList propagationParams,

                                            ParameterDriversList measurementParams,

                                            EstimationsProvider estimations,

                                            Evaluation evalInfo) {




                System.out.println("\n=== Iteration " + iteration +

                           "   Evaluation " + evalCount + " ===");




                // Print RMS

                System.out.println("RMS: " + evalInfo.getRMS());




                // Access residuals via the evaluation object

                double[] residuals = evalInfo.getResiduals().toArray();

                for (int i = 0; i < residuals.length; i++) {

                    System.out.printf("Residual[%d] = %+g%n", i, residuals[i]);

                }




                // Covariance (diagonal)

                RealMatrix cov = evalInfo.getCovariances(1e-10);

                if (cov != null) {

                    System.out.println("Covariance diagonal:");

                    for (int i = 0; i < cov.getRowDimension(); i++) {

                        System.out.printf("  σ[%d]^2 = %g%n", i, cov.getEntry(i, i));

                    }

                }

            }

        };




        return observer;




    }

Digging into this a little more, the estimator does much better if I remove the drag force. Is this expected? Does the BatchLSEstimator just not do well with these higher order dynamics even if it’s added to both the propagator for the initial synthetic ‘measurements’ as well as the propagatorBuilder for the estimator?

However, even with taking out the drag, the error between my estimated and true initial state (true state is used as the a priori) is still 2 km. I would have expected it to be closer to machine precision. Does this indicate that I need range rate AND range measurements?

Hi @sswen20,

I don’t have time to dig into your code right now but usually when you do this kind of thing you should get the initial orbit with very good precision, and the residuals should indeed be the same order of magnitude as the sigma.
There’s must be a small bug somewhere. What happens if you generate measurements with no noise ?

The issue that I was using seemed to be due to the propagator used to determine the measurements and estimates. I was creating a separate propagator for the measurements from the propagatorBuilder used for my estimates. Even though I was using the same force models and attitude providers, the difference was causing the resulting trajectories to differ. Is that just due to the difference in providing one with a SpacecraftState and one with an Orbit?

Hi,

No, there shouldn’t be a difference, a builder creates a Propagator that you could make by hand.
An issue I had in the past, was that I didn’t give the initial mass to the builder, so it was using the default value 1000kg. Did you pass explicitly something to your builder for that parameter? It could explain the differences you see with drag.

Edit: maybe I missunderstood your question. So to answer another one, you shoudl’nt use an Orbit for propagation (with shifted by). It uses Keplerian motion more or less. You need a NumericalPropagator for realistic trajectory

Cheers,
Romain.

That’s good to know! I switched back to building a Propagator ‘by hand’ for the measurements and created the propagator builder for the estimator. With setting the mass for the builder, that fixed my problem! Thank you for recommending that!

One last thing, is there a way to set the initial uncertainty in the apriori state with the BatchLSEstimator?

1 Like

Hi @sswen20,

For this, you’ll have to use a SequentialBatchLSEstimator.
See the tutorial here.

Cheers,
Maxime