Hi @bcazabonne, I am running into this same error when building an EKF using a Numerical Propagator.
My EKF class looks like this:
class ExtendedKalmanFilter(
private val propagatorBuilder: PropagatorBuilder,
private val station: GroundStation,
private val frame: Frame,
private val noiseModel: NoiseModel
) {
private lateinit var kalmanEstimator: KalmanEstimator
fun initializeKalmanEstimator() {
val choleskyDecomposer = CholeskyDecomposer(1E-12, 1E-12)
val initialCovariance = DiagonalMatrix(doubleArrayOf(10000.0, 10000.0, 10000.0, 1.0, 1.0, 1.0))
val initialProcessNoise = DiagonalMatrix(doubleArrayOf(1e-10, 1e-10, 1e-10, 1e-10, 1e-10, 1e-10))
val builder = KalmanEstimatorBuilder()
builder.addPropagationConfiguration(propagatorBuilder(), ConstantProcessNoise(initialCovariance, initialProcessNoise))
builder.decomposer(choleskyDecomposer)
kalmanEstimator = builder.build()
}
private fun propagatorBuilder() = propagatorBuilder
fun estimate(measurement: AngularRaDec) {
val estimatedPropagators = kalmanEstimator.estimationStep(measurement)
}
fun getEstimatedState(): RealVector {
return kalmanEstimator.physicalEstimatedState
}
fun getEstimatedCovariance(): RealMatrix {
return kalmanEstimator.physicalEstimatedCovarianceMatrix
}
}
This error occurs when I call kalmanEstimator = builder.build() in my initialization. How do I resolve this issue?
I have created the following tests and when I run testKalmanInitialization, I run into the error. I am unsure what other parameters need to be defined. I thought it may have something to do with the “drivers” or the normalized parameters but my understanding of both of these is lacking for sure.
class ExtendedKalmanFilterTest {
private lateinit var ekf: ExtendedKalmanFilter
private lateinit var propagatorBuilder: PropagatorBuilder
private lateinit var groundStation: GroundStation
private lateinit var frame: Frame
private lateinit var noiseModel: NoiseModel
companion object {
val initialOrbit: Orbit = CartesianOrbit(
PVCoordinates(
Vector3D(6871.0e3, 0.0, 0.0),
Vector3D(0.0, 7.8e3, 0.0)
),
FramesFactory.getEME2000(),
AbsoluteDate.J2000_EPOCH,
Constants.EGM96_EARTH_MU
)
}
@BeforeEach
fun setUp() {
val dataDirectory = File("src/main/resources/orekit-data-master.zip")
System.setProperty(DataProvidersManager.OREKIT_DATA_PATH, dataDirectory.absolutePath)
propagatorBuilder = NumericalPropagatorBuilder(initialOrbit)
frame = FramesFactory.getEME2000()
noiseModel = NoiseModel()
val node = Node(
id = UUID.fromString("07dab967-fefb-4671-a229-3ce22f277850"),
name = "OurSky Node0_6b",
location = Point(
-77.919167,
39.095472,
224.9
),
cameraId = UUID.fromString("dff9fc6e-97e2-4fdc-986b-ed719511a5f2"),
createdBy = UUID.fromString("0fab28b9-fa97-4836-b0d8-f205d364bb96"),
diagnosticsEnabled = true,
elevationMask = null,
minAltitude = 20,
mountId = UUID.fromString("2306d93b-1c35-4c74-a1ca-cb140fc0b264"),
observatoryId = null,
opticalTubeId = UUID.fromString("3b1243a8-3272-49df-996a-c276f74cf732"),
organizationId = UUID.fromString("140fc9e4-ea1d-4452-941f-8bda318aec0c"),
slewTiming = null,
state = NodeState.READY,
createdAt = OffsetDateTime.now()
)
val topoFrame = topoFrameForPoint(node.location, node.name)
groundStation = GroundStation(topoFrame)
ekf = ExtendedKalmanFilter(propagatorBuilder, groundStation, frame, noiseModel)
ekf.initializeKalmanEstimator()
}
@Test
fun testKalmanInitialization() {
assertDoesNotThrow { ekf.initializeKalmanEstimator() }
}
@Test
fun testEstimation() {
val initialPosition = initialOrbit.pvCoordinates.position
val ra = FastMath.atan2(initialPosition.y, initialPosition.x)
val dec = FastMath.atan2(initialPosition.z, FastMath.sqrt(initialPosition.x * initialPosition.x + initialPosition.y * initialPosition.y))
val error = 5.0 * FastMath.PI / (180.0 * 3600.0) // 5 arcseconds in radians
val raWithError = ra + error
val decWithError = dec + error
val date = AbsoluteDate.J2000_EPOCH.shiftedBy(1000.0)
val sensorData = SensorData("node-1", listOf(0.1, 0.2), listOf(0.05, 0.06), hasGPSTiming = true)
val measurementWithError = ekf.createMeasurement(date, raWithError, decWithError, sensorData, 5.0)
assertDoesNotThrow { ekf.estimate(measurementWithError) }
}
@Test
fun testGetEstimatedState() {
assertDoesNotThrow {
val estimatedState = ekf.getEstimatedState()
assertNotNull(estimatedState)
}
}
@Test
fun testGetEstimatedCovariance() {
assertDoesNotThrow {
val covarianceMatrix = ekf.getEstimatedCovariance()
assertNotNull(covarianceMatrix)
}
}
}
Another thought was that I may be building my propagator incorrectly?
class NumericalPropagatorBuilder(var initialOrbit: Orbit) : PropagatorBuilder {
private val frame: Frame = FramesFactory.getEME2000()
private val mu: Double = Constants.EGM96_EARTH_MU
private val integrator: DormandPrince853Integrator
private val numericalPropagator: NumericalPropagator
init {
val minStep = 0.001
val maxStep = 500.0
val positionError = 0.001
val initialOrbit = initialOrbit
val orbitType = OrbitType.CARTESIAN
val tolerance = NumericalPropagator.tolerances(positionError, initialOrbit, orbitType)
integrator = DormandPrince853Integrator(minStep, maxStep, tolerance[0], tolerance[1])
numericalPropagator = NumericalPropagator(integrator)
numericalPropagator.setMu(Constants.EGM96_EARTH_MU)
numericalPropagator.initialState = SpacecraftState(initialOrbit)
val gravityField = GravityFieldFactory.getNormalizedProvider(12, 12)
val gravityForceModel = HolmesFeatherstoneAttractionModel(
FramesFactory.getEME2000(),
gravityField
)
numericalPropagator.addForceModel(gravityForceModel)
}
override fun buildPropagator(normalizedParameters: DoubleArray): Propagator {
return numericalPropagator
}
override fun copy(): PropagatorBuilder {
return NumericalPropagatorBuilder(initialOrbit)
}
override fun getFrame(): Frame {
return frame
}
override fun getInitialOrbitDate(): AbsoluteDate {
return initialOrbit.date
}
override fun getMu(): Double {
return mu
}
override fun getOrbitalParametersDrivers(): ParameterDriversList {
return ParameterDriversList()
}
override fun getOrbitType(): OrbitType {
return OrbitType.CARTESIAN
}
override fun getPositionAngleType(): PositionAngleType {
return PositionAngleType.MEAN
}
override fun getPropagationParametersDrivers(): ParameterDriversList {
return ParameterDriversList()
}
override fun getSelectedNormalizedParameters(): DoubleArray {
return doubleArrayOf()
}
override fun resetOrbit(newOrbit: Orbit) {
this.initialOrbit = newOrbit
}
// dummy BLS
override fun buildLeastSquaresModel(
builders: Array<PropagatorBuilder>,
measurements: MutableList<ObservedMeasurement<*>>,
estimatedMeasurementsParameters: ParameterDriversList,
observer: ModelObserver
): AbstractBatchLSModel? {
return null
}
}
Apologies for the very long response 8 months after the fact. Thank you in advance! Also, I am using Kotlin so that should explain the funky syntax!