Merge commit 'origin/master'
[fw/tmflights] / parse
1 #!/usr/bin/env nickle
2
3 /*
4  * Pressure Sensor Model, version 1.1
5  *
6  * written by Holly Grimes
7  *
8  * Uses the International Standard Atmosphere as described in
9  *   "A Quick Derivation relating altitude to air pressure" (version 1.03)
10  *    from the Portland State Aerospace Society, except that the atmosphere
11  *    is divided into layers with each layer having a different lapse rate.
12  *
13  * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007
14  *    at site <http://en.wikipedia.org/wiki/International_Standard_Atmosphere
15  *
16  * Height measurements use the local tangent plane.  The postive z-direction is up.
17  *
18  * All measurements are given in SI units (Kelvin, Pascal, meter, meters/second^2).
19  *   The lapse rate is given in Kelvin/meter, the gas constant for air is given
20  *   in Joules/(kilogram-Kelvin).
21  */
22
23 const real GRAVITATIONAL_ACCELERATION = -9.80665;
24 const real AIR_GAS_CONSTANT = 287.053;
25 const int NUMBER_OF_LAYERS = 7;
26 const real MAXIMUM_ALTITUDE = 84852;
27 const real MINIMUM_PRESSURE = 0.3734;
28 const real LAYER0_BASE_TEMPERATURE = 288.15;
29 const real LAYER0_BASE_PRESSURE = 101325;
30
31 /* lapse rate and base altitude for each layer in the atmosphere */
32 const real[NUMBER_OF_LAYERS] lapse_rate = {
33         -0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002
34 };
35 const int[NUMBER_OF_LAYERS] base_altitude = {
36         0, 11000, 20000, 32000, 47000, 51000, 71000
37 };
38
39
40 /* outputs atmospheric pressure associated with the given altitude. altitudes
41    are measured with respect to the mean sea level */
42 real altitude_to_pressure(real altitude) {
43  
44    real base_temperature = LAYER0_BASE_TEMPERATURE;
45    real base_pressure = LAYER0_BASE_PRESSURE;
46
47    real pressure;
48    real base; /* base for function to determine pressure */
49    real exponent; /* exponent for function to determine pressure */
50    int layer_number; /* identifies layer in the atmosphere */
51    int delta_z; /* difference between two altitudes */
52    
53    if (altitude > MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */
54       return 0;
55
56    /* calculate the base temperature and pressure for the atmospheric layer
57       associated with the inputted altitude */
58    for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) {
59       delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
60       if (lapse_rate[layer_number] == 0.0) {
61          exponent = GRAVITATIONAL_ACCELERATION * delta_z 
62               / AIR_GAS_CONSTANT / base_temperature;
63          base_pressure *= exp(exponent);
64       }
65       else {
66          base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
67          exponent = GRAVITATIONAL_ACCELERATION / 
68               (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
69          base_pressure *= pow(base, exponent);
70       }
71       base_temperature += delta_z * lapse_rate[layer_number];
72    }
73
74    /* calculate the pressure at the inputted altitude */
75    delta_z = altitude - base_altitude[layer_number];
76    if (lapse_rate[layer_number] == 0.0) {
77       exponent = GRAVITATIONAL_ACCELERATION * delta_z 
78            / AIR_GAS_CONSTANT / base_temperature;
79       pressure = base_pressure * exp(exponent);
80    }
81    else {
82       base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
83       exponent = GRAVITATIONAL_ACCELERATION /
84            (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
85       pressure = base_pressure * pow(base, exponent);
86    } 
87
88    return pressure;
89 }
90
91
92 /* outputs the altitude associated with the given pressure. the altitude
93    returned is measured with respect to the mean sea level */
94 real pressure_to_altitude(real pressure) {
95
96    real next_base_temperature = LAYER0_BASE_TEMPERATURE;
97    real next_base_pressure = LAYER0_BASE_PRESSURE;
98
99    real altitude;
100    real base_pressure;
101    real base_temperature;
102    real base; /* base for function to determine base pressure of next layer */
103    real exponent; /* exponent for function to determine base pressure
104                              of next layer */
105    real coefficient;
106    int layer_number; /* identifies layer in the atmosphere */
107    int delta_z; /* difference between two altitudes */
108
109    if (pressure < 0)  /* illegal pressure */
110       return -1;
111    if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */
112       return MAXIMUM_ALTITUDE;
113
114    /* calculate the base temperature and pressure for the atmospheric layer
115       associated with the inputted pressure. */
116    layer_number = -1;
117    do {
118       layer_number++;
119       base_pressure = next_base_pressure;
120       base_temperature = next_base_temperature;
121       delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
122       if (lapse_rate[layer_number] == 0.0) {
123          exponent = GRAVITATIONAL_ACCELERATION * delta_z 
124               / AIR_GAS_CONSTANT / base_temperature;
125          next_base_pressure *= exp(exponent);
126       }
127       else {
128          base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
129          exponent = GRAVITATIONAL_ACCELERATION / 
130               (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
131          next_base_pressure *= pow(base, exponent);
132       }
133       next_base_temperature += delta_z * lapse_rate[layer_number];
134    }
135    while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure);
136
137    /* calculate the altitude associated with the inputted pressure */
138    if (lapse_rate[layer_number] == 0.0) {
139       coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION) 
140                                                     * base_temperature;
141       altitude = base_altitude[layer_number]
142                     + coefficient * log(pressure / base_pressure);
143    }
144    else {
145       base = pressure / base_pressure;
146       exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number] 
147                                        / GRAVITATIONAL_ACCELERATION;
148       coefficient = base_temperature / lapse_rate[layer_number];
149       altitude = base_altitude[layer_number]
150                       + coefficient * (pow(base, exponent) - 1);
151    }
152
153    return altitude;
154 }
155
156 real feet_to_meters(real feet)
157 {
158     return feet * (12 * 2.54 / 100);
159 }
160
161 real meters_to_feet(real meters)
162 {
163     return meters / (12 * 2.54 / 100);
164 }
165
166 /*
167  * Values for our MP3H6115A pressure sensor
168  *
169  * From the data sheet:
170  *
171  * Pressure range: 15-115 kPa
172  * Voltage at 115kPa: 2.82
173  * Output scale: 27mV/kPa
174  *
175  * 
176  * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa
177  * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa
178  */
179
180 real counts_per_kPa = 27 * 2047 / 3300;
181 real counts_at_101_3kPa = 1674;
182
183 real count_to_kPa(real count)
184 {
185         return (count / 2047 + 0.095) / 0.009;
186 }
187 typedef struct {
188         int     type;
189         int     time;
190         int     a;
191         int     b;
192 } flight_record;
193
194 flight_record
195 read_record(file in) {
196         flight_record   r;
197         File::fscanf(in, "%c %x %x %x\n",
198                &r.type, &r.time, &r.a, &r.b);
199         return r;
200 }
201
202 #real g_count = 264.8;
203 real g_count = 262;
204 int g_base = 15735;
205
206 real
207 count_to_g(real count)
208 {
209         return (g_base + g_count - count) / g_count;
210 }
211
212 real base_alt = 0;
213 real base_sec = 0;
214 real last_alt;
215 real last_sec;
216 real last_alt_speed;
217 real accel_speed;
218 real accel_meters;
219
220 real[...]       barometer;
221 real[...]       accelerometer;
222
223 real sinc(real x) = x != 0 ? sin(x)/x : 1;
224
225 real gaussian(real x) = exp(-(x**2)/2) / sqrt(2 * pi);
226
227 load "/usr/share/nickle/examples/kaiser.5c"
228
229 real[...] convolve(real[...] d, real[...] e) {
230         real sample(n) = n < 0 ? d[0] : n >= dim(d) ? d[dim(d)-1] : d[n];
231         real w = (dim(e) - 1) / 2;
232         real c(int center) {
233                 real    v = 0;
234                 for (int o = -w; o <= w; o++)
235                         v += sample(center + o) * e[o + w];
236                 return v;
237         }
238         return (real[dim(d)]) { [n] = c(n) };
239 }
240
241 real sum(real[...] x) { real s = 0; for(int i = 0; i < dim(x); i++) s += x[i]; return s; }
242
243 real[...] kaiser_filter(real[...] d, int half_width) {
244 #       real[half_width * 2 + 1] fir = { [n] = sinc(2 * pi * n / (2 * half_width)) };
245         real M = half_width * 2 + 1;
246         real[M] fir = { [n] = kaiser(n, M, 8) };
247         real fir_sum = sum(fir);
248         for (int i = 0; i < dim(fir); i++) fir[i] /= fir_sum;
249         return convolve(d, fir);
250 }
251
252 int[...] int_filter(int[...] d, int shift) {
253         /* Emulate the exponential IIR filter used in the TeleMetrum flight
254         software */
255
256         int     v = d[0];
257         int     n;
258         int[dim(d)] ret;
259
260         for (n = 0; n < dim(d); n++) {
261                 v -= (v + (1 << (shift - 1))) >> shift;
262                 v += (d[n] + (1 << (shift - 1))) >> shift;
263                 ret[n] = v;
264         }
265         return ret;
266 }
267
268 real gravity = 9.80665;
269
270 int[...] pressure_value, accelerometer_value;
271 real[...] clock;
272
273 void readsamples_log(file in) {
274         setdim(pressure_value, 0);
275         setdim(accelerometer_value, 0);
276         while (!File::end(in)) {
277                 flight_record r = read_record(in);
278                 if (r.type == 'F') {
279                         g_base = r.a;
280                 }
281                 if (r.type == 'A') {
282                         clock[dim(clock)] = r.time / 100;
283                         pressure_value[dim(pressure_value)] = r.b;
284                         accelerometer_value[dim(accelerometer_value)] = r.a;
285                 }
286         }
287 }
288
289 typedef struct {
290         int     time;
291         int     accel;
292         int     pressure;
293         string  state;
294 } telem_record;
295
296 autoimport String;
297
298 telem_record read_telem(file in) {
299         string[*]       r = wordsplit(chomp(fgets(in)), " ");
300
301         if (dim(r) < 15)
302                 return read_telem(in);
303         return (telem_record) {
304                 .time = string_to_integer(r[10]),
305                 .accel = string_to_integer(r[12]),
306                 .pressure = string_to_integer(r[14]),
307                 .state = r[9]
308         };
309 }
310
311 void readsamples_telem(file in) {
312         setdim(clock, 0);
313         setdim(pressure_value, 0);
314         setdim(accelerometer_value, 0);
315
316         telem_record[...] save = {};
317         
318         setdim(save, 0);
319         while (!File::end(in)) {
320                 save[dim(save)] = read_telem(in);
321                 if (save[dim(save)-1].state == "boost")
322                         break;
323         }
324         int     start = dim(save) - 4;
325
326         int     accel_total = 0;
327         for (int i = 0; i < start; i++)
328                 accel_total += save[i].accel;
329         g_base = accel_total // start;
330
331         for (int i = start; i < dim(save); i++) {
332                 clock[dim(clock)] = save[i].time/100;
333                 pressure_value[dim(pressure_value)] = save[i].pressure;
334                 accelerometer_value[dim(accelerometer_value)] = save[i].accel;
335         }
336         
337         while (!File::end(in)) {
338                 telem_record t = read_telem(in);
339                 if (dim(clock) > 0 &&
340                     abs(t.time / 100 - clock[dim(clock)-1]) > 500)
341                         break;
342                 clock[dim(clock)] = t.time / 100;
343                 pressure_value[dim(pressure_value)] = t.pressure;
344                 accelerometer_value[dim(accelerometer_value)] = t.accel;
345         }
346 }
347
348 readsamples_log(stdin);
349
350 int[...] int_integrate(int[...] d, int base) {
351         int v = 0;
352         int[dim(d)] ret;
353
354         ret[0] = 0;
355         for (int i = 1; i < dim(d); i++)
356                 ret[i] = (v += (d[i-1] + d[i] + 1) // 2);
357         return ret;
358 }
359
360 int[...] int_differentiate(int[...] d) {
361         return (int[dim(d)]) { [n] = n == 0 ? 0 : d[n] - d[n-1] };
362 }
363
364 int average(int[...] d, int n) {
365         int     sum = 0;
366         for (int i = 0; i < n; i++)
367                 sum += d[n];
368         return sum // n;
369 }
370
371 int[...] rebase(int[...] d, int m, int a) = (int[dim(d)]) { [n] = d[n] * m + a };
372
373 int size = dim(accelerometer_value);
374
375 if (false) {
376         accelerometer_value = rebase(accelerometer_value, -1, g_base);
377         int accel_i0_base = average(accelerometer_value, 30);
378         int[size] pres_d0 = int_filter(pressure_value, 4);
379         int[size] accel_i0 = int_filter(accelerometer_value, 4);
380         int[size] pres_d1 = int_filter(int_differentiate(pres_d0), 4);
381         int[size] accel_i1 = int_integrate(accelerometer_value, accel_i0_base);
382         int[size] pres_d2 = int_filter(int_differentiate(pres_d1), 4);
383         int[size] accel_i2 = int_integrate(accel_i1, 0);
384
385         real count_to_altitude(int count) = pressure_to_altitude(count_to_kPa(count / 16) * 1000);
386
387         for (int i = 0; i < size; i++)
388                 printf("%g %g %g %g %g %g %g %g %g\n",
389                        clock[i] - clock[0],
390                        count_to_altitude(pres_d0[i]) - count_to_altitude(pres_d0[0]), accel_i2[i] / 10000 / g_count * gravity,
391                        pres_d1[i] * 100, accel_i1[i] / 100 / g_count * gravity,
392                        pres_d2[i] * 10000, accel_i0[i] / g_count * gravity,
393                        count_to_altitude(pressure_value[i]) -
394                        count_to_altitude(pressure_value[0]), accelerometer_value[i]
395                        / g_count * gravity);
396
397 } else {
398         real[size] accelerometer = { [n] = gravity * (count_to_g(accelerometer_value[n]) - 1.0) };
399         real[size] barometer = { [n] = pressure_to_altitude(count_to_kPa(pressure_value[n] / 16) * 1000) };
400         real[size] filtered_accelerometer = kaiser_filter(accelerometer, 8);
401         real[size] filtered_barometer = kaiser_filter(barometer, 32);
402
403         real[...] integrate(real[...] d) {
404                 real[dim(d)] ret;
405                 for (int i = 0; i < dim(ret); i++)
406                         ret[i] = i == 0 ? 0 : ret[i-1] + (d[i-1] + d[i]) / 2 * (clock[i] - clock[i-1]);
407                 return ret;
408         }
409
410         real[...] differentiate(real[...] d) {
411                 real[dim(d)] ret;
412                 for (int i = 1; i < dim(ret); i++)
413                         ret[i] = (d[i] - d[i-1]) / (clock[i] - clock[i-1]);
414                 ret[0] = ret[1];
415                 return ret;
416         }
417
418         real[size] accel_speed = integrate(filtered_accelerometer);
419         real[size] accel_pos = integrate(accel_speed);
420         real[size] baro_speed = differentiate(filtered_barometer);
421         real[size] baro_accel = differentiate(baro_speed);
422
423         printf("%7s %12s %12s %12s %12s %12s %12s %12s %12s\n",
424                "time",
425                "height(baro)",
426                "height(accel)",
427                "speed(baro)",
428                "speed(accel)",
429                "accel(baro)",
430                "accel(accel)",
431                "raw(baro)",
432                "raw(accel)");
433         for (int i = 0; i < size; i++)
434                 printf("%7.2f %12.6f %12.6f %12.6f %12.6f %12.6f %12.6f %12.6f %12.6f\n",
435                        clock[i] - clock[0],
436                        filtered_barometer[i] - filtered_barometer[0], accel_pos[i],
437                        baro_speed[i], accel_speed[i],
438                        baro_accel[i], filtered_accelerometer[i],
439                        barometer[i] - barometer[0], accelerometer[i]);
440 }