#!/usr/bin/env nickle /* * Pressure Sensor Model, version 1.1 * * written by Holly Grimes * * Uses the International Standard Atmosphere as described in * "A Quick Derivation relating altitude to air pressure" (version 1.03) * from the Portland State Aerospace Society, except that the atmosphere * is divided into layers with each layer having a different lapse rate. * * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007 * at site MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */ return 0; /* calculate the base temperature and pressure for the atmospheric layer associated with the inputted altitude */ for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) { delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; if (lapse_rate[layer_number] == 0.0) { exponent = GRAVITATIONAL_ACCELERATION * delta_z / AIR_GAS_CONSTANT / base_temperature; base_pressure *= exp(exponent); } else { base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; exponent = GRAVITATIONAL_ACCELERATION / (AIR_GAS_CONSTANT * lapse_rate[layer_number]); base_pressure *= pow(base, exponent); } base_temperature += delta_z * lapse_rate[layer_number]; } /* calculate the pressure at the inputted altitude */ delta_z = altitude - base_altitude[layer_number]; if (lapse_rate[layer_number] == 0.0) { exponent = GRAVITATIONAL_ACCELERATION * delta_z / AIR_GAS_CONSTANT / base_temperature; pressure = base_pressure * exp(exponent); } else { base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; exponent = GRAVITATIONAL_ACCELERATION / (AIR_GAS_CONSTANT * lapse_rate[layer_number]); pressure = base_pressure * pow(base, exponent); } return pressure; } /* outputs the altitude associated with the given pressure. the altitude returned is measured with respect to the mean sea level */ real pressure_to_altitude(real pressure) { real next_base_temperature = LAYER0_BASE_TEMPERATURE; real next_base_pressure = LAYER0_BASE_PRESSURE; real altitude; real base_pressure; real base_temperature; real base; /* base for function to determine base pressure of next layer */ real exponent; /* exponent for function to determine base pressure of next layer */ real coefficient; int layer_number; /* identifies layer in the atmosphere */ int delta_z; /* difference between two altitudes */ if (pressure < 0) /* illegal pressure */ return -1; if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */ return MAXIMUM_ALTITUDE; /* calculate the base temperature and pressure for the atmospheric layer associated with the inputted pressure. */ layer_number = -1; do { layer_number++; base_pressure = next_base_pressure; base_temperature = next_base_temperature; delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number]; if (lapse_rate[layer_number] == 0.0) { exponent = GRAVITATIONAL_ACCELERATION * delta_z / AIR_GAS_CONSTANT / base_temperature; next_base_pressure *= exp(exponent); } else { base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0; exponent = GRAVITATIONAL_ACCELERATION / (AIR_GAS_CONSTANT * lapse_rate[layer_number]); next_base_pressure *= pow(base, exponent); } next_base_temperature += delta_z * lapse_rate[layer_number]; } while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure); /* calculate the altitude associated with the inputted pressure */ if (lapse_rate[layer_number] == 0.0) { coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION) * base_temperature; altitude = base_altitude[layer_number] + coefficient * log(pressure / base_pressure); } else { base = pressure / base_pressure; exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number] / GRAVITATIONAL_ACCELERATION; coefficient = base_temperature / lapse_rate[layer_number]; altitude = base_altitude[layer_number] + coefficient * (pow(base, exponent) - 1); } return altitude; } real feet_to_meters(real feet) { return feet * (12 * 2.54 / 100); } real meters_to_feet(real meters) { return meters / (12 * 2.54 / 100); } /* * Values for our MP3H6115A pressure sensor * * From the data sheet: * * Pressure range: 15-115 kPa * Voltage at 115kPa: 2.82 * Output scale: 27mV/kPa * * * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa */ real counts_per_kPa = 27 * 2047 / 3300; real counts_at_101_3kPa = 1674; real count_to_kPa(real count) { return (count / 2047 + 0.095) / 0.009; } typedef struct { int type; int time; int a; int b; } flight_record; flight_record read_record(file in) { flight_record r; File::fscanf(in, "%c %x %x %x\n", &r.type, &r.time, &r.a, &r.b); return r; } real g_count = 264.8; real count_to_g(real count) { return (15792 + g_count - count) / g_count; } real base_alt = 0; real base_sec = 0; real last_alt; real last_sec; real last_alt_speed; real accel_speed; real accel_meters; real[...] barometer; real[...] accelerometer; real sinc(real x) = x != 0 ? sin(x)/x : 1; real gaussian(real x) = exp(-(x**2)/2) / sqrt(2 * pi); load "/usr/share/nickle/examples/kaiser.5c" real[...] convolve(real[...] d, real[...] e) { real sample(n) = n < 0 ? d[0] : n >= dim(d) ? d[dim(d)-1] : d[n]; real w = (dim(e) - 1) / 2; real c(int center) { real v = 0; for (int o = -w; o <= w; o++) v += sample(center + o) * e[o + w]; return v; } return (real[dim(d)]) { [n] = c(n) }; } real sum(real[...] x) { real s = 0; for(int i = 0; i < dim(x); i++) s += x[i]; return s; } real[...] filter(real[...] d, int half_width) { # real[half_width * 2 + 1] fir = { [n] = sinc(2 * pi * n / (2 * half_width)) }; real M = half_width * 2 + 1; real[M] fir = { [n] = kaiser(n, M, 8) }; real fir_sum = sum(fir); for (int i = 0; i < dim(fir); i++) fir[i] /= fir_sum; return convolve(d, fir); } real gravity = 9.80665; real[...] pressure_value, accelerometer_value; real[...] clock; void readsamples(file in) { setdim(pressure_value, 0); setdim(accelerometer_value, 0); while (!File::end(in)) { flight_record r = read_record(in); if (r.type == 'A') { clock[dim(clock)] = r.time / 100; pressure_value[dim(pressure_value)] = r.b; accelerometer_value[dim(accelerometer_value)] = r.a; } } } readsamples(stdin); int size = dim(accelerometer_value); real[size] accelerometer = { [n] = gravity * (count_to_g(accelerometer_value[n]) - 1.0) }; real[size] barometer = { [n] = pressure_to_altitude(count_to_kPa(pressure_value[n] / 16) * 1000) }; real[size] filtered_accelerometer = filter(accelerometer, 8); real[size] filtered_barometer = filter(barometer, 128); real[...] integrate(real[...] d) { real[dim(d)] ret; for (int i = 0; i < dim(ret); i++) ret[i] = i == 0 ? 0 : ret[i-1] + (d[i-1] + d[i]) / 2 * (clock[i] - clock[i-1]); return ret; } real[...] differentiate(real[...] d) { real[dim(d)] ret; for (int i = 1; i < dim(ret); i++) ret[i] = (d[i] - d[i-1]) / (clock[i] - clock[i-1]); ret[0] = ret[1]; return ret; } real[size] accel_speed = integrate(filtered_accelerometer); real[size] accel_pos = integrate(accel_speed); real[size] baro_speed = differentiate(filtered_barometer); real[size] baro_accel = differentiate(baro_speed); for (int i = 0; i < size; i++) printf("%g %g %g %g %g %g %g %g %g\n", clock[i] - clock[0], filtered_barometer[i] - filtered_barometer[0], accel_pos[i], baro_speed[i], accel_speed[i], baro_accel[i], filtered_accelerometer[i], barometer[i] - barometer[0], accelerometer[i]);