1 /*
2  * Serial download of barograph data from a Brauniger IQ Variometer.
3  *
4  * Copyright (C) 2004 Chris Jones
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 2 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 59 Temple Place - Suite 330, Boston, MA 02111 USA
19  */
20 
21 #include "defs.h"
22 #include "gbser.h"
23 #include <errno.h>
24 
25 static void* serial_handle;
26 
27 #define MYNAME "BRAUNIGER-IQ"
28 #define PRESTRKNAME "PRESALTTRK"
29 
30 typedef enum {
31   st_sync,
32   st_fl_num,
33   st_data_len,
34   st_ser_num,
35   st_pilot_name,
36   st_start_date,
37   st_start_year,
38   st_max_alt_1,
39   st_max_alt_2,
40   st_max_climb,
41   st_flight_dur,
42   st_log_ival,
43   st_start_time,
44   st_end_time,
45   st_sample_alt,
46   st_sample_spd,
47   num_states
48 } state_t;
49 state_t state;
50 #if __cplusplus
51 inline state_t operator++(state_t& rs, int)
52 {
53   return rs = (state_t)((int)rs + 1);
54 }
55 #endif
56 
57 static const int reqd_bytes[num_states] = { 6, 1, 2, 2, 25, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1 };
58 
rd_init(const char * fname)59 static void rd_init(const char* fname)
60 {
61   if (serial_handle = gbser_init(fname), NULL == serial_handle) {
62     fatal(MYNAME ": Can't open port '%s'\n", fname);
63   }
64   if (gbser_set_port(serial_handle, 9600, 8, 0, 1) != gbser_OK) {
65     fatal(MYNAME ": Can't configure port '%s'\n", fname);
66   }
67 }
68 
rd_deinit(void)69 static void rd_deinit(void)
70 {
71   gbser_deinit(serial_handle);
72   serial_handle = NULL;
73 }
74 
75 /**
76  * Process a data record.
77  * @return zero when all expected data has been received
78  */
process_data(const unsigned char * data)79 static int process_data(const unsigned char* data)
80 {
81   static int remaining = 100;
82   static struct tm tm;
83   static time_t start, creation;
84   static route_head* track;
85   static unsigned char interval;
86   time_t finish;
87   waypoint* wpt = NULL;
88   int i;
89 
90   if (global_opts.debug_level >= 3) {
91     for (i = 0; i < reqd_bytes[state]; i++) {
92       printf("%.2x ", data[i]);
93     }
94     puts("");
95   }
96 
97   remaining -= reqd_bytes[state];
98   switch (state) {
99   case st_sync:
100     if (memcmp(data, "\x30\x31\x32\x33\x34\x35", 6) != 0) {
101       fatal(MYNAME ": Could not synchronise\n");
102     }
103     break;
104 
105   case st_fl_num:
106     if (global_opts.debug_level >= 1) {
107       printf(MYNAME ": Flight Number: %d\n", data[0]);
108     }
109     break;
110 
111   case st_data_len:
112     remaining = (data[0] << 8) + data[1] - 2;
113     if (global_opts.debug_level >= 1) {
114       printf(MYNAME ": Data Length: %d\n", remaining);
115     }
116     break;
117 
118   case st_ser_num:
119     if (global_opts.debug_level >= 1) {
120       printf(MYNAME ": Serial Number: %d\n", (data[0] << 8) + data[1]);
121     }
122     break;
123 
124   case st_pilot_name:
125     if (global_opts.debug_level >= 1) {
126       printf(MYNAME ": Pilot Name: %.25s\n", data);
127     }
128     break;
129 
130   case st_start_date:
131     i = (data[0] << 8) + data[1];
132     tm.tm_mday = i / 100;
133     tm.tm_mon = (i % 100) - 1;
134     break;
135 
136   case st_start_year:
137     tm.tm_year = ((data[0] << 8) + data[1]) - 1900;
138     break;
139 
140   case st_max_alt_1:
141     if (global_opts.debug_level >= 1) {
142       printf(MYNAME ": Max Altitude 1: %dm\n", (data[0] << 8) + data[1]);
143     }
144     break;
145 
146   case st_max_alt_2:
147     if (global_opts.debug_level >= 1) {
148       printf(MYNAME ": Max Altitude 2: %dm\n", (data[0] << 8) + data[1]);
149     }
150     break;
151 
152   case st_max_climb:
153     if (global_opts.debug_level >= 1) {
154       i = (data[0] << 8) + data[1];
155       printf(MYNAME ": Max climb: %d.%dm/s\n", i / 10, i % 10);
156     }
157     break;
158 
159   case st_flight_dur:
160     if (global_opts.debug_level >= 1) {
161       i = (data[0] << 8) + data[1];
162       printf(MYNAME ": Flight Time: %d:%d\n", i / 100, i % 100);
163     }
164     break;
165 
166   case st_log_ival:
167     interval = data[0];
168     if (global_opts.debug_level >= 1) {
169       printf(MYNAME ": Logging Interval: %ds\n", interval);
170     }
171     break;
172 
173   case st_start_time:
174     i = (data[0] << 8) + data[1];
175     tm.tm_hour = i / 100;
176     tm.tm_min = (i % 100) - 1;
177     tm.tm_sec = 0;
178     creation = start = mktime(&tm);
179     if (global_opts.debug_level >= 1) {
180       printf(MYNAME ": Start Time: %s", ctime(&start));
181     }
182     break;
183 
184   case st_end_time:
185     i = (data[0] << 8) + data[1];
186     tm.tm_hour = i / 100;
187     tm.tm_min = (i % 100) - 1;
188     finish = mktime(&tm);
189     if (global_opts.debug_level >= 1) {
190       printf(MYNAME ": End Time: %s", ctime(&finish));
191     }
192     if (remaining) {
193       track = route_head_alloc();
194       track->rte_name = xstrdup(PRESTRKNAME);
195       track->rte_desc = xstrdup("Brauniger-IQ Barograph");
196       track_add_head(track);
197     } else {
198       warning(MYNAME ": No barograph recorded for this flight\n");
199     }
200     break;
201 
202   case st_sample_alt:
203     wpt = waypt_new();
204     wpt->latitude = wpt->longitude = 0.0;
205     wpt->creation_time = creation;
206     creation += interval;
207     wpt->altitude = (data[0] << 8) + data[1];
208     track_add_wpt(track, wpt);
209     if (global_opts.debug_level >= 2) {
210       printf(MYNAME ": remaining=%d, Altitude=%fm, ", remaining, wpt->altitude);
211     }
212     break;
213 
214   case st_sample_spd:
215     if (global_opts.debug_level >= 2) {
216       printf("Airspeed=%dkmh\n", data[0]);
217     }
218     state = st_sample_alt;
219     return remaining;
220 
221   default:
222     fatal(MYNAME ": Bad internal state\n");
223   }
224   state++;
225   return remaining;
226 }
227 
data_read(void)228 static void data_read(void)
229 {
230   unsigned char ibuf[25];
231   int rd_cnt;
232 
233   if (global_opts.debug_level >= 0) {
234     puts(MYNAME ":  Select recorded flight in memo mode.");
235     puts(MYNAME ":  Press Memo button for two seconds...");
236   }
237 
238   // Wait until something arrives
239   if (global_opts.debug_level >= 0) {
240     puts(MYNAME ":  Downloading flight...");
241   }
242 
243   // Read data until there is none left to read
244   state = st_sync;
245   for (;;) {
246     /* wait up to 5 seconds for more data */
247     rd_cnt = gbser_read_wait(serial_handle, ibuf, reqd_bytes[state], 5000);
248     if (rd_cnt < 0) {
249       fatal(MYNAME ": Serial error\n");
250     } else if (rd_cnt < reqd_bytes[state]) {
251       fatal(MYNAME ": Incomplete download\n");
252     }
253 
254     if (!process_data(ibuf)) {
255       if (global_opts.debug_level >= 0) {
256         puts(MYNAME "  ...Finished");
257       }
258       return;
259     }
260   }
261 }
262 
263 static arglist_t brauniger_iq_args[] = {
264   ARG_TERMINATOR
265 };
266 
267 ff_vecs_t brauniger_iq_vecs = {
268   ff_type_serial,
269   { ff_cap_none, ff_cap_read, ff_cap_none},
270   rd_init,
271   NULL,
272   rd_deinit,
273   NULL,
274   data_read,
275   NULL,
276   NULL,
277   brauniger_iq_args,
278   CET_CHARSET_UTF8, 1		/* master process: don't convert anything | CET-REVIEW */
279 };
280