altos: Correct model error covariance matrix
[fw/altos] / src / kalman / kalman.5c
1 #!/usr/bin/env nickle
2
3 /*
4  * Copyright © 2011 Keith Packard <keithp@keithp.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
18  */
19
20 autoimport ParseArgs;
21
22 load "load_csv.5c"
23 import load_csv;
24
25 load "matrix.5c"
26 import matrix;
27
28 load "kalman_filter.5c"
29 import kalman;
30
31 /*
32  * AltOS keeps speed and accel scaled
33  * by 4 bits to provide additional precision
34  */
35 real    height_scale = 1.0;
36 real    accel_scale = 16.0;
37 real    speed_scale = 16.0;
38
39 /*
40  * State:
41  *
42  * x[0] = height
43  * x[1] = velocity
44  * x[2] = acceleration
45  */
46
47 /*
48  * Measurement
49  *
50  * z[0] = height
51  * z[1] = acceleration
52  */
53
54 real default_σ_m = 5;
55 real default_σ_h = 20;
56 real default_σ_a = 2;
57
58 real[3,3] model_error(t, Φ) = multiply_mat_val ((real[3,3]) {
59                 { t**5 / 20, t**4 / 8, t**3 / 6 },
60                 { t**4 /  8, t**3 / 3, t**2 / 2 },
61                 { t**3 /  6, t**2 / 2, t }
62         }, Φ);
63
64 parameters_t param_both(real t, real σ_m, real σ_h, real σ_a) {
65         if (σ_m == 0)
66                 σ_m = default_σ_m;
67         if (σ_h == 0)
68                 σ_h = default_σ_h;
69         if (σ_a == 0)
70                 σ_a = default_σ_a;
71
72         σ_m = imprecise(σ_m) * accel_scale;
73         σ_h = imprecise(σ_h) * height_scale;
74         σ_a = imprecise(σ_a) * accel_scale;
75
76         t = imprecise(t);
77
78         return (parameters_t) {
79 /*
80  * Equation computing state k from state k-1
81  *
82  * height = height- + velocity- * t + acceleration- * t² / 2
83  * velocity = velocity- + acceleration- * t
84  * acceleration = acceleration-
85  */
86                 .a = (real[3,3]) {
87                         { 1,
88                           t * height_scale / speed_scale , t**2/2 * height_scale / accel_scale },
89                         { 0, 1, t * speed_scale / accel_scale },
90                         { 0, 0, 1 }
91                 },
92 /*
93  * Model error covariance. The only inaccuracy in the
94  * model is the assumption that acceleration is constant
95  */
96                 .q = model_error (t, σ_m**2),
97 /*
98  * Measurement error covariance
99  * Our sensors are independent, so
100  * this matrix is zero off-diagonal
101  */
102                 .r = (real[2,2]) {
103                         { σ_h ** 2, 0 },
104                         { 0, σ_a ** 2 },
105                 },
106 /*
107  * Extract measurements from state,
108  * this just pulls out the height and acceleration
109  * values.
110  */
111                 .h = (real[2,3]) {
112                         { 1, 0, 0 },
113                         { 0, 0, 1 },
114                 },
115          };
116 }
117
118 parameters_t param_baro(real t, real σ_m, real σ_h) {
119         if (σ_m == 0)
120                 σ_m = default_σ_m;
121         if (σ_h == 0)
122                 σ_h = default_σ_h;
123
124         σ_m = imprecise(σ_m) * accel_scale;
125         σ_h = imprecise(σ_h) * height_scale;
126
127         t = imprecise(t);
128         return (parameters_t) {
129 /*
130  * Equation computing state k from state k-1
131  *
132  * height = height- + velocity- * t + acceleration- * t² / 2
133  * velocity = velocity- + acceleration- * t
134  * acceleration = acceleration-
135  */
136                 .a = (real[3,3]) {
137                         { 1, t * height_scale / speed_scale , t**2/2 * height_scale / accel_scale },
138                         { 0, 1, t * speed_scale / accel_scale },
139                         { 0, 0, 1 }
140                 },
141 /*
142  * Model error covariance. The only inaccuracy in the
143  * model is the assumption that acceleration is constant
144  */
145                 .q = model_error (t, σ_m**2),
146 /*
147  * Measurement error covariance
148  * Our sensors are independent, so
149  * this matrix is zero off-diagonal
150  */
151                 .r = (real[1,1]) {
152                         { σ_h ** 2 },
153                 },
154 /*
155  * Extract measurements from state,
156  * this just pulls out the height
157  * values.
158  */
159                 .h = (real[1,3]) {
160                         { 1, 0, 0 },
161                 },
162          };
163 }
164
165 parameters_t param_accel(real t, real σ_m, real σ_a) {
166         if (σ_m == 0)
167                 σ_m = default_σ_m;
168         if (σ_a == 0)
169                 σ_a = default_σ_a;
170
171         σ_m = imprecise(σ_m) * accel_scale;
172         σ_a = imprecise(σ_a) * accel_scale;
173
174         t = imprecise(t);
175         return (parameters_t) {
176 /*
177  * Equation computing state k from state k-1
178  *
179  * height = height- + velocity- * t + acceleration- * t² / 2
180  * velocity = velocity- + acceleration- * t
181  * acceleration = acceleration-
182  */
183                 .a = (real[3,3]) {
184                         { 1, t * height_scale / speed_scale , t**2/2 * height_scale / accel_scale },
185                         { 0, 1, t * speed_scale / accel_scale },
186                         { 0, 0, 1 }
187                 },
188 /*
189  * Model error covariance. The only inaccuracy in the
190  * model is the assumption that acceleration is constant
191  */
192                 .q = model_error (t, σ_m**2),
193 /*
194  * Measurement error covariance
195  * Our sensors are independent, so
196  * this matrix is zero off-diagonal
197  */
198                 .r = (real[1,1]) {
199                         { σ_a ** 2 },
200                 },
201 /*
202  * Extract measurements from state,
203  * this just pulls out the acceleration
204  * values.
205  */
206                 .h = (real[1,3]) {
207                         { 0, 0, 1 },
208                 },
209          };
210 }
211
212 parameters_t param_vel(real t) {
213         static real σ_m = .1;
214         static real σ_v = imprecise(10);
215
216         return (parameters_t) {
217 /*
218  * Equation computing state k from state k-1
219  *
220  * height = height- + velocity- * t + acceleration- * t² / 2
221  * velocity = velocity- + acceleration- * t
222  * acceleration = acceleration-
223  */
224                 .a = (real[3,3]) {
225                         { 1, imprecise(t), imprecise((t**2)/2) },
226                         { 0, 1, imprecise(t) },
227                         { 0, 0, 1 }
228                 },
229 /*
230  * Model error covariance. The only inaccuracy in the
231  * model is the assumption that acceleration is constant
232  */
233                 .q = model_error (t, σ_m**2),
234 /*
235  * Measurement error covariance
236  * Our sensors are independent, so
237  * this matrix is zero off-diagonal
238  */
239                 .r = (real[1,1]) {
240                         { σ_v ** 2 },
241                 },
242 /*
243  * Extract measurements from state,
244  * this just pulls out the velocity
245  * values.
246  */
247                 .h = (real[1,3]) {
248                         { 0, 1, 0 },
249                 },
250          };
251 }
252
253 real    max_baro_height = 18000;
254
255 bool    just_kalman = true;
256 real    accel_input_scale = 1;
257
258 void run_flight(string name, file f, bool summary) {
259         state_t current_both = {
260                 .x = (real[3]) { 0, 0, 0 },
261                 .p = (real[3,3]) { { 0 ... } ... },
262         };
263         state_t current_accel = current_both;
264         state_t current_baro = current_both;
265         real    t;
266         real    kalman_apogee_time = -1;
267         real    kalman_apogee = 0;
268         real    raw_apogee_time_first;
269         real    raw_apogee_time_last;
270         real    raw_apogee = 0;
271         real    default_descent_rate = 20;
272         real    speed = 0;
273         real    prev_acceleration = 0;
274         state_t apogee_state;
275         parameters_fast_t       fast_both;
276         parameters_fast_t       fast_baro;
277         parameters_fast_t       fast_accel;
278         real                    fast_delta_t = 0;
279         bool                    fast = true;
280
281         for (;;) {
282                 record_t        record = parse_record(f, accel_input_scale);
283                 if (record.done)
284                         break;
285                 if (is_uninit(&t))
286                         t = record.time;
287                 real delta_t = record.time - t;
288                 if (delta_t <= 0)
289                         continue;
290                 t = record.time;
291                 if (record.height > raw_apogee) {
292                         raw_apogee_time_first = record.time;
293                         raw_apogee = record.height;
294                 }
295                 if (record.height == raw_apogee)
296                         raw_apogee_time_last = record.time;
297
298                 real    acceleration = record.acceleration;
299                 real    height = record.height;
300
301                 speed = (speed + (acceleration + prev_acceleration / 2) * delta_t);
302                 prev_acceleration = acceleration;
303
304                 vec_t z_both = (real[2]) { record.height * height_scale,  record.acceleration * accel_scale };
305                 vec_t z_accel = (real[1]) { record.acceleration * accel_scale };
306                 vec_t z_baro = (real[1]) { record.height * height_scale };
307
308
309                 if (fast) {
310                         if (delta_t != fast_delta_t) {
311                                 fast_both = convert_to_fast(param_both(delta_t, 0, 0, 0));
312                                 fast_accel = convert_to_fast(param_accel(delta_t, 0, 0));
313                                 fast_baro = convert_to_fast(param_baro(delta_t, 0, 0));
314                                 fast_delta_t = delta_t;
315                         }
316
317                         current_both.x = predict_fast(current_both.x, fast_both);
318                         current_accel.x = predict_fast(current_accel.x, fast_accel);
319                         current_baro.x = predict_fast(current_baro.x, fast_baro);
320
321                         current_both.x = correct_fast(current_both.x, z_both, fast_both);
322                         current_accel.x = correct_fast(current_accel.x, z_accel, fast_accel);
323                         current_baro.x = correct_fast(current_baro.x, z_baro, fast_baro);
324                 } else {
325                         parameters_t p_both = param_both(delta_t, 0, 0, 0);
326                         parameters_t p_accel = param_accel(delta_t, 0, 0);
327                         parameters_t p_baro = param_baro(delta_t, 0, 0);
328
329                         state_t pred_both = predict(current_both, p_both);
330                         state_t pred_accel = predict(current_accel, p_accel);
331                         state_t pred_baro = predict(current_baro, p_baro);
332
333                         state_t next_both = correct(pred_both, z_both, p_both);
334                         state_t next_accel = correct(pred_accel, z_accel, p_accel);
335                         state_t next_baro = correct(pred_baro, z_baro, p_baro);
336                         current_both = next_both;
337                         current_accel = next_accel;
338                         current_baro = next_baro;
339                 }
340
341                 printf ("%16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f\n",
342                         record.time,
343                         record.height, speed, record.acceleration,
344                         current_both.x[0] / height_scale, current_both.x[1] / speed_scale, current_both.x[2] / accel_scale,
345                         current_accel.x[0] / height_scale, current_accel.x[1] / speed_scale, current_accel.x[2] / accel_scale,
346                         current_baro.x[0] / height_scale, current_baro.x[1] / speed_scale, current_baro.x[2] / accel_scale);
347                 if (kalman_apogee_time < 0) {
348                         if (current_both.x[1] < -1 && current_accel.x[1] < -1 && current_baro.x[1] < -1) {
349                                 kalman_apogee = current_both.x[0];
350                                 kalman_apogee_time = record.time;
351                                 break;
352                         }
353                 }
354         }
355         real raw_apogee_time = (raw_apogee_time_last + raw_apogee_time_first) / 2;
356         if (summary && !just_kalman) {
357                 printf("%s: kalman (%8.2f m %6.2f s) raw (%8.2f m %6.2f s) error %6.2f s\n",
358                        name,
359                        kalman_apogee, kalman_apogee_time,
360                        raw_apogee, raw_apogee_time,
361                        kalman_apogee_time - raw_apogee_time);
362         }
363 }
364
365 void main() {
366         bool    summary = false;
367         int     user_argind = 1;
368         real    time_step = 0.01;
369         string  compute = "none";
370         string  prefix = "AO_K";
371         real    σ_m = 1;
372         real    σ_h = 4;
373         real    σ_a = 1;
374
375         ParseArgs::argdesc argd = {
376                 .args = {
377                         { .var = { .arg_flag = &summary },
378                           .abbr = 's',
379                           .name = "summary",
380                           .desc = "Print a summary of the flight" },
381                         { .var = { .arg_real = &max_baro_height },
382                           .abbr = 'm',
383                           .name = "maxbaro",
384                           .expr_name = "height",
385                           .desc = "Set maximum usable barometer height" },
386                         { .var = { .arg_real = &accel_input_scale, },
387                           .abbr = 'a',
388                           .name = "accel",
389                           .expr_name = "<accel-scale>",
390                           .desc = "Set accelerometer scale factor" },
391                         { .var = { .arg_real = &time_step, },
392                           .abbr = 't',
393                           .name = "time",
394                           .expr_name = "<time-step>",
395                           .desc = "Set time step for convergence" },
396                         { .var = { .arg_string = &prefix },
397                           .abbr = 'p',
398                           .name = "prefix",
399                           .expr_name = "<prefix>",
400                           .desc = "Prefix for compute output" },
401                         { .var = { .arg_string = &compute },
402                           .abbr = 'c',
403                           .name = "compute",
404                           .expr_name = "{both,baro,accel}",
405                           .desc = "Compute Kalman factor through convergence" },
406                         { .var = { .arg_real = &σ_m },
407                           .abbr = 'M',
408                           .name = "model",
409                           .expr_name = "<model-accel-error>",
410                           .desc = "Model co-variance for acceleration" },
411                         { .var = { .arg_real = &σ_h },
412                           .abbr = 'H',
413                           .name = "height",
414                           .expr_name = "<measure-height-error>",
415                           .desc = "Measure co-variance for height" },
416                         { .var = { .arg_real = &σ_a },
417                           .abbr = 'A',
418                           .name = "accel",
419                           .expr_name = "<measure-accel-error>",
420                           .desc = "Measure co-variance for acceleration" },
421                 },
422
423                 .unknown = &user_argind,
424         };
425
426         ParseArgs::parseargs(&argd, &argv);
427
428         if (compute != "none") {
429                 parameters_t    param;
430
431                 printf ("/* Kalman matrix for %s\n", compute);
432                 printf (" *     step = %f\n", time_step);
433                 printf (" *     σ_m = %f\n", σ_m);
434                 switch (compute) {
435                 case "both":
436                         printf (" *     σ_h = %f\n", σ_h);
437                         printf (" *     σ_a = %f\n", σ_a);
438                         param = param_both(time_step, σ_m, σ_h, σ_a);
439                         break;
440                 case "accel":
441                         printf (" *     σ_a = %f\n", σ_a);
442                         param = param_accel(time_step, σ_m, σ_a);
443                         break;
444                 case "baro":
445                         printf (" *     σ_h = %f\n", σ_h);
446                         param = param_baro(time_step, σ_m, σ_h);
447                         break;
448                 }
449                 printf (" */\n\n");
450                 mat_t k = converge(param);
451                 int[] d = dims(k);
452                 int time_inc = floor(1/time_step + 0.5);
453                 for (int i = 0; i < d[0]; i++)
454                         for (int j = 0; j < d[1]; j++) {
455                                 string name;
456                                 if (d[1] == 1)
457                                         name = sprintf("%s_K%d_%d", prefix, i, time_inc);
458                                 else
459                                         name = sprintf("%s_K%d%d_%d", prefix, i, j, time_inc);
460                                 printf ("#define %s to_fix32(%12.10f)\n", name, k[i,j]);
461                         }
462                 printf ("\n");
463                 exit(0);
464         }
465         string[dim(argv) - user_argind] rest = { [i] = argv[i+user_argind] };
466
467         #       height_scale = accel_scale = speed_scale = 1;
468
469         if (dim(rest) == 0)
470                 run_flight("<stdin>", stdin, summary);
471         else {
472                 for (int i = 0; i < dim(rest); i++) {
473                         twixt(file f = File::open(rest[i], "r"); File::close(f)) {
474                                 run_flight(rest[i], f, summary);
475                         }
476                 }
477         }
478 }
479 main();
480 #kalman(stdin);
481 #dump(stdin);