altos/stm32f4-disco: Start hooking up stm32f413 USB for the disco board
[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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 package org.altusmetrum.altoslib_13;
20
21 import java.text.*;
22 import java.util.concurrent.*;
23 import java.io.*;
24 import java.time.*;
25
26 public class AltosGPS implements Cloneable {
27
28         public final static int MISSING = AltosLib.MISSING;
29
30         public int      nsat;
31         public boolean  locked;
32         public boolean  connected;
33         public double   lat;            /* degrees (+N -S) */
34         public double   lon;            /* degrees (+E -W) */
35         public double   alt;            /* m */
36         public int      year;
37         public int      month;
38         public int      day;
39         public int      hour;
40         public int      minute;
41         public int      second;
42
43         public double   ground_speed;   /* m/s */
44         public int      course;         /* degrees */
45         public double   climb_rate;     /* m/s */
46         public double   pdop;           /* unitless */
47         public double   hdop;           /* unitless */
48         public double   vdop;           /* unitless */
49         public double   h_error;        /* m */
50         public double   v_error;        /* m */
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         /* Return time since epoc in seconds */
78         public long seconds() {
79                 if (year == AltosLib.MISSING)
80                         return AltosLib.MISSING;
81                 if (month == AltosLib.MISSING)
82                         return AltosLib.MISSING;
83                 if (day == AltosLib.MISSING)
84                         return AltosLib.MISSING;
85                 if (hour == AltosLib.MISSING)
86                         return AltosLib.MISSING;
87                 if (minute == AltosLib.MISSING)
88                         return AltosLib.MISSING;
89                 if (second == AltosLib.MISSING)
90                         return AltosLib.MISSING;
91                 OffsetDateTime  odt = OffsetDateTime.of(year, month, day, hour, minute, second, 0, ZoneOffset.UTC);
92                 return odt.toEpochSecond();
93         }
94
95         public AltosGPS(AltosTelemetryMap map) throws ParseException {
96                 String  state = map.get_string(AltosTelemetryLegacy.AO_TELEM_GPS_STATE,
97                                                AltosTelemetryLegacy.AO_TELEM_GPS_STATE_ERROR);
98
99                 nsat = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_NUM_SAT, 0);
100                 if (state.equals(AltosTelemetryLegacy.AO_TELEM_GPS_STATE_LOCKED)) {
101                         connected = true;
102                         locked = true;
103                         lat = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_LATITUDE, MISSING, 1.0e-7);
104                         lon = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_LONGITUDE, MISSING, 1.0e-7);
105                         alt = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_ALTITUDE, MISSING);
106                         year = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_YEAR, MISSING);
107                         if (year != MISSING)
108                                 year += 2000;
109                         month = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_MONTH, MISSING);
110                         day = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_DAY, MISSING);
111
112                         hour = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_HOUR, 0);
113                         minute = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_MINUTE, 0);
114                         second = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_SECOND, 0);
115
116                         ground_speed = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_HORIZONTAL_SPEED,
117                                                       AltosLib.MISSING, 1/100.0);
118                         course = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_COURSE,
119                                              AltosLib.MISSING);
120                         pdop = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_PDOP, MISSING, 1.0);
121                         hdop = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_HDOP, MISSING, 1.0);
122                         vdop = map.get_double(AltosTelemetryLegacy.AO_TELEM_GPS_VDOP, MISSING, 1.0);
123                         h_error = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_HERROR, MISSING);
124                         v_error = map.get_int(AltosTelemetryLegacy.AO_TELEM_GPS_VERROR, MISSING);
125                 } else if (state.equals(AltosTelemetryLegacy.AO_TELEM_GPS_STATE_UNLOCKED)) {
126                         connected = true;
127                         locked = false;
128                 } else {
129                         connected = false;
130                         locked = false;
131                 }
132         }
133
134         public boolean parse_string (String line, boolean says_done) {
135                 String[] bits = line.split("\\s+");
136                 if (bits.length == 0)
137                         return false;
138                 if (line.startsWith("Date:")) {
139                         if (bits.length < 2)
140                                 return false;
141                         String[] d = bits[1].split("/");
142                         if (d.length < 3)
143                                 return false;
144                         year = Integer.parseInt(d[0]) + 2000;
145                         month = Integer.parseInt(d[1]);
146                         day = Integer.parseInt(d[2]);
147                 } else if (line.startsWith("Time:")) {
148                         if (bits.length < 2)
149                                 return false;
150                         String[] d = bits[1].split(":");
151                         if (d.length < 3)
152                                 return false;
153                         hour = Integer.parseInt(d[0]);
154                         minute = Integer.parseInt(d[1]);
155                         second = Integer.parseInt(d[2]);
156                 } else if (line.startsWith("Lat/Lon:")) {
157                         if (bits.length < 3)
158                                 return false;
159                         lat = Integer.parseInt(bits[1]) * 1.0e-7;
160                         lon = Integer.parseInt(bits[2]) * 1.0e-7;
161                 } else if (line.startsWith("Alt:")) {
162                         if (bits.length < 2)
163                                 return false;
164                         alt = Integer.parseInt(bits[1]);
165                 } else if (line.startsWith("Flags:")) {
166                         if (bits.length < 2)
167                                 return false;
168                         int status = Integer.decode(bits[1]);
169                         connected = (status & AltosLib.AO_GPS_RUNNING) != 0;
170                         locked = (status & AltosLib.AO_GPS_VALID) != 0;
171                         if (!says_done)
172                                 return false;
173                 } else if (line.startsWith("Sats:")) {
174                         if (bits.length < 2)
175                                 return false;
176                         nsat = Integer.parseInt(bits[1]);
177                         cc_gps_sat = new AltosGPSSat[nsat];
178                         for (int i = 0; i < nsat; i++) {
179                                 int     svid = Integer.parseInt(bits[2+i*2]);
180                                 int     cc_n0 = Integer.parseInt(bits[3+i*2]);
181                                 cc_gps_sat[i] = new AltosGPSSat(svid, cc_n0);
182                         }
183                 } else if (line.startsWith("done")) {
184                         return false;
185                 } else
186                         return false;
187                 return true;
188         }
189
190         public AltosGPS(String[] words, int i, int version) throws ParseException {
191                 AltosParse.word(words[i++], "GPS");
192                 nsat = AltosParse.parse_int(words[i++]);
193                 AltosParse.word(words[i++], "sat");
194
195                 connected = false;
196                 locked = false;
197                 lat = lon = 0;
198                 alt = 0;
199                 ClearGPSTime();
200                 if ((words[i]).equals("unlocked")) {
201                         connected = true;
202                         i++;
203                 } else if ((words[i]).equals("not-connected")) {
204                         i++;
205                 } else if (words.length >= 40) {
206                         locked = true;
207                         connected = true;
208
209                         if (version > 1)
210                                 ParseGPSDate(words[i++]);
211                         else
212                                 year = month = day = 0;
213                         ParseGPSTime(words[i++]);
214                         lat = AltosParse.parse_coord(words[i++]);
215                         lon = AltosParse.parse_coord(words[i++]);
216                         alt = AltosParse.parse_int(words[i++]);
217                         if (version > 1 || (i < words.length && !words[i].equals("SAT"))) {
218                                 ground_speed = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "m/s(H)"));
219                                 course = AltosParse.parse_int(words[i++]);
220                                 climb_rate = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "m/s(V)"));
221                                 hdop = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "(hdop)"));
222                                 h_error = AltosParse.parse_int(words[i++]);
223                                 v_error = AltosParse.parse_int(words[i++]);
224                         }
225                 } else {
226                         i++;
227                 }
228                 if (i < words.length) {
229                         AltosParse.word(words[i++], "SAT");
230                         int tracking_channels = 0;
231                         if (words[i].equals("not-connected"))
232                                 tracking_channels = 0;
233                         else
234                                 tracking_channels = AltosParse.parse_int(words[i]);
235                         i++;
236                         cc_gps_sat = new AltosGPSSat[tracking_channels];
237                         for (int chan = 0; chan < tracking_channels; chan++) {
238                                 cc_gps_sat[chan] = new AltosGPSSat();
239                                 cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]);
240                                 /* Older versions included SiRF status bits */
241                                 if (version < 2)
242                                         i++;
243                                 cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]);
244                         }
245                 } else
246                         cc_gps_sat = new AltosGPSSat[0];
247         }
248
249         public void set_latitude(int in_lat) {
250                 lat = in_lat / 10.0e7;
251         }
252
253         public void set_longitude(int in_lon) {
254                 lon = in_lon / 10.0e7;
255         }
256
257         public void set_time(int in_hour, int in_minute, int in_second) {
258                 hour = in_hour;
259                 minute = in_minute;
260                 second = in_second;
261         }
262
263         public void set_date(int in_year, int in_month, int in_day) {
264                 year = in_year;
265                 month = in_month;
266                 day = in_day;
267         }
268
269         /*
270         public void set_flags(int in_flags) {
271                 flags = in_flags;
272         }
273         */
274
275         public void set_altitude(int in_altitude) {
276                 alt = in_altitude;
277         }
278
279         public void add_sat(int svid, int c_n0) {
280                 if (cc_gps_sat == null) {
281                         cc_gps_sat = new AltosGPSSat[1];
282                 } else {
283                         AltosGPSSat[] new_gps_sat = new AltosGPSSat[cc_gps_sat.length + 1];
284                         for (int i = 0; i < cc_gps_sat.length; i++)
285                                 new_gps_sat[i] = cc_gps_sat[i];
286                         cc_gps_sat = new_gps_sat;
287                 }
288                 AltosGPSSat     sat = new AltosGPSSat();
289                 sat.svid = svid;
290                 sat.c_n0 = c_n0;
291                 cc_gps_sat[cc_gps_sat.length - 1] = sat;
292         }
293
294         private void init() {
295                 lat = AltosLib.MISSING;
296                 lon = AltosLib.MISSING;
297                 alt = AltosLib.MISSING;
298                 ground_speed = AltosLib.MISSING;
299                 course = AltosLib.MISSING;
300                 climb_rate = AltosLib.MISSING;
301                 pdop = AltosLib.MISSING;
302                 hdop = AltosLib.MISSING;
303                 vdop = AltosLib.MISSING;
304                 h_error = AltosLib.MISSING;
305                 v_error = AltosLib.MISSING;
306                 ClearGPSTime();
307                 cc_gps_sat = null;
308         }
309
310         public AltosGPS() {
311                 init();
312         }
313
314         public AltosGPS clone() {
315                 AltosGPS        g = new AltosGPS();
316
317                 g.nsat = nsat;
318                 g.locked = locked;
319                 g.connected = connected;
320                 g.lat = lat;            /* degrees (+N -S) */
321                 g.lon = lon;            /* degrees (+E -W) */
322                 g.alt = alt;            /* m */
323                 g.year = year;
324                 g.month = month;
325                 g.day = day;
326                 g.hour = hour;
327                 g.minute = minute;
328                 g.second = second;
329
330                 g.ground_speed = ground_speed;  /* m/s */
331                 g.course = course;              /* degrees */
332                 g.climb_rate = climb_rate;      /* m/s */
333                 g.pdop = pdop;          /* unitless */
334                 g.hdop = hdop;          /* unitless */
335                 g.vdop = vdop;          /* unitless */
336                 g.h_error = h_error;    /* m */
337                 g.v_error = v_error;    /* m */
338
339                 if (cc_gps_sat != null) {
340                         g.cc_gps_sat = new AltosGPSSat[cc_gps_sat.length];
341                         for (int i = 0; i < cc_gps_sat.length; i++) {
342                                 g.cc_gps_sat[i] = new AltosGPSSat(cc_gps_sat[i].svid,
343                                                                   cc_gps_sat[i].c_n0);
344                         }
345                 }
346                 return g;
347         }
348
349         public AltosGPS(AltosGPS old) {
350                 if (old != null) {
351                         nsat = old.nsat;
352                         locked = old.locked;
353                         connected = old.connected;
354                         lat = old.lat;          /* degrees (+N -S) */
355                         lon = old.lon;          /* degrees (+E -W) */
356                         alt = old.alt;          /* m */
357                         year = old.year;
358                         month = old.month;
359                         day = old.day;
360                         hour = old.hour;
361                         minute = old.minute;
362                         second = old.second;
363
364                         ground_speed = old.ground_speed;        /* m/s */
365                         course = old.course;            /* degrees */
366                         climb_rate = old.climb_rate;    /* m/s */
367                         pdop = old.pdop;                /* unitless? */
368                         hdop = old.hdop;                /* unitless? */
369                         vdop = old.vdop;                /* unitless? */
370                         h_error = old.h_error;          /* m */
371                         v_error = old.v_error;          /* m */
372
373                         if (old.cc_gps_sat != null) {
374                                 cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length];
375                                 for (int i = 0; i < old.cc_gps_sat.length; i++) {
376                                         cc_gps_sat[i] = new AltosGPSSat();
377                                         cc_gps_sat[i].svid = old.cc_gps_sat[i].svid;
378                                         cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0;
379                                 }
380                         }
381                 } else {
382                         init();
383                 }
384         }
385
386         static public void provide_data(AltosDataListener listener, AltosLink link) throws InterruptedException {
387                 try {
388                         AltosGPS gps = new AltosGPS(link, link.config_data());
389                         if (gps != null)
390                                 listener.set_gps(gps);
391                 } catch (TimeoutException te) {
392                 }
393         }
394
395         public AltosGPS (AltosLink link, AltosConfigData config_data) throws TimeoutException, InterruptedException {
396                 boolean says_done = config_data.compare_version("1.0") >= 0;
397                 init();
398                 link.printf("g\n");
399                 for (;;) {
400                         String line = link.get_reply_no_dialog(5000);
401                         if (line == null)
402                                 throw new TimeoutException();
403                         if (!parse_string(line, says_done))
404                                 break;
405                 }
406         }
407 }