2626#include " unscented_kalman_filter.h"
2727namespace kf {
2828
29- // / @brief Class for fitting tracks using the Unscented Kalman Filter (UKF) algorithm.
30- // / @tparam DIM_X Dimension of the state vector.
31- // / @tparam DIM_Z Dimension of the measurement vector.
32- // / @tparam DIM_V Dimension of the process noise vector.
33- // / @tparam DIM_N Dimension of the measurement noise vector.
29+ /* * @brief Class for fitting tracks using the Unscented Kalman Filter (UKF) algorithm.
30+ *
31+ * Serves as a templated base class for UKF calculations. It hold no physics information, just
32+ * the machinery that underlies the UKF formalism. It is a modified version of the UKF provided
33+ * by OpenKF, that has been expanded to allow for more hooks into the method.
34+ *
35+ * Templated because I believe Eigen can do quite a bit of operation for small matrices like we have here if
36+ * the size is known at compile time. Worth checking that though.
37+ *
38+ * @tparam DIM_X Dimension of the state vector.
39+ * @tparam DIM_Z Dimension of the measurement vector.
40+ * @tparam DIM_V Dimension of the process noise vector.
41+ * @tparam DIM_N Dimension of the measurement noise vector.
42+ */
3443template <int32_t DIM_X = 6 , int32_t DIM_Z = 3 , int32_t DIM_V = 2 , int32_t DIM_N = 3 >
35- class TrackFitterUKFBase : public KalmanFilter <DIM_X, DIM_Z> {
36-
44+ class TrackFitterUKFBase {
3745public:
3846 // Augmented state vector is just the process noise and state vector. The measurement noise is not included as that
3947 // is independent of the propagation and measurement model and just adds linearly.
4048 static constexpr int32_t DIM_A{DIM_X + DIM_V}; // /< @brief Augmented state dimension
4149 static constexpr int32_t SIGMA_DIM_A{2 * DIM_A + 1 }; // /< @brief Sigma points dimension for augmented state
50+ Matrix<DIM_A, SIGMA_DIM_A> m_matSigmaXa{Matrix<DIM_A, SIGMA_DIM_A>::Zero ()}; // /< @brief Sigma points matrix
4251
4352protected:
44- using KalmanFilter<DIM_X, DIM_Z>::m_vecX; // from Base KalmanFilter class
45- using KalmanFilter<DIM_X, DIM_Z>::m_matP; // from Base KalmanFilter class
46-
47- float32_t m_weight0; // / @brief unscented transform weight 0 for mean
48- float32_t m_weighti; // / @brief unscented transform weight i for none mean samples
49- float32_t m_kappa{0 }; // /< @brief Kappa parameter for finding sigma points
50-
51- Vector<DIM_A> m_vecXa{Vector<DIM_A>::Zero ()}; // / @brief augmented state vector (incl. process
52- // / and measurement noise means)
53- Matrix<DIM_A, DIM_A> m_matPa{Matrix<DIM_A, DIM_A>::Zero ()}; // / @brief augmented state covariance (incl.
54- // / process and measurement noise covariances)
55-
56- // Add variables to track the covariances of the process and measurement noise.
57- Matrix<DIM_V, DIM_V> m_matQ; // @brief Process noise covariance matrix
58- Matrix<DIM_N, DIM_N> m_matR; // @brief Measurement noise covariance matrix
53+ Vector<DIM_X> m_vecX{Vector<DIM_X>::Zero ()}; // / @brief estimated state vector
54+ Matrix<DIM_X, DIM_X> m_matP{Matrix<DIM_X, DIM_X>::Zero ()}; // / @brief state covariance matrix
55+
56+ // / @brief Augmented state vector (incl. process and measurement noise means)
57+ Vector<DIM_A> m_vecXa{Vector<DIM_A>::Zero ()};
58+ // / @brief augmented state covariance (incl. process and measurement noise covariances)
59+ Matrix<DIM_A, DIM_A> m_matPa{Matrix<DIM_A, DIM_A>::Zero ()};
60+
61+ // Process and measurement noise covariance matrices
62+ // / Process noise covariance matrix (Q)
63+ Matrix<DIM_V, DIM_V> m_matQ{Matrix<DIM_V, DIM_V>::Zero ()};
64+ // / Measurement noise covariance matrix (R)
65+ Matrix<DIM_N, DIM_N> m_matR{Matrix<DIM_N, DIM_N>::Zero ()};
66+
67+ // / Unscented transform weight for the mean sigma point
68+ float32_t m_weight0;
69+ // / Unscented transform weight for the other sigma points
70+ float32_t m_weighti;
71+ // / Kappa parameter for sigma point calculation
72+ float32_t m_kappa{3 - DIM_A};
5973
6074public:
61- Matrix<DIM_A, SIGMA_DIM_A> m_matSigmaXa{Matrix<DIM_A, SIGMA_DIM_A>::Zero ()}; // /< @brief Sigma points matrix
62-
63- TrackFitterUKFBase ()
64- : KalmanFilter<DIM_X, DIM_Z>(), m_kappa(3 - DIM_A), m_matQ(Matrix<DIM_V, DIM_V>::Zero()),
65- m_matR (Matrix<DIM_N, DIM_N>::Zero())
66- {
67- // 1. calculate weights
68- updateWeights ();
69- }
70-
71- ~TrackFitterUKFBase () {}
75+ TrackFitterUKFBase () { updateWeights (); }
76+ ~TrackFitterUKFBase () = default ;
7277
7378 void setKappa (float32_t kappa)
7479 {
7580 m_kappa = kappa; // Set the kappa parameter for sigma point calculation
7681 updateWeights (); // Update the weights based on the new kappa value
7782 }
7883
79- // /
80- // / @brief adding process noise covariance Q to the augmented state covariance
81- // / matPa in the middle element of the diagonal.
82- // /
83- void setCovarianceQ (const Matrix<DIM_V, DIM_V> &matQ)
84- {
85- m_matQ = matQ; // Store the process noise covariance matrix
86- }
87-
88- // /
89- // / @brief set the measurement noise covariance R to be used in the update step
90- // /
91- void setCovarianceR (const Matrix<DIM_N, DIM_N> &matR)
92- {
93- m_matR = matR; // Store the measurement noise covariance matrix
94- }
95-
96- // / Add state vector (m_vecX) to the augment state vector (m_vecXa) and also
97- // / add covariance matrix (m_matP) to the augment covariance (m_matPa).
98- void updateAugWithState ()
99- {
100- // Copy state vector to augmented state vector
101- for (int32_t i{0 }; i < DIM_X; ++i) {
102- m_vecXa[i] = m_vecX[i];
103- }
104-
105- // Copy state covariance matrix to augmented covariance matrix
106- for (int32_t i{0 }; i < DIM_X; ++i) {
107- for (int32_t j{0 }; j < DIM_X; ++j) {
108- m_matPa (i, j) = m_matP (i, j);
109- }
110- }
111- }
112-
113- virtual std::array<float32_t , DIM_V> calculateProcessNoiseMean ()
114- {
115- // Calculate the expectation value of the process noise using the current value of the state vector m_vecX
116- std::array<float32_t , DIM_V> processNoiseMean{0 };
117-
118- // TODO: Set the mean energy loss based on the momentum and particle type. Probably best to track stopping power?
119- return processNoiseMean;
120- }
121-
122- virtual Matrix<DIM_V, DIM_V> calculateProcessNoiseCovariance ()
123- {
124- // Calculate the process noise covariance matrix
125- Matrix<DIM_V, DIM_V> matQ{Matrix<DIM_V, DIM_V>::Zero ()};
126- matQ = m_matQ; // Use the stored process noise covariance matrix
127-
128- // TODO: Set the process noise covariance for angular straggle and energy loss.
129- return matQ;
130- }
131-
132- void updateAugWithProcessNoise ()
133- {
134- auto processNoiseMean = calculateProcessNoiseMean ();
135- m_matQ = calculateProcessNoiseCovariance ();
136-
137- // Add the mean process noise to the augmented state vector
138- for (int32_t i{0 }; i < DIM_V; ++i) {
139- m_vecXa[DIM_X + i] = processNoiseMean[i];
140- }
84+ /* *
85+ * @brief Set process noise covariance Q to be used in the prediction step.
86+ */
87+ void setCovarianceQ (const Matrix<DIM_V, DIM_V> &matQ) { m_matQ = matQ; }
88+ /* *
89+ * @brief Set the measurement noise covariance R to be used in the update step.
90+ */
91+ void setCovarianceR (const Matrix<DIM_N, DIM_N> &matR) { m_matR = matR; }
92+ virtual Vector<DIM_X> &vecX () { return m_vecX; }
93+ virtual const Vector<DIM_X> &vecX () const { return m_vecX; }
14194
142- // Add process noise covariance to the augmented covariance matrix
143- const int32_t S_IDX{DIM_X};
144- const int32_t L_IDX{S_IDX + DIM_V};
95+ virtual Matrix<DIM_X, DIM_X> &matP () { return m_matP; }
96+ virtual const Matrix<DIM_X, DIM_X> &matP () const { return m_matP; }
14597
146- for (int32_t i{S_IDX}; i < L_IDX; ++i) {
147- for (int32_t j{S_IDX}; j < L_IDX; ++j) {
148- m_matPa (i, j) = m_matQ (i - S_IDX, j - S_IDX);
149- }
150- }
151- }
152-
153- // /
154- // / @brief update the augmented state vector and covariance matrix
155- // / This functions fully updates the augmented state vector (m_vecXa) and covariance matrix (m_matPa)
156- // / by setting both the state vector and process noise components.
157- // /
98+ /* *
99+ * @brief update the augmented state vector and covariance matrix
100+ *
101+ * This function fully updates the augmented state vector and covariance matrix using
102+ * the state and process noise.
103+ */
158104 void updateAugmentedStateAndCovariance ()
159105 {
160106 updateAugWithState ();
161107 updateAugWithProcessNoise ();
162108 }
163109
164- // /
165- // / @brief state prediction step of the unscented Kalman filter (UKF).
166- // / @param predictionModelFunc callback to the prediction/process model
167- // / function
168- // /
110+ /* *
111+ * @brief state prediction step of the unscented Kalman filter (UKF).
112+ * @param predictionModelFunc callback to the prediction/process model function.
113+ * @param vecZ actual measurement vector.`
114+ *
115+ * A template is used here for performace reasons since there is only really a single prediction
116+ * model used. Honestly it probably does not make a difference compared to an std::function, but
117+ * this was not changed from OpenKF.
118+ *
119+ * This modifies the state vector and state covariance.
120+ */
169121 template <typename PredictionModelCallback>
170122 void predictUKF (PredictionModelCallback predictionModelFunc, const Vector<DIM_Z> &vecZ)
171123 {
@@ -199,11 +151,11 @@ class TrackFitterUKFBase : public KalmanFilter<DIM_X, DIM_Z> {
199151 calculateWeightedMeanAndCovariance<DIM_X>(sigmaXx, m_vecX, m_matP);
200152 }
201153
202- // /
203- // / @brief measurement correction step of the unscented Kalman filter (UKF).
204- // / @param measurementModelFunc callback to the measurement model function
205- // / @param vecZ actual measurement vector.
206- // /
154+ /* *
155+ * @brief measurement correction step of the unscented Kalman filter (UKF).
156+ * @param measurementModelFunc callback to the measurement model function
157+ * @param vecZ actual measurement vector.
158+ * /
207159 template <typename MeasurementModelCallback>
208160 void correctUKF (MeasurementModelCallback measurementModelFunc, const Vector<DIM_Z> &vecZ)
209161 {
@@ -235,7 +187,6 @@ class TrackFitterUKFBase : public KalmanFilter<DIM_X, DIM_Z> {
235187 // Add in the measurement noise covariance matrix to the measurement covariance matrix.
236188 matPzz += m_matR; // Add measurement noise covariance
237189
238- // TODO: calculate cross correlation
239190 const Matrix<DIM_X, DIM_Z> matPxz{calculateCrossCorrelation (sigmaXx, m_vecX, sigmaZ, vecZhat)};
240191
241192 // kalman gain
@@ -246,9 +197,9 @@ class TrackFitterUKFBase : public KalmanFilter<DIM_X, DIM_Z> {
246197 }
247198
248199protected:
249- // /
250- // / @brief algorithm to calculate the weights used to draw the sigma points
251- // /
200+ /* *
201+ * @brief Set the weights used to calculate sigma points.
202+ * /
252203 void updateWeights ()
253204 {
254205 static_assert (DIM_A > 0 , " DIM_A is Zero which leads to numerical issue." );
@@ -258,17 +209,59 @@ class TrackFitterUKFBase : public KalmanFilter<DIM_X, DIM_Z> {
258209 m_weight0 = m_kappa / denoTerm;
259210 m_weighti = 0 .5F / denoTerm;
260211 }
212+ /* *
213+ * @brief Add state vector and state covariance matrix to the augmented state vector covariance matrix.
214+ */
215+ void updateAugWithState ()
216+ {
217+ // Copy state vector to augmented state vector
218+ for (int32_t i{0 }; i < DIM_X; ++i) {
219+ m_vecXa[i] = m_vecX[i];
220+ }
221+
222+ // Copy state covariance matrix to augmented covariance matrix
223+ for (int32_t i{0 }; i < DIM_X; ++i) {
224+ for (int32_t j{0 }; j < DIM_X; ++j) {
225+ m_matPa (i, j) = m_matP (i, j);
226+ }
227+ }
228+ }
229+
230+ virtual std::array<float32_t , DIM_V> calculateProcessNoiseMean () { return std::array<float32_t , DIM_V>{0 }; }
231+
232+ virtual Matrix<DIM_V, DIM_V> calculateProcessNoiseCovariance () { return m_matQ; }
233+
234+ void updateAugWithProcessNoise ()
235+ {
236+ auto processNoiseMean = calculateProcessNoiseMean ();
237+ m_matQ = calculateProcessNoiseCovariance ();
238+
239+ // Add the mean process noise to the augmented state vector
240+ for (int32_t i{0 }; i < DIM_V; ++i) {
241+ m_vecXa[DIM_X + i] = processNoiseMean[i];
242+ }
243+
244+ // Add process noise covariance to the augmented covariance matrix
245+ const int32_t S_IDX{DIM_X};
246+ const int32_t L_IDX{S_IDX + DIM_V};
247+
248+ for (int32_t i{S_IDX}; i < L_IDX; ++i) {
249+ for (int32_t j{S_IDX}; j < L_IDX; ++j) {
250+ m_matPa (i, j) = m_matQ (i - S_IDX, j - S_IDX);
251+ }
252+ }
253+ }
261254
262- // /
263- // / @brief algorithm to calculate the deterministic sigma points for
264- // / the unscented transformation
265- // /
266- // / @param vecX mean of the normally distributed state
267- // / @param matPxx covariance of the normally distributed state
268- // / @param STATE_DIM dimension of the vector used to calculate the sigma points
269- // / @param SIGMA_DIM number of sigma points required (default is 2 * STATE_DIM + 1)
270- // / @return matrix of sigma points where each column is a sigma point
271- // /
255+ /* *
256+ * @brief Algorithm to calculate the deterministic sigma points for
257+ * the unscented transformation.
258+ *
259+ * @param vecX Mean of the normally distributed state.
260+ * @param matPxx Covariance of the normally distributed state.
261+ * @param STATE_DIM Dimension of the vector used to calculate the sigma points.
262+ * @param SIGMA_DIM Number of sigma points required (default is 2 * STATE_DIM + 1).
263+ * @return Matrix of sigma points where each column is a sigma point.
264+ * /
272265 template <int32_t STATE_DIM, int32_t SIGMA_DIM = 2 * STATE_DIM + 1 >
273266 Matrix<STATE_DIM, SIGMA_DIM>
274267 calculateSigmaPoints (const Vector<STATE_DIM> &vecXa, const Matrix<STATE_DIM, STATE_DIM> &matPa)
@@ -321,14 +314,12 @@ class TrackFitterUKFBase : public KalmanFilter<DIM_X, DIM_Z> {
321314 return sigmaXa;
322315 }
323316
324- // /
325- // / @brief calculate the weighted mean and covariance given a set of sigma
326- // / points
327- // / @param[in] sigmaX matrix of (probably posterior) sigma points where each column contain single
328- // / sigma point.
329- // / @param[out] vecX output weighted mean of the sigma points
330- // / @param[out] matPxx output weighted covariance of the sigma points
331- // /
317+ /* *
318+ * @brief Calculate the weighted mean and covariance given a set of sigma points.
319+ * @param[in] sigmaX Matrix of (probably posterior) sigma points where each column contains a single sigma point.
320+ * @param[out] vecX Output weighted mean of the sigma points.
321+ * @param[out] matPxx Output weighted covariance of the sigma points.
322+ */
332323 template <int32_t STATE_DIM, int32_t SIGMA_DIM>
333324 void calculateWeightedMeanAndCovariance (const Matrix<STATE_DIM, SIGMA_DIM> &sigmaX, Vector<STATE_DIM> &vecX,
334325 Matrix<STATE_DIM, STATE_DIM> &matPxx)
@@ -356,17 +347,17 @@ class TrackFitterUKFBase : public KalmanFilter<DIM_X, DIM_Z> {
356347 }
357348 }
358349
359- // /
360- // / @brief calculate the cross-correlation given two sets sigma points X and Y
361- // / and their means x and y
362- // / @param sigmaX first matrix of sigma points where each column contain
363- // / single sigma point
364- // / @param vecX mean of the first set of sigma points
365- // / @param sigmaY second matrix of sigma points where each column contain
366- // / single sigma point
367- // / @param vecY mean of the second set of sigma points
368- // / @return matPxy, the cross-correlation matrix
369- // /
350+ /* *
351+ * @brief calculate the cross-correlation given two sets sigma points X and Y
352+ * and their means x and y
353+ * @param sigmaX first matrix of sigma points where each column contain
354+ * single sigma point
355+ * @param vecX mean of the first set of sigma points
356+ * @param sigmaY second matrix of sigma points where each column contain
357+ * single sigma point
358+ * @param vecY mean of the second set of sigma points
359+ * @return matPxy, the cross-correlation matrix
360+ * /
370361 template <int32_t SIGMA_DIM>
371362 Matrix<DIM_X, DIM_Z> calculateCrossCorrelation (const Matrix<DIM_X, SIGMA_DIM> &sigmaX, const Vector<DIM_X> &vecX,
372363 const Matrix<DIM_Z, SIGMA_DIM> &sigmaY, const Vector<DIM_Z> &vecY)
@@ -389,8 +380,11 @@ class TrackFitterUKFBase : public KalmanFilter<DIM_X, DIM_Z> {
389380 }
390381};
391382
392- // Define template dimension variables for clarity and reuse
393-
383+ /* *
384+ * @brief Class for fitting tracks using the Unscented Kalman Filter (UKF) algorithm.
385+ *
386+ * UKF specialized for fitting tracks. This class is where all of the physics is
387+ */
394388class TrackFitterUKF : public TrackFitterUKFBase <6 , 3 , 1 , 3 > {
395389protected:
396390 static constexpr int32_t TF_DIM_X = 6 ;
@@ -402,6 +396,7 @@ class TrackFitterUKF : public TrackFitterUKFBase<6, 3, 1, 3> {
402396 std::unique_ptr<AtTools::AtStepper> fStepper {nullptr };
403397 AtTools::AtPropagator::StepState fMeanStep ; // / Holds the step information for POCA propagation of mean state
404398 ROOT::Math::Plane3D fMeasurementPlane ; // /< Holds the measurement plane for the track fitter
399+
405400public:
406401 bool fEnableEnStraggling {true }; // /< @brief Flag to enable/disable energy straggling
407402 double fMaxStragglingFactor {1 . / 3 .}; // /< @brief Maximum straggling factor for energy loss
0 commit comments