563bef4b71ceaecc4804f35ae14a4b0f2cafd698
[fw/altos] / altoslib / AltosConfigData.java
1 /*
2  * Copyright © 2011 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 package org.altusmetrum.altoslib_4;
19
20 import java.util.*;
21 import java.text.*;
22 import java.util.concurrent.*;
23
24 public class AltosConfigData implements Iterable<String> {
25
26         /* Version information */
27         public String   manufacturer;
28         public String   product;
29         public int      serial;
30         public int      flight;
31         public int      log_format;
32         public int      log_space;
33         public String   version;
34
35         /* Strings returned */
36         public LinkedList<String>       lines;
37
38         /* Config information */
39         /* HAS_FLIGHT*/
40         public int      main_deploy;
41         public int      apogee_delay;
42         public int      apogee_lockout;
43
44         /* HAS_RADIO */
45         public int      radio_frequency;
46         public String   callsign;
47         public int      radio_enable;
48         public int      radio_calibration;
49         /* Old HAS_RADIO values */
50         public int      radio_channel;
51         public int      radio_setting;
52
53         /* HAS_ACCEL */
54         public int      accel_cal_plus, accel_cal_minus;
55         public int      pad_orientation;
56
57         /* HAS_LOG */
58         public int      flight_log_max;
59
60         /* HAS_IGNITE */
61         public int      ignite_mode;
62
63         /* HAS_AES */
64         public String   aes_key;
65
66         /* AO_PYRO_NUM */
67         public AltosPyro[]      pyros;
68         public int              npyro;
69         public int              pyro;
70
71         /* HAS_APRS */
72         public int              aprs_interval;
73
74         /* HAS_BEEP */
75         public int              beep;
76
77         /* Storage info replies */
78         public int      storage_size;
79         public int      storage_erase_unit;
80
81         /* Log listing replies */
82         public int      stored_flight;
83
84         /* HAS_TRACKER */
85         public int      tracker_motion;
86         public int      tracker_interval;
87
88         public static String get_string(String line, String label) throws  ParseException {
89                 if (line.startsWith(label)) {
90                         String  quoted = line.substring(label.length()).trim();
91
92                         if (quoted.startsWith("\""))
93                                 quoted = quoted.substring(1);
94                         if (quoted.endsWith("\""))
95                                 quoted = quoted.substring(0,quoted.length()-1);
96                         return quoted;
97                 }
98                 throw new ParseException("mismatch", 0);
99         }
100
101         public static int get_int(String line, String label) throws NumberFormatException, ParseException {
102                 if (line.startsWith(label)) {
103                         String tail = line.substring(label.length()).trim();
104                         String[] tokens = tail.split("\\s+");
105                         if (tokens.length > 0)
106                                 return  Integer.parseInt(tokens[0]);
107                 }
108                 throw new ParseException("mismatch", 0);
109         }
110
111         public static int[] get_values(String line, String label) throws NumberFormatException, ParseException {
112                 if (line.startsWith(label)) {
113                         String tail = line.substring(label.length()).trim();
114                         String[] tokens = tail.split("\\s+");
115                         if (tokens.length > 1) {
116                                 int[]   values = new int[2];
117                                 values[0] = Integer.parseInt(tokens[0]);
118                                 values[1] = Integer.parseInt(tokens[1]);
119                                 return values;
120                         }
121                 }
122                 throw new ParseException("mismatch", 0);
123         }
124
125         public Iterator<String> iterator() {
126                 return lines.iterator();
127         }
128
129         public int log_space() {
130                 if (log_space > 0)
131                         return log_space;
132
133                 if (storage_size > 0) {
134                         int     space = storage_size;
135
136                         if (storage_erase_unit > 0 && use_flash_for_config())
137                                 space -= storage_erase_unit;
138
139                         if (space > 0)
140                                 return space;
141                 }
142                 return 0;
143         }
144
145         public int log_available() {
146                 switch (log_format) {
147                 case AltosLib.AO_LOG_FORMAT_TINY:
148                         if (stored_flight == 0)
149                                 return 1;
150                         return 0;
151                 case AltosLib.AO_LOG_FORMAT_TELEMETRY:
152                 case AltosLib.AO_LOG_FORMAT_TELESCIENCE:
153                         return 1;
154                 default:
155                         if (flight_log_max <= 0)
156                                 return 1;
157                         int     log_max = flight_log_max * 1024;
158                         int     log_space = log_space();
159                         int     log_used;
160
161                         if (stored_flight <= 0)
162                                 log_used = 0;
163                         else
164                                 log_used = stored_flight * log_max;
165                         int     log_avail;
166
167                         if (log_used >= log_space)
168                                 log_avail = 0;
169                         else
170                                 log_avail = (log_space - log_used) / log_max;
171
172                         return log_avail;
173                 }
174         }
175
176         public boolean has_monitor_battery() {
177                 if (product.startsWith("TeleBT"))
178                         return true;
179                 return false;
180         }
181
182         int[] parse_version(String v) {
183                 String[] parts = v.split("\\.");
184                 int r[] = new int[parts.length];
185
186                 for (int i = 0; i < parts.length; i++) {
187                         try {
188                                 r[i] = AltosLib.fromdec(parts[i]);
189                         } catch (NumberFormatException n) {
190                                 r[i] = 0;
191                         }
192                 }
193
194                 return r;
195         }
196
197         public int compare_version(String other) {
198                 int[]   me = parse_version(version);
199                 int[]   them = parse_version(other);
200
201                 int     l = Math.min(me.length, them.length);
202
203                 for (int i = 0; i < l; i++) {
204                         int     d = me[i] - them[i];
205                         if (d != 0)
206                                 return d;
207                 }
208                 if (me.length > l)
209                         return 1;
210                 if (them.length > l)
211                         return -1;
212                 return 0;
213         }
214
215         public void reset() {
216                 lines = new LinkedList<String>();
217
218                 manufacturer = "unknown";
219                 product = "unknown";
220                 serial = 0;
221                 flight = 0;
222                 log_format = AltosLib.AO_LOG_FORMAT_UNKNOWN;
223                 log_space = -1;
224                 version = "unknown";
225
226                 main_deploy = -1;
227                 apogee_delay = -1;
228                 apogee_lockout = -1;
229
230                 radio_frequency = -1;
231                 callsign = null;
232                 radio_enable = -1;
233                 radio_calibration = -1;
234                 radio_channel = -1;
235                 radio_setting = -1;
236
237                 accel_cal_plus = -1;
238                 accel_cal_minus = -1;
239                 pad_orientation = -1;
240
241                 flight_log_max = -1;
242                 ignite_mode = -1;
243
244                 aes_key = "";
245
246                 pyro = 0;
247                 npyro = 0;
248                 pyros = null;
249
250                 aprs_interval = -1;
251
252                 beep = -1;
253
254                 tracker_motion = -1;
255                 tracker_interval = -1;
256
257                 storage_size = -1;
258                 storage_erase_unit = -1;
259                 stored_flight = 0;
260         }
261
262         public void parse_line(String line) {
263                 lines.add(line);
264                 /* Version replies */
265                 try { manufacturer = get_string(line, "manufacturer"); } catch (Exception e) {}
266                 try { product = get_string(line, "product"); } catch (Exception e) {}
267                 try { serial = get_int(line, "serial-number"); } catch (Exception e) {}
268                 try { flight = get_int(line, "current-flight"); } catch (Exception e) {}
269                 try { log_format = get_int(line, "log-format"); } catch (Exception e) {}
270                 try { log_space = get_int(line, "log-space"); } catch (Exception e) {}
271                 try { version = get_string(line, "software-version"); } catch (Exception e) {}
272
273                 /* Version also contains MS5607 info, which we ignore here */
274
275                 /* Config show replies */
276
277                 /* HAS_FLIGHT */
278                 try { main_deploy = get_int(line, "Main deploy:"); } catch (Exception e) {}
279                 try { apogee_delay = get_int(line, "Apogee delay:"); } catch (Exception e) {}
280                 try { apogee_lockout = get_int(line, "Apogee lockout:"); } catch (Exception e) {}
281
282                 /* HAS_RADIO */
283                 try {
284                         radio_frequency = get_int(line, "Frequency:");
285                         if (radio_frequency < 0)
286                                 radio_frequency = 434550;
287                 } catch (Exception e) {}
288                 try { callsign = get_string(line, "Callsign:"); } catch (Exception e) {}
289                 try { radio_enable = get_int(line, "Radio enable:"); } catch (Exception e) {}
290                 try { radio_calibration = get_int(line, "Radio cal:"); } catch (Exception e) {}
291
292                 /* Old HAS_RADIO values */
293                 try { radio_channel = get_int(line, "Radio channel:"); } catch (Exception e) {}
294                 try { radio_setting = get_int(line, "Radio setting:"); } catch (Exception e) {}
295
296                 /* HAS_ACCEL */
297                 try {
298                         if (line.startsWith("Accel cal")) {
299                                 String[] bits = line.split("\\s+");
300                                 if (bits.length >= 6) {
301                                         accel_cal_plus = Integer.parseInt(bits[3]);
302                                         accel_cal_minus = Integer.parseInt(bits[5]);
303                                 }
304                         }
305                 } catch (Exception e) {}
306                 try { pad_orientation = get_int(line, "Pad orientation:"); } catch (Exception e) {}
307
308                 /* HAS_LOG */
309                 try { flight_log_max = get_int(line, "Max flight log:"); } catch (Exception e) {}
310
311                 /* HAS_IGNITE */
312                 try { ignite_mode = get_int(line, "Ignite mode:"); } catch (Exception e) {}
313
314                 /* HAS_AES */
315                 try { aes_key = get_string(line, "AES key:"); } catch (Exception e) {}
316
317                 /* AO_PYRO_NUM */
318                 try {
319                         npyro = get_int(line, "Pyro-count:");
320                         pyros = new AltosPyro[npyro];
321                         pyro = 0;
322                 } catch (Exception e) {}
323                 if (npyro > 0) {
324                         try {
325                                 AltosPyro p = new AltosPyro(pyro, line);
326                                 if (pyro < npyro)
327                                         pyros[pyro++] = p;
328                         } catch (Exception e) {}
329                 }
330
331                 /* HAS_APRS */
332                 try { aprs_interval = get_int(line, "APRS interval:"); } catch (Exception e) {}
333
334                 /* HAS_BEEP */
335                 try { beep = get_int(line, "Beeper setting:"); } catch (Exception e) {}
336
337                 /* HAS_TRACKER */
338                 try {
339                         int[] values = get_values(line, "Tracker setting:");
340                         tracker_motion = values[0];
341                         tracker_interval = values[1];
342                 } catch (Exception e) {}
343
344                 /* Storage info replies */
345                 try { storage_size = get_int(line, "Storage size:"); } catch (Exception e) {}
346                 try { storage_erase_unit = get_int(line, "Storage erase unit:"); } catch (Exception e) {}
347
348                 /* Log listing replies */
349                 try { get_int(line, "flight"); stored_flight++; }  catch (Exception e) {}
350         }
351
352         public AltosConfigData() {
353                 reset();
354         }
355
356         private void read_link(AltosLink link, String finished) throws InterruptedException, TimeoutException {
357                 for (;;) {
358                         String line = link.get_reply();
359                         if (line == null)
360                                 throw new TimeoutException();
361                         if (line.contains("Syntax error"))
362                                 continue;
363                         this.parse_line(line);
364
365                         /* signals the end of the version info */
366                         if (line.startsWith(finished))
367                                 break;
368                 }
369         }
370
371         public boolean has_frequency() {
372                 return radio_frequency >= 0 || radio_setting >= 0 || radio_channel >= 0;
373         }
374
375         public void set_frequency(double freq) {
376                 int     frequency = radio_frequency;
377                 int     setting = radio_setting;
378
379                 if (frequency > 0) {
380                         radio_frequency = (int) Math.floor (freq * 1000 + 0.5);
381                         radio_channel = -1;
382                 } else if (setting > 0) {
383                         radio_setting =AltosConvert.radio_frequency_to_setting(freq,
384                                                                                     radio_calibration);
385                         radio_channel = -1;
386                 } else {
387                         radio_channel = AltosConvert.radio_frequency_to_channel(freq);
388                 }
389         }
390
391         public double frequency() {
392                 int     channel = radio_channel;
393                 int     setting = radio_setting;
394
395                 if (radio_frequency < 0 && channel < 0 && setting < 0)
396                         return -1;
397
398                 if (channel < 0)
399                         channel = 0;
400                 if (setting < 0)
401                         setting = 0;
402
403                 return AltosConvert.radio_to_frequency(radio_frequency,
404                                                        setting,
405                                                        radio_calibration,
406                                                        channel);
407         }
408
409         boolean use_flash_for_config() {
410                 if (product.startsWith("TeleMega"))
411                         return false;
412                 if (product.startsWith("TeleMetrum-v2"))
413                         return false;
414                 return true;
415         }
416
417
418         public void get_values(AltosConfigValues source) throws AltosConfigDataException {
419
420                 /* HAS_FLIGHT */
421                 if (main_deploy >= 0)
422                         main_deploy = source.main_deploy();
423                 if (apogee_delay >= 0)
424                         apogee_delay = source.apogee_delay();
425                 if (apogee_lockout >= 0)
426                         apogee_lockout = source.apogee_lockout();
427
428                 /* HAS_RADIO */
429                 if (has_frequency())
430                         set_frequency(source.radio_frequency());
431                 if (radio_enable >= 0)
432                         radio_enable = source.radio_enable();
433                 if (callsign != null)
434                         callsign = source.callsign();
435                 if (radio_calibration >= 0)
436                         radio_calibration = source.radio_calibration();
437
438                 /* HAS_ACCEL */
439                 if (pad_orientation >= 0)
440                         pad_orientation = source.pad_orientation();
441
442                 /* HAS_LOG */
443                 if (flight_log_max >= 0)
444                         flight_log_max = source.flight_log_max();
445
446                 /* HAS_IGNITE */
447                 if (ignite_mode >= 0)
448                         ignite_mode = source.ignite_mode();
449
450                 /* AO_PYRO_NUM */
451                 if (npyro > 0)
452                         pyros = source.pyros();
453
454                 /* HAS_APRS */
455                 if (aprs_interval >= 0)
456                         aprs_interval = source.aprs_interval();
457
458                 /* HAS_BEEP */
459                 if (beep >= 0)
460                         beep = source.beep();
461                 /* HAS_TRACKER */
462                 if (tracker_motion >= 0)
463                         tracker_motion = source.tracker_motion();
464                 if (tracker_interval >= 0)
465                         tracker_interval = source.tracker_interval();
466         }
467
468         public void set_values(AltosConfigValues dest) {
469                 dest.set_serial(serial);
470                 dest.set_product(product);
471                 dest.set_version(version);
472                 dest.set_main_deploy(main_deploy);
473                 dest.set_apogee_delay(apogee_delay);
474                 dest.set_apogee_lockout(apogee_lockout);
475                 dest.set_radio_calibration(radio_calibration);
476                 dest.set_radio_frequency(frequency());
477                 boolean max_enabled = true;
478
479                 if (log_space() == 0)
480                         max_enabled = false;
481
482                 switch (log_format) {
483                 case AltosLib.AO_LOG_FORMAT_TINY:
484                         max_enabled = false;
485                         break;
486                 default:
487                         if (stored_flight > 0)
488                                 max_enabled = false;
489                         break;
490                 }
491
492                 dest.set_flight_log_max_enabled(max_enabled);
493                 dest.set_radio_enable(radio_enable);
494                 dest.set_flight_log_max_limit(log_space() / 1024);
495                 dest.set_flight_log_max(flight_log_max);
496                 dest.set_ignite_mode(ignite_mode);
497                 dest.set_pad_orientation(pad_orientation);
498                 dest.set_callsign(callsign);
499                 if (npyro > 0)
500                         dest.set_pyros(pyros);
501                 else
502                         dest.set_pyros(null);
503                 dest.set_aprs_interval(aprs_interval);
504                 dest.set_beep(beep);
505                 dest.set_tracker_motion(tracker_motion);
506                 dest.set_tracker_interval(tracker_interval);
507         }
508
509         public void save(AltosLink link, boolean remote) throws InterruptedException, TimeoutException {
510
511                 /* HAS_FLIGHT */
512                 if (main_deploy >= 0)
513                         link.printf("c m %d\n", main_deploy);
514                 if (apogee_delay >= 0)
515                         link.printf("c d %d\n", apogee_delay);
516                 if (apogee_lockout >= 0)
517                         link.printf("c L %d\n", apogee_lockout);
518
519                 /* Don't mess with radio calibration when remote */
520                 if (radio_calibration > 0 && !remote)
521                         link.printf("c f %d\n", radio_calibration);
522
523                 /* HAS_RADIO */
524                 if (has_frequency()) {
525                         boolean has_frequency = radio_frequency >= 0;
526                         boolean has_setting = radio_setting > 0;
527                         double frequency = frequency();
528                         link.set_radio_frequency(frequency,
529                                                         has_frequency,
530                                                         has_setting,
531                                                         radio_calibration);
532                         /* When remote, reset the dongle frequency at the same time */
533                         if (remote) {
534                                 link.stop_remote();
535                                 link.set_radio_frequency(frequency);
536                                 link.start_remote();
537                         }
538                 }
539
540                 if (callsign != null)
541                         link.printf("c c %s\n", callsign);
542                 if (radio_enable >= 0)
543                         link.printf("c e %d\n", radio_enable);
544
545                 /* HAS_ACCEL */
546                 /* UI doesn't support accel cal */
547                 if (pad_orientation >= 0)
548                         link.printf("c o %d\n", pad_orientation);
549
550                 /* HAS_LOG */
551                 if (flight_log_max != 0)
552                         link.printf("c l %d\n", flight_log_max);
553
554                 /* HAS_IGNITE */
555                 if (ignite_mode >= 0)
556                         link.printf("c i %d\n", ignite_mode);
557
558                 /* HAS_AES */
559                 /* UI doesn't support AES key config */
560
561                 /* AO_PYRO_NUM */
562                 if (npyro > 0) {
563                         for (int p = 0; p < pyros.length; p++) {
564                                 link.printf("c P %s\n",
565                                                    pyros[p].toString());
566                         }
567                 }
568
569                 /* HAS_APRS */
570                 if (aprs_interval >= 0)
571                         link.printf("c A %d\n", aprs_interval);
572
573                 /* HAS_BEEP */
574                 if (beep >= 0)
575                         link.printf("c b %d\n", beep);
576
577                 /* HAS_TRACKER */
578                 if (tracker_motion >= 0 && tracker_interval >= 0)
579                         link.printf("c t %d %d\n", tracker_motion, tracker_interval);
580
581                 link.printf("c w\n");
582                 link.flush_output();
583         }
584
585         public AltosConfigData(AltosLink link) throws InterruptedException, TimeoutException {
586                 reset();
587                 link.printf("c s\nf\nv\n");
588                 read_link(link, "software-version");
589                 switch (log_format) {
590                 case AltosLib.AO_LOG_FORMAT_UNKNOWN:
591                 case AltosLib.AO_LOG_FORMAT_NONE:
592                         break;
593                 default:
594                         link.printf("l\n");
595                         read_link(link, "done");
596                         break;
597                 }
598         }
599
600 }