altos: Replace C code attiny async output with inline asm
[fw/altos] / src / micropeak / micro-log-parse.5c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 exception non_hexchar(int c);
19 exception file_ended();
20 exception invalid_crc();
21
22 int
23 get_nonwhite(file f)
24 {
25         int     c;
26
27         for (;;) {
28                 if (File::end(f))
29                         raise file_ended();
30                 if (!Ctype::isspace((c = File::getc(f))))
31                         return c;
32         }
33 }
34
35 int
36 get_hexc(file f)
37 {
38         int     c = get_nonwhite(f);
39
40         if ('0' <= c && c <= '9')
41                 return c - '0';
42         if ('a' <= c && c <= 'f')
43                 return c - 'a' + 10;
44         if ('A' <= c && c <= 'F')
45                 return c - 'A' + 10;
46         raise non_hexchar(c);
47 }
48
49 int POLY = 0x8408;
50
51 int
52 log_crc(int crc, int byte)
53 {
54         int     i;
55
56         for (i = 0; i < 8; i++) {
57                 if (((crc & 0x0001) ^ (byte & 0x0001)) != 0)
58                         crc = (crc >> 1) ^ POLY;
59                 else
60                         crc = crc >> 1;
61                 byte >>= 1;
62         }
63         return crc & 0xffff;
64 }
65
66 int     file_crc;
67
68
69 int
70 get_hex(file f)
71 {
72         int     a = get_hexc(f);
73         int     b = get_hexc(f);
74
75         int h = (a << 4) + b;
76
77         file_crc = log_crc(file_crc, h);
78         return h;
79 }
80
81 bool
82 find_header(file f)
83 {
84         while (!File::end(f)) {
85                 if (get_nonwhite(f) == 'M' && get_nonwhite(f) == 'P')
86                         return true;
87         }
88         return false;
89 }
90
91 int
92 get_32(file f)
93 {
94         int     v = 0;
95         for (int i = 0; i < 4; i++) {
96                 v += get_hex(f) << (i * 8);
97         }
98         return v;
99 }
100
101 int
102 get_16(file f)
103 {
104         int     v = 0;
105         for (int i = 0; i < 2; i++) {
106                 v += get_hex(f) << (i * 8);
107         }
108         return v;
109 }
110
111 int
112 swap16(int i) {
113         return ((i << 8) & 0xff00) | ((i >> 8) & 0xff);
114 }
115 typedef struct {
116         int     ground_baro;
117         int     min_baro;
118         int[*]  samples;
119 } log_t;
120
121 log_t
122 get_log(file f) {
123         log_t   log;
124
125         if (!find_header(f))
126                 raise file_ended();
127         file_crc = 0xffff;
128         log.ground_baro = get_32(f);
129         log.min_baro = get_32(f);
130         int nsamples = get_16(f);
131         log.samples = (int[nsamples]) { [i] = get_16(f) };
132
133         int current_crc = swap16(~file_crc & 0xffff);
134         int crc = get_16(f);
135
136         if (crc != current_crc)
137                 raise invalid_crc();
138         return log;
139 }
140
141 /*
142  * Pressure Sensor Model, version 1.1
143  *
144  * written by Holly Grimes
145  *
146  * Uses the International Standard Atmosphere as described in
147  *   "A Quick Derivation relating altitude to air pressure" (version 1.03)
148  *    from the Portland State Aerospace Society, except that the atmosphere
149  *    is divided into layers with each layer having a different lapse rate.
150  *
151  * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007
152  *    at site <http://en.wikipedia.org/wiki/International_Standard_Atmosphere
153  *
154  * Height measurements use the local tangent plane.  The postive z-direction is up.
155  *
156  * All measurements are given in SI units (Kelvin, Pascal, meter, meters/second^2).
157  *   The lapse rate is given in Kelvin/meter, the gas constant for air is given
158  *   in Joules/(kilogram-Kelvin).
159  */
160
161 const real GRAVITATIONAL_ACCELERATION = -9.80665;
162 const real AIR_GAS_CONSTANT = 287.053;
163 const int NUMBER_OF_LAYERS = 7;
164 const real MAXIMUM_ALTITUDE = 84852;
165 const real MINIMUM_PRESSURE = 0.3734;
166 const real LAYER0_BASE_TEMPERATURE = 288.15;
167 const real LAYER0_BASE_PRESSURE = 101325;
168
169 /* lapse rate and base altitude for each layer in the atmosphere */
170 const real[NUMBER_OF_LAYERS] lapse_rate = {
171         -0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002
172 };
173 const int[NUMBER_OF_LAYERS] base_altitude = {
174         0, 11000, 20000, 32000, 47000, 51000, 71000
175 };
176
177
178 /* outputs atmospheric pressure associated with the given altitude. altitudes
179    are measured with respect to the mean sea level */
180 real altitude_to_pressure(real altitude) {
181
182    real base_temperature = LAYER0_BASE_TEMPERATURE;
183    real base_pressure = LAYER0_BASE_PRESSURE;
184
185    real pressure;
186    real base; /* base for function to determine pressure */
187    real exponent; /* exponent for function to determine pressure */
188    int layer_number; /* identifies layer in the atmosphere */
189    int delta_z; /* difference between two altitudes */
190
191    if (altitude > MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */
192       return 0;
193
194    /* calculate the base temperature and pressure for the atmospheric layer
195       associated with the inputted altitude */
196    for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) {
197       delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
198       if (lapse_rate[layer_number] == 0.0) {
199          exponent = GRAVITATIONAL_ACCELERATION * delta_z
200               / AIR_GAS_CONSTANT / base_temperature;
201          base_pressure *= exp(exponent);
202       }
203       else {
204          base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
205          exponent = GRAVITATIONAL_ACCELERATION /
206               (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
207          base_pressure *= pow(base, exponent);
208       }
209       base_temperature += delta_z * lapse_rate[layer_number];
210    }
211
212    /* calculate the pressure at the inputted altitude */
213    delta_z = altitude - base_altitude[layer_number];
214    if (lapse_rate[layer_number] == 0.0) {
215       exponent = GRAVITATIONAL_ACCELERATION * delta_z
216            / AIR_GAS_CONSTANT / base_temperature;
217       pressure = base_pressure * exp(exponent);
218    }
219    else {
220       base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
221       exponent = GRAVITATIONAL_ACCELERATION /
222            (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
223       pressure = base_pressure * pow(base, exponent);
224    }
225
226    return pressure;
227 }
228
229
230 /* outputs the altitude associated with the given pressure. the altitude
231    returned is measured with respect to the mean sea level */
232 real pressure_to_altitude(real pressure) {
233
234    real next_base_temperature = LAYER0_BASE_TEMPERATURE;
235    real next_base_pressure = LAYER0_BASE_PRESSURE;
236
237    real altitude;
238    real base_pressure;
239    real base_temperature;
240    real base; /* base for function to determine base pressure of next layer */
241    real exponent; /* exponent for function to determine base pressure
242                              of next layer */
243    real coefficient;
244    int layer_number; /* identifies layer in the atmosphere */
245    int delta_z; /* difference between two altitudes */
246
247    if (pressure < 0)  /* illegal pressure */
248       return -1;
249    if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */
250       return MAXIMUM_ALTITUDE;
251
252    /* calculate the base temperature and pressure for the atmospheric layer
253       associated with the inputted pressure. */
254    layer_number = -1;
255    do {
256       layer_number++;
257       base_pressure = next_base_pressure;
258       base_temperature = next_base_temperature;
259       delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
260       if (lapse_rate[layer_number] == 0.0) {
261          exponent = GRAVITATIONAL_ACCELERATION * delta_z
262               / AIR_GAS_CONSTANT / base_temperature;
263          next_base_pressure *= exp(exponent);
264       }
265       else {
266          base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
267          exponent = GRAVITATIONAL_ACCELERATION /
268               (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
269          next_base_pressure *= pow(base, exponent);
270       }
271       next_base_temperature += delta_z * lapse_rate[layer_number];
272    }
273    while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure);
274
275    /* calculate the altitude associated with the inputted pressure */
276    if (lapse_rate[layer_number] == 0.0) {
277       coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION)
278                                                     * base_temperature;
279       altitude = base_altitude[layer_number]
280                     + coefficient * log(pressure / base_pressure);
281    }
282    else {
283       base = pressure / base_pressure;
284       exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number]
285                                        / GRAVITATIONAL_ACCELERATION;
286       coefficient = base_temperature / lapse_rate[layer_number];
287       altitude = base_altitude[layer_number]
288                       + coefficient * (pow(base, exponent) - 1);
289    }
290
291    return altitude;
292 }
293
294 real feet_to_meters(real feet)
295 {
296     return feet * (12 * 2.54 / 100);
297 }
298
299 real meters_to_feet(real meters)
300 {
301     return meters / (12 * 2.54 / 100);
302 }
303
304
305 real    time = 0;
306 int     sample = 0;
307 real    interval = 0.192;
308 real    ground_alt = 0;
309
310 void show(int pa)
311 {
312         printf ("%9.2f %9.1f %d\n", time, pressure_to_altitude(pa) - ground_alt, pa);
313         sample++;
314         time += interval;
315 }
316
317 int mix_in (int high, int low)
318 {
319         return  high - (high & 0xffff) + low;
320 }
321
322 bool closer (int target, int a, int b)
323 {
324         return abs (target - a) < abs(target - b);
325 }
326
327 void
328 dump_log(log_t log) {
329         int cur = log.ground_baro;
330
331         ground_alt = pressure_to_altitude(cur);
332         show(cur);
333         for (int l = 0; l < dim(log.samples); l++) {
334                 int     k = log.samples[l];
335                 int     same = mix_in(cur, k);
336                 int     up = mix_in(cur + 0x10000, k);
337                 int     down = mix_in(cur - 0x10000, k);
338
339                 if (closer (cur, same, up)) {
340                         if (closer (cur, same, down))
341                                 cur = same;
342                         else
343                                 cur = down;
344                 } else {
345                         if (closer (cur, up, down))
346                                 cur = up;
347                         else
348                                 cur = down;
349                 }
350                 show(cur);
351         }
352 }
353
354
355 log_t log = get_log(stdin);
356 dump_log(log);