FlightGear next
AV400.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: AV400.cxx
3 * SPDX-FileComment: Garmin 400 series protocol class
4 * SPDX-FileCopyrightText: Copyright (C) 2006 Curtis L. Olson - http://www.flightgear.org/~curt
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#ifdef HAVE_CONFIG_H
9# include "config.h"
10#endif
11
12#include <cstdio>
13#include <cstdlib>
14#include <cstring>
15
16#include <simgear/debug/logstream.hxx>
17#include <simgear/math/sg_geodesy.hxx>
18#include <simgear/io/iochannel.hxx>
19#include <simgear/timing/sg_time.hxx>
20
21#include <Main/fg_props.hxx>
22#include <Main/globals.hxx>
23
24#include "AV400.hxx"
25
28
31
32
33// generate AV400 message
35 // cout << "generating garmin message" << endl;
36
37 char msg_z[32], msg_A[32], msg_B[32], msg_C[32], msg_D[32];
38 char msg_Q[32], msg_T[32], msg_type2[256];
39 // the following could be implemented, but currently are unused
40 // char msg_E[32], msg_G[32], msg_I[32], msg_K[32], msg_L[32], msg_S[32];
41 // char msg_l[32];
42
43 char dir;
44 int deg;
45 double min;
46
47 // create msg_z
48 snprintf( msg_z, sizeof(msg_z), "z%05.0f\r\n", fdm.get_Altitude() );
49
50 // create msg_A
51 snprintf( msg_A, sizeof(msg_A), "A");
52
53 double latd = fdm.get_Latitude() * SGD_RADIANS_TO_DEGREES;
54 if ( latd < 0.0 ) {
55 latd = -latd;
56 dir = 'S';
57 } else {
58 dir = 'N';
59 }
60 deg = (int)latd;
61 min = (latd - (double)deg) * 60.0 * 100.0;
62 snprintf( msg_A, 32, "A%c %02d %04.0f\r\n", dir, deg, min);
63
64 // create msg_B
65 double lond = fdm.get_Longitude() * SGD_RADIANS_TO_DEGREES;
66 if ( lond < 0.0 ) {
67 lond = -lond;
68 dir = 'W';
69 } else {
70 dir = 'E';
71 }
72 deg = (int)lond;
73 min = (lond - (double)deg) * 60.0 * 100.0;
74 snprintf( msg_B, 32, "B%c %03d %04.0f\r\n", dir, deg, min);
75
76 // create msg_C
77 float magdeg = fgGetDouble( "/environment/magnetic-variation-deg" );
78 double vn = fgGetDouble( "/velocities/speed-north-fps" );
79 double ve = fgGetDouble( "/velocities/speed-east-fps" );
80 double gnd_trk_true = atan2( ve, vn ) * SGD_RADIANS_TO_DEGREES;
81 double gnd_trk_mag = gnd_trk_true - magdeg;
82 if ( gnd_trk_mag < 0.0 ) { gnd_trk_mag += 360.0; }
83 if ( gnd_trk_mag >= 360.0 ) { gnd_trk_mag -= 360.0; }
84 snprintf( msg_C, 32, "C%03.0f\r\n", gnd_trk_mag);
85
86 // create msg_D
87 double speed_kt = sqrt( vn*vn + ve*ve ) * SG_FPS_TO_KT;
88 if ( speed_kt > 999.0 ) {
89 speed_kt = 999.0;
90 }
91 snprintf( msg_D, 32, "D%03.0f\r\n", speed_kt);
92
93 // create msg_E (not implemented)
94 // create msg_G (not implemented)
95 // create msg_I (not implemented)
96 // create msg_K (not implemented)
97 // create msg_L (not implemented)
98
99 // create msg_Q
100 if ( magdeg < 0.0 ) {
101 magdeg = -magdeg;
102 dir = 'W';
103 } else {
104 dir = 'E';
105 }
106 snprintf( msg_Q, 32, "Q%c%03.0f\r\n", dir, magdeg * 10.0 );
107
108 // create msg_S (not implemented)
109
110 // create msg_T
111 snprintf( msg_T, 32, "T---------\r\n" );
112
113 // create msg_l (not implemented)
114
115 // sentence type 2
116 snprintf( msg_type2, 256, "w01%c\r\n", (char)65 );
117
118 // assemble message
119 std::string sentence;
120 sentence += '\002'; // STX
121 sentence += msg_z; // altitude
122 sentence += msg_A; // latitude
123 sentence += msg_B; // longitude
124 sentence += msg_C; // ground track
125 sentence += msg_D; // ground speed (kt)
126 // sentence += "E-----\r\n";
127 // sentence += "G-----\r\n";
128 // sentence += "I----\r\n";
129 // sentence += "K-----\r\n";
130 // sentence += "L----\r\n";
131 sentence += msg_Q; // magvar
132 // sentence += "S-----\r\n";
133 sentence += msg_T; // end of type 1 messages (must be sent)
134 sentence += msg_type2; // type2 message
135 // sentence += "l------\r\n";
136 sentence += '\003'; // ETX
137
138 // cout << sentence;
139 length = sentence.length();
140 // cout << endl << "length = " << length << endl;
141 strncpy( buf, sentence.c_str(), length );
142
143 return true;
144}
145
146
147// parse AV400 message
149 SG_LOG( SG_IO, SG_INFO, "parse garmin message" );
150
151 std::string msg = buf;
152 msg = msg.substr( 0, length );
153 SG_LOG( SG_IO, SG_INFO, "entire message = " << msg );
154
155 std::string::size_type begin_line, end_line, begin, end;
156 begin_line = begin = 0;
157
158 // extract out each line
159 end_line = msg.find("\n", begin_line);
160 while ( end_line != std::string::npos ) {
161 std::string line = msg.substr(begin_line, end_line - begin_line);
162 begin_line = end_line + 1;
163 SG_LOG( SG_IO, SG_INFO, " input line = " << line );
164
165 // leading character
166 std::string start = msg.substr(begin, 1);
167 ++begin;
168 SG_LOG( SG_IO, SG_INFO, " start = " << start );
169
170 // sentence
171 end = msg.find(",", begin);
172 if ( end == std::string::npos ) {
173 return false;
174 }
175
176 std::string sentence = msg.substr(begin, end - begin);
177 begin = end + 1;
178 SG_LOG( SG_IO, SG_INFO, " sentence = " << sentence );
179
180 double lon_deg, lon_min, lat_deg, lat_min;
181 double lon, lat, speed, heading, altitude;
182
183 if ( sentence == "GPRMC" ) {
184 // time
185 end = msg.find(",", begin);
186 if ( end == std::string::npos ) {
187 return false;
188 }
189
190 std::string utc = msg.substr(begin, end - begin);
191 begin = end + 1;
192 SG_LOG( SG_IO, SG_INFO, " utc = " << utc );
193
194 // junk
195 end = msg.find(",", begin);
196 if ( end == std::string::npos ) {
197 return false;
198 }
199
200 std::string junk = msg.substr(begin, end - begin);
201 begin = end + 1;
202 SG_LOG( SG_IO, SG_INFO, " junk = " << junk );
203
204 // lat val
205 end = msg.find(",", begin);
206 if ( end == std::string::npos ) {
207 return false;
208 }
209
210 std::string lat_str = msg.substr(begin, end - begin);
211 begin = end + 1;
212
213 lat_deg = atof( lat_str.substr(0, 2).c_str() );
214 lat_min = atof( lat_str.substr(2).c_str() );
215
216 // lat dir
217 end = msg.find(",", begin);
218 if ( end == std::string::npos ) {
219 return false;
220 }
221
222 std::string lat_dir = msg.substr(begin, end - begin);
223 begin = end + 1;
224
225 lat = lat_deg + ( lat_min / 60.0 );
226 if ( lat_dir == "S" ) {
227 lat *= -1;
228 }
229
230 fdm.set_Latitude( lat * SGD_DEGREES_TO_RADIANS );
231 SG_LOG( SG_IO, SG_INFO, " lat = " << lat );
232
233 // lon val
234 end = msg.find(",", begin);
235 if ( end == std::string::npos ) {
236 return false;
237 }
238
239 std::string lon_str = msg.substr(begin, end - begin);
240 begin = end + 1;
241
242 lon_deg = atof( lon_str.substr(0, 3).c_str() );
243 lon_min = atof( lon_str.substr(3).c_str() );
244
245 // lon dir
246 end = msg.find(",", begin);
247 if ( end == std::string::npos ) {
248 return false;
249 }
250
251 std::string lon_dir = msg.substr(begin, end - begin);
252 begin = end + 1;
253
254 lon = lon_deg + ( lon_min / 60.0 );
255 if ( lon_dir == "W" ) {
256 lon *= -1;
257 }
258
259 fdm.set_Longitude( lon * SGD_DEGREES_TO_RADIANS );
260 SG_LOG( SG_IO, SG_INFO, " lon = " << lon );
261
262#if 0
263 double sl_radius, lat_geoc;
264 sgGeodToGeoc( fdm.get_Latitude(),
265 fdm.get_Altitude(),
266 &sl_radius, &lat_geoc );
267 fdm.set_Geocentric_Position( lat_geoc,
268 fdm.get_Longitude(),
269 sl_radius + fdm.get_Altitude() );
270#endif
271
272 // speed
273 end = msg.find(",", begin);
274 if ( end == std::string::npos ) {
275 return false;
276 }
277
278 std::string speed_str = msg.substr(begin, end - begin);
279 begin = end + 1;
280 speed = atof( speed_str.c_str() );
281 fdm.set_V_calibrated_kts( speed );
282 // fdm.set_V_ground_speed( speed );
283 SG_LOG( SG_IO, SG_INFO, " speed = " << speed );
284
285 // heading
286 end = msg.find(",", begin);
287 if ( end == std::string::npos ) {
288 return false;
289 }
290
291 std::string hdg_str = msg.substr(begin, end - begin);
292 begin = end + 1;
293 heading = atof( hdg_str.c_str() );
294 fdm.set_Euler_Angles( fdm.get_Phi(),
295 fdm.get_Theta(),
296 heading * SGD_DEGREES_TO_RADIANS );
297 SG_LOG( SG_IO, SG_INFO, " heading = " << heading );
298 } else if ( sentence == "PGRMZ" ) {
299 // altitude
300 end = msg.find(",", begin);
301 if ( end == std::string::npos ) {
302 return false;
303 }
304
305 std::string alt_str = msg.substr(begin, end - begin);
306 altitude = atof( alt_str.c_str() );
307 begin = end + 1;
308
309 // altitude units
310 end = msg.find(",", begin);
311 if ( end == std::string::npos ) {
312 return false;
313 }
314
315 std::string alt_units = msg.substr(begin, end - begin);
316 begin = end + 1;
317
318 if ( alt_units != "F" && alt_units != "f" ) {
319 altitude *= SG_METER_TO_FEET;
320 }
321
322 fdm.set_Altitude( altitude );
323
324 SG_LOG( SG_IO, SG_INFO, " altitude = " << altitude );
325
326 }
327
328 // printf("%.8f %.8f\n", lon, lat);
329
330 begin = begin_line;
331 end_line = msg.find("\n", begin_line);
332 }
333
334 return true;
335}
336
337
338// open hailing frequencies
340 if ( is_enabled() ) {
341 SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
342 << "is already in use, ignoring" );
343 return false;
344 }
345
346 SGIOChannel *io = get_io_channel();
347
348 if ( ! io->open( get_direction() ) ) {
349 SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
350 return false;
351 }
352
353 set_enabled( true );
354
355 return true;
356}
357
358
359// process work for this port
361 SGIOChannel *io = get_io_channel();
362
363 if ( get_direction() == SG_IO_OUT ) {
364 gen_message();
365 if ( ! io->write( buf, length ) ) {
366 SG_LOG( SG_IO, SG_WARN, "Error writing data." );
367 return false;
368 }
369 } else if ( get_direction() == SG_IO_IN ) {
370 if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
371 SG_LOG( SG_IO, SG_ALERT, "Success reading data." );
372 if ( parse_message() ) {
373 SG_LOG( SG_IO, SG_ALERT, "Success parsing data." );
374 } else {
375 SG_LOG( SG_IO, SG_ALERT, "Error parsing data." );
376 }
377 } else {
378 SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
379 return false;
380 }
381 if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
382 SG_LOG( SG_IO, SG_ALERT, "Success reading data." );
383 if ( parse_message() ) {
384 SG_LOG( SG_IO, SG_ALERT, "Success parsing data." );
385 } else {
386 SG_LOG( SG_IO, SG_ALERT, "Error parsing data." );
387 }
388 } else {
389 SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
390 return false;
391 }
392 }
393
394 return true;
395}
396
397
398// close the channel
400 SGIOChannel *io = get_io_channel();
401
402 set_enabled( false );
403
404 if ( ! io->close() ) {
405 return false;
406 }
407
408 return true;
409}
double altitude
Definition ADA.cxx:46
double lat_geoc
Definition ADA.cxx:44
#define min(X, Y)
bool parse_message()
Definition AV400.cxx:148
FlightProperties fdm
Definition AV400.hxx:57
bool close()
Definition AV400.cxx:399
bool open()
Definition AV400.cxx:339
FGAV400()
Definition AV400.cxx:26
bool gen_message()
Definition AV400.cxx:34
~FGAV400()
Definition AV400.cxx:29
bool process()
Definition AV400.cxx:360
SGProtocolDir get_direction() const
Definition protocol.hxx:65
SGIOChannel * get_io_channel() const
Definition protocol.hxx:90
void set_enabled(const bool b)
Definition protocol.hxx:88
bool is_enabled() const
Definition protocol.hxx:87
static double atof(const string &str)
Definition options.cxx:107
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
Definition proptest.cpp:30
#define FG_MAX_MSG_SIZE
Definition protocol.hxx:33