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