first cut at turnon scripts for EasyTimer v2
[fw/altos] / src / kernel / ao_motor_flight.c
1 /*
2  * Copyright © 2022 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 #ifndef AO_FLIGHT_TEST
20 #include "ao.h"
21 #include <ao_log.h>
22 #endif
23
24 #include <ao_flight.h>
25
26 /* Main flight thread. */
27
28 enum ao_flight_state    ao_flight_state;        /* current flight state */
29 AO_TICK_TYPE            ao_launch_tick;         /* time of first boost detect */
30
31 #if HAS_SENSOR_ERRORS
32 /* Any sensor can set this to mark the flight computer as 'broken' */
33 uint8_t                 ao_sensor_errors;
34 #endif
35
36 /*
37  * track min/max data over a long interval to detect
38  * resting
39  */
40 static AO_TICK_TYPE     ao_interval_end;
41
42 #define init_bounds(_cur, _min, _max) do {                              \
43                 _min = _max = _cur;                                     \
44         } while (0)
45
46 #define check_bounds(_cur, _min, _max) do {     \
47                 if (_cur < _min)                \
48                         _min = _cur;            \
49                 if (_cur > _max)                \
50                         _max = _cur;            \
51         } while(0)
52
53 uint8_t                 ao_flight_force_idle;
54
55 /* Compute ADC value change given a defined pressure change in Pa */
56
57 static inline int16_t
58 ao_delta_pressure_to_adc(uint32_t pressure)
59 {
60         static const double volts_base = AO_PRESSURE_VOLTS_BASE;
61         static const double volts_max = AO_PRESSURE_VOLTS_MAX;
62
63         /* Compute change in voltage from the sensor */
64         double  volts = (double) pressure / AO_FULL_SCALE_PRESSURE * (volts_max - volts_base);
65
66         /* voltage divider in front of the ADC input to decivolts */
67         double  adc_dv = volts * (10.0 * (double) AO_PRESSURE_DIV_MINUS /
68                                   ((double) AO_PRESSURE_DIV_PLUS + (double) AO_PRESSURE_DIV_MINUS));
69
70         /* convert to ADC output value */
71         double  adc = adc_dv * AO_ADC_MAX / AO_ADC_REFERENCE_DV;
72
73         if (adc > AO_ADC_MAX)
74                 adc = AO_ADC_MAX;
75         if (adc < 0)
76                 adc = 0;
77
78         return (int16_t) adc;
79 }
80
81 #define AO_BOOST_DETECT                 ao_delta_pressure_to_adc(AO_BOOST_DETECT_PRESSURE)
82 #define AO_QUIET_DETECT                 ao_delta_pressure_to_adc(AO_QUIET_DETECT_PRESSURE)
83
84 /*
85  * Landing is detected by getting constant readings from pressure sensor
86  * for a fairly long time (AO_INTERVAL_TICKS), along with the max being
87  * less than the boost detect pressure
88  */
89 #define AO_INTERVAL_TICKS       AO_SEC_TO_TICKS(10)
90
91 static AO_TICK_TYPE             ao_interval_end;
92 static motor_pressure_t         ao_interval_min_motor_pressure, ao_interval_max_motor_pressure;
93
94 #define abs(a)  ((a) < 0 ? -(a) : (a))
95
96 void
97 ao_flight(void)
98 {
99         ao_sample_init();
100         ao_flight_state = ao_flight_startup;
101         for (;;) {
102
103                 /*
104                  * Process ADC samples, just looping
105                  * until the sensors are calibrated.
106                  */
107                 if (!ao_sample())
108                         continue;
109
110                 switch (ao_flight_state) {
111                 case ao_flight_startup:
112
113                         if (!ao_flight_force_idle)
114                         {
115                                 /* Set pad mode - we can fly! */
116                                 ao_flight_state = ao_flight_pad;
117 #if HAS_USB && !HAS_FLIGHT_DEBUG && !HAS_SAMPLE_PROFILE && !DEBUG
118                                 /* Disable the USB controller in flight mode
119                                  * to save power
120                                  */
121 #if HAS_FAKE_FLIGHT
122                                 if (!ao_fake_flight_active)
123 #endif
124                                         ao_usb_disable();
125 #endif
126
127 #if AO_LED_RED
128                                 /* signal successful initialization by turning off the LED */
129                                 ao_led_off(AO_LED_RED);
130 #endif
131                         } else {
132                                 /* Set idle mode */
133                                 ao_flight_state = ao_flight_idle;
134 #if HAS_SENSOR_ERRORS
135                                 if (ao_sensor_errors)
136                                         ao_flight_state = ao_flight_invalid;
137 #endif
138
139 #if AO_LED_RED
140                                 /* signal successful initialization by turning off the LED */
141                                 ao_led_off(AO_LED_RED);
142 #endif
143                         }
144                         /* wakeup threads due to state change */
145                         ao_wakeup(&ao_flight_state);
146
147                         break;
148
149                 case ao_flight_pad:
150                         /* pad to boost:
151                          *
152                          * motor pressure rise > 50psi
153                          */
154                         if (ao_sample_motor_pressure - ao_ground_motor_pressure >= AO_BOOST_DETECT) 
155                         {
156                                 ao_flight_state = ao_flight_boost;
157                                 ao_wakeup(&ao_flight_state);
158
159                                 ao_launch_tick = ao_sample_tick;
160
161                                 /* start logging data */
162 #if HAS_LOG
163                                 ao_log_start();
164 #endif
165                                 /* Initialize landing detection interval values */
166                                 ao_interval_end = ao_sample_tick + AO_INTERVAL_TICKS;
167
168                                 init_bounds(ao_sample_motor_pressure, ao_interval_min_motor_pressure, ao_interval_max_motor_pressure);
169                         }
170                         break;
171                 case ao_flight_boost:
172                         /* boost to landed:
173                          *
174                          * motor pressure low and stable for more than 10 seconds
175                          */
176                         check_bounds(ao_sample_motor_pressure, ao_interval_min_motor_pressure,
177                                      ao_interval_max_motor_pressure);
178
179                         if ((AO_TICK_SIGNED) (ao_sample_tick - ao_interval_end) >= 0) {
180                                 if (ao_interval_max_motor_pressure - ao_ground_motor_pressure <= AO_BOOST_DETECT &&
181                                     ao_interval_max_motor_pressure - ao_interval_min_motor_pressure <= AO_QUIET_DETECT_PRESSURE)
182                                 {
183                                         ao_flight_state = ao_flight_landed;
184                                         ao_wakeup(&ao_flight_state);
185 #if HAS_ADC
186                                         /* turn off the ADC capture */
187                                         ao_timer_set_adc_interval(0);
188 #endif
189                                 }
190
191                                 /* Reset interval values */
192                                 ao_interval_end = ao_sample_tick + AO_INTERVAL_TICKS;
193
194                                 init_bounds(ao_sample_motor_pressure, ao_interval_min_motor_pressure, ao_interval_max_motor_pressure);
195                         }
196                         break;
197                 default:
198                         break;
199                 }
200         }
201 }
202
203 #if HAS_FLIGHT_DEBUG
204 static inline int int_part(ao_v_t i)    { return i >> 4; }
205 static inline int frac_part(ao_v_t i)   { return ((i & 0xf) * 100 + 8) / 16; }
206
207 static void
208 ao_flight_dump(void)
209 {
210 #if HAS_ACCEL
211         ao_v_t  accel;
212
213         accel = ((ao_config.accel_plus_g - ao_sample_accel) * ao_accel_scale) >> 16;
214 #endif
215
216         printf ("sample:\n");
217         printf ("  tick        %d\n", ao_sample_tick);
218 #if HAS_BARO
219         printf ("  raw pres    %ld\n", ao_sample_pres);
220 #endif
221 #if HAS_ACCEL
222         printf ("  raw accel   %d\n", ao_sample_accel);
223 #endif
224 #if HAS_BARO
225         printf ("  ground pres %ld\n", ao_ground_pres);
226         printf ("  ground alt  %ld\n", ao_ground_height);
227 #endif
228 #if HAS_ACCEL
229         printf ("  raw accel   %d\n", ao_sample_accel);
230         printf ("  groundaccel %d\n", ao_ground_accel);
231         printf ("  accel_2g    %d\n", ao_accel_2g);
232 #endif
233
234 #if HAS_BARO
235         printf ("  alt         %ld\n", ao_sample_alt);
236         printf ("  height      %ld\n", ao_sample_height);
237 #endif
238
239 #if HAS_ACCEL
240         printf ("  accel       %d.%02d\n", int_part(accel), frac_part(accel));
241 #endif
242
243
244         printf ("kalman:\n");
245         printf ("  height      %ld\n", ao_height);
246         printf ("  speed       %d.%02d\n", int_part(ao_speed), frac_part(ao_speed));
247         printf ("  accel       %d.%02d\n", int_part(ao_accel), frac_part(ao_accel));
248         printf ("  max_height  %ld\n", ao_max_height);
249         printf ("  avg_height  %ld\n", ao_avg_height);
250         printf ("  error_h     %ld\n", ao_error_h);
251 #if !HAS_ACCEL
252         printf ("  error_avg   %d\n", ao_error_h_sq_avg);
253 #endif
254 }
255
256 static void
257 ao_gyro_test(void)
258 {
259         ao_flight_state = ao_flight_test;
260         ao_getchar();
261         ao_flight_state = ao_flight_idle;
262 }
263
264 uint8_t ao_orient_test;
265
266 static void
267 ao_orient_test_select(void)
268 {
269         ao_orient_test = !ao_orient_test;
270         printf("orient test %d\n", ao_orient_test);
271 }
272
273 const struct ao_cmds ao_flight_cmds[] = {
274         { ao_flight_dump,       "F\0Dump flight status" },
275         { ao_gyro_test,         "G\0Test gyro code" },
276         { ao_orient_test_select,"O\0Test orientation code" },
277         { 0, NULL },
278 };
279 #endif
280
281 static struct ao_task   flight_task;
282
283 void
284 ao_flight_init(void)
285 {
286         ao_flight_state = ao_flight_startup;
287 #if HAS_FLIGHT_DEBUG
288         ao_cmd_register(&ao_flight_cmds[0]);
289 #endif
290         ao_add_task(&flight_task, ao_flight, "flight");
291 }