java: Add -target 1.6 to all java compiles
[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; 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 /*
19  * Sensor data conversion functions
20  */
21 package org.altusmetrum.altoslib_2;
22
23 public class AltosConvert {
24         /*
25          * Pressure Sensor Model, version 1.1
26          *
27          * written by Holly Grimes
28          *
29          * Uses the International Standard Atmosphere as described in
30          *   "A Quick Derivation relating altitude to air pressure" (version 1.03)
31          *    from the Portland State Aerospace Society, except that the atmosphere
32          *    is divided into layers with each layer having a different lapse rate.
33          *
34          * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007
35          *    at site <http://en.wikipedia.org/wiki/International_Standard_Atmosphere
36          *
37          * Height measurements use the local tangent plane.  The postive z-direction is up.
38          *
39          * All measurements are given in SI units (Kelvin, Pascal, meter, meters/second^2).
40          *   The lapse rate is given in Kelvin/meter, the gas constant for air is given
41          *   in Joules/(kilogram-Kelvin).
42          */
43
44         public static final double GRAVITATIONAL_ACCELERATION = -9.80665;
45         public static final double AIR_GAS_CONSTANT             = 287.053;
46         public static final double NUMBER_OF_LAYERS             = 7;
47         public static final double MAXIMUM_ALTITUDE             = 84852.0;
48         public static final double MINIMUM_PRESSURE             = 0.3734;
49         public static final double LAYER0_BASE_TEMPERATURE      = 288.15;
50         public static final double LAYER0_BASE_PRESSURE = 101325;
51
52         /* lapse rate and base altitude for each layer in the atmosphere */
53         public static final double[] lapse_rate = {
54                 -0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002
55         };
56
57         public static final int[] base_altitude = {
58                 0, 11000, 20000, 32000, 47000, 51000, 71000
59         };
60
61         /* outputs atmospheric pressure associated with the given altitude.
62          * altitudes are measured with respect to the mean sea level
63          */
64         public static double
65         altitude_to_pressure(double altitude)
66         {
67                 double base_temperature = LAYER0_BASE_TEMPERATURE;
68                 double base_pressure = LAYER0_BASE_PRESSURE;
69
70                 double pressure;
71                 double base; /* base for function to determine pressure */
72                 double exponent; /* exponent for function to determine pressure */
73                 int layer_number; /* identifies layer in the atmosphere */
74                 double delta_z; /* difference between two altitudes */
75
76                 if (altitude > MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */
77                         return 0;
78
79                 /* calculate the base temperature and pressure for the atmospheric layer
80                    associated with the inputted altitude */
81                 for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) {
82                         delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
83                         if (lapse_rate[layer_number] == 0.0) {
84                                 exponent = GRAVITATIONAL_ACCELERATION * delta_z
85                                         / AIR_GAS_CONSTANT / base_temperature;
86                                 base_pressure *= Math.exp(exponent);
87                         }
88                         else {
89                                 base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
90                                 exponent = GRAVITATIONAL_ACCELERATION /
91                                         (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
92                                 base_pressure *= Math.pow(base, exponent);
93                         }
94                         base_temperature += delta_z * lapse_rate[layer_number];
95                 }
96
97                 /* calculate the pressure at the inputted altitude */
98                 delta_z = altitude - base_altitude[layer_number];
99                 if (lapse_rate[layer_number] == 0.0) {
100                         exponent = GRAVITATIONAL_ACCELERATION * delta_z
101                                 / AIR_GAS_CONSTANT / base_temperature;
102                         pressure = base_pressure * Math.exp(exponent);
103                 }
104                 else {
105                         base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
106                         exponent = GRAVITATIONAL_ACCELERATION /
107                                 (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
108                         pressure = base_pressure * Math.pow(base, exponent);
109                 }
110
111                 return pressure;
112         }
113
114
115 /* outputs the altitude associated with the given pressure. the altitude
116    returned is measured with respect to the mean sea level */
117         public static double
118         pressure_to_altitude(double pressure)
119         {
120
121                 double next_base_temperature = LAYER0_BASE_TEMPERATURE;
122                 double next_base_pressure = LAYER0_BASE_PRESSURE;
123
124                 double altitude;
125                 double base_pressure;
126                 double base_temperature;
127                 double base; /* base for function to determine base pressure of next layer */
128                 double exponent; /* exponent for function to determine base pressure
129                                     of next layer */
130                 double coefficient;
131                 int layer_number; /* identifies layer in the atmosphere */
132                 int delta_z; /* difference between two altitudes */
133
134                 if (pressure < 0)  /* illegal pressure */
135                         return -1;
136                 if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */
137                         return MAXIMUM_ALTITUDE;
138
139                 /* calculate the base temperature and pressure for the atmospheric layer
140                    associated with the inputted pressure. */
141                 layer_number = -1;
142                 do {
143                         layer_number++;
144                         base_pressure = next_base_pressure;
145                         base_temperature = next_base_temperature;
146                         delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
147                         if (lapse_rate[layer_number] == 0.0) {
148                                 exponent = GRAVITATIONAL_ACCELERATION * delta_z
149                                         / AIR_GAS_CONSTANT / base_temperature;
150                                 next_base_pressure *= Math.exp(exponent);
151                         }
152                         else {
153                                 base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
154                                 exponent = GRAVITATIONAL_ACCELERATION /
155                                         (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
156                                 next_base_pressure *= Math.pow(base, exponent);
157                         }
158                         next_base_temperature += delta_z * lapse_rate[layer_number];
159                 }
160                 while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure);
161
162                 /* calculate the altitude associated with the inputted pressure */
163                 if (lapse_rate[layer_number] == 0.0) {
164                         coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION)
165                                 * base_temperature;
166                         altitude = base_altitude[layer_number]
167                                 + coefficient * Math.log(pressure / base_pressure);
168                 }
169                 else {
170                         base = pressure / base_pressure;
171                         exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number]
172                                 / GRAVITATIONAL_ACCELERATION;
173                         coefficient = base_temperature / lapse_rate[layer_number];
174                         altitude = base_altitude[layer_number]
175                                 + coefficient * (Math.pow(base, exponent) - 1);
176                 }
177
178                 return altitude;
179         }
180
181         public static double
182         cc_battery_to_voltage(double battery)
183         {
184                 return battery / 32767.0 * 5.0;
185         }
186
187         public static double
188         cc_ignitor_to_voltage(double ignite)
189         {
190                 return ignite / 32767 * 15.0;
191         }
192
193         public static double
194         barometer_to_pressure(double count)
195         {
196                 return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0;
197         }
198
199         static double
200         thermometer_to_temperature(double thermo)
201         {
202                 return (thermo - 19791.268) / 32728.0 * 1.25 / 0.00247;
203         }
204
205         static double mega_adc(int raw) {
206                 return raw / 4095.0;
207         }
208
209         static public double mega_battery_voltage(int v_batt) {
210                 if (v_batt != AltosLib.MISSING)
211                         return 3.3 * mega_adc(v_batt) * (15.0 + 27.0) / 27.0;
212                 return AltosLib.MISSING;
213         }
214
215         static double mega_pyro_voltage(int raw) {
216                 if (raw != AltosLib.MISSING)
217                         return 3.3 * mega_adc(raw) * (100.0 + 27.0) / 27.0;
218                 return AltosLib.MISSING;
219         }
220
221         static double tele_mini_voltage(int sensor) {
222                 double  supply = 3.3;
223
224                 return sensor / 32767.0 * supply * 127/27;
225         }
226
227         static double easy_mini_voltage(int sensor) {
228                 double  supply = 3.0;
229
230                 return sensor / 32767.0 * supply * 127/27;
231         }
232
233         public static double radio_to_frequency(int freq, int setting, int cal, int channel) {
234                 double  f;
235
236                 if (freq > 0)
237                         f = freq / 1000.0;
238                 else {
239                         if (setting <= 0)
240                                 setting = cal;
241                         f = 434.550 * setting / cal;
242                         /* Round to nearest 50KHz */
243                         f = Math.floor (20.0 * f + 0.5) / 20.0;
244                 }
245                 return f + channel * 0.100;
246         }
247
248         public static int radio_frequency_to_setting(double frequency, int cal) {
249                 double  set = frequency / 434.550 * cal;
250
251                 return (int) Math.floor (set + 0.5);
252         }
253
254         public static int radio_frequency_to_channel(double frequency) {
255                 int     channel = (int) Math.floor ((frequency - 434.550) / 0.100 + 0.5);
256
257                 if (channel < 0)
258                         channel = 0;
259                 if (channel > 9)
260                         channel = 9;
261                 return channel;
262         }
263
264         public static double radio_channel_to_frequency(int channel) {
265                 return 434.550 + channel * 0.100;
266         }
267
268         public static int[] ParseHex(String line) {
269                 String[] tokens = line.split("\\s+");
270                 int[] array = new int[tokens.length];
271
272                 for (int i = 0; i < tokens.length; i++)
273                         try {
274                                 array[i] = Integer.parseInt(tokens[i], 16);
275                         } catch (NumberFormatException ne) {
276                                 return null;
277                         }
278                 return array;
279         }
280
281         public static double meters_to_feet(double meters) {
282                 return meters * (100 / (2.54 * 12));
283         }
284
285         public static double feet_to_meters(double feet) {
286                 return feet * 12 * 2.54 / 100.0;
287         }
288
289         public static double meters_to_miles(double meters) {
290                 return meters_to_feet(meters) / 5280;
291         }
292
293         public static double miles_to_meters(double miles) {
294                 return feet_to_meters(miles * 5280);
295         }
296
297         public static double meters_to_mph(double mps) {
298                 return meters_to_miles(mps) * 3600;
299         }
300
301         public static double mph_to_meters(double mps) {
302                 return miles_to_meters(mps) / 3600;
303         }
304
305         public static double meters_to_mach(double meters) {
306                 return meters / 343;            /* something close to mach at usual rocket sites */
307         }
308
309         public static double meters_to_g(double meters) {
310                 return meters / 9.80665;
311         }
312
313         public static double c_to_f(double c) {
314                 return c * 9/5 + 32;
315         }
316
317         public static double f_to_c(double c) {
318                 return (c - 32) * 5/9;
319         }
320
321         public static boolean imperial_units = false;
322
323         public static AltosDistance distance = new AltosDistance();
324
325         public static AltosHeight height = new AltosHeight();
326
327         public static AltosSpeed speed = new AltosSpeed();
328
329         public static AltosAccel accel = new AltosAccel();
330
331         public static AltosTemperature temperature = new AltosTemperature();
332
333         public static String show_gs(String format, double a) {
334                 a = meters_to_g(a);
335                 format = format.concat(" g");
336                 return String.format(format, a);
337         }
338
339         public static String say_gs(double a) {
340                 return String.format("%6.0 gees", meters_to_g(a));
341         }
342
343         public static int checksum(int[] data, int start, int length) {
344                 int     csum = 0x5a;
345                 for (int i = 0; i < length; i++)
346                         csum += data[i + start];
347                 return csum & 0xff;
348         }
349 }