daf06c19f4c29225c2bbcd676596af06a831a124
[fw/altos] / altoslib / AltosState.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  * Track flight state from telemetry or eeprom data stream
20  */
21
22 package org.altusmetrum.altoslib_1;
23
24 public class AltosState implements Cloneable {
25         public AltosRecord record;
26
27         public static final int set_position = 1;
28         public static final int set_gps = 2;
29         public static final int set_data = 4;
30
31         public int set;
32
33         /* derived data */
34
35         public long     report_time;
36
37         public double   time;
38         public double   prev_time;
39         public double   time_change;
40         public int      tick;
41         public int      boost_tick;
42
43         public int      state;
44         public int      flight;
45         public int      serial;
46         public boolean  landed;
47         public boolean  ascent; /* going up? */
48         public boolean  boost;  /* under power */
49         public int      rssi;
50         public int      status;
51
52         public double   ground_altitude;
53         public double   ground_pressure;
54         public double   altitude;
55         public double   height;
56         public double   pressure;
57         public double   acceleration;
58         public double   battery_voltage;
59         public double   pyro_voltage;
60         public double   temperature;
61         public double   apogee_voltage;
62         public double   main_voltage;
63         public double   speed;
64
65         public double   prev_height;
66         public double   prev_speed;
67         public double   prev_acceleration;
68
69         public double   max_height;
70         public double   max_acceleration;
71         public double   max_speed;
72
73         public double   kalman_height, kalman_speed, kalman_acceleration;
74
75         public AltosGPS gps;
76         public AltosGPS temp_gps;
77         public boolean  gps_pending;
78         public int gps_sequence;
79
80         public AltosIMU imu;
81         public AltosMag mag;
82
83         public static final int MIN_PAD_SAMPLES = 10;
84
85         public int      npad;
86         public int      gps_waiting;
87         public boolean  gps_ready;
88
89         public int      ngps;
90
91         public AltosGreatCircle from_pad;
92         public double   elevation;      /* from pad */
93         public double   range;          /* total distance */
94
95         public double   gps_height;
96
97         public double pad_lat, pad_lon, pad_alt;
98
99         public int      speak_tick;
100         public double   speak_altitude;
101
102         public String   callsign;
103         public double   accel_plus_g;
104         public double   accel_minus_g;
105         public double   accel;
106         public double   ground_accel;
107
108         public int      log_format;
109
110         public AltosMs5607      baro;
111
112         public AltosRecordCompanion     companion;
113
114         public double speed() {
115                 return speed;
116         }
117
118         public double max_speed() {
119                 return max_speed;
120         }
121
122         public void set_npad(int npad) {
123                 this.npad = npad;
124                 gps_waiting = MIN_PAD_SAMPLES - npad;
125                 if (this.gps_waiting < 0)
126                         gps_waiting = 0;
127                 gps_ready = gps_waiting == 0;
128         }
129
130         public void init() {
131                 record = null;
132
133                 set = 0;
134
135                 report_time = System.currentTimeMillis();
136                 time = AltosRecord.MISSING;
137                 time_change = AltosRecord.MISSING;
138                 prev_time = AltosRecord.MISSING;
139                 tick = AltosRecord.MISSING;
140                 boost_tick = AltosRecord.MISSING;
141                 state = AltosLib.ao_flight_invalid;
142                 flight = AltosRecord.MISSING;
143                 landed = false;
144                 boost = false;
145                 rssi = AltosRecord.MISSING;
146                 status = 0;
147
148                 ground_altitude = AltosRecord.MISSING;
149                 ground_pressure = AltosRecord.MISSING;
150                 altitude = AltosRecord.MISSING;
151                 height = AltosRecord.MISSING;
152                 pressure = AltosRecord.MISSING;
153                 acceleration = AltosRecord.MISSING;
154                 temperature = AltosRecord.MISSING;
155
156                 prev_height = AltosRecord.MISSING;
157                 prev_speed = AltosRecord.MISSING;
158                 prev_acceleration = AltosRecord.MISSING;
159
160                 battery_voltage = AltosRecord.MISSING;
161                 pyro_voltage = AltosRecord.MISSING;
162                 apogee_voltage = AltosRecord.MISSING;
163                 main_voltage = AltosRecord.MISSING;
164
165                 speed = AltosRecord.MISSING;
166
167                 kalman_height = AltosRecord.MISSING;
168                 kalman_speed = AltosRecord.MISSING;
169                 kalman_acceleration = AltosRecord.MISSING;
170
171                 max_speed = 0;
172                 max_height = 0;
173                 max_acceleration = 0;
174
175                 gps = null;
176                 temp_gps = null;
177                 gps_sequence = 0;
178                 gps_pending = false;
179
180                 imu = null;
181                 mag = null;
182
183                 set_npad(0);
184                 ngps = 0;
185
186                 from_pad = null;
187                 elevation = AltosRecord.MISSING;
188                 range = AltosRecord.MISSING;
189                 gps_height = AltosRecord.MISSING;
190
191                 pad_lat = AltosRecord.MISSING;
192                 pad_lon = AltosRecord.MISSING;
193                 pad_alt = AltosRecord.MISSING;
194
195                 speak_tick = AltosRecord.MISSING;
196                 speak_altitude = AltosRecord.MISSING;
197
198                 callsign = null;
199
200                 accel_plus_g = AltosRecord.MISSING;
201                 accel_minus_g = AltosRecord.MISSING;
202                 accel = AltosRecord.MISSING;
203                 ground_accel = AltosRecord.MISSING;
204                 log_format = AltosRecord.MISSING;
205                 serial = AltosRecord.MISSING;
206
207                 baro = null;
208                 companion = null;
209         }
210
211         void copy(AltosState old) {
212
213                 record = null;
214
215                 if (old == null) {
216                         init();
217                         return;
218                 }
219
220                 report_time = old.report_time;
221                 time = old.time;
222                 time_change = 0;
223                 tick = old.tick;
224                 boost_tick = old.boost_tick;
225
226                 state = old.state;
227                 flight = old.flight;
228                 landed = old.landed;
229                 ascent = old.ascent;
230                 boost = old.boost;
231                 rssi = old.rssi;
232                 status = old.status;
233                 
234                 set = 0;
235
236                 ground_altitude = old.ground_altitude;
237                 altitude = old.altitude;
238                 height = old.height;
239                 pressure = old.pressure;
240                 acceleration = old.acceleration;
241                 battery_voltage = old.battery_voltage;
242                 pyro_voltage = old.pyro_voltage;
243                 temperature = old.temperature;
244                 apogee_voltage = old.apogee_voltage;
245                 main_voltage = old.main_voltage;
246                 speed = old.speed;
247
248                 prev_height = old.height;
249                 prev_speed = old.speed;
250                 prev_acceleration = old.acceleration;
251                 prev_time = old.time;
252
253                 max_height = old.max_height;
254                 max_acceleration = old.max_acceleration;
255                 max_speed = old.max_speed;
256
257                 kalman_height = old.kalman_height;
258                 kalman_speed = old.kalman_speed;
259                 kalman_acceleration = old.kalman_acceleration;
260
261                 if (old.gps != null)
262                         gps = old.gps.clone();
263                 else
264                         gps = null;
265                 if (old.temp_gps != null)
266                         temp_gps = old.temp_gps.clone();
267                 else
268                         temp_gps = null;
269                 gps_sequence = old.gps_sequence;
270                 gps_pending = old.gps_pending;
271
272                 if (old.imu != null)
273                         imu = old.imu.clone();
274                 else
275                         imu = null;
276
277                 if (old.mag != null)
278                         mag = old.mag.clone();
279                 else
280                         mag = null;
281
282                 npad = old.npad;
283                 gps_waiting = old.gps_waiting;
284                 gps_ready = old.gps_ready;
285                 ngps = old.ngps;
286
287                 if (old.from_pad != null)
288                         from_pad = old.from_pad.clone();
289                 else
290                         from_pad = null;
291
292                 elevation = old.elevation;
293                 range = old.range;
294
295                 gps_height = old.gps_height;
296                 pad_lat = old.pad_lat;
297                 pad_lon = old.pad_lon;
298                 pad_alt = old.pad_alt;
299
300                 speak_tick = old.speak_tick;
301                 speak_altitude = old.speak_altitude;
302
303                 callsign = old.callsign;
304
305                 accel_plus_g = old.accel_plus_g;
306                 accel_minus_g = old.accel_minus_g;
307                 accel = old.accel;
308                 ground_accel = old.ground_accel;
309
310                 log_format = old.log_format;
311                 serial = old.serial;
312
313                 baro = old.baro;
314                 companion = old.companion;
315         }
316
317         double altitude() {
318                 if (altitude != AltosRecord.MISSING)
319                         return altitude;
320                 if (gps != null)
321                         return gps.alt;
322                 return AltosRecord.MISSING;
323         }
324
325         void update_vertical_pos() {
326
327                 double  alt = altitude();
328
329                 if (state == AltosLib.ao_flight_pad && alt != AltosRecord.MISSING && ground_pressure == AltosRecord.MISSING) {
330                         if (ground_altitude == AltosRecord.MISSING)
331                                 ground_altitude = alt;
332                         else
333                                 ground_altitude = (ground_altitude * 7 + alt) / 8;
334                 }
335
336                 if (kalman_height != AltosRecord.MISSING)
337                         height = kalman_height;
338                 else if (altitude != AltosRecord.MISSING && ground_altitude != AltosRecord.MISSING)
339                         height = altitude - ground_altitude;
340                 else
341                         height = AltosRecord.MISSING;
342
343                 if (height != AltosRecord.MISSING && height > max_height)
344                         max_height = height;
345
346                 update_speed();
347         }
348
349         double motion_filter_value() {
350                 return 1/ Math.exp(time_change/10.0);
351         }
352
353         void update_speed() {
354                 if (kalman_speed != AltosRecord.MISSING)
355                         speed = kalman_speed;
356                 else if (state != AltosLib.ao_flight_invalid &&
357                          time_change != AltosRecord.MISSING)
358                 {
359                         if (ascent && acceleration != AltosRecord.MISSING)
360                         {
361                                 if (prev_speed == AltosRecord.MISSING)
362                                         speed = acceleration * time_change;
363                                 else
364                                         speed = prev_speed + acceleration * time_change;
365                         }
366                         else if (height != AltosRecord.MISSING &&
367                                  prev_height != AltosRecord.MISSING &&
368                                  time_change != 0)
369                         {
370                                 double  new_speed = (height - prev_height) / time_change;
371
372                                 if (prev_speed == AltosRecord.MISSING)
373                                         speed = new_speed;
374                                 else {
375                                         double  filter = motion_filter_value();
376
377                                         speed = prev_speed * filter + new_speed * (1-filter);
378                                 }
379                         }
380                 }
381                 if (acceleration == AltosRecord.MISSING) {
382                         if (prev_speed != AltosRecord.MISSING && time_change != 0) {
383                                 double  new_acceleration = (speed - prev_speed) / time_change;
384
385                                 if (prev_acceleration == AltosRecord.MISSING)
386                                         acceleration = new_acceleration;
387                                 else {
388                                         double filter = motion_filter_value();
389
390                                         acceleration = prev_acceleration * filter + new_acceleration * (1-filter);
391                                 }
392                         }
393                 }
394                 if (boost && speed != AltosRecord.MISSING && speed > max_speed)
395                         max_speed = speed;
396         }
397         
398         void update_accel() {
399                 if (accel == AltosRecord.MISSING)
400                         return;
401                 if (ground_accel == AltosRecord.MISSING)
402                         return;
403                 if (accel_plus_g == AltosRecord.MISSING)
404                         return;
405                 if (accel_minus_g == AltosRecord.MISSING)
406                         return;
407
408                 double counts_per_g = (accel_minus_g - accel_plus_g) / 2.0;
409                 double counts_per_mss = counts_per_g / 9.80665;
410
411                 acceleration = (ground_accel - accel) / counts_per_mss;
412
413                 /* Only look at accelerometer data under boost */
414                 if (boost && acceleration != AltosRecord.MISSING && (max_acceleration == AltosRecord.MISSING || acceleration > max_acceleration))
415                         max_acceleration = acceleration;
416                 update_speed();
417         }
418
419         void update_time() {
420                 if (tick != AltosRecord.MISSING) {
421                         time = tick / 100.0;
422                         if (prev_time != AltosRecord.MISSING)
423                                 time_change = time - prev_time;
424                 }
425         }
426
427         void update_gps() {
428                 elevation = 0;
429                 range = -1;
430                 gps_height = 0;
431
432                 if (gps == null)
433                         return;
434
435                 if (gps.locked && gps.nsat >= 4) {
436                         /* Track consecutive 'good' gps reports, waiting for 10 of them */
437                         if (state == AltosLib.ao_flight_pad) {
438                                 set_npad(npad+1);
439                                 if (pad_lat != AltosRecord.MISSING) {
440                                         pad_lat = (pad_lat * 31 + gps.lat) / 32;
441                                         pad_lon = (pad_lon * 31 + gps.lon) / 32;
442                                         pad_alt = (pad_alt * 31 + gps.alt) / 32;
443                                 }
444                         }
445                         if (pad_lat == AltosRecord.MISSING) {
446                                 pad_lat = gps.lat;
447                                 pad_lon = gps.lon;
448                                 pad_alt = gps.alt;
449                         }
450                 }
451                 if (gps.lat != 0 && gps.lon != 0 &&
452                     pad_lat != AltosRecord.MISSING &&
453                     pad_lon != AltosRecord.MISSING)
454                 {
455                         double h = height;
456
457                         if (h == AltosRecord.MISSING)
458                                 h = 0;
459                         from_pad = new AltosGreatCircle(pad_lat, pad_lon, 0, gps.lat, gps.lon, h);
460                         elevation = from_pad.elevation;
461                         range = from_pad.range;
462                         gps_height = gps.alt - pad_alt;
463                 }
464         }
465
466         public void set_tick(int tick) {
467                 if (tick != AltosRecord.MISSING) {
468                         if (this.tick != AltosRecord.MISSING) {
469                                 while (tick < this.tick)
470                                         tick += 65536;
471                                 time_change = (tick - this.tick) / 100.0;
472                         } else
473                                 time_change = 0;
474                         this.tick = tick;
475                         update_time();
476                 }
477         }
478
479         public void set_boost_tick(int boost_tick) {
480                 if (boost_tick != AltosRecord.MISSING)
481                         this.boost_tick = boost_tick;
482         }
483
484         public String state_name() {
485                 return AltosLib.state_name(state);
486         }
487
488         public void set_state(int state) {
489                 if (state != AltosLib.ao_flight_invalid) {
490                         this.state = state;
491                         ascent = (AltosLib.ao_flight_boost <= state &&
492                                   state <= AltosLib.ao_flight_coast);
493                         boost = (AltosLib.ao_flight_boost == state);
494                 }
495
496         }
497
498         public void set_flight(int flight) {
499
500                 /* When the flight changes, reset the state */
501                 if (flight != AltosRecord.MISSING) {
502                         if (this.flight != AltosRecord.MISSING &&
503                             this.flight != flight) {
504                                 init();
505                         }
506                         this.flight = flight;
507                 }
508         }
509
510         public void set_serial(int serial) {
511                 /* When the serial changes, reset the state */
512                 if (serial != AltosRecord.MISSING) {
513                         if (this.serial != AltosRecord.MISSING &&
514                             this.serial != serial) {
515                                 init();
516                         }
517                         this.serial = serial;
518                 }
519         }
520
521         public int rssi() {
522                 if (rssi == AltosRecord.MISSING)
523                         return 0;
524                 return rssi;
525         }
526
527         public void set_rssi(int rssi, int status) {
528                 if (rssi != AltosRecord.MISSING) {
529                         this.rssi = rssi;
530                         this.status = status;
531                 }
532         }
533
534         public void set_altitude(double altitude) {
535                 if (altitude != AltosRecord.MISSING) {
536                         this.altitude = altitude;
537                         update_vertical_pos();
538                         set |= set_position;
539                 }
540         }
541
542         public void set_ground_altitude(double ground_altitude) {
543                 if (ground_altitude != AltosRecord.MISSING) {
544                         this.ground_altitude = ground_altitude;
545                         update_vertical_pos();
546                 }
547         }
548
549         public void set_ground_pressure (double pressure) {
550                 if (pressure != AltosRecord.MISSING) {
551                         this.ground_pressure = pressure;
552                         set_ground_altitude(AltosConvert.pressure_to_altitude(pressure));
553                         update_vertical_pos();
554                 }
555         }
556
557         public void set_gps(AltosGPS gps, int sequence) {
558                 if (gps != null) {
559                         System.out.printf ("gps date: %d-%d-%d time %d:%d:%d\n",
560                                            gps.year, gps.month, gps.day,
561                                            gps.hour, gps.minute, gps.second);
562                         this.gps = gps.clone();
563                         gps_sequence = sequence;
564                         update_gps();
565                         update_vertical_pos();
566                         set |= set_gps;
567                 }
568         }
569
570         public void set_kalman(double height, double speed, double acceleration) {
571                 if (height != AltosRecord.MISSING) {
572                         kalman_height = height;
573                         kalman_speed = speed;
574                         kalman_acceleration = acceleration;
575                         update_vertical_pos();
576                 }
577         }
578
579         public void set_pressure(double pressure) {
580                 if (pressure != AltosRecord.MISSING) {
581                         this.pressure = pressure;
582                         set_altitude(AltosConvert.pressure_to_altitude(pressure));
583                 }
584         }
585
586         public void make_baro() {
587                 if (baro == null)
588                         baro = new AltosMs5607();
589         }
590
591         public void set_ms5607(int pres, int temp) {
592                 if (baro != null) {
593                         baro.set(pres, temp);
594
595                         set_pressure(baro.pa);
596                         set_temperature(baro.cc / 100.0);
597                 }
598         }
599
600         public void make_companion (int nchannels) {
601                 if (companion == null)
602                         companion = new AltosRecordCompanion(nchannels);
603         }
604
605         public void set_companion(AltosRecordCompanion companion) {
606                 this.companion = companion;
607         }
608
609         public void set_accel_g(double accel_plus_g, double accel_minus_g) {
610                 if (accel_plus_g != AltosRecord.MISSING) {
611                         this.accel_plus_g = accel_plus_g;
612                         this.accel_minus_g = accel_minus_g;
613                         update_accel();
614                 }
615         }
616         public void set_ground_accel(double ground_accel) {
617                 if (ground_accel != AltosRecord.MISSING) {
618                         this.ground_accel = ground_accel;
619                         update_accel();
620                 }
621         }
622
623         public void set_accel(double accel) {
624                 if (accel != AltosRecord.MISSING) {
625                         this.accel = accel;
626                 }
627                 update_accel();
628         }
629
630         public void set_temperature(double temperature) {
631                 if (temperature != AltosRecord.MISSING) {
632                         this.temperature = temperature;
633                         set |= set_data;
634                 }
635         }
636
637         public void set_battery_voltage(double battery_voltage) {
638                 if (battery_voltage != AltosRecord.MISSING) {
639                         this.battery_voltage = battery_voltage;
640                         set |= set_data;
641                 }
642         }
643
644         public void set_pyro_voltage(double pyro_voltage) {
645                 if (pyro_voltage != AltosRecord.MISSING) {
646                         this.pyro_voltage = pyro_voltage;
647                         set |= set_data;
648                 }
649         }
650
651         public void set_apogee_voltage(double apogee_voltage) {
652                 if (apogee_voltage != AltosRecord.MISSING) {
653                         this.apogee_voltage = apogee_voltage;
654                         set |= set_data;
655                 }
656         }
657
658         public void set_main_voltage(double main_voltage) {
659                 if (main_voltage != AltosRecord.MISSING) {
660                         this.main_voltage = main_voltage;
661                         set |= set_data;
662                 }
663         }
664
665
666         public double time_since_boost() {
667                 if (tick == AltosRecord.MISSING)
668                         return 0.0;
669
670                 if (boost_tick != AltosRecord.MISSING) {
671                         return (tick - boost_tick) / 100.0;
672                 }
673                 return tick / 100.0;
674         }
675
676         public boolean valid() {
677                 return tick != AltosRecord.MISSING && serial != AltosRecord.MISSING;
678         }
679
680         public AltosGPS make_temp_gps() {
681                 if (temp_gps == null) {
682                         temp_gps = new AltosGPS(gps);
683                         temp_gps.cc_gps_sat = null;
684                 }
685                 gps_pending = true;
686                 return temp_gps;
687         }
688
689         public void set_temp_gps() {
690                 set_gps(temp_gps, gps_sequence + 1);
691                 gps_pending = false;
692                 temp_gps = null;
693         }
694
695         public void init (AltosRecord cur, AltosState prev_state) {
696
697                 System.out.printf ("init\n");
698                 if (cur == null)
699                         cur = new AltosRecord();
700
701                 record = cur;
702
703                 /* Discard previous state if it was for a different board */
704                 if (prev_state != null && prev_state.serial != cur.serial)
705                         prev_state = null;
706
707                 copy(prev_state);
708
709                 set_ground_altitude(cur.ground_altitude());
710                 set_altitude(cur.altitude());
711
712                 set_kalman(cur.kalman_height, cur.kalman_speed, cur.kalman_acceleration);
713
714                 report_time = System.currentTimeMillis();
715
716                 set_temperature(cur.temperature());
717                 set_apogee_voltage(cur.drogue_voltage());
718                 set_main_voltage(cur.main_voltage());
719                 set_battery_voltage(cur.battery_voltage());
720
721                 set_pressure(cur.pressure());
722
723                 set_tick(cur.tick);
724                 set_state(cur.state);
725
726                 set_accel_g (cur.accel_minus_g, cur.accel_plus_g);
727                 set_ground_accel(cur.ground_accel);
728                 set_accel (cur.accel);
729
730                 if (cur.gps_sequence != gps_sequence)
731                         set_gps(cur.gps, cur.gps_sequence);
732
733         }
734
735         public AltosState clone() {
736                 AltosState s = new AltosState();
737                 s.copy(this);
738                 return s;
739         }
740
741         public AltosState(AltosRecord cur) {
742                 init(cur, null);
743         }
744
745         public AltosState (AltosRecord cur, AltosState prev) {
746                 init(cur, prev);
747         }
748
749         public AltosState () {
750                 init();
751         }
752 }