Hi,
I’m trying to implement an OD procedure through batch LSE for a real GEO measurements.
Here the pseudocode
`
Set time scale to UTC
utc = TimeScalesFactory.getUTC()
Define reference system and earth, moon and sun model
inertialFrame = FramesFactory.getEME2000()
teme = FramesFactory.getTEME()
gcrf = FramesFactory.getGCRF()
itrf = FramesFactory.getITRF(ITRFVersion.ITRF_2020,IERSConventions.IERS_2010, False) # boolean if true, tidal effects are ignored when interpolating EOP
eci = gcrf
ecef = itrf
wgs84Ellipsoid = ReferenceEllipsoid.getWgs84(ecef)
moon = CelestialBodyFactory.getMoon()
sun = CelestialBodyFactory.getSun()
earth = OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, Constants.WGS84_EARTH_FLATTENING, itrf)
Read TDM files
raw_tdm_file_path = "/home/tanozar/Research/Orbit_Determinartion/TDM_prova_OD/TDM_38091_2023_02_14_new.kvn"
tdm = createTdm(raw_tdm_file_path)
Read TLE
tleLine1 = "1 38091U 12008A 23045.13109083 .00000066 00000-0 00000-0 0 9993"
tleLine2 = "2 38091 2.1185 71.6844 0013244 308.9480 229.1262 1.00270740 40252"
tleReference = TLE(tleLine1, tleLine2)
name_obs = TdmMetadata.cast_(Segment.cast_(tdm.getSegments().get(0)).getMetadata()).getParticipants().get(1)
sat_name = TdmMetadata.cast_(Segment.cast_(tdm.getSegments().get(0)).getMetadata()).getParticipants().get(2)
startDateTdm = TdmMetadata.cast_(Segment.cast_(tdm.getSegments().get(0)).getMetadata()).getStartTime()
finalDateTdm = TdmMetadata.cast_(Segment.cast_(tdm.getSegments().get(tdm.segments.size()-1)).getMetadata()).getStopTime()
Set observatory location on Orekit Frames
longitude = radians(41.765278)
latitude = radians(13.375)
altitude = float(550.0)
station_frame = TopocentricFrame(earth, GeodeticPoint(latitude, longitude, altitude), name_obs)
station = GroundStation(station_frame)
station.getPrimeMeridianOffsetDriver().setReferenceDate(AbsoluteDate.J2000_EPOCH)
station.getPolarOffsetXDriver().setReferenceDate(AbsoluteDate.J2000_EPOCH)
station.getPolarOffsetYDriver().setReferenceDate(AbsoluteDate.J2000_EPOCH)
Retrieve measurements from List Observvation
angle_1_ra, angle_2_dec, epoch, epoch_dt= [], [], [], []
listSegments_tot = ArrayList()
listSegments = tdm.getSegments()
listSegments_tot.addAll(listSegments)
listObservationsBlock = [ObservationsBlock.cast_(Segment.cast_(segment).getData()) for segment in listSegments_tot]
for obsBlock in listObservationsBlock:
angle_1_ra.append(obsBlock.getObservations().get(0).getMeasurement())
angle_2_dec.append(obsBlock.getObservations().get(1).getMeasurement())
epoch_dt.append(absolutedate_to_datetime(obsBlock.getObservations().get(0).getEpoch()))
epoch.append(obsBlock.getObservations().get(0).getEpoch())
SGP4 TLE Propagator
nadirPointing = NadirPointing(eci, wgs84Ellipsoid)
sgp4Propagator = SGP4(tleReference, nadirPointing, 2000.)
tleInitialState = sgp4Propagator.getInitialState() # tle initial state
tleEpoch = tleInitialState.getDate() # tle release epoch
tleOrbit_TEME = tleInitialState.getOrbit() # orbit from tle in teme ref
tlePV_ECI = tleOrbit_TEME.getPVCoordinates(epoch[0],eci) # state in eci ref at first measurement time
tleOrbit_ECI = CartesianOrbit(tlePV_ECI, eci, wgs84Ellipsoid.getGM()) # cartesian orbit from eci state
tleOrbit_keplerian_conv = OrbitType.KEPLERIAN.convertType(tleOrbit_ECI) # conversion in keplerian orbit
tleOrbit_keplerian = KeplerianOrbit(tleOrbit_keplerian_conv.getPVCoordinates(), inertialFrame, epoch[0], Constants.EIGEN5C_EARTH_MU)
tle_at_epoch = TLE(tleReference.getSatelliteNumber(), tleReference.getClassification(), tleReference.getLaunchYear(),
tleReference.getLaunchNumber(), tleReference.getLaunchPiece(), tleReference.getEphemerisType(), tleReference.getElementNumber(),
tleOrbit_keplerian.date, tleOrbit_keplerian.getKeplerianMeanMotion(), tleReference.getMeanMotionFirstDerivative(), tleReference.getMeanMotionSecondDerivative(),
tleOrbit_keplerian.e, tleOrbit_keplerian.i, tleOrbit_keplerian.perigeeArgument, tleOrbit_keplerian.rightAscensionOfAscendingNode, tleOrbit_keplerian.meanAnomaly,
tleReference.getRevolutionNumberAtEpoch(), tleReference.getBStar(), TimeScalesFactory.getUTC())
Read ephemeris and interpolate to measurements time
pod1 = '/home/tanozar/Research/Orbit_Determinartion/Effemeridi/ephem_38091.SP3'#/2023_02_14/WUM0MGXFIN_20230450000_01D_05M_ORB.SP3'
eph1 = ephem_read(pod1,'PC05',epoch_dt,station)
ra_ephem = eph1.loc[:,'Ra'].values
dec_ephem = eph1.loc[:,'Dec'].values
INITIAL ORBIT DETERMINATION (CIRCULAR HYPOTESIS)
raDec1 = AngularRaDec(station, inertialFrame, epoch[0], [radians(angle_1_ra[0]),radians(angle_2_dec[0])], JArray_double([1e-5,1e-5]), JArray_double([1.,1.]), ObservableSatellite(0))
raDec2 = AngularRaDec(station, inertialFrame, epoch[1], [radians(angle_1_ra[1]),radians(angle_2_dec[1])], JArray_double([1e-5,1e-5]), JArray_double([1.,1.]), ObservableSatellite(0))
iod_laplace_cartesian_circ_orbit = IOD_2points(raDec1,raDec2,station,gcrf,wgs84Ellipsoid,inertialFrame)
# ) Keplerian orbit (Laplace circular)
iod_laplace_keplerian_circ_orbit = KeplerianOrbit(iod_laplace_cartesian_circ_orbit.getPVCoordinates(),inertialFrame,raDec1.getDate(),Constants.EIGEN5C_EARTH_MU)
# ) Laplace circular TLE
iod_laplace_circ_TLE = TLE(tleReference.getSatelliteNumber(), tleReference.getClassification(), tleReference.getLaunchYear(),
tleReference.getLaunchNumber(), tleReference.getLaunchPiece(), tleReference.getEphemerisType(), tleReference.getElementNumber(),
iod_laplace_keplerian_circ_orbit.date, iod_laplace_keplerian_circ_orbit.getKeplerianMeanMotion(), tleReference.getMeanMotionFirstDerivative(), tleReference.getMeanMotionSecondDerivative(),
iod_laplace_keplerian_circ_orbit.e, iod_laplace_keplerian_circ_orbit.i, iod_laplace_keplerian_circ_orbit.perigeeArgument, iod_laplace_keplerian_circ_orbit.rightAscensionOfAscendingNode, iod_laplace_keplerian_circ_orbit.meanAnomaly,
tleReference.getRevolutionNumberAtEpoch(), tleReference.getBStar(), TimeScalesFactory.getUTC())
ORBIT DETERMINATION
# ) Estimator parameters
estimator_position_scale = 100.0 # m
estimator_convergence_thres = 1e-3
estimator_max_iterations = 50
estimator_max_evaluations = 35
# ) Orbit propagator parameters
prop_min_step = 0.001 # s
prop_max_step = 30.0 # sdd
prop_position_error = 10.0 # m
integratorBuilder = DormandPrince853IntegratorBuilder(prop_min_step, prop_max_step, prop_position_error)
#integratorBuilder = DormandPrince54IntegratorBuilder(prop_min_step, prop_max_step, prop_position_error)
propagatorBuilder_tmp = NumericalPropagatorBuilder(iod_laplace_cartesian_circ_orbit, integratorBuilder, PositionAngle.MEAN, estimator_position_scale)
propagatorBuilder_tmp.setAttitudeProvider(nadirPointing)
propagatorBuilder_tmp.setMass(2000.0) #mass of satellite
# ) Add Perturbations
propagatorBuilder = addPerturbations(propagatorBuilder_tmp, ecef, moon, sun, wgs84Ellipsoid)
# ) Set estimator properties
matrix_decomposer = QRDecomposer(1e-11)
#optimizer = GaussNewtonOptimizer(matrix_decomposer, False)
optimizer = LevenbergMarquardtOptimizer()
estimator = BatchLSEstimator(optimizer, propagatorBuilder)
estimator.setParametersConvergenceThreshold(estimator_convergence_thres)
estimator.setMaxIterations(estimator_max_iterations)
estimator.setMaxEvaluations(estimator_max_evaluations)
# ) OD parameters
weight = JArray_double([1., 1.])
sigma = JArray_double([4e-5, 4e-5])
# ) Add measurements to batch estimator
for i in range(len(angle_1_ra)):
currentRaDec = [radians(angle_1_ra[i]), radians(angle_2_dec[i])]
measurement = AngularRaDec(station,inertialFrame, epoch[i], currentRaDec, sigma, weight, ObservableSatellite(0))
estimator.addMeasurement(measurement)
# ) Estimator
estimator.setObserver(my_observer())
estimatedPropagatorArray = estimator.estimate()
estimatedPropagator = estimatedPropagatorArray[0]
estimatedInitialState = estimatedPropagator.getInitialState()
# ) Retrieve keplerian orbit
batch_orb = OrbitType.KEPLERIAN.convertType(estimatedInitialState.getOrbit())
batch_keplerian_orbit = KeplerianOrbit(batch_orb.getPVCoordinates(), inertialFrame, epoch[0], Constants.EIGEN5C_EARTH_MU)
batch_tle = TLE(tleReference.getSatelliteNumber(), tleReference.getClassification(), tleReference.getLaunchYear(),
tleReference.getLaunchNumber(), tleReference.getLaunchPiece(), tleReference.getEphemerisType(), tleReference.getElementNumber(),
batch_keplerian_orbit.date, batch_keplerian_orbit.getKeplerianMeanMotion(), tleReference.getMeanMotionFirstDerivative(), tleReference.getMeanMotionSecondDerivative(),
batch_keplerian_orbit.e, batch_keplerian_orbit.i, batch_keplerian_orbit.perigeeArgument, batch_keplerian_orbit.rightAscensionOfAscendingNode, batch_keplerian_orbit.meanAnomaly,
tleReference.getRevolutionNumberAtEpoch(), tleReference.getBStar(), TimeScalesFactory.getUTC())
Propagate and compare real measurements and TLE wrt the precise ephemeris
ref_tle = TLE(tleLine1, tleLine2)
new_tle = TLE(batch_tle.getLine1(), batch_tle.getLine2())
sgp4Propagator1 = SGP4(ref_tle, nadirPointing, 2000.)
sgp4Propagator2 = SGP4(new_tle, nadirPointing, 2000.)
# start_time_dt = absolutedate_to_datetime(startDateTdm)
# end_time_dt = absolutedate_to_datetime(startDateTdm.shiftedBy(3600.0 *24*2))
# time_n = np.arange(start_time_dt, end_time_dt, dt.timedelta(seconds=60)).astype(dt.datetime)
norm1,norm2, norm3 = [], [], []
x1, y1, z1, x2, y2, z2 = [], [], [], [], [], []
ra1, ra2, ra3, dec1, dec2, dec3 = [], [], [], [], [], []
for i in range(len(epoch_dt)):
#for i in range(int(len(time_n))):
time_n_tmp = epoch_dt[i]
#sec = float(time_n_tmp.second) + float(time_n_tmp.microsecond)/1e6
startDate = datetime_to_absolutedate(time_n_tmp)#AbsoluteDate(int(time_n_tmp.year), int(time_n_tmp.month), int(time_n_tmp.day), int(time_n_tmp.hour), int(time_n_tmp.minute), sec, TimeScalesFactory.getUTC())
obs_state = station_frame.getPVCoordinates(startDate, inertialFrame)
obs_tmp = obs_state.getPosition()
# Ref TLE state
sc_state1 = sgp4Propagator1.getPVCoordinates(startDate, inertialFrame)
pos_tmp1 = sc_state1.getPosition()
x1.append(pos_tmp1.x)
y1.append(pos_tmp1.y)
z1.append(pos_tmp1.z)
norm1.append(np.linalg.norm(pos_tmp1.toArray()))
ra_tmp1, dec_tmp1 = rECI2TopoRaDec(pos_tmp1, obs_tmp)
ra1.append(ra_tmp1)
dec1.append(dec_tmp1)
# OD state
sc_state2 = sgp4Propagator2.getPVCoordinates(startDate, inertialFrame)
pos_tmp2 = sc_state2.getPosition()
norm2.append(np.linalg.norm(pos_tmp2.toArray()))
x2.append(pos_tmp2.x)
y2.append(pos_tmp2.y)
z2.append(pos_tmp2.z)
ra_tmp2, dec_tmp2 = rECI2TopoRaDec(pos_tmp2, obs_tmp)
ra2.append(ra_tmp2)
dec2.append(dec_tmp2)
# sc_state3 = sgp4Propagator3.getPVCoordinates(startDate, inertialFrame)
# pos_tmp3 = sc_state3.getPosition()
# norm3.append(linalg.norm(pos_tmp3.toArray()))
# ra_tmp3, dec_tmp3 = transform.rECI2TopoRaDec(pos_tmp3, obs_tmp)
# ra3.append(ra_tmp3)
# dec3.append(dec_tmp3)
####################################
############### PLOT ###############
####################################
err_ra_tle_ephem = [np.abs(i-j) for i,j in zip(ra1, ra_ephem)]
err_dec__tle_ephem = [np.abs(i-j) for i,j in zip(dec1, dec_ephem)]
err_ra_OD_ephem = [np.abs(i-j) for i,j in zip(ra_ephem, ra2)]
err_dec_OD_ephem = [np.abs(i-j) for i,j in zip(dec_ephem, dec2)]
plt.figure()
plt.subplot(211)
plt.plot(epoch_dt,err_ra_tle_ephem,'r*',label='RA error TLE/Ephem')
plt.plot(epoch_dt,err_ra_OD_ephem,'g*',label='RA error OD/Ephem')
plt.legend()
plt.grid('minor')
plt.xlabel('DATE [UTC]')
plt.ylabel('Angular error [deg]')
plt.subplot(212)
plt.plot(epoch_dt,err_dec__tle_ephem,'r*',label='Dec error TLE/Ephem')
plt.plot(epoch_dt,err_dec_OD_ephem,'g*',label='Dec error OD/Ephem')
plt.legend()
plt.grid('minor')
plt.xlabel('DATE [UTC]')
plt.ylabel('Angular error [deg]')
plt.show()`
These are the TLE that I retrieve from IOD, OD compared wrt the reference TLE
TLE reference
1 38091U 12008A 23045.79524248 .00000066 00000-0 00000-0 0 9999
2 38091 2.2394 72.4721 0012948 306.8422 109.8802 1.00266834 40254
TLE IOD
1 38091U 12008A 23045.79524248 .00000066 00000-0 00000-0 0 9999
2 38091 1.9532 185.4967 0146323 104.0013 202.2751 1.03092309 40258
TLE OD
1 38091U 12008A 23045.79524248 .00000066 00000-0 00000-0 0 9999
2 38091 1.4439 253.1767 0013068 139.2320 98.6334 1.00267526 40256
The figure below shows the RA/DEC error comparison between TLE, OD and precise ephemeris
It’s clear that there is something wrong but I’m not sure where the error, or better the errorS are. I’m quite sure that the real measurements are accurate.
Do you have any suggestions?
Cheers
Gaetano