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