altosui: Convert from channels to frequencies
[fw/altos] / altosui / 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 altosui;
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         static final double GRAVITATIONAL_ACCELERATION = -9.80665;
45         static final double AIR_GAS_CONSTANT            = 287.053;
46         static final double NUMBER_OF_LAYERS            = 7;
47         static final double MAXIMUM_ALTITUDE            = 84852.0;
48         static final double MINIMUM_PRESSURE            = 0.3734;
49         static final double LAYER0_BASE_TEMPERATURE     = 288.15;
50         static final double LAYER0_BASE_PRESSURE        = 101325;
51
52         /* lapse rate and base altitude for each layer in the atmosphere */
53         static final double[] lapse_rate = {
54                 -0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002
55         };
56
57         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         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         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         static double
182         cc_battery_to_voltage(double battery)
183         {
184                 return battery / 32767.0 * 5.0;
185         }
186
187         static double
188         cc_ignitor_to_voltage(double ignite)
189         {
190                 return ignite / 32767 * 15.0;
191         }
192
193         static double
194         radio_setting_to_frequency(int setting, int cal) {
195                 double  f;
196
197                 f = 434.550 * setting / cal;
198                 /* Round to nearest 50KHz */
199                 f = Math.floor (20.0 * f + 0.5) / 20.0;
200                 return f;
201         }
202
203         static int
204         radio_frequency_to_setting(double frequency, int cal) {
205                 double  set = frequency / 434.550 * cal;
206
207                 return (int) Math.floor (set + 0.5);
208         }
209
210         static double
211         radio_channel_to_frequency(int channel) {
212                 return 434.550 + channel * 0.100;
213         }
214
215         static int
216         radio_frequency_to_channel(double frequency) {
217                 int     channel = (int) Math.floor ((frequency - 434.550) / 0.100 + 0.5);
218
219                 if (channel < 0)
220                         channel = 0;
221                 if (channel > 9)
222                         channel = 9;
223                 return channel;
224         }
225 }