altoslib: Parse TeleGPS state value from GPS telemetry packet
[fw/altos] / altoslib / AltosGPS.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 package org.altusmetrum.altoslib_4;
19
20 import java.text.*;
21 import java.util.concurrent.*;
22
23 public class AltosGPS implements Cloneable {
24
25         public final static int MISSING = AltosLib.MISSING;
26
27         public int      nsat;
28         public boolean  locked;
29         public boolean  connected;
30         public double   lat;            /* degrees (+N -S) */
31         public double   lon;            /* degrees (+E -W) */
32         public double   alt;            /* m */
33         public int      year;
34         public int      month;
35         public int      day;
36         public int      hour;
37         public int      minute;
38         public int      second;
39
40         public double   ground_speed;   /* m/s */
41         public int      course;         /* degrees */
42         public double   climb_rate;     /* m/s */
43         public double   hdop;           /* unitless */
44         public double   vdop;           /* unitless */
45         public int      h_error;        /* m */
46         public int      v_error;        /* m */
47
48         public int      state;          /* for TeleGPS */
49
50         static final int AO_GPS_STATE_VALID = 0x80;
51
52         public AltosGPSSat[] cc_gps_sat;        /* tracking data */
53
54         public void ParseGPSDate(String date) throws ParseException {
55                 String[] ymd = date.split("-");
56                 if (ymd.length != 3)
57                         throw new ParseException("error parsing GPS date " + date + " got " + ymd.length, 0);
58                 year = AltosParse.parse_int(ymd[0]);
59                 month = AltosParse.parse_int(ymd[1]);
60                 day = AltosParse.parse_int(ymd[2]);
61         }
62
63         public void ParseGPSTime(String time) throws ParseException {
64                 String[] hms = time.split(":");
65                 if (hms.length != 3)
66                         throw new ParseException("Error parsing GPS time " + time + " got " + hms.length, 0);
67                 hour = AltosParse.parse_int(hms[0]);
68                 minute = AltosParse.parse_int(hms[1]);
69                 second = AltosParse.parse_int(hms[2]);
70         }
71
72         public void ClearGPSTime() {
73                 year = month = day = AltosLib.MISSING;
74                 hour = minute = second = AltosLib.MISSING;
75         }
76
77         public AltosGPS(AltosTelemetryMap map) throws ParseException {
78                 String  state = map.get_string(AltosTelemetryLegacy.AO_TELEM_GPS_STATE,
79                                                AltosTelemetryLegacy.AO_TELEM_GPS_STATE_ERROR);
80
81                 nsat = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_NUM_SAT, 0);
82                 if (state.equals(AltosTelemetryLegacy.AO_TELEM_GPS_STATE_LOCKED)) {
83                         connected = true;
84                         locked = true;
85                         lat = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_LATITUDE, MISSING, 1.0e-7);
86                         lon = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_LONGITUDE, MISSING, 1.0e-7);
87                         alt = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_ALTITUDE, MISSING);
88                         year = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_YEAR, MISSING);
89                         if (year != MISSING)
90                                 year += 2000;
91                         month = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_MONTH, MISSING);
92                         day = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_DAY, MISSING);
93
94                         hour = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_HOUR, 0);
95                         minute = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_MINUTE, 0);
96                         second = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_SECOND, 0);
97
98                         ground_speed = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_HORIZONTAL_SPEED,
99                                                       AltosLib.MISSING, 1/100.0);
100                         course = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_COURSE,
101                                              AltosLib.MISSING);
102                         hdop = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_HDOP, MISSING, 1.0);
103                         vdop = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_VDOP, MISSING, 1.0);
104                         h_error = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_HERROR, MISSING);
105                         v_error = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_VERROR, MISSING);
106                 } else if (state.equals(AltosTelemetryLegacy.AO_TELEM_GPS_STATE_UNLOCKED)) {
107                         connected = true;
108                         locked = false;
109                 } else {
110                         connected = false;
111                         locked = false;
112                 }
113         }
114
115         public boolean parse_string (String line, boolean says_done) {
116                 String[] bits = line.split("\\s+");
117                 if (bits.length == 0)
118                         return false;
119                 if (line.startsWith("Date:")) {
120                         if (bits.length < 2)
121                                 return false;
122                         String[] d = bits[1].split("/");
123                         if (d.length < 3)
124                                 return false;
125                         year = Integer.parseInt(d[0]) + 2000;
126                         month = Integer.parseInt(d[1]);
127                         day = Integer.parseInt(d[2]);
128                 } else if (line.startsWith("Time:")) {
129                         if (bits.length < 2)
130                                 return false;
131                         String[] d = bits[1].split(":");
132                         if (d.length < 3)
133                                 return false;
134                         hour = Integer.parseInt(d[0]);
135                         minute = Integer.parseInt(d[1]);
136                         second = Integer.parseInt(d[2]);
137                 } else if (line.startsWith("Lat/Lon:")) {
138                         if (bits.length < 3)
139                                 return false;
140                         lat = Integer.parseInt(bits[1]) * 1.0e-7;
141                         lon = Integer.parseInt(bits[2]) * 1.0e-7;
142                 } else if (line.startsWith("Alt:")) {
143                         if (bits.length < 2)
144                                 return false;
145                         alt = Integer.parseInt(bits[1]);
146                 } else if (line.startsWith("Flags:")) {
147                         if (bits.length < 2)
148                                 return false;
149                         int status = Integer.decode(bits[1]);
150                         connected = (status & AltosLib.AO_GPS_RUNNING) != 0;
151                         locked = (status & AltosLib.AO_GPS_VALID) != 0;
152                         if (!says_done)
153                                 return false;
154                 } else if (line.startsWith("Sats:")) {
155                         if (bits.length < 2)
156                                 return false;
157                         nsat = Integer.parseInt(bits[1]);
158                         cc_gps_sat = new AltosGPSSat[nsat];
159                         for (int i = 0; i < nsat; i++) {
160                                 int     svid = Integer.parseInt(bits[2+i*2]);
161                                 int     cc_n0 = Integer.parseInt(bits[3+i*2]);
162                                 cc_gps_sat[i] = new AltosGPSSat(svid, cc_n0);
163                         }
164                 } else if (line.startsWith("done")) {
165                         return false;
166                 } else
167                         return false;
168                 return true;
169         }
170
171         public AltosGPS(String[] words, int i, int version) throws ParseException {
172                 AltosParse.word(words[i++], "GPS");
173                 nsat = AltosParse.parse_int(words[i++]);
174                 AltosParse.word(words[i++], "sat");
175
176                 connected = false;
177                 locked = false;
178                 lat = lon = 0;
179                 alt = 0;
180                 ClearGPSTime();
181                 if ((words[i]).equals("unlocked")) {
182                         connected = true;
183                         i++;
184                 } else if ((words[i]).equals("not-connected")) {
185                         i++;
186                 } else if (words.length >= 40) {
187                         locked = true;
188                         connected = true;
189
190                         if (version > 1)
191                                 ParseGPSDate(words[i++]);
192                         else
193                                 year = month = day = 0;
194                         ParseGPSTime(words[i++]);
195                         lat = AltosParse.parse_coord(words[i++]);
196                         lon = AltosParse.parse_coord(words[i++]);
197                         alt = AltosParse.parse_int(words[i++]);
198                         if (version > 1 || (i < words.length && !words[i].equals("SAT"))) {
199                                 ground_speed = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(H)"));
200                                 course = AltosParse.parse_int(words[i++]);
201                                 climb_rate = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(V)"));
202                                 hdop = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "(hdop)"));
203                                 h_error = AltosParse.parse_int(words[i++]);
204                                 v_error = AltosParse.parse_int(words[i++]);
205                         }
206                 } else {
207                         i++;
208                 }
209                 if (i < words.length) {
210                         AltosParse.word(words[i++], "SAT");
211                         int tracking_channels = 0;
212                         if (words[i].equals("not-connected"))
213                                 tracking_channels = 0;
214                         else
215                                 tracking_channels = AltosParse.parse_int(words[i]);
216                         i++;
217                         cc_gps_sat = new AltosGPSSat[tracking_channels];
218                         for (int chan = 0; chan < tracking_channels; chan++) {
219                                 cc_gps_sat[chan] = new AltosGPSSat();
220                                 cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]);
221                                 /* Older versions included SiRF status bits */
222                                 if (version < 2)
223                                         i++;
224                                 cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]);
225                         }
226                 } else
227                         cc_gps_sat = new AltosGPSSat[0];
228         }
229
230         public void set_latitude(int in_lat) {
231                 lat = in_lat / 10.0e7;
232         }
233
234         public void set_longitude(int in_lon) {
235                 lon = in_lon / 10.0e7;
236         }
237
238         public void set_time(int in_hour, int in_minute, int in_second) {
239                 hour = in_hour;
240                 minute = in_minute;
241                 second = in_second;
242         }
243
244         public void set_date(int in_year, int in_month, int in_day) {
245                 year = in_year;
246                 month = in_month;
247                 day = in_day;
248         }
249
250         /*
251         public void set_flags(int in_flags) {
252                 flags = in_flags;
253         }
254         */
255
256         public void set_altitude(int in_altitude) {
257                 alt = in_altitude;
258         }
259
260         public void add_sat(int svid, int c_n0) {
261                 if (cc_gps_sat == null) {
262                         cc_gps_sat = new AltosGPSSat[1];
263                 } else {
264                         AltosGPSSat[] new_gps_sat = new AltosGPSSat[cc_gps_sat.length + 1];
265                         for (int i = 0; i < cc_gps_sat.length; i++)
266                                 new_gps_sat[i] = cc_gps_sat[i];
267                         cc_gps_sat = new_gps_sat;
268                 }
269                 AltosGPSSat     sat = new AltosGPSSat();
270                 sat.svid = svid;
271                 sat.c_n0 = c_n0;
272                 cc_gps_sat[cc_gps_sat.length - 1] = sat;
273         }
274
275         public AltosGPS() {
276                 lat = AltosLib.MISSING;
277                 lon = AltosLib.MISSING;
278                 alt = AltosLib.MISSING;
279                 ClearGPSTime();
280                 cc_gps_sat = null;
281         }
282
283         public AltosGPS clone() {
284                 AltosGPS        g = new AltosGPS();
285
286                 g.nsat = nsat;
287                 g.locked = locked;
288                 g.connected = connected;
289                 g.lat = lat;            /* degrees (+N -S) */
290                 g.lon = lon;            /* degrees (+E -W) */
291                 g.alt = alt;            /* m */
292                 g.year = year;
293                 g.month = month;
294                 g.day = day;
295                 g.hour = hour;
296                 g.minute = minute;
297                 g.second = second;
298
299                 g.ground_speed = ground_speed;  /* m/s */
300                 g.course = course;              /* degrees */
301                 g.climb_rate = climb_rate;      /* m/s */
302                 g.hdop = hdop;          /* unitless? */
303                 g.h_error = h_error;    /* m */
304                 g.v_error = v_error;    /* m */
305                 g.state = state;
306
307                 if (cc_gps_sat != null) {
308                         g.cc_gps_sat = new AltosGPSSat[cc_gps_sat.length];
309                         for (int i = 0; i < cc_gps_sat.length; i++) {
310                                 g.cc_gps_sat[i] = new AltosGPSSat(cc_gps_sat[i].svid,
311                                                                   cc_gps_sat[i].c_n0);
312                         }
313                 }
314                 return g;
315         }
316
317         public AltosGPS(AltosGPS old) {
318                 if (old != null) {
319                         nsat = old.nsat;
320                         locked = old.locked;
321                         connected = old.connected;
322                         lat = old.lat;          /* degrees (+N -S) */
323                         lon = old.lon;          /* degrees (+E -W) */
324                         alt = old.alt;          /* m */
325                         year = old.year;
326                         month = old.month;
327                         day = old.day;
328                         hour = old.hour;
329                         minute = old.minute;
330                         second = old.second;
331
332                         ground_speed = old.ground_speed;        /* m/s */
333                         course = old.course;            /* degrees */
334                         climb_rate = old.climb_rate;    /* m/s */
335                         hdop = old.hdop;                /* unitless? */
336                         h_error = old.h_error;  /* m */
337                         v_error = old.v_error;  /* m */
338                         state = old.state;
339
340                         if (old.cc_gps_sat != null) {
341                                 cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length];
342                                 for (int i = 0; i < old.cc_gps_sat.length; i++) {
343                                         cc_gps_sat[i] = new AltosGPSSat();
344                                         cc_gps_sat[i].svid = old.cc_gps_sat[i].svid;
345                                         cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0;
346                                 }
347                         }
348                 } else {
349                         lat = AltosLib.MISSING;
350                         lon = AltosLib.MISSING;
351                         alt = AltosLib.MISSING;
352                         ClearGPSTime();
353                         cc_gps_sat = null;
354                         state = 0;
355                 }
356         }
357
358         static public void update_state(AltosState state, AltosLink link, AltosConfigData config_data) throws InterruptedException {
359                 try {
360                         AltosGPS        gps = new AltosGPS(link, config_data);
361
362                         if (gps != null) {
363                                 state.set_gps(gps, state.gps_sequence++);
364                                 return;
365                         }
366                 } catch (TimeoutException te) {
367                 }
368                 state.set_gps(null, 0);
369         }
370
371         public AltosGPS (AltosLink link, AltosConfigData config_data) throws TimeoutException, InterruptedException {
372                 boolean says_done = config_data.compare_version("1.0") >= 0;
373                 link.printf("g\n");
374                 for (;;) {
375                         String line = link.get_reply_no_dialog(5000);
376                         if (line == null)
377                                 throw new TimeoutException();
378                         if (!parse_string(line, says_done))
379                                 break;
380                 }
381         }
382 }