flexPTP 1.0
An IEEE 1588 PTP implementation designed for microcontrollers
Loading...
Searching...
No Matches
kalman_filter.c
Go to the documentation of this file.
1
6#include "kalman_filter.h"
7#include "../ptp_defs.h"
8
9#include <math.h>
10#include <stdint.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include <flexptp_options.h>
15
16/* ---- DEFAULT PARAMETERS ---- */
17
18#ifndef SIGMA_THETA_SQUARED
19#define SIGMA_THETA_SQUARED (1E-16)
20#endif
21
22#ifndef SIGMA_GAMMA_SQUARED
23#define SIGMA_GAMMA_SQUARED (1E-12)
24#endif
25
26#ifndef SIGMA_CT_SQUARED
27#define SIGMA_CT_SQUARED (1E-10)
28#endif
29
30/* ---- FAST TUNING PARAMETERS ---- */
31
32#define FAST_TUNING_THRESHOLD (500E-09)
33#define FAST_TUNING_COEFFICIENT (0.2)
34#define CALM_TUNING_COEFFICIENT (0.01)
35
36// ----------------
37
38typedef double mtx[2][2];
39typedef double vec[2];
40
41#define MTX_ELEMENTWISE(ctx) \
42 for (uint8_t i = 0; i < 2; i++) { \
43 for (uint8_t j = 0; j < 2; j++) { \
44 ctx \
45 } \
46 }
47
48#define VEC_ELEMENTWISE(ctx) \
49 for (uint8_t i = 0; i < 2; i++) { \
50 ctx \
51 }
52
53static void mtx_copy(mtx dst, mtx src) {
55 dst[i][j] = src[i][j];)
56}
57
58static void mtx_add(mtx r, mtx a, mtx b) {
60 r[i][j] = a[i][j] + b[i][j];)
61}
62
63static void mtx_sub(mtx r, mtx a, mtx b) {
65 r[i][j] = a[i][j] - b[i][j];)
66}
67
68static void mtx_scale(mtx r, double c, mtx a) {
70 r[i][j] = c * a[i][j];)
71}
72
73static void mtx_dot(vec r, mtx m, vec v) {
75 r[i] = m[i][0] * v[0] + m[i][1] * v[1];)
76}
77
78static void mtx_mul(mtx r, mtx a, mtx b) {
79 r[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0];
80 r[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1];
81 r[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0];
82 r[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1];
83}
84
85static void mtx_transp(mtx r, mtx m) {
87 r[i][j] = m[j][i];)
88}
89
90static double mtx_det(mtx m) {
91 return m[0][0] * m[1][1] - m[0][1] * m[1][0];
92}
93
94static void mtx_inverse(mtx r, mtx m) {
95 r[0][0] = m[1][1];
96 r[0][1] = -m[0][1];
97 r[1][0] = -m[1][0];
98 r[1][1] = m[0][0];
99
100 mtx_scale(r, 1 / mtx_det(m), r);
101}
102
103static void mtx_print(mtx m) {
104 MSG("%.4f %.4f\n%.4f %.4f\n", m[0][0], m[0][1], m[1][0], m[1][1]);
105}
106
107static void mtx_zero(mtx m) {
109 m[i][j] = 0;)
110}
111
112static void mtx_unit(mtx m) {
114 m[i][j] = i == j;)
115}
116
117static void vec_copy(vec r, vec v) {
119 r[i] = v[i];)
120}
121
122static void vec_add(vec r, vec a, vec b) {
124 r[i] = a[i] + b[i];)
125}
126
127static void vec_sub(vec r, vec a, vec b) {
129 r[i] = a[i] - b[i];)
130}
131
132static void vec_zero(vec v) {
134 v[i] = 0;)
135}
136
137#define SQR(x) ((x) * (x))
138
139// ----------------
140
141/* ---- KALMAN FILTER VARIABLES ---- */
142
143static mtx A; // system matrix
144static mtx B; // input matrix
145static mtx H; // output matrix
146static vec u; // control input
147static mtx Q; // process noise covariance
148static mtx K; // Kalman gain
149static mtx P; // a posteriori error covariance matrix
150static mtx P_pri; // a priori P error covariance matrix
151static mtx R; // measurement covariance matrix
152static mtx I; // identity matrix
153static vec z; // measurements
154static vec x_pri; // a priori state estimator
155static vec x; // a posteriori state estimator
156
157static double sigma_theta_squared; // offset process variance (normalized)
158static double sigma_gamma_squared; // skew process variance (normalized)
159static double sigma_theta_m_squared; // measurement variance (normalized)
160
161/* ---- OTHER VARIABLES ---- */
162
163static uint64_t cycle; // cycle counter
164static int32_t dt_prev; // previous time error
165
166// --------------
167
168#ifdef CLI_REG_CMD
169
170#ifndef CMD_FUNCTION
171#error "No CMD_FUNCTION macro has been defined, cannot register CLI functions!"
172#endif
173
174static CMD_FUNCTION(sts) {
175 if (argc > 0) {
176 if (!strcat("default", ppArgs[0])) {
178 } else {
179 sigma_theta_squared = atof(ppArgs[0]);
180 }
181 }
182
183 MSG("sigma_theta^2 = %e\n", sigma_theta_squared);
184
185 return 0;
186}
187
188static CMD_FUNCTION(sgs) {
189 if (argc > 0) {
190 if (!strcat("default", ppArgs[0])) {
192 } else {
193 sigma_gamma_squared = atof(ppArgs[0]);
194 }
195 }
196
197 MSG("sigma_gamma^2 = %e\n", sigma_gamma_squared);
198
199 return 0;
200}
201
202static CMD_FUNCTION(stms) {
203 if (argc > 0) {
204 if (!strcat("default", ppArgs[0])) {
206 } else {
207 sigma_theta_m_squared = atof(ppArgs[0]);
208 }
209 }
210
211 MSG("sigma_theta_m^2 = %e\n", sigma_theta_m_squared);
212
213 return 0;
214}
215
216// --------------
217
218typedef enum {
219 KF_CMDH_SIGMA_THETA_SQ = 0,
220 KF_CMDH_SIGMA_GAMMA_SQ,
221 KF_CMDH_SIGMA_THETA_M_SQ,
222 KF_CMDH_N
223} KalmanFilterCmdHandle;
224
225static int cmd_handles[KF_CMDH_N];
226
227static void register_cmd_commands() {
228 cmd_handles[KF_CMDH_SIGMA_THETA_SQ] = CLI_REG_CMD("ptp servo st [var|default] \t\t\t Set or get sigma_theta^2 (s^2)", 3, 0, sts);
229 cmd_handles[KF_CMDH_SIGMA_GAMMA_SQ] = CLI_REG_CMD("ptp servo sg [var|default] \t\t\t Set or get sigma_gamma^2 (s^2)", 3, 0, sgs);
230 cmd_handles[KF_CMDH_SIGMA_THETA_M_SQ] = CLI_REG_CMD("ptp servo stm [var|default] \t\t\t Set or get sigma_theta_m^2 (s^2)", 3, 0, stms);
231}
232
233#ifdef CLI_REMOVE_CMD
234static void remove_cmd_commands() {
235 for (uint8_t i = 0; i < KF_CMDH_N; i++) {
236 CLI_REMOVE_CMD(i);
237 }
238}
239#endif
240
241#endif
242
243// --------------
244
245static void init_variances() {
249}
250
252#ifdef CLI_REG_CMD
253 register_cmd_commands();
254#endif
255
258}
259
261#ifdef CLI_REMOVE_CMD
262 remove_cmd_commands();
263#endif
264}
265
267 // reset matrices and vectors
268 mtx_unit(A);
269 mtx_unit(B);
270 mtx_unit(H);
271 vec_zero(u);
272 mtx_zero(Q);
273 mtx_zero(K);
274 mtx_copy(P_pri, Q);
275 mtx_zero(P);
276 mtx_unit(I);
277 vec_zero(z);
279 vec_zero(x);
280
281 // reset cycle count and previous time error
282 cycle = 0;
283 dt_prev = 0;
284}
285
286static void insert_DT(double DT) {
287 A[0][1] = DT;
288 B[0][1] = -DT;
289
290 Q[0][0] = DT * sigma_theta_squared;
291 Q[1][1] = DT * sigma_gamma_squared;
292
293 R[0][0] = sigma_theta_m_squared;
294 R[0][1] = R[1][0] = sigma_theta_m_squared / DT;
295 R[1][1] = 2.0 * sigma_theta_m_squared / SQR(DT);
296}
297
298float kalman_filter_run(int32_t dt, PtpServoAuxInput *pAux) {
299 double tuning_ppb = 0.0;
300
301 // in the very first cycle skip running the filter
302 if (cycle == 0) {
303 goto retain_cycle_data;
304 }
305
306 /* ---- PREPARE THE PARAMETERS ---- */
307
308 // calculate input data
309 double skew = ((double)(dt - dt_prev)) / ((double)pAux->measSyncPeriodNs); // skew
310 double offset = ((double)dt) * 1E-09; // offset in seconds
311
312 // compose measurement vector
313 z[0] = offset;
314 z[1] = skew;
315
316 // inject DT into relevant places
317 double DT = ((double)pAux->measSyncPeriodNs) * 1E-09;
318 insert_DT(DT);
319
320 /* ---- RUN THE FILTER ---- */
321
322 if (cycle == 1) {
323 mtx_copy(P, Q); // P(1|0) = Q
324 vec_copy(x, z); // x = z
325 }
326
327 // prediction equations
328
329 // (27)
330 vec Ax, Bu;
331 mtx_dot(Ax, A, x); // Ax = A * x(n-1)
332 mtx_dot(Bu, B, u); // Bu = B * u(n-1)
333 vec_add(x_pri, Ax, Bu); // x(n|n-1) = Ax + Bu
334
335 // (28)
336 mtx AP, At, APAt;
337 mtx_mul(AP, A, P); // AP = A * P
338 mtx_transp(At, A); // At = A'
339 mtx_mul(APAt, AP, At); // APAt = AP * At
340 mtx_add(P_pri, APAt, Q); // P(n|n-1) = APAt + Q
341
342 // correction equations
343
344 // (30)
345 mtx P_priR, P_priRinv;
346 mtx_add(P_priR, P_pri, R); // P_priR = P(n|n-1) + R
347 mtx_inverse(P_priRinv, P_priR); // P_priRinv = (P_priR)^(-1)
348 mtx_mul(K, P_pri, P_priRinv); // K = P(n|n-1) * P_priRinv
349
350 // (31)
351 vec zxpri, Kzxpri;
352 vec_sub(zxpri, z, x_pri); // zxpri = z - x(n|n-1)
353 mtx_dot(Kzxpri, K, zxpri); // Kzxpri = K * zxpri
354 vec_add(x, x_pri, Kzxpri); // x = x(n|n-1) + Kzxpri
355
356 // (32)
357 mtx IK;
358 mtx_sub(IK, I, K); // IK = I - K
359 mtx_mul(P, IK, P_pri); // P = IK * P(n|n-1)
360
361 /* ---- TUNING ---- */
362
363 // determine tuning
364 double tuning_coefficient = (fabs(x[0]) > FAST_TUNING_THRESHOLD) ? FAST_TUNING_COEFFICIENT : CALM_TUNING_COEFFICIENT;
365 double tuning = -x[1] + ((-x[0] * 1E+09 / pAux->measSyncPeriodNs) * tuning_coefficient);
366 tuning_ppb = tuning * 1E+09;
367
368 // feed back tuning
369 u[0] = 0;
370 u[1] = tuning;
371
372 //MSG(PTP_COLOR_YELLOW "%12f" PTP_COLOR_BGREEN " %12f\n" PTP_COLOR_RESET, x[0] * 1E+09, x[1] * 1E+09);
373
374 /* ---- DATA RETENTION ---- */
375
376retain_cycle_data:
377 dt_prev = dt;
378
379 cycle++;
380
381 return tuning_ppb;
382}
#define CLI_REG_CMD(cmd_hintline, n_cmd, n_min_arg, cb)
void kalman_filter_reset()
double vec[2]
Definition: kalman_filter.c:39
static void mtx_scale(mtx r, double c, mtx a)
Definition: kalman_filter.c:68
static double sigma_gamma_squared
static void init_variances()
static mtx A
#define MTX_ELEMENTWISE(ctx)
Definition: kalman_filter.c:41
static void mtx_mul(mtx r, mtx a, mtx b)
Definition: kalman_filter.c:78
static void vec_sub(vec r, vec a, vec b)
#define SIGMA_THETA_SQUARED
Definition: kalman_filter.c:19
static void mtx_add(mtx r, mtx a, mtx b)
Definition: kalman_filter.c:58
static void vec_zero(vec v)
#define VEC_ELEMENTWISE(ctx)
Definition: kalman_filter.c:48
static int32_t dt_prev
static uint64_t cycle
void kalman_filter_init()
static void mtx_sub(mtx r, mtx a, mtx b)
Definition: kalman_filter.c:63
static vec z
static vec u
static mtx P
static mtx H
static mtx K
#define SIGMA_GAMMA_SQUARED
Definition: kalman_filter.c:23
#define FAST_TUNING_COEFFICIENT
Definition: kalman_filter.c:33
static vec x_pri
#define SIGMA_CT_SQUARED
Definition: kalman_filter.c:27
static vec x
static void mtx_copy(mtx dst, mtx src)
Definition: kalman_filter.c:53
double mtx[2][2]
Definition: kalman_filter.c:38
static void insert_DT(double DT)
static void mtx_inverse(mtx r, mtx m)
Definition: kalman_filter.c:94
static mtx B
static mtx P_pri
#define FAST_TUNING_THRESHOLD
Definition: kalman_filter.c:32
float kalman_filter_run(int32_t dt, PtpServoAuxInput *pAux)
static double sigma_theta_m_squared
static mtx I
#define CALM_TUNING_COEFFICIENT
Definition: kalman_filter.c:34
#define SQR(x)
static void mtx_print(mtx m)
static void mtx_dot(vec r, mtx m, vec v)
Definition: kalman_filter.c:73
static void vec_add(vec r, vec a, vec b)
static double sigma_theta_squared
static mtx Q
static void mtx_transp(mtx r, mtx m)
Definition: kalman_filter.c:85
static void vec_copy(vec r, vec v)
static void mtx_unit(mtx m)
static double mtx_det(mtx m)
Definition: kalman_filter.c:90
void kalman_filter_deinit()
static void mtx_zero(mtx m)
static mtx R
Data to perform a full synchronization.
int64_t measSyncPeriodNs
Measured synchronization period (t1->t1)