altoslib/altosuilib/altosui: More work towards using AltosFlightSeries for analysis
[fw/altos] / altoslib / AltosConvert.java
1 /*
2  * Copyright © 2010 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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 /*
20  * Sensor data conversion functions
21  */
22 package org.altusmetrum.altoslib_11;
23
24 import java.util.*;
25
26 public class AltosConvert {
27
28         public static final double gravity = 9.80665;
29
30         /*
31          * Pressure Sensor Model, version 1.1
32          *
33          * written by Holly Grimes
34          *
35          * Uses the International Standard Atmosphere as described in
36          *   "A Quick Derivation relating altitude to air pressure" (version 1.03)
37          *    from the Portland State Aerospace Society, except that the atmosphere
38          *    is divided into layers with each layer having a different lapse rate.
39          *
40          * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007
41          *    at site <http://en.wikipedia.org/wiki/International_Standard_Atmosphere
42          *
43          * Height measurements use the local tangent plane.  The postive z-direction is up.
44          *
45          * All measurements are given in SI units (Kelvin, Pascal, meter, meters/second^2).
46          *   The lapse rate is given in Kelvin/meter, the gas constant for air is given
47          *   in Joules/(kilogram-Kelvin).
48          */
49
50         private static final double GRAVITATIONAL_ACCELERATION = -gravity;
51         private static final double AIR_GAS_CONSTANT            = 287.053;
52         private static final double NUMBER_OF_LAYERS            = 7;
53         private static final double MAXIMUM_ALTITUDE            = 84852.0;
54         private static final double MINIMUM_PRESSURE            = 0.3734;
55         private static final double LAYER0_BASE_TEMPERATURE     = 288.15;
56         private static final double LAYER0_BASE_PRESSURE        = 101325;
57
58         /* lapse rate and base altitude for each layer in the atmosphere */
59         private static final double[] lapse_rate = {
60                 -0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002
61         };
62
63         private static final int[] base_altitude = {
64                 0, 11000, 20000, 32000, 47000, 51000, 71000
65         };
66
67         /* outputs atmospheric pressure associated with the given altitude.
68          * altitudes are measured with respect to the mean sea level
69          */
70         public static double
71         altitude_to_pressure(double altitude)
72         {
73                 double base_temperature = LAYER0_BASE_TEMPERATURE;
74                 double base_pressure = LAYER0_BASE_PRESSURE;
75
76                 double pressure;
77                 double base; /* base for function to determine pressure */
78                 double exponent; /* exponent for function to determine pressure */
79                 int layer_number; /* identifies layer in the atmosphere */
80                 double delta_z; /* difference between two altitudes */
81
82                 if (altitude > MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */
83                         return 0;
84
85                 /* calculate the base temperature and pressure for the atmospheric layer
86                    associated with the inputted altitude */
87                 for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) {
88                         delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
89                         if (lapse_rate[layer_number] == 0.0) {
90                                 exponent = GRAVITATIONAL_ACCELERATION * delta_z
91                                         / AIR_GAS_CONSTANT / base_temperature;
92                                 base_pressure *= Math.exp(exponent);
93                         }
94                         else {
95                                 base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
96                                 exponent = GRAVITATIONAL_ACCELERATION /
97                                         (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
98                                 base_pressure *= Math.pow(base, exponent);
99                         }
100                         base_temperature += delta_z * lapse_rate[layer_number];
101                 }
102
103                 /* calculate the pressure at the inputted altitude */
104                 delta_z = altitude - base_altitude[layer_number];
105                 if (lapse_rate[layer_number] == 0.0) {
106                         exponent = GRAVITATIONAL_ACCELERATION * delta_z
107                                 / AIR_GAS_CONSTANT / base_temperature;
108                         pressure = base_pressure * Math.exp(exponent);
109                 }
110                 else {
111                         base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
112                         exponent = GRAVITATIONAL_ACCELERATION /
113                                 (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
114                         pressure = base_pressure * Math.pow(base, exponent);
115                 }
116
117                 return pressure;
118         }
119
120
121 /* outputs the altitude associated with the given pressure. the altitude
122    returned is measured with respect to the mean sea level */
123         public static double
124         pressure_to_altitude(double pressure)
125         {
126
127                 double next_base_temperature = LAYER0_BASE_TEMPERATURE;
128                 double next_base_pressure = LAYER0_BASE_PRESSURE;
129
130                 double altitude;
131                 double base_pressure;
132                 double base_temperature;
133                 double base; /* base for function to determine base pressure of next layer */
134                 double exponent; /* exponent for function to determine base pressure
135                                     of next layer */
136                 double coefficient;
137                 int layer_number; /* identifies layer in the atmosphere */
138                 int delta_z; /* difference between two altitudes */
139
140                 if (pressure < 0)  /* illegal pressure */
141                         return -1;
142                 if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */
143                         return MAXIMUM_ALTITUDE;
144
145                 /* calculate the base temperature and pressure for the atmospheric layer
146                    associated with the inputted pressure. */
147                 layer_number = -1;
148                 do {
149                         layer_number++;
150                         base_pressure = next_base_pressure;
151                         base_temperature = next_base_temperature;
152                         delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
153                         if (lapse_rate[layer_number] == 0.0) {
154                                 exponent = GRAVITATIONAL_ACCELERATION * delta_z
155                                         / AIR_GAS_CONSTANT / base_temperature;
156                                 next_base_pressure *= Math.exp(exponent);
157                         }
158                         else {
159                                 base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
160                                 exponent = GRAVITATIONAL_ACCELERATION /
161                                         (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
162                                 next_base_pressure *= Math.pow(base, exponent);
163                         }
164                         next_base_temperature += delta_z * lapse_rate[layer_number];
165                 }
166                 while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure);
167
168                 /* calculate the altitude associated with the inputted pressure */
169                 if (lapse_rate[layer_number] == 0.0) {
170                         coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION)
171                                 * base_temperature;
172                         altitude = base_altitude[layer_number]
173                                 + coefficient * Math.log(pressure / base_pressure);
174                 }
175                 else {
176                         base = pressure / base_pressure;
177                         exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number]
178                                 / GRAVITATIONAL_ACCELERATION;
179                         coefficient = base_temperature / lapse_rate[layer_number];
180                         altitude = base_altitude[layer_number]
181                                 + coefficient * (Math.pow(base, exponent) - 1);
182                 }
183
184                 return altitude;
185         }
186
187         public static double
188         cc_battery_to_voltage(double battery)
189         {
190                 return battery / 32767.0 * 5.0;
191         }
192
193         public static double
194         cc_igniter_to_voltage(double ignite)
195         {
196                 return ignite / 32767 * 15.0;
197         }
198
199         public static double
200         barometer_to_pressure(double count)
201         {
202                 return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0;
203         }
204
205         static double
206         thermometer_to_temperature(double thermo)
207         {
208                 return (thermo - 19791.268) / 32728.0 * 1.25 / 0.00247;
209         }
210
211         static double mega_adc(int raw) {
212                 return raw / 4095.0;
213         }
214
215         static public double mega_battery_voltage(int v_batt) {
216                 if (v_batt != AltosLib.MISSING)
217                         return 3.3 * mega_adc(v_batt) * (5.6 + 10.0) / 10.0;
218                 return AltosLib.MISSING;
219         }
220
221         static double mega_pyro_voltage(int raw) {
222                 if (raw != AltosLib.MISSING)
223                         return 3.3 * mega_adc(raw) * (100.0 + 27.0) / 27.0;
224                 return AltosLib.MISSING;
225         }
226
227         static double tele_mini_3_adc(int raw) {
228                 return raw / 4095.0;
229         }
230
231         static public double tele_mini_3_battery_voltage(int v_batt) {
232                 if (v_batt != AltosLib.MISSING)
233                         return 3.3 * tele_mini_3_adc(v_batt) * (5.6 + 10.0) / 10.0;
234                 return AltosLib.MISSING;
235         }
236
237         static double tele_mini_3_pyro_voltage(int raw) {
238                 if (raw != AltosLib.MISSING)
239                         return 3.3 * tele_mini_3_adc(raw) * (100.0 + 27.0) / 27.0;
240                 return AltosLib.MISSING;
241         }
242
243         static double tele_mini_2_voltage(int sensor) {
244                 double  supply = 3.3;
245
246                 return sensor / 32767.0 * supply * 127/27;
247         }
248
249         static double tele_gps_voltage(int sensor) {
250                 double  supply = 3.3;
251
252                 return sensor / 32767.0 * supply * (5.6 + 10.0) / 10.0;
253         }
254
255         static double tele_bt_3_battery(int raw) {
256                 if (raw == AltosLib.MISSING)
257                         return AltosLib.MISSING;
258                 return 3.3 * mega_adc(raw) * (5.1 + 10.0) / 10.0;
259         }
260
261         static double easy_mini_voltage(int sensor, int serial) {
262                 double  supply = 3.3;
263                 double  diode_offset = 0.0;
264
265                 /* early prototypes had a 3.0V regulator */
266                 if (serial < 1000)
267                         supply = 3.0;
268
269                 /* Purple v1.0 boards had the sensor after the
270                  * blocking diode, which drops about 150mV
271                  */
272                 if (serial < 1665)
273                         diode_offset = 0.150;
274
275                 return sensor / 32767.0 * supply * 127/27 + diode_offset;
276         }
277
278         public static double radio_to_frequency(int freq, int setting, int cal, int channel) {
279                 double  f;
280
281                 if (freq > 0)
282                         f = freq / 1000.0;
283                 else {
284                         if (setting <= 0)
285                                 setting = cal;
286                         f = 434.550 * setting / cal;
287                         /* Round to nearest 50KHz */
288                         f = Math.floor (20.0 * f + 0.5) / 20.0;
289                 }
290                 return f + channel * 0.100;
291         }
292
293         public static int radio_frequency_to_setting(double frequency, int cal) {
294                 double  set = frequency / 434.550 * cal;
295
296                 return (int) Math.floor (set + 0.5);
297         }
298
299         public static int radio_frequency_to_channel(double frequency) {
300                 int     channel = (int) Math.floor ((frequency - 434.550) / 0.100 + 0.5);
301
302                 if (channel < 0)
303                         channel = 0;
304                 if (channel > 9)
305                         channel = 9;
306                 return channel;
307         }
308
309         public static double radio_channel_to_frequency(int channel) {
310                 return 434.550 + channel * 0.100;
311         }
312
313         public static int telem_to_rssi(int telem) {
314                 return telem / 2 - 74;
315         }
316
317         public static int[] ParseHex(String line) {
318                 String[] tokens = line.split("\\s+");
319                 int[] array = new int[tokens.length];
320
321                 for (int i = 0; i < tokens.length; i++)
322                         try {
323                                 array[i] = Integer.parseInt(tokens[i], 16);
324                         } catch (NumberFormatException ne) {
325                                 return null;
326                         }
327                 return array;
328         }
329
330         public static double meters_to_feet(double meters) {
331                 return meters * (100 / (2.54 * 12));
332         }
333
334         public static double feet_to_meters(double feet) {
335                 return feet * 12 * 2.54 / 100.0;
336         }
337
338         public static double meters_to_miles(double meters) {
339                 return meters_to_feet(meters) / 5280;
340         }
341
342         public static double miles_to_meters(double miles) {
343                 return feet_to_meters(miles * 5280);
344         }
345
346         public static double meters_to_mph(double mps) {
347                 return meters_to_miles(mps) * 3600;
348         }
349
350         public static double mph_to_meters(double mps) {
351                 return miles_to_meters(mps) / 3600;
352         }
353
354         public static double mps_to_fps(double mps) {
355                 return meters_to_miles(mps) * 5280;
356         }
357
358         public static double fps_to_mps(double mps) {
359                 return miles_to_meters(mps) / 5280;
360         }
361
362         public static double meters_to_mach(double meters) {
363                 return meters / 343;            /* something close to mach at usual rocket sites */
364         }
365
366         public static double meters_to_g(double meters) {
367                 return meters / 9.80665;
368         }
369
370         public static double c_to_f(double c) {
371                 return c * 9/5 + 32;
372         }
373
374         public static double f_to_c(double c) {
375                 return (c - 32) * 5/9;
376         }
377
378         public static double psi_to_pa(double psi) {
379                 return psi * 6894.76;
380         }
381
382         public static double pa_to_psi(double pa) {
383                 return pa / 6894.76;
384         }
385
386         public static double n_to_lb(double n) {
387                 return n * 0.22480894;
388         }
389
390         public static double lb_to_n(double lb) {
391                 return lb / 0.22480894;
392         }
393
394         public static double acceleration_from_sensor(double sensor, double plus_g, double minus_g, double ground) {
395                 if (sensor == AltosLib.MISSING)
396                         return AltosLib.MISSING;
397
398                 if (plus_g == AltosLib.MISSING || minus_g == AltosLib.MISSING)
399                         return AltosLib.MISSING;
400
401                 if (ground == AltosLib.MISSING)
402                         ground = plus_g;
403
404                 double counts_per_g = (plus_g - minus_g) / 2.0;
405                 double counts_per_mss = counts_per_g / gravity;
406                 return (sensor - ground) / counts_per_mss;
407         }
408
409         public static boolean imperial_units = false;
410
411         public static AltosDistance distance = new AltosDistance();
412
413         public static AltosHeight height = new AltosHeight();
414
415         public static AltosPressure pressure = new AltosPressure();
416
417         public static AltosForce force = new AltosForce();
418
419         public static AltosSpeed speed = new AltosSpeed();
420
421         public static AltosAccel accel = new AltosAccel();
422
423         public static AltosTemperature temperature = new AltosTemperature();
424
425         public static AltosOrient orient = new AltosOrient();
426
427         public static AltosVoltage voltage = new AltosVoltage();
428
429         public static AltosLatitude latitude = new AltosLatitude();
430
431         public static AltosLongitude longitude = new AltosLongitude();
432
433         public static AltosRotationRate rotation_rate = new AltosRotationRate();
434
435         public static AltosStateName state_name = new AltosStateName();
436
437         public static AltosPyroName pyro_name = new AltosPyroName();
438
439         public static AltosUnits magnetic_field = null;
440
441         public static String show_gs(String format, double a) {
442                 a = meters_to_g(a);
443                 format = format.concat(" g");
444                 return String.format(format, a);
445         }
446
447         public static String say_gs(double a) {
448                 return String.format("%6.0 gees", meters_to_g(a));
449         }
450
451         public static int checksum(int[] data, int start, int length) {
452                 int     csum = 0x5a;
453                 for (int i = 0; i < length; i++)
454                         csum += data[i + start];
455                 return csum & 0xff;
456         }
457
458         public static int checksum(List<Byte> data, int start, int length) {
459                 int     csum = 0x5a;
460                 for (int i = 0; i < length; i++)
461                         csum += data.get(i+start);
462                 return csum & 0xff;
463         }
464
465         public static double beep_value_to_freq(int value) {
466                 if (value == 0)
467                         return 4000;
468                 return 1.0/2.0 * (24.0e6/32.0) / (double) value;
469         }
470
471         public static int beep_freq_to_value(double freq) {
472                 if (freq == 0)
473                         return 94;
474                 return (int) Math.floor (1.0/2.0 * (24.0e6/32.0) / freq + 0.5);
475         }
476
477         public static final int BEARING_LONG = 0;
478         public static final int BEARING_SHORT = 1;
479         public static final int BEARING_VOICE = 2;
480
481         public static String bearing_to_words(int length, double bearing) {
482                 String [][] bearing_string = {
483                         {
484                                 "North", "North North East", "North East", "East North East",
485                                 "East", "East South East", "South East", "South South East",
486                                 "South", "South South West", "South West", "West South West",
487                                 "West", "West North West", "North West", "North North West"
488                         }, {
489                                 "N", "NNE", "NE", "ENE",
490                                 "E", "ESE", "SE", "SSE",
491                                 "S", "SSW", "SW", "WSW",
492                                 "W", "WNW", "NW", "NNW"
493                         }, {
494                                 "north", "nor nor east", "north east", "east nor east",
495                                 "east", "east sow east", "south east", "sow sow east",
496                                 "south", "sow sow west", "south west", "west sow west",
497                                 "west", "west nor west", "north west", "nor nor west "
498                         }
499                 };
500                 return bearing_string[length][(int)((bearing / 90 * 8 + 1) / 2)%16];
501         }
502 }