15cf7d641b0f68a3248de98c443096579fe5938b
[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_11;
23
24 import java.io.*;
25
26 public class AltosState implements Cloneable {
27
28         public static final int set_position = 1;
29         public static final int set_gps = 2;
30         public static final int set_data = 4;
31
32         public int set;
33
34         static final double filter_len = 2.0;
35         static final double ascent_filter_len = 0.5;
36         static final double descent_filter_len = 5.0;
37
38         /* derived data */
39
40         public long     received_time;
41
42         public double   time;
43         public double   prev_time;
44         public double   time_change;
45         public int      tick;
46         private int     prev_tick;
47         public int      boost_tick;
48
49         class AltosValue {
50                 double  value;
51                 double  prev_value;
52                 private double  max_value;
53                 private double  set_time;
54                 private double  prev_set_time;
55
56                 boolean can_max() { return true; }
57
58                 void set(double new_value, double time) {
59                         if (new_value != AltosLib.MISSING) {
60                                 value = new_value;
61                                 if (can_max() && (max_value == AltosLib.MISSING || value > max_value))
62                                         max_value = value;
63                                 set_time = time;
64                         }
65                 }
66
67                 void set_filtered(double new_value, double time) {
68                         if (prev_value != AltosLib.MISSING) {
69                                 double f = 1/Math.exp((time - prev_set_time) / filter_len);
70                                 new_value = f * new_value + (1-f) * prev_value;
71                         }
72                         set(new_value, time);
73                 }
74
75                 double value() {
76                         return value;
77                 }
78
79                 double max() {
80                         return max_value;
81                 }
82
83                 double prev() {
84                         return prev_value;
85                 }
86
87                 double change() {
88                         if (value != AltosLib.MISSING && prev_value != AltosLib.MISSING)
89                                 return value - prev_value;
90                         return AltosLib.MISSING;
91                 }
92
93                 double rate() {
94                         double c = change();
95                         double t = set_time - prev_set_time;
96
97                         if (c != AltosLib.MISSING && t != 0)
98                                 return c / t;
99                         return AltosLib.MISSING;
100                 }
101
102                 double integrate() {
103                         if (value == AltosLib.MISSING)
104                                 return AltosLib.MISSING;
105                         if (prev_value == AltosLib.MISSING)
106                                 return AltosLib.MISSING;
107
108                         return (value + prev_value) / 2 * (set_time - prev_set_time);
109                 }
110
111                 double time() {
112                         return set_time;
113                 }
114
115                 void set_derivative(AltosValue in) {
116                         double  n = in.rate();
117
118                         if (n == AltosLib.MISSING)
119                                 return;
120
121                         double  p = prev_value;
122                         double  pt = prev_set_time;
123
124                         if (p == AltosLib.MISSING) {
125                                 p = 0;
126                                 pt = in.time() - 0.01;
127                         }
128
129                         /* Clip changes to reduce noise */
130                         double  ddt = in.time() - pt;
131                         double  ddv = (n - p) / ddt;
132
133                         final double max = 100000;
134
135                         /* 100gs */
136                         if (Math.abs(ddv) > max) {
137                                 if (n > p)
138                                         n = p + ddt * max;
139                                 else
140                                         n = p - ddt * max;
141                         }
142
143                         double filter_len;
144
145                         if (ascent)
146                                 filter_len = ascent_filter_len;
147                         else
148                                 filter_len = descent_filter_len;
149
150                         double f = 1/Math.exp(ddt/ filter_len);
151                         n = p * f + n * (1-f);
152
153                         set(n, in.time());
154                 }
155
156                 void set_integral(AltosValue in) {
157                         double  change = in.integrate();
158
159                         if (change != AltosLib.MISSING) {
160                                 double  prev = prev_value;
161                                 if (prev == AltosLib.MISSING)
162                                         prev = 0;
163                                 set(prev + change, in.time());
164                         }
165                 }
166
167                 void copy(AltosValue old) {
168                         value = old.value;
169                         set_time = old.set_time;
170                         prev_value = old.value;
171                         prev_set_time = old.set_time;
172                         max_value = old.max_value;
173                 }
174
175                 void finish_update() {
176                         prev_value = value;
177                         prev_set_time = set_time;
178                 }
179
180                 AltosValue() {
181                         value = AltosLib.MISSING;
182                         prev_value = AltosLib.MISSING;
183                         max_value = AltosLib.MISSING;
184                 }
185
186         }
187
188         class AltosCValue {
189
190                 class AltosIValue extends AltosValue {
191                         boolean can_max() {
192                                 return c_can_max();
193                         }
194
195                         AltosIValue() {
196                                 super();
197                         }
198                 };
199
200                 public AltosIValue      measured;
201                 public AltosIValue      computed;
202
203                 boolean can_max() { return true; }
204
205                 boolean c_can_max() { return can_max(); }
206
207                 double value() {
208                         double v = measured.value();
209                         if (v != AltosLib.MISSING)
210                                 return v;
211                         return computed.value();
212                 }
213
214                 boolean is_measured() {
215                         return measured.value() != AltosLib.MISSING;
216                 }
217
218                 double max() {
219                         double m = measured.max();
220
221                         if (m != AltosLib.MISSING)
222                                 return m;
223                         return computed.max();
224                 }
225
226                 double prev_value() {
227                         if (measured.value != AltosLib.MISSING && measured.prev_value != AltosLib.MISSING)
228                                 return measured.prev_value;
229                         return computed.prev_value;
230                 }
231
232                 AltosValue altos_value() {
233                         if (measured.value() != AltosLib.MISSING)
234                                 return measured;
235                         return computed;
236                 }
237
238                 double change() {
239                         double c = measured.change();
240                         if (c == AltosLib.MISSING)
241                                 c = computed.change();
242                         return c;
243                 }
244
245                 double rate() {
246                         double r = measured.rate();
247                         if (r == AltosLib.MISSING)
248                                 r = computed.rate();
249                         return r;
250                 }
251
252                 void set_measured(double new_value, double time) {
253                         measured.set(new_value, time);
254                 }
255
256                 void set_computed(double new_value, double time) {
257                         computed.set(new_value, time);
258                 }
259
260                 void set_derivative(AltosValue in) {
261                         computed.set_derivative(in);
262                 }
263
264                 void set_derivative(AltosCValue in) {
265                         set_derivative(in.altos_value());
266                 }
267
268                 void set_integral(AltosValue in) {
269                         computed.set_integral(in);
270                 }
271
272                 void set_integral(AltosCValue in) {
273                         set_integral(in.altos_value());
274                 }
275
276                 void copy(AltosCValue old) {
277                         measured.copy(old.measured);
278                         computed.copy(old.computed);
279                 }
280
281                 void finish_update() {
282                         measured.finish_update();
283                         computed.finish_update();
284                 }
285
286                 public AltosCValue() {
287                         measured = new AltosIValue();
288                         computed = new AltosIValue();
289                 }
290         }
291
292         private int     state;
293         public int      flight;
294         public int      serial;
295         public int      altitude_32;
296         public int      receiver_serial;
297         public boolean  landed;
298         public boolean  ascent; /* going up? */
299         public boolean  boost;  /* under power */
300         public int      rssi;
301         public int      status;
302         public int      device_type;
303         public int      config_major;
304         public int      config_minor;
305         public int      apogee_delay;
306         public int      main_deploy;
307         public int      flight_log_max;
308
309         private double pressure_to_altitude(double p) {
310                 if (p == AltosLib.MISSING)
311                         return AltosLib.MISSING;
312                 return AltosConvert.pressure_to_altitude(p);
313         }
314
315         private AltosCValue ground_altitude;
316
317         public double ground_altitude() {
318                 return ground_altitude.value();
319         }
320
321         public void set_ground_altitude(double a) {
322                 ground_altitude.set_measured(a, time);
323         }
324
325         class AltosGpsGroundAltitude extends AltosValue {
326                 void set(double a, double t) {
327                         super.set(a, t);
328                         pad_alt = value();
329                         gps_altitude.set_gps_height();
330                 }
331
332                 void set_filtered(double a, double t) {
333                         super.set_filtered(a, t);
334                         pad_alt = value();
335                         gps_altitude.set_gps_height();
336                 }
337
338                 AltosGpsGroundAltitude() {
339                         super();
340                 }
341         }
342
343         private AltosGpsGroundAltitude gps_ground_altitude;
344
345         public double gps_ground_altitude() {
346                 return gps_ground_altitude.value();
347         }
348
349         public void set_gps_ground_altitude(double a) {
350                 gps_ground_altitude.set(a, time);
351         }
352
353         class AltosGroundPressure extends AltosCValue {
354                 void set_filtered(double p, double time) {
355                         computed.set_filtered(p, time);
356                         if (!is_measured())
357                                 ground_altitude.set_computed(pressure_to_altitude(computed.value()), time);
358                 }
359
360                 void set_measured(double p, double time) {
361                         super.set_measured(p, time);
362                         ground_altitude.set_computed(pressure_to_altitude(p), time);
363                 }
364
365                 AltosGroundPressure () {
366                         super();
367                 }
368         }
369
370         private AltosGroundPressure ground_pressure;
371
372         public double ground_pressure() {
373                 return ground_pressure.value();
374         }
375
376         public void set_ground_pressure (double pressure) {
377                 ground_pressure.set_measured(pressure, time);
378         }
379
380         class AltosAltitude extends AltosCValue {
381
382                 private void set_speed(AltosValue v) {
383                         if (!acceleration.is_measured() || !ascent)
384                                 speed.set_derivative(this);
385                 }
386
387                 void set_computed(double a, double time) {
388                         super.set_computed(a,time);
389                         set_speed(computed);
390                         set |= set_position;
391                 }
392
393                 void set_measured(double a, double time) {
394                         super.set_measured(a,time);
395                         set_speed(measured);
396                         set |= set_position;
397                 }
398
399                 AltosAltitude() {
400                         super();
401                 }
402         }
403
404         private AltosAltitude   altitude;
405
406         class AltosGpsAltitude extends AltosValue {
407
408                 private void set_gps_height() {
409                         double  a = value();
410                         double  g = gps_ground_altitude.value();
411
412                         if (a != AltosLib.MISSING && g != AltosLib.MISSING)
413                                 gps_height = a - g;
414                         else
415                                 gps_height = AltosLib.MISSING;
416                 }
417
418                 void set(double a, double t) {
419                         super.set(a, t);
420                         set_gps_height();
421                 }
422
423                 AltosGpsAltitude() {
424                         super();
425                 }
426         }
427
428         private AltosGpsAltitude        gps_altitude;
429
430         private AltosValue              gps_ground_speed;
431         private AltosValue              gps_ascent_rate;
432         private AltosValue              gps_course;
433         private AltosValue              gps_speed;
434
435         public double altitude() {
436                 double a = altitude.value();
437                 if (a != AltosLib.MISSING)
438                         return a;
439                 return gps_altitude.value();
440         }
441
442         public double max_altitude() {
443                 double a = altitude.max();
444                 if (a != AltosLib.MISSING)
445                         return a;
446                 return gps_altitude.max();
447         }
448
449         public void set_altitude(double new_altitude) {
450                 altitude.set_measured(new_altitude, time);
451         }
452
453         public double gps_altitude() {
454                 return gps_altitude.value();
455         }
456
457         public double max_gps_altitude() {
458                 return gps_altitude.max();
459         }
460
461         public void set_gps_altitude(double new_gps_altitude) {
462                 gps_altitude.set(new_gps_altitude, time);
463         }
464
465         public double gps_ground_speed() {
466                 return gps_ground_speed.value();
467         }
468
469         public double max_gps_ground_speed() {
470                 return gps_ground_speed.max();
471         }
472
473         public double gps_ascent_rate() {
474                 return gps_ascent_rate.value();
475         }
476
477         public double max_gps_ascent_rate() {
478                 return gps_ascent_rate.max();
479         }
480
481         public double gps_course() {
482                 return gps_course.value();
483         }
484
485         public double gps_speed() {
486                 return gps_speed.value();
487         }
488
489         public double max_gps_speed() {
490                 return gps_speed.max();
491         }
492
493         class AltosPressure extends AltosValue {
494                 void set(double p, double time) {
495                         super.set(p, time);
496                         if (state == AltosLib.ao_flight_pad)
497                                 ground_pressure.set_filtered(p, time);
498                         double a = pressure_to_altitude(p);
499                         altitude.set_computed(a, time);
500                 }
501
502                 AltosPressure() {
503                         super();
504                 }
505         }
506
507         private AltosPressure   pressure;
508
509         public double pressure() {
510                 return pressure.value();
511         }
512
513         public void set_pressure(double p) {
514                 pressure.set(p, time);
515         }
516
517         public double baro_height() {
518                 double a = altitude();
519                 double g = ground_altitude();
520                 if (a != AltosLib.MISSING && g != AltosLib.MISSING)
521                         return a - g;
522                 return AltosLib.MISSING;
523         }
524
525         public double height() {
526                 double k = kalman_height.value();
527                 if (k != AltosLib.MISSING)
528                         return k;
529
530                 double b = baro_height();
531                 if (b != AltosLib.MISSING)
532                         return b;
533
534                 return gps_height();
535         }
536
537         public double max_height() {
538                 double  k = kalman_height.max();
539                 if (k != AltosLib.MISSING)
540                         return k;
541
542                 double a = altitude.max();
543                 double g = ground_altitude();
544                 if (a != AltosLib.MISSING && g != AltosLib.MISSING)
545                         return a - g;
546                 return max_gps_height();
547         }
548
549         public double gps_height() {
550                 double a = gps_altitude();
551                 double g = gps_ground_altitude();
552
553                 if (a != AltosLib.MISSING && g != AltosLib.MISSING)
554                         return a - g;
555                 return AltosLib.MISSING;
556         }
557
558         public double max_gps_height() {
559                 double a = gps_altitude.max();
560                 double g = gps_ground_altitude();
561
562                 if (a != AltosLib.MISSING && g != AltosLib.MISSING)
563                         return a - g;
564                 return AltosLib.MISSING;
565         }
566
567         class AltosSpeed extends AltosCValue {
568
569                 boolean can_max() {
570                         return state < AltosLib.ao_flight_fast || state == AltosLib.ao_flight_stateless;
571                 }
572
573                 void set_accel() {
574                         acceleration.set_derivative(this);
575                 }
576
577                 void set_derivative(AltosCValue in) {
578                         super.set_derivative(in);
579                         set_accel();
580                 }
581
582                 void set_computed(double new_value, double time) {
583                         super.set_computed(new_value, time);
584                         set_accel();
585                 }
586
587                 void set_measured(double new_value, double time) {
588                         super.set_measured(new_value, time);
589                         set_accel();
590                 }
591
592                 AltosSpeed() {
593                         super();
594                 }
595         }
596
597         private AltosSpeed speed;
598
599         public double speed() {
600                 double v = kalman_speed.value();
601                 if (v != AltosLib.MISSING)
602                         return v;
603                 v = speed.value();
604                 if (v != AltosLib.MISSING)
605                         return v;
606                 v = gps_speed();
607                 if (v != AltosLib.MISSING)
608                         return v;
609                 return AltosLib.MISSING;
610         }
611
612         public double max_speed() {
613                 double v = kalman_speed.max();
614                 if (v != AltosLib.MISSING)
615                         return v;
616                 v = speed.max();
617                 if (v != AltosLib.MISSING)
618                         return v;
619                 v = max_gps_speed();
620                 if (v != AltosLib.MISSING)
621                         return v;
622                 return AltosLib.MISSING;
623         }
624
625         class AltosAccel extends AltosCValue {
626
627                 boolean can_max() {
628                         return state < AltosLib.ao_flight_fast || state == AltosLib.ao_flight_stateless;
629                 }
630
631                 void set_measured(double a, double time) {
632                         super.set_measured(a, time);
633                         if (ascent)
634                                 speed.set_integral(this.measured);
635                 }
636
637                 AltosAccel() {
638                         super();
639                 }
640         }
641
642         AltosAccel acceleration;
643
644         public double acceleration() {
645                 return acceleration.value();
646         }
647
648         public double max_acceleration() {
649                 return acceleration.max();
650         }
651
652         public AltosCValue      orient;
653
654         public void set_orient(double new_orient) {
655                 orient.set_measured(new_orient, time);
656         }
657
658         public double orient() {
659                 return orient.value();
660         }
661
662         public double max_orient() {
663                 return orient.max();
664         }
665
666         public AltosValue       kalman_height, kalman_speed, kalman_acceleration;
667
668         public void set_kalman(double height, double speed, double acceleration) {
669                 kalman_height.set(height, time);
670                 kalman_speed.set(speed, time);
671                 kalman_acceleration.set(acceleration, time);
672         }
673
674         public double   battery_voltage;
675         public double   pyro_voltage;
676         public double   temperature;
677         public double   apogee_voltage;
678         public double   main_voltage;
679
680         public double   ignitor_voltage[];
681
682         public AltosGPS gps;
683         public AltosGPS temp_gps;
684         public int temp_gps_sat_tick;
685         public boolean  gps_pending;
686         public int gps_sequence;
687
688         public AltosIMU imu;
689         public AltosMag mag;
690
691         public static final int MIN_PAD_SAMPLES = 10;
692
693         public int      npad;
694         public int      gps_waiting;
695         public boolean  gps_ready;
696
697         public int      ngps;
698
699         public AltosGreatCircle from_pad;
700         public double   elevation;      /* from pad */
701         public double   range;          /* total distance */
702
703         public double   gps_height;
704
705         public double pad_lat, pad_lon, pad_alt;
706
707         public int      speak_tick;
708         public double   speak_altitude;
709
710         public String   callsign;
711         public String   firmware_version;
712
713         public double   accel_plus_g;
714         public double   accel_minus_g;
715         public double   accel;
716         public double   ground_accel;
717         public double   ground_accel_avg;
718
719         public int      log_format;
720         public int      log_space;
721         public String   product;
722
723         public AltosMs5607      baro;
724
725         public AltosCompanion   companion;
726
727         public int      pyro_fired;
728
729         public void set_npad(int npad) {
730                 this.npad = npad;
731                 gps_waiting = MIN_PAD_SAMPLES - npad;
732                 if (this.gps_waiting < 0)
733                         gps_waiting = 0;
734                 gps_ready = gps_waiting == 0;
735         }
736
737         public void init() {
738                 set = 0;
739
740                 received_time = System.currentTimeMillis();
741                 time = AltosLib.MISSING;
742                 time_change = AltosLib.MISSING;
743                 prev_time = AltosLib.MISSING;
744                 tick = AltosLib.MISSING;
745                 prev_tick = AltosLib.MISSING;
746                 boost_tick = AltosLib.MISSING;
747                 state = AltosLib.ao_flight_invalid;
748                 flight = AltosLib.MISSING;
749                 landed = false;
750                 boost = false;
751                 rssi = AltosLib.MISSING;
752                 status = 0;
753                 device_type = AltosLib.MISSING;
754                 config_major = AltosLib.MISSING;
755                 config_minor = AltosLib.MISSING;
756                 apogee_delay = AltosLib.MISSING;
757                 main_deploy = AltosLib.MISSING;
758                 flight_log_max = AltosLib.MISSING;
759
760                 ground_altitude = new AltosCValue();
761                 ground_pressure = new AltosGroundPressure();
762                 altitude = new AltosAltitude();
763                 pressure = new AltosPressure();
764                 speed = new AltosSpeed();
765                 acceleration = new AltosAccel();
766                 orient = new AltosCValue();
767
768                 temperature = AltosLib.MISSING;
769                 battery_voltage = AltosLib.MISSING;
770                 pyro_voltage = AltosLib.MISSING;
771                 apogee_voltage = AltosLib.MISSING;
772                 main_voltage = AltosLib.MISSING;
773                 ignitor_voltage = null;
774
775                 kalman_height = new AltosValue();
776                 kalman_speed = new AltosValue();
777                 kalman_acceleration = new AltosValue();
778
779                 gps = null;
780                 temp_gps = null;
781                 temp_gps_sat_tick = 0;
782                 gps_sequence = 0;
783                 gps_pending = false;
784
785                 imu = null;
786                 last_imu_time = AltosLib.MISSING;
787                 rotation = null;
788                 ground_rotation = null;
789
790                 mag = null;
791                 accel_zero_along = AltosLib.MISSING;
792                 accel_zero_across = AltosLib.MISSING;
793                 accel_zero_through = AltosLib.MISSING;
794
795                 accel_ground_along = AltosLib.MISSING;
796                 accel_ground_across = AltosLib.MISSING;
797                 accel_ground_through = AltosLib.MISSING;
798
799                 pad_orientation = AltosLib.MISSING;
800
801                 gyro_zero_roll = AltosLib.MISSING;
802                 gyro_zero_pitch = AltosLib.MISSING;
803                 gyro_zero_yaw = AltosLib.MISSING;
804
805                 set_npad(0);
806                 ngps = 0;
807
808                 from_pad = null;
809                 elevation = AltosLib.MISSING;
810                 range = AltosLib.MISSING;
811                 gps_height = AltosLib.MISSING;
812
813                 pad_lat = AltosLib.MISSING;
814                 pad_lon = AltosLib.MISSING;
815                 pad_alt = AltosLib.MISSING;
816
817                 gps_altitude = new AltosGpsAltitude();
818                 gps_ground_altitude = new AltosGpsGroundAltitude();
819                 gps_ground_speed = new AltosValue();
820                 gps_speed = new AltosValue();
821                 gps_ascent_rate = new AltosValue();
822                 gps_course = new AltosValue();
823
824                 speak_tick = AltosLib.MISSING;
825                 speak_altitude = AltosLib.MISSING;
826
827                 callsign = null;
828                 firmware_version = null;
829
830                 accel_plus_g = AltosLib.MISSING;
831                 accel_minus_g = AltosLib.MISSING;
832                 accel = AltosLib.MISSING;
833
834                 ground_accel = AltosLib.MISSING;
835                 ground_accel_avg = AltosLib.MISSING;
836
837                 log_format = AltosLib.MISSING;
838                 log_space = AltosLib.MISSING;
839                 product = null;
840                 serial = AltosLib.MISSING;
841                 receiver_serial = AltosLib.MISSING;
842                 altitude_32 = AltosLib.MISSING;
843
844                 baro = null;
845                 companion = null;
846
847                 pyro_fired = 0;
848         }
849
850         void finish_update() {
851                 prev_tick = tick;
852
853                 ground_altitude.finish_update();
854                 altitude.finish_update();
855                 pressure.finish_update();
856                 speed.finish_update();
857                 acceleration.finish_update();
858                 orient.finish_update();
859
860                 kalman_height.finish_update();
861                 kalman_speed.finish_update();
862                 kalman_acceleration.finish_update();
863         }
864
865         void copy(AltosState old) {
866
867                 if (old == null) {
868                         init();
869                         return;
870                 }
871
872                 received_time = old.received_time;
873                 time = old.time;
874                 time_change = old.time_change;
875                 prev_time = old.time;
876
877                 tick = old.tick;
878                 prev_tick = old.tick;
879                 boost_tick = old.boost_tick;
880
881                 state = old.state;
882                 flight = old.flight;
883                 landed = old.landed;
884                 ascent = old.ascent;
885                 boost = old.boost;
886                 rssi = old.rssi;
887                 status = old.status;
888                 device_type = old.device_type;
889                 config_major = old.config_major;
890                 config_minor = old.config_minor;
891                 apogee_delay = old.apogee_delay;
892                 main_deploy = old.main_deploy;
893                 flight_log_max = old.flight_log_max;
894
895                 set = 0;
896
897                 ground_pressure.copy(old.ground_pressure);
898                 ground_altitude.copy(old.ground_altitude);
899                 altitude.copy(old.altitude);
900                 pressure.copy(old.pressure);
901                 speed.copy(old.speed);
902                 acceleration.copy(old.acceleration);
903                 orient.copy(old.orient);
904
905                 battery_voltage = old.battery_voltage;
906                 pyro_voltage = old.pyro_voltage;
907                 temperature = old.temperature;
908                 apogee_voltage = old.apogee_voltage;
909                 main_voltage = old.main_voltage;
910                 ignitor_voltage = old.ignitor_voltage;
911
912                 kalman_height.copy(old.kalman_height);
913                 kalman_speed.copy(old.kalman_speed);
914                 kalman_acceleration.copy(old.kalman_acceleration);
915
916                 if (old.gps != null)
917                         gps = old.gps.clone();
918                 else
919                         gps = null;
920                 if (old.temp_gps != null)
921                         temp_gps = old.temp_gps.clone();
922                 else
923                         temp_gps = null;
924                 temp_gps_sat_tick = old.temp_gps_sat_tick;
925                 gps_sequence = old.gps_sequence;
926                 gps_pending = old.gps_pending;
927
928                 if (old.imu != null)
929                         imu = old.imu.clone();
930                 else
931                         imu = null;
932                 last_imu_time = old.last_imu_time;
933
934                 if (old.rotation != null)
935                         rotation = new AltosRotation (old.rotation);
936
937                 if (old.ground_rotation != null) {
938                         ground_rotation = new AltosRotation(old.ground_rotation);
939                 }
940
941                 accel_zero_along = old.accel_zero_along;
942                 accel_zero_across = old.accel_zero_across;
943                 accel_zero_through = old.accel_zero_through;
944
945                 accel_ground_along = old.accel_ground_along;
946                 accel_ground_across = old.accel_ground_across;
947                 accel_ground_through = old.accel_ground_through;
948                 pad_orientation = old.pad_orientation;
949
950                 gyro_zero_roll = old.gyro_zero_roll;
951                 gyro_zero_pitch = old.gyro_zero_pitch;
952                 gyro_zero_yaw = old.gyro_zero_yaw;
953
954                 if (old.mag != null)
955                         mag = old.mag.clone();
956                 else
957                         mag = null;
958
959                 npad = old.npad;
960                 gps_waiting = old.gps_waiting;
961                 gps_ready = old.gps_ready;
962                 ngps = old.ngps;
963
964                 if (old.from_pad != null)
965                         from_pad = old.from_pad.clone();
966                 else
967                         from_pad = null;
968
969                 elevation = old.elevation;
970                 range = old.range;
971
972                 gps_height = old.gps_height;
973
974                 gps_altitude.copy(old.gps_altitude);
975                 gps_ground_altitude.copy(old.gps_ground_altitude);
976                 gps_ground_speed.copy(old.gps_ground_speed);
977                 gps_ascent_rate.copy(old.gps_ascent_rate);
978                 gps_course.copy(old.gps_course);
979                 gps_speed.copy(old.gps_speed);
980
981                 pad_lat = old.pad_lat;
982                 pad_lon = old.pad_lon;
983                 pad_alt = old.pad_alt;
984
985                 speak_tick = old.speak_tick;
986                 speak_altitude = old.speak_altitude;
987
988                 callsign = old.callsign;
989                 firmware_version = old.firmware_version;
990
991                 accel_plus_g = old.accel_plus_g;
992                 accel_minus_g = old.accel_minus_g;
993                 accel = old.accel;
994                 ground_accel = old.ground_accel;
995                 ground_accel_avg = old.ground_accel_avg;
996
997                 log_format = old.log_format;
998                 log_space = old.log_space;
999                 product = old.product;
1000                 serial = old.serial;
1001                 receiver_serial = old.receiver_serial;
1002                 altitude_32 = old.altitude_32;
1003
1004                 baro = old.baro;
1005                 companion = old.companion;
1006
1007                 pyro_fired = old.pyro_fired;
1008         }
1009
1010         void update_time() {
1011         }
1012
1013         void update_gps() {
1014                 elevation = AltosLib.MISSING;
1015                 range = AltosLib.MISSING;
1016
1017                 if (gps == null)
1018                         return;
1019
1020                 if (gps.locked && gps.nsat >= 4) {
1021                         /* Track consecutive 'good' gps reports, waiting for 10 of them */
1022                         if (state == AltosLib.ao_flight_pad || state == AltosLib.ao_flight_stateless) {
1023                                 set_npad(npad+1);
1024                                 if (pad_lat != AltosLib.MISSING && (npad < 10 || state == AltosLib.ao_flight_pad)) {
1025                                         pad_lat = (pad_lat * 31 + gps.lat) / 32;
1026                                         pad_lon = (pad_lon * 31 + gps.lon) / 32;
1027                                         gps_ground_altitude.set_filtered(gps.alt, time);
1028                                 }
1029                         }
1030                         if (pad_lat == AltosLib.MISSING) {
1031                                 pad_lat = gps.lat;
1032                                 pad_lon = gps.lon;
1033                                 gps_ground_altitude.set(gps.alt, time);
1034                         }
1035                         gps_altitude.set(gps.alt, time);
1036                         if (gps.climb_rate != AltosLib.MISSING)
1037                                 gps_ascent_rate.set(gps.climb_rate, time);
1038                         if (gps.ground_speed != AltosLib.MISSING)
1039                                 gps_ground_speed.set(gps.ground_speed, time);
1040                         if (gps.climb_rate != AltosLib.MISSING && gps.ground_speed != AltosLib.MISSING)
1041                                 gps_speed.set(Math.sqrt(gps.ground_speed * gps.ground_speed +
1042                                                         gps.climb_rate * gps.climb_rate), time);
1043                         if (gps.course != AltosLib.MISSING)
1044                                 gps_course.set(gps.course, time);
1045                 }
1046                 if (gps.lat != 0 && gps.lon != 0 &&
1047                     pad_lat != AltosLib.MISSING &&
1048                     pad_lon != AltosLib.MISSING)
1049                 {
1050                         double h = height();
1051
1052                         if (h == AltosLib.MISSING)
1053                                 h = 0;
1054                         from_pad = new AltosGreatCircle(pad_lat, pad_lon, 0, gps.lat, gps.lon, h);
1055                         elevation = from_pad.elevation;
1056                         range = from_pad.range;
1057                 }
1058         }
1059
1060         public void set_tick(int new_tick) {
1061                 if (new_tick != AltosLib.MISSING) {
1062                         if (prev_tick != AltosLib.MISSING) {
1063                                 while (new_tick < prev_tick - 1000) {
1064                                         new_tick += 65536;
1065                                 }
1066                         }
1067                         tick = new_tick;
1068                         time = tick / 100.0;
1069                         time_change = time - prev_time;
1070                 }
1071         }
1072
1073         public void set_boost_tick(int boost_tick) {
1074                 if (boost_tick != AltosLib.MISSING)
1075                         this.boost_tick = boost_tick;
1076         }
1077
1078         public String state_name() {
1079                 return AltosLib.state_name(state);
1080         }
1081
1082         public void set_product(String product) {
1083                 this.product = product;
1084         }
1085
1086         public void set_state(int state) {
1087                 if (state != AltosLib.ao_flight_invalid) {
1088                         this.state = state;
1089                         ascent = (AltosLib.ao_flight_boost <= state &&
1090                                   state <= AltosLib.ao_flight_coast);
1091                         boost = (AltosLib.ao_flight_boost == state);
1092                 }
1093         }
1094
1095         public int state() {
1096                 return state;
1097         }
1098
1099         public void set_device_type(int device_type) {
1100                 this.device_type = device_type;
1101                 switch (device_type) {
1102                 case AltosLib.product_telegps:
1103                         this.state = AltosLib.ao_flight_stateless;
1104                         break;
1105                 }
1106         }
1107
1108         public void set_log_format(int log_format) {
1109                 this.log_format = log_format;
1110                 switch (log_format) {
1111                 case AltosLib.AO_LOG_FORMAT_TELEGPS:
1112                         this.state = AltosLib.ao_flight_stateless;
1113                         break;
1114                 }
1115         }
1116
1117         public void set_log_space(int log_space) {
1118                 this.log_space = log_space;
1119         }
1120
1121         public void set_flight_params(int apogee_delay, int main_deploy) {
1122                 this.apogee_delay = apogee_delay;
1123                 this.main_deploy = main_deploy;
1124         }
1125
1126         public void set_config(int major, int minor, int flight_log_max) {
1127                 config_major = major;
1128                 config_minor = minor;
1129                 this.flight_log_max = flight_log_max;
1130         }
1131
1132         public void set_callsign(String callsign) {
1133                 this.callsign = callsign;
1134         }
1135
1136         public void set_firmware_version(String version) {
1137                 firmware_version = version;
1138         }
1139
1140         public int compare_version(String other_version) {
1141                 if (firmware_version == null)
1142                         return AltosLib.MISSING;
1143                 return AltosLib.compare_version(firmware_version, other_version);
1144         }
1145
1146         private void re_init() {
1147                 int bt = boost_tick;
1148                 int rs = receiver_serial;
1149                 init();
1150                 boost_tick = bt;
1151                 receiver_serial = rs;
1152         }
1153
1154         public void set_flight(int flight) {
1155
1156                 /* When the flight changes, reset the state */
1157                 if (flight != AltosLib.MISSING) {
1158                         if (this.flight != AltosLib.MISSING &&
1159                             this.flight != flight) {
1160                                 re_init();
1161                         }
1162                         this.flight = flight;
1163                 }
1164         }
1165
1166         public void set_serial(int serial) {
1167                 /* When the serial changes, reset the state */
1168                 if (serial != AltosLib.MISSING) {
1169                         if (this.serial != AltosLib.MISSING &&
1170                             this.serial != serial) {
1171                                 re_init();
1172                         }
1173                         this.serial = serial;
1174                 }
1175         }
1176
1177         public void set_receiver_serial(int serial) {
1178                 if (serial != AltosLib.MISSING)
1179                         receiver_serial = serial;
1180         }
1181
1182         public boolean altitude_32() {
1183                 return altitude_32 == 1;
1184         }
1185
1186         public void set_altitude_32(int altitude_32) {
1187                 if (altitude_32 != AltosLib.MISSING)
1188                         this.altitude_32 = altitude_32;
1189         }
1190
1191         public int rssi() {
1192                 if (rssi == AltosLib.MISSING)
1193                         return 0;
1194                 return rssi;
1195         }
1196
1197         public void set_rssi(int rssi, int status) {
1198                 if (rssi != AltosLib.MISSING) {
1199                         this.rssi = rssi;
1200                         this.status = status;
1201                 }
1202         }
1203
1204         public void set_received_time(long ms) {
1205                 received_time = ms;
1206         }
1207
1208         public void set_gps(AltosGPS gps, int sequence) {
1209                 if (gps != null) {
1210                         this.gps = gps.clone();
1211                         gps_sequence = sequence;
1212                         update_gps();
1213                         set |= set_gps;
1214                 }
1215         }
1216
1217
1218         public double   accel_zero_along;
1219         public double   accel_zero_across;
1220         public double   accel_zero_through;
1221
1222         public AltosRotation    rotation;
1223         public AltosRotation    ground_rotation;
1224
1225         public void set_accel_zero(double zero_along, double zero_across, double zero_through) {
1226                 if (zero_along != AltosLib.MISSING) {
1227                         accel_zero_along = zero_along;
1228                         accel_zero_across = zero_across;
1229                         accel_zero_through = zero_through;
1230                 }
1231         }
1232
1233         public int pad_orientation;
1234
1235         public double   accel_ground_along, accel_ground_across, accel_ground_through;
1236
1237         void update_pad_rotation() {
1238                 if (pad_orientation != AltosLib.MISSING && accel_ground_along != AltosLib.MISSING) {
1239                         rotation = new AltosRotation(AltosIMU.convert_accel(accel_ground_across - accel_zero_across),
1240                                                      AltosIMU.convert_accel(accel_ground_through - accel_zero_through),
1241                                                      AltosIMU.convert_accel(accel_ground_along - accel_zero_along),
1242                                                      pad_orientation);
1243                         ground_rotation = rotation;
1244                         orient.set_computed(rotation.tilt(), time);
1245                 }
1246         }
1247
1248         public void set_accel_ground(double ground_along, double ground_across, double ground_through) {
1249                 accel_ground_along = ground_along;
1250                 accel_ground_across = ground_across;
1251                 accel_ground_through = ground_through;
1252                 update_pad_rotation();
1253         }
1254
1255         public void set_pad_orientation(int pad_orientation) {
1256                 this.pad_orientation = pad_orientation;
1257                 update_pad_rotation();
1258         }
1259
1260         public double   gyro_zero_roll;
1261         public double   gyro_zero_pitch;
1262         public double   gyro_zero_yaw;
1263
1264         public void set_gyro_zero(double roll, double pitch, double yaw) {
1265                 if (roll != AltosLib.MISSING) {
1266                         gyro_zero_roll = roll;
1267                         gyro_zero_pitch = pitch;
1268                         gyro_zero_yaw = yaw;
1269                 }
1270         }
1271
1272         public double   last_imu_time;
1273
1274         private double radians(double degrees) {
1275                 if (degrees == AltosLib.MISSING)
1276                         return AltosLib.MISSING;
1277                 return degrees * Math.PI / 180.0;
1278         }
1279
1280         private void update_orient() {
1281                 if (last_imu_time != AltosLib.MISSING) {
1282                         double  t = time - last_imu_time;
1283
1284                         double  pitch = radians(gyro_pitch());
1285                         double  yaw = radians(gyro_yaw());
1286                         double  roll = radians(gyro_roll());
1287
1288                         if (t > 0 & pitch != AltosLib.MISSING && rotation != null) {
1289                                 rotation.rotate(t, pitch, yaw, roll);
1290                                 orient.set_computed(rotation.tilt(), time);
1291                         }
1292                 }
1293                 last_imu_time = time;
1294         }
1295
1296         public void set_imu(AltosIMU imu) {
1297                 if (imu != null)
1298                         imu = imu.clone();
1299                 this.imu = imu;
1300                 update_orient();
1301         }
1302
1303         private double gyro_zero_overflow(double first) {
1304                 double v = first / 128.0;
1305                 if (v < 0)
1306                         v = Math.ceil(v);
1307                 else
1308                         v = Math.floor(v);
1309                 return v * 128.0;
1310         }
1311
1312         public void check_imu_wrap(AltosIMU imu) {
1313                 if (this.imu == null) {
1314                         gyro_zero_roll += gyro_zero_overflow(imu.gyro_roll);
1315                         gyro_zero_pitch += gyro_zero_overflow(imu.gyro_pitch);
1316                         gyro_zero_yaw += gyro_zero_overflow(imu.gyro_yaw);
1317                 }
1318         }
1319
1320         public double accel_along() {
1321                 if (imu != null && accel_zero_along != AltosLib.MISSING)
1322                         return AltosIMU.convert_accel(imu.accel_along - accel_zero_along);
1323                 return AltosLib.MISSING;
1324         }
1325
1326         public double accel_across() {
1327                 if (imu != null && accel_zero_across != AltosLib.MISSING)
1328                         return AltosIMU.convert_accel(imu.accel_across - accel_zero_across);
1329                 return AltosLib.MISSING;
1330         }
1331
1332         public double accel_through() {
1333                 if (imu != null && accel_zero_through != AltosLib.MISSING)
1334                         return AltosIMU.convert_accel(imu.accel_through - accel_zero_through);
1335                 return AltosLib.MISSING;
1336         }
1337
1338         public double gyro_roll() {
1339                 if (imu != null && gyro_zero_roll != AltosLib.MISSING) {
1340                         return AltosIMU.convert_gyro(imu.gyro_roll - gyro_zero_roll);
1341                 }
1342                 return AltosLib.MISSING;
1343         }
1344
1345         public double gyro_pitch() {
1346                 if (imu != null && gyro_zero_pitch != AltosLib.MISSING) {
1347                         return AltosIMU.convert_gyro(imu.gyro_pitch - gyro_zero_pitch);
1348                 }
1349                 return AltosLib.MISSING;
1350         }
1351
1352         public double gyro_yaw() {
1353                 if (imu != null && gyro_zero_yaw != AltosLib.MISSING) {
1354                         return AltosIMU.convert_gyro(imu.gyro_yaw - gyro_zero_yaw);
1355                 }
1356                 return AltosLib.MISSING;
1357         }
1358
1359         public void set_mag(AltosMag mag) {
1360                 this.mag = mag.clone();
1361         }
1362
1363         public double mag_along() {
1364                 if (mag != null)
1365                         return AltosMag.convert_gauss(mag.along);
1366                 return AltosLib.MISSING;
1367         }
1368
1369         public double mag_across() {
1370                 if (mag != null)
1371                         return AltosMag.convert_gauss(mag.across);
1372                 return AltosLib.MISSING;
1373         }
1374
1375         public double mag_through() {
1376                 if (mag != null)
1377                         return AltosMag.convert_gauss(mag.through);
1378                 return AltosLib.MISSING;
1379         }
1380
1381         public AltosMs5607 make_baro() {
1382                 if (baro == null)
1383                         baro = new AltosMs5607();
1384                 return baro;
1385         }
1386
1387         public void set_ms5607(AltosMs5607 ms5607) {
1388                 baro = ms5607;
1389
1390                 if (baro != null) {
1391                         set_pressure(baro.pa);
1392                         set_temperature(baro.cc / 100.0);
1393                 }
1394         }
1395
1396         public void set_ms5607(int pres, int temp) {
1397                 if (baro != null) {
1398                         baro.set(pres, temp);
1399
1400                         set_pressure(baro.pa);
1401                         set_temperature(baro.cc / 100.0);
1402                 }
1403         }
1404
1405         public void set_companion(AltosCompanion companion) {
1406                 this.companion = companion;
1407         }
1408
1409         void update_accel() {
1410                 if (accel == AltosLib.MISSING)
1411                         return;
1412                 if (accel_plus_g == AltosLib.MISSING)
1413                         return;
1414                 if (accel_minus_g == AltosLib.MISSING)
1415                         return;
1416
1417                 double counts_per_g = (accel_minus_g - accel_plus_g) / 2.0;
1418                 double counts_per_mss = counts_per_g / 9.80665;
1419                 acceleration.set_measured((accel_plus_g - accel) / counts_per_mss, time);
1420         }
1421
1422         public void set_accel_g(double accel_plus_g, double accel_minus_g) {
1423                 if (accel_plus_g != AltosLib.MISSING) {
1424                         this.accel_plus_g = accel_plus_g;
1425                         this.accel_minus_g = accel_minus_g;
1426                         update_accel();
1427                 }
1428         }
1429
1430         public void set_ground_accel(double ground_accel) {
1431                 if (ground_accel != AltosLib.MISSING)
1432                         this.ground_accel = ground_accel;
1433         }
1434
1435         public void set_accel(double accel) {
1436                 if (accel != AltosLib.MISSING) {
1437                         this.accel = accel;
1438                         if (state == AltosLib.ao_flight_pad) {
1439                                 if (ground_accel_avg == AltosLib.MISSING)
1440                                         ground_accel_avg = accel;
1441                                 else
1442                                         ground_accel_avg = (ground_accel_avg * 7 + accel) / 8;
1443                         }
1444                 }
1445                 update_accel();
1446         }
1447
1448         public void set_temperature(double temperature) {
1449                 if (temperature != AltosLib.MISSING) {
1450                         this.temperature = temperature;
1451                         set |= set_data;
1452                 }
1453         }
1454
1455         public void set_battery_voltage(double battery_voltage) {
1456                 if (battery_voltage != AltosLib.MISSING) {
1457                         this.battery_voltage = battery_voltage;
1458                         set |= set_data;
1459                 }
1460         }
1461
1462         public void set_pyro_voltage(double pyro_voltage) {
1463                 if (pyro_voltage != AltosLib.MISSING) {
1464                         this.pyro_voltage = pyro_voltage;
1465                         set |= set_data;
1466                 }
1467         }
1468
1469         public void set_apogee_voltage(double apogee_voltage) {
1470                 if (apogee_voltage != AltosLib.MISSING) {
1471                         this.apogee_voltage = apogee_voltage;
1472                         set |= set_data;
1473                 }
1474         }
1475
1476         public void set_main_voltage(double main_voltage) {
1477                 if (main_voltage != AltosLib.MISSING) {
1478                         this.main_voltage = main_voltage;
1479                         set |= set_data;
1480                 }
1481         }
1482
1483         public void set_ignitor_voltage(double[] voltage) {
1484                 this.ignitor_voltage = voltage;
1485         }
1486
1487         public void set_pyro_fired(int fired) {
1488                 this.pyro_fired = fired;
1489         }
1490
1491         public double time_since_boost() {
1492                 if (tick == AltosLib.MISSING)
1493                         return 0.0;
1494
1495                 if (boost_tick == AltosLib.MISSING)
1496                         return tick / 100.0;
1497                 return (tick - boost_tick) / 100.0;
1498         }
1499
1500         public boolean valid() {
1501                 return tick != AltosLib.MISSING && serial != AltosLib.MISSING;
1502         }
1503
1504         public AltosGPS make_temp_gps(boolean sats) {
1505                 if (temp_gps == null) {
1506                         temp_gps = new AltosGPS(gps);
1507                 }
1508                 gps_pending = true;
1509                 if (sats) {
1510                         if (tick != temp_gps_sat_tick)
1511                                 temp_gps.cc_gps_sat = null;
1512                         temp_gps_sat_tick = tick;
1513                 }
1514                 return temp_gps;
1515         }
1516
1517         public void set_temp_gps() {
1518                 set_gps(temp_gps, gps_sequence + 1);
1519                 gps_pending = false;
1520                 temp_gps = null;
1521         }
1522
1523         public AltosState clone() {
1524                 AltosState s = new AltosState();
1525                 s.copy(this);
1526
1527                 /* Code to test state save/restore. Enable only for that purpose
1528                  */
1529                 if (false) {
1530                         AltosJson       json = new AltosJson(this);
1531                         String          onetrip = json.toPrettyString();
1532                         AltosJson       back = AltosJson.fromString(onetrip);
1533                         AltosState      tripstate = (AltosState) back.make(this.getClass());
1534                         AltosJson       tripjson = new AltosJson(tripstate);
1535                         String          twotrip = tripjson.toPrettyString();
1536
1537                         if (!onetrip.equals(twotrip)) {
1538                                 try {
1539                                         FileWriter one_file = new FileWriter("one.json", true);
1540                                         one_file.write(onetrip);
1541                                         one_file.flush();
1542                                         FileWriter two_file = new FileWriter("two.json", true);
1543                                         two_file.write(twotrip);
1544                                         two_file.flush();
1545                                 } catch (Exception e) {
1546                                 }
1547                                 System.out.printf("json error\n");
1548                                 System.exit(1);
1549                         }
1550                 }
1551                 return s;
1552         }
1553
1554         public AltosState () {
1555                 init();
1556         }
1557 }