FlightGear next
AV400WSim.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: AV400WSim.cxx
3 * SPDX-FileComment: <text>Garmin 400 series protocal class. This AV400WSim
4 * protocol generates the set of "simulator" commands a garmin 400 WAAS
5 * series gps would expect as input in simulator mode. The AV400W
6 * protocol parses the set of commands that a garmin 400W series gps
7 * would emit.
8 *
9 * The Garmin WAAS GPS uses 2 serial channels to communicate with the
10 * simulator. These 2 channels are represented by the FGAV400WSimA and
11 * the FGAV400WSimB classes. The "A" channel is similar to the previous
12 * AVSim400 protocol. The "B" channel is considered the "GPS" channel and
13 * uses a different protocol than the "A" channel. The GPS unit expects
14 * input on the "B" channel at two different frequencies (1hz and 5hz,
15 * normally). The "B" channel also expects responses to certain output
16 * messages.
17 *
18 * Original AV400Sim code Written by Curtis Olson, started Janauary 2009.</text>
19 * SPDX-FileCopyrightText: Copyright (c) 2011 Bruce Hellstrom - http://www.celebritycc.com
20 * SPDX-FileContributor: Copyright (C) 2009 Curtis L. Olson - http://www.flightgear.org/~curt
21 * SPDX-License-Identifier: GPL-2.0-or-later
22 */
23
24#ifdef HAVE_CONFIG_H
25# include "config.h"
26#endif
27
28#include <cstdio>
29
30#include <simgear/debug/logstream.hxx>
31#include <simgear/math/sg_geodesy.hxx>
32#include <simgear/io/iochannel.hxx>
33#include <simgear/timing/sg_time.hxx>
34
36#include <Main/fg_props.hxx>
37#include <Main/globals.hxx>
38
39#include "AV400WSim.hxx"
40
43
46
47
48// generate AV400WSimA message
50 // cout << "generating garmin message" << endl;
51
52 char msg_h[32], msg_i[32], msg_j[32], msg_k[32], msg_l[32];
53 //char msg_type2[256];
54
55 double alt;
56
57 // create msg_h
58 double obs = fgGetDouble( "/instrumentation/nav[0]/radials/selected-deg" );
59 snprintf( msg_h, 32, "h%04d\r\n", (int)(obs*10) );
60
61 // create msg_i
62 double fuel = fgGetDouble( "/consumables/fuel/total-fuel-gals" );
63 if ( fuel > 999.9 ) { fuel = 999.9; }
64 snprintf( msg_i, 32, "i%04.0f\r\n", fuel*10.0 );
65
66 // create msg_j
67 double gph = fgGetDouble( "/engines/engine[0]/fuel-flow-gph" );
68 gph += fgGetDouble( "/engines/engine[1]/fuel-flow-gph" );
69 gph += fgGetDouble( "/engines/engine[2]/fuel-flow-gph" );
70 gph += fgGetDouble( "/engines/engine[3]/fuel-flow-gph" );
71 if ( gph > 999.9 ) { gph = 999.9; }
72 snprintf( msg_j, 32, "j%04.0f\r\n", gph*10.0 );
73
74 // create msg_k
75 snprintf( msg_k, 32, "k%04d%02d%02d%02d%02d%02d\r\n",
76 fgGetInt( "/sim/time/utc/year"),
77 fgGetInt( "/sim/time/utc/month"),
78 fgGetInt( "/sim/time/utc/day"),
79 fgGetInt( "/sim/time/utc/hour"),
80 fgGetInt( "/sim/time/utc/minute"),
81 fgGetInt( "/sim/time/utc/second") );
82
83 // create msg_l
84 alt = fgGetDouble( "/instrumentation/pressure-alt-ft" );
85 if ( alt > 99999.0 ) { alt = 99999.0; }
86 snprintf( msg_l, 32, "l%05.0f\r\n", alt );
87
88 // sentence type 2
89 //sprintf( msg_type2, "w01%c\r\n", (char)65 );
90
91 // assemble message
92 std::string sentence;
93 sentence += '\002'; // STX
94 sentence += msg_h; // obs heading in deg (*10)
95 sentence += msg_i; // total fuel in gal (*10)
96 sentence += msg_j; // fuel flow gph (*10)
97 sentence += msg_k; // date/time (UTC)
98 sentence += msg_l; // pressure altitude
99 //sentence += msg_type2; // type2 message
100 sentence += '\003'; // ETX
101
102 // cout << sentence;
103 length = sentence.length();
104 // cout << endl << "length = " << length << endl;
105 strncpy( buf, sentence.c_str(), length );
106
107 return true;
108}
109
110
111// parse AV400SimA message
113 SG_LOG( SG_IO, SG_INFO, "parse AV400WSimA message" );
114
115 std::string msg = buf;
116 msg = msg.substr( 0, length );
117 SG_LOG( SG_IO, SG_INFO, "entire message = " << msg );
118
119 std::string ident = msg.substr(0, 1);
120 if ( ident == "i" ) {
121 std::string side = msg.substr(1,1);
122 std::string num = msg.substr(2,3);
123 if ( side == "-" ) {
124 fgSetDouble("/instrumentation/av400w/cdi-deflection", 0.0);
125 }
126 else {
127 int pos = atoi(num.c_str());
128 if ( side == "L" ) {
129 pos *= -1;
130 }
131 fgSetDouble("/instrumentation/av400w/cdi-deflection",
132 (double)pos / 10.0);
133 //printf( "i, %s%s, %f\n", side.c_str(), num.c_str(), (double)(pos / 10.0) );
134 }
135 }
136 else if ( ident == "j" ) {
137 std::string side = msg.substr(1,1);
138 std::string num = msg.substr(2,3);
139 if ( side == "-" ) {
140 fgSetDouble("/instrumentation/av400w/gs-deflection", 0.0);
141 }
142 else {
143 int pos = atoi(num.c_str());
144 if ( side == "B" ) {
145 pos *= -1;
146 }
147 // convert glideslope to -3.5 to 3.5
148 fgSetDouble("/instrumentation/av400w/gs-deflection",
149 (double)pos / 28.57);
150 //printf( "j, %s%s, %f\n", side.c_str(), num.c_str(), (double)(pos / 28.57) );
151 }
152 }
153 else if ( ident == "k" ) {
154 std::string ind = msg.substr(1,1);
155 if ( ind == "T" ) {
156 fgSetBool("/instrumentation/av400w/to-flag", true);
157 fgSetBool("/instrumentation/av400w/from-flag", false);
158 //printf( "set to-flag\n" );
159 } else if ( ind == "F" ) {
160 fgSetBool("/instrumentation/av400w/to-flag", false);
161 fgSetBool("/instrumentation/av400w/from-flag", true);
162 //printf( "set from flag\n" );
163 } else {
164 fgSetBool("/instrumentation/av400w/to-flag", false);
165 fgSetBool("/instrumentation/av400w/from-flag", false);
166 //printf( "set t/f both false\n" );
167 }
168 //printf( "k, %s\n", ind.c_str() );
169 }
170 else if ( ident == "S" ) {
171 std::string ind = msg.substr(1,5);
172 //printf( "S - %s\n", ind.c_str() );
173 }
174 else {
175 // SG_LOG( SG_IO, SG_ALERT, "unknown AV400Sim message = " << msg );
176 }
177
178 return true;
179}
180
181
182// open hailing frequencies
184 if ( is_enabled() ) {
185 SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
186 << "is already in use, ignoring" );
187 return false;
188 }
189
190 SGIOChannel *io = get_io_channel();
191
192 if ( ! io->open( get_direction() ) ) {
193 SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
194 return false;
195 }
196
197 set_enabled( true );
198
199 return true;
200}
201
202
203// process work for this port
205 SGIOChannel *io = get_io_channel();
206
207 // until we have parsers/generators for the reverse direction,
208 // this is hardwired to expect that the physical GPS is slaving
209 // from FlightGear.
210
211 // Send FlightGear data to the external device
212 gen_message();
213 if ( ! io->write( buf, length ) ) {
214 SG_LOG( SG_IO, SG_WARN, "Error writing data." );
215 return false;
216 }
217
218 // read the device messages back
219 while ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
220 // SG_LOG( SG_IO, SG_ALERT, "Success reading data." );
221 if ( parse_message() ) {
222 // SG_LOG( SG_IO, SG_ALERT, "Success parsing data." );
223 } else {
224 // SG_LOG( SG_IO, SG_ALERT, "Error parsing data." );
225 }
226 }
227
228 return true;
229}
230
231
232// close the channel
234 SGIOChannel *io = get_io_channel();
235
236 set_enabled( false );
237
238 if ( ! io->close() ) {
239 return false;
240 }
241
242 return true;
243}
244
245// Start of FGAV400WSimB class methods
247hz2(0.0),
248hz2count(0),
249hz2cycles(0),
250flight_phase(-1),
251req_hostid(true),
252req_sbas(false)
253{
254 hal.clear();
255 val.clear();
256 hal.append( "\0\0", 2 );
257 val.append( "\0\0", 2 );
258 outputctr = 0;
259 sbas_sel.append( "\0\x01", 2 );
260 fdm = new FlightProperties;
261}
262
264 delete fdm;
265}
266
267
269 char chksum = 0;
270 std::string data = "Cj\r\n";
271 data += "COPYRIGHT 2008 GARMIN LTD. \r\n";
272 data += "SFTW P/N # 006-B0339-0A\r\n";
273 data += "SOFTWARE VER # 3\r\n";
274 data += "SOFTWARE REV # 2\r\n";
275 data += "SOFTWARE DATE 11/03/2008\r\n";
276 data += "SW CRC 8F5E7DD1 AE5D4563\r\n";
277 data += "HDWR P/N # 012-00857-01 \r\n";
278 data += "SERIAL # 085701214976140\r\n";
279 data += "MANUFACTUR DATE 02/26/2007\r\n";
280 data += "OPTIONS LIST iiiiiiiiii";
281
282 // calculate the checksum
283 for ( std::string::const_iterator cli = data.begin(); cli != data.end(); cli++ ) {
284 chksum ^= *cli;
285 }
286
287 std::string sentence( "@@" );
288 sentence += data;
289 sentence.push_back( chksum );
290 sentence += "\x0D\n";
291
292 length = sentence.length();
293 char *bufptr = buf;
294 for ( std::string::const_iterator cli = sentence.begin(); cli != sentence.end(); cli++ ) {
295 *bufptr++ = *cli;
296 }
297
298 return true;
299}
300
302 char chksum = 0;
303 std::string data = "WA";
304 data.push_back( '\0' );
305 data += sbas_sel;
306
307 // calculate the checksum
308 for ( std::string::const_iterator cli = data.begin(); cli != data.end(); cli++ ) {
309 chksum ^= *cli;
310 }
311
312 std::string sentence( "@@" );
313 sentence += data;
314 sentence.push_back( chksum );
315 sentence += "\x0D\n";
316
317 length = sentence.length();
318 char *bufptr = buf;
319 for ( std::string::const_iterator cli = sentence.begin(); cli != sentence.end(); cli++ ) {
320 *bufptr++ = *cli;
321 }
322
323 return true;
324}
325
326// Wh - Visible SBAS Satellites (hz2)
328 char chksum = 0;
329
330 // generate the Wh message
331 std::string data = "Wh";
332 data.push_back( '\x0F' );
333 data.append( "\x3f\x00\x00\x20\x00\x20", 6 );
334 data.append( "\x4f\x00\x00\x28\x00\x30", 6 );
335 data.append( "\x2d\x00\x00\x48\x01\x05", 6 );
336 data.append( "\x1d\x00\x00\x10\x01\x10", 6 );
337 data.append( "\x50\x00\x00\x33\x00\x50", 6 );
338 data.append( "\x22\x00\x00\x16\x00\x90", 6 );
339 data.append( "\x40\x00\x00\x20\x00\x20", 6 );
340 data.append( "\x50\x00\x00\x28\x00\x30", 6 );
341 data.append( "\x2e\x00\x00\x48\x01\x05", 6 );
342 data.append( "\x1e\x00\x00\x10\x01\x10", 6 );
343 data.append( "\x51\x00\x00\x33\x00\x50", 6 );
344 data.append( "\x23\x00\x00\x16\x00\x90", 6 );
345 data.append( "\x1f\x00\x00\x10\x01\x10", 6 );
346 data.append( "\x52\x00\x00\x33\x00\x50", 6 );
347 data.append( "\x24\x00\x00\x16\x00\x90", 6 );
348 data.push_back( '0' );
349
350 // calculate the checksum
351 for ( std::string::const_iterator cli = data.begin(); cli != data.end(); cli++ ) {
352 chksum ^= *cli;
353 }
354
355 std::string sentence( "@@" );
356 sentence += data;
357 sentence.push_back( chksum );
358 sentence += "\x0D\n";
359
360 length = sentence.length();
361 char *bufptr = buf;
362 for ( std::string::const_iterator cli = sentence.begin(); cli != sentence.end(); cli++ ) {
363 *bufptr++ = *cli;
364 }
365
366 return true;
367}
368
369
370// Wx - Channel Status Message (hz2)
372 char chksum = 0;
373
374 // Now process the Wx message
375 std::string data = "Wx";
376 data.push_back( (char)( fgGetInt( "/sim/time/utc/month") & 0xFF ) );
377 data.push_back( (char)( fgGetInt( "/sim/time/utc/day") & 0xFF ) );
378 data.push_back( (char)( (fgGetInt( "/sim/time/utc/year") >> 8 ) & 0xFF ) );
379 data.push_back( (char)( fgGetInt( "/sim/time/utc/year") & 0xFF ) );
380 data.push_back( (char)( fgGetInt( "/sim/time/utc/hour") & 0xFF ) );
381 data.push_back( (char)( fgGetInt( "/sim/time/utc/minute") & 0xFF ) );
382 data.push_back( (char)( fgGetInt( "/sim/time/utc/second") & 0xFF ) );
383 data.append( "\x00\x00\x00\x00", 4 );
384
385 for ( int xctr = 0; xctr < 15; xctr++ ) {
386 data.append( "\x00\x00\x00\x00\x00\x00\x00\x00", 8 );
387 }
388 data.push_back( '\0' );
389
390 // calculate the checksum
391 for ( std::string::const_iterator cli = data.begin(); cli != data.end(); cli++ ) {
392 chksum ^= *cli;
393 }
394
395 std::string sentence( "@@" );
396 sentence += data;
397 sentence.push_back( chksum );
398 sentence += "\x0D\n";
399
400 // cout << sentence;
401 length = sentence.length();
402 char *bufptr = buf;
403 for ( std::string::const_iterator cli = sentence.begin(); cli != sentence.end(); cli++ ) {
404 *bufptr++ = *cli;
405 }
406
407 return true;
408}
409
410
411// Wt - Position and Navigation status
413 char chksum = 0;
414
415 // generate the Wt message
416 std::string data = "Wt";
417 data.push_back( (char)( fgGetInt( "/sim/time/utc/month") & 0xFF ) );
418 data.push_back( (char)( fgGetInt( "/sim/time/utc/day") & 0xFF ) );
419 data.push_back( (char)( (fgGetInt( "/sim/time/utc/year") >> 8 ) & 0xFF ) );
420 data.push_back( (char)( fgGetInt( "/sim/time/utc/year") & 0xFF ) );
421 data.push_back( (char)( fgGetInt( "/sim/time/utc/hour") & 0xFF ) );
422 data.push_back( (char)( fgGetInt( "/sim/time/utc/minute") & 0xFF ) );
423 data.push_back( (char)( fgGetInt( "/sim/time/utc/second") & 0xFF ) );
424 data.append( "\x00\x00\x00\x00", 4 );
425
426 // get latitude in milliarcseconds
427 double latd = fdm->get_Latitude() * SGD_RADIANS_TO_DEGREES;
428 latd *= DEG_TO_MILLIARCSECS;
429 int latitude = (int)latd;
430 data.push_back( (char)( ( latitude >> 24 ) & 0xFF ) );
431 data.push_back( (char)( ( latitude >> 16 ) & 0xFF ) );
432 data.push_back( (char)( ( latitude >> 8 ) & 0xFF ) );
433 data.push_back( (char)( latitude & 0xFF ) );
434
435 // get longitude in milliarcseconds
436 double lond = fdm->get_Longitude() * SGD_RADIANS_TO_DEGREES;
437 lond *= DEG_TO_MILLIARCSECS;
438 int longitude = (int)lond;
439 data.push_back( (char)( ( longitude >> 24 ) & 0xFF ) );
440 data.push_back( (char)( ( longitude >> 16 ) & 0xFF ) );
441 data.push_back( (char)( ( longitude >> 8 ) & 0xFF ) );
442 data.push_back( (char)( longitude & 0xFF ) );
443
444
445 // Altitude settings
446 double alt = fdm->get_Altitude();
447 if ( alt > 99999.0 ) { alt = 99999.0; }
448
449 // send the WGS-84 ellipsoid height om /-1, (just use regular altitude)
450 alt *= SG_FEET_TO_METER;
451 int altm = (int)( alt * 100.0f );
452 data.push_back( (char)( ( altm >> 24 ) & 0xFF ) );
453 data.push_back( (char)( ( altm >> 16 ) & 0xFF ) );
454 data.push_back( (char)( ( altm >> 8 ) & 0xFF ) );
455 data.push_back( (char)( altm & 0xFF ) );
456
457 // put in the geoid height in 0.1 meters
458 data.push_back( (char)( ( altm >> 24 ) & 0xFF ) );
459 data.push_back( (char)( ( altm >> 16 ) & 0xFF ) );
460 data.push_back( (char)( ( altm >> 8 ) & 0xFF ) );
461 data.push_back( (char)( altm & 0xFF ) );
462
463 // get ground speed
464 double gskt = fgGetDouble( "/velocities/groundspeed-kt" );
465 gskt *= SG_KT_TO_MPS;
466 int gsm = (int)( gskt * 100.0f );
467 data.push_back( (char)( ( gsm >> 8 ) & 0xFF ) );
468 data.push_back( (char)( gsm & 0xFF ) );
469
470 // ground track
471 double trkdeg = fgGetDouble("/orientation/heading-deg");
472 int hdg = (int)(trkdeg * 10.0f);
473 data.push_back( (char)( ( hdg >> 8 ) & 0xFF ) );
474 data.push_back( (char)( hdg & 0xFF ) );
475
476 // vertical velocity
477 double climb_fpm = fgGetDouble( "/velocities/vertical-speed-fps" );
478 climb_fpm *= SG_FEET_TO_METER;
479 int vvm = (int)( climb_fpm * 50.0f );
480 data.push_back( (char)( ( vvm >> 8 ) & 0xFF ) );
481 data.push_back( (char)( vvm & 0xFF ) );
482
483 // navigation solution status
484 data.push_back( '\0' );
485
486 // HFOM/VFOM
487 data.append( "\0\x09\0\x09", 4 );
488
489 // ARINC 748 Mode
490 data.push_back( '\x0D' );
491
492 // Channel Tracking
493 data += "\x7F\xFF";
494
495 // calculate the checksum
496 for ( std::string::const_iterator cli = data.begin(); cli != data.end(); cli++ ) {
497 chksum ^= *cli;
498 }
499
500 std::string sentence( "@@" );
501 sentence += data;
502 sentence.push_back( chksum );
503 sentence += "\x0D\n";
504
505 length = sentence.length();
506 char *bufptr = buf;
507 for ( std::string::const_iterator cli = sentence.begin(); cli != sentence.end(); cli++ ) {
508 *bufptr++ = *cli;
509 }
510
511 return true;
512}
513
514
515// Wm - Data integrity status
517 char chksum = 0;
518
519 // generate the Wt message
520 std::string data = "Wm";
521
522 // flight phase
523 data.push_back( flight_phase );
524
525 // HAL and VAL
526 if ( hal.empty() ) {
527 data.append( "\0\0", 2 );
528 }
529 else {
530 data += hal;
531 }
532
533 if ( val.empty() ) {
534 data.append( "\0\0", 2 );
535 }
536 else {
537 data += val;
538 }
539
540 // Integrity status
541 data.append( "\x00\x00\x00", 3 );
542 data.append( "\x00\x01\x00\x01\x00\x01\x00\x01", 8 );
543 data.append( "\x00\x0F\x00\x0F\x00\x0F", 6 );
544
545 // calculate the checksum
546 for ( std::string::const_iterator cli = data.begin(); cli != data.end(); cli++ ) {
547 chksum ^= *cli;
548 }
549
550 std::string sentence( "@@" );
551 sentence += data;
552 sentence.push_back( chksum );
553 sentence += "\x0D\n";
554
555 length = sentence.length();
556 char *bufptr = buf;
557 for ( std::string::const_iterator cli = sentence.begin(); cli != sentence.end(); cli++ ) {
558 *bufptr++ = *cli;
559 }
560
561 return true;
562}
563
564// Wv - 3d velocity
566 char chksum = 0;
567
568 // generate the Wt message
569 std::string data = "Wv";
570
571 // data is valid
572 data += "1";
573
574 // N velocity in .01 m/s
575 double vn_mps = fgGetDouble( "/velocities/speed-north-fps" ) * SG_FEET_TO_METER;
576 int vnm = (int)( vn_mps * 100 );
577 data.push_back( (char)( ( vnm >> 24 ) & 0xFF ) );
578 data.push_back( (char)( ( vnm >> 16 ) & 0xFF ) );
579 data.push_back( (char)( ( vnm >> 8 ) & 0xFF ) );
580 data.push_back( (char)( vnm & 0xFF ) );
581
582 // E velocity in .01 m/s
583 double ve_mps = fgGetDouble( "/velocities/speed-east-fps" ) * SG_FEET_TO_METER;
584 int vne = (int)( ve_mps * 100 );
585 data.push_back( (char)( ( vne >> 24 ) & 0xFF ) );
586 data.push_back( (char)( ( vne >> 16 ) & 0xFF ) );
587 data.push_back( (char)( ( vne >> 8 ) & 0xFF ) );
588 data.push_back( (char)( vne & 0xFF ) );
589
590 // Up velocity in .01 m/s
591 double climb_mps = fgGetDouble( "/velocities/vertical-speed-fps" ) * SG_FEET_TO_METER;
592 int vnup = (int)( climb_mps * 100 );
593 data.push_back( (char)( ( vnup >> 24 ) & 0xFF ) );
594 data.push_back( (char)( ( vnup >> 16 ) & 0xFF ) );
595 data.push_back( (char)( ( vnup >> 8 ) & 0xFF ) );
596 data.push_back( (char)( vnup & 0xFF ) );
597
598 // calculate the checksum
599 for ( std::string::const_iterator cli = data.begin(); cli != data.end(); cli++ ) {
600 chksum ^= *cli;
601 }
602
603 std::string sentence( "@@" );
604 sentence += data;
605 sentence.push_back( chksum );
606 sentence += "\x0D\n";
607
608 // cout << sentence;
609 length = sentence.length();
610 char *bufptr = buf;
611 for ( std::string::const_iterator cli = sentence.begin(); cli != sentence.end(); cli++ ) {
612 *bufptr++ = *cli;
613 }
614
615 return true;
616}
617
618
619bool FGAV400WSimB::verify_checksum( std::string message, int datachars ) {
620 bool bRet = false;
621 std::string dataseg = message.substr(SOM_SIZE, datachars);
622 char chksum = 0;
623 char cs = message[SOM_SIZE + datachars];
624 for ( std::string::const_iterator cli = dataseg.begin();
625 cli != dataseg.end(); cli++ ) {
626 chksum ^= *cli;
627 }
628
629 if ( chksum == cs ) {
630 bRet = true;
631 }
632 else {
633 SG_LOG( SG_IO, SG_INFO, "bad input checksum: " << message );
634 //std::string msgid = asciitize_message( message );
635 //printf( "FGAV400SimB::verify_checksum bad input checksum:\n%s\n", msgid.c_str() );
636 }
637
638 return( bRet );
639}
640
641
642std::string FGAV400WSimB::asciitize_message( std::string message ) {
643 std::string asciimsg;
644
645 for ( std::string::const_iterator cli = message.begin();
646 cli != message.end(); cli++ )
647 {
648 unsigned char uc = static_cast<unsigned char>(*cli);
649 if ( uc >= 32 && uc <= 127 ) {
650 asciimsg += *cli;
651 }
652 else {
653 char tempbuf[20];
654 snprintf( tempbuf, 20, "\\x%02X", uc );
655 asciimsg += tempbuf;
656 }
657 }
658
659 return( asciimsg );
660}
661
663 std::string message;
664 char *bufctr = buf;
665
666 for ( int xctr = 0; xctr < length; xctr++ ) {
667 message.push_back( *bufctr++ );
668 }
669 return( message );
670}
671
672
673// parse AV400Sim message
675 SG_LOG( SG_IO, SG_INFO, "parse AV400WSimB message" );
676
677 std::string msg = buffer_to_string();
678
679 std::string som = msg.substr(0, 2);
680 if ( som != "@@" ) {
681 SG_LOG( SG_IO, SG_INFO, "bad start message" );
682 return false;
683 }
684
685 std::string ident = msg.substr(2,2);
686
687 if ( ident == "AH" ) { // Flight Phase
688 if ( verify_checksum( msg, 3 ) ) {
689 flight_phase = msg[4];
690 //std::string ascmsg = asciitize_message( msg );
691 //printf( "%10d received AH %s\n", outputctr, ascmsg.c_str() );
692 switch( flight_phase ) {
693 case FGAV400WSimB::PHASE_OCEANIC: // Oceanic
694 if ( hal.empty() ) {
695 hal = "\x39\xE0";
696 }
697 if ( val.empty() ) {
698 val = "\x00\x00";
699 }
700 fgSetBool( "/instrumentation/av400w/has-gs", false );
701 break;
702
703 case PHASE_ENROUTE: // Enroute
704 if ( hal.empty() ) {
705 hal = "\x1C\xF0";
706 }
707 if ( val.empty() ) {
708 val = "\x00\x00";
709 }
710 fgSetBool( "/instrumentation/av400w/has-gs", false );
711 break;
712
713 case PHASE_TERM: // Terminal
714 if ( hal.empty() ) {
715 hal = "\x0E\x78";
716 }
717 if ( val.empty() ) {
718 val = "\x00\x00";
719 }
720 fgSetBool( "/instrumentation/av400w/has-gs", false );
721 break;
722
723 case PHASE_NONPREC: // Non Precision Approach
724 if ( hal.empty() ) {
725 hal = "\x04\x57";
726 }
727 if ( val.empty() ) {
728 val = "\x00\x00";
729 }
730 fgSetBool( "/instrumentation/av400w/has-gs", false );
731 break;
732
733 case PHASE_LNAVVNAV: // LNAV/VNAV
734 if ( hal.empty() ) {
735 hal = "\x04\x57";
736 }
737 if ( val.empty() ) {
738 val = "\x00\x64";
739 }
740 fgSetBool( "/instrumentation/av400w/has-gs", true );
741 break;
742
743 case PHASE_LPVLP: // LPV/LP
744 if ( hal.empty() ) {
745 hal = "\x00\x00";
746 }
747 if ( val.empty() ) {
748 val = "\x00\x00";
749 }
750 fgSetBool( "/instrumentation/av400w/has-gs", true );
751 break;
752
753 default:
754 if ( hal.empty() ) {
755 hal = "\x00\x00";
756 }
757 if ( val.empty() ) {
758 val = "\x00\x00";
759 }
760 fgSetBool( "/instrumentation/av400w/has-gs", false );
761 break;
762 }
763 //printf( "AH flight status: %c\n", flight_phase + '0' );
764 }
765 }
766 else if ( ident == "AI" ) { // HAL
767 if ( verify_checksum( msg, 4 ) ) {
768 hal = msg.substr(4,2);
769 //printf( "%10d received AI\n", outputctr );
770 }
771 }
772 else if ( ident == "Cj" ) { // Host ID
773 if ( verify_checksum( msg, 2 ) ) {
774 req_hostid = true;
775 //printf( "%10d received Cj\n", outputctr );
776 }
777 }
778 else if ( ident == "WA" ) { // SBAS selection
779 if ( verify_checksum( msg, 5 ) ) {
780 sbas_sel = msg.substr( 5, 2 );
781 req_sbas = true;
782 //printf( "%10d received WA\n", outputctr );
783 }
784 }
785 else if ( ident == "Wd" ) { // VAL
786 if ( verify_checksum( msg, 4 ) ) {
787 val = msg.substr( 4, 2 );
788 //printf( "%10d received Wd\n", outputctr );
789 }
790 }
791 else if ( ident == "WY" ) { // ???? Not listed in protocol document
792 // Do nothing until we know what it does
793 }
794 else {
795 std::string unkmsg = msg.substr( 0, 4 );
796 printf( "parse_message unknown: %s\n", unkmsg.c_str() );
797 }
798
799 return true;
800}
801
802
803// open hailing frequencies
805 if ( is_enabled() ) {
806 SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
807 << "is already in use, ignoring" );
808 return false;
809 }
810
811 SGIOChannel *io = get_io_channel();
812
813 if ( ! io->open( get_direction() ) ) {
814 SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
815 return false;
816 }
817
818 set_enabled( true );
819
820 return true;
821}
822
823
824// process work for this port
826 SGIOChannel *io = get_io_channel();
827
828 // read the device messages back
829 // Because the protocol allows for binary data, we can't just read
830 // ascii lines.
831 char readbuf[10];
832 char *bufptr = buf;
833 int templen;
834 bool gotCr = false;
835 bool gotLf = false;
836 bool som1 = false;
837 bool som2 = false;
838 length = 0;
839
840 while ( ( templen = io->read( readbuf, 1 ) ) == 1 ) {
841 if ( !som1 && !som2 ) {
842 if ( *readbuf == '@' ) {
843 som1 = true;
844 }
845 else {
846 continue;
847 }
848 }
849 else if ( !som2 ) {
850 if ( *readbuf == '@' ) {
851 som2 = true;
852 }
853 else {
854 som1 = false;
855 continue;
856 }
857 }
858 else if ( som1 && som2 ) {
859 if ( *readbuf == '\n' && !gotCr ) { // check for a carriage return
860 gotCr = true;
861 }
862 else if ( *readbuf == '\n' && gotCr ) { // see if we got a cr/lf
863 gotLf = true;
864 }
865 else if ( gotCr ) { // we had a cr but the next char was not a lf, so just must be data
866 gotCr = false;
867 }
868 }
869
870 *bufptr++ = *readbuf;
871 length++;
872
873 if ( gotCr && gotLf ) { // message done
874 if ( parse_message() ) {
875 // SG_LOG( SG_IO, SG_ALERT, "Success parsing data." );
876 } else {
877 // SG_LOG( SG_IO, SG_ALERT, "Error parsing data." );
878 }
879 length = 0;
880 break;
881 }
882 }
883
884
885 // Check for polled messages
886 if ( req_hostid ) {
888 if ( ! io->write( buf, length ) ) {
889 SG_LOG( SG_IO, SG_WARN, "Error writing data." );
890 printf( "Error sending HostID\n" );
891 return false;
892 }
893 //printf( "Sent HostID, %d bytes\n", length );
894 req_hostid = false;
895 }
896 else if ( req_sbas ) {
898 if ( ! io->write( buf, length ) ) {
899 SG_LOG( SG_IO, SG_WARN, "Error writing data." );
900 printf( "Error sending SBAS\n" );
901 return false;
902 }
903 //printf( "Sent SBAS, %d bytes\n", length );
904 req_sbas = false;
905 }
906
907 // Send the 5Hz messages
909 if ( ! io->write( buf, length ) ) {
910 SG_LOG( SG_IO, SG_WARN, "Error writing data." );
911 printf( "Error writing hz message\n" );
912 return false;
913 }
914 //printf( "Sent Wt, %d bytes\n", length );
915
917 if ( ! io->write( buf, length ) ) {
918 SG_LOG( SG_IO, SG_WARN, "Error writing data." );
919 printf( "Error writing hz message\n" );
920 return false;
921 }
922 //printf( "Sent Wm, %d bytes\n", length );
923
925 if ( ! io->write( buf, length ) ) {
926 SG_LOG( SG_IO, SG_WARN, "Error writing data." );
927 printf( "Error writing hz message\n" );
928 return false;
929 }
930 //printf( "Sent Wv, %d bytes\n", length );
931
932 hz2count++;
933 if ( hz2 > 0 && ( hz2count % hz2cycles == 0 ) ) {
934 // Send the 1Hz messages
936 if ( ! io->write( buf, length ) ) {
937 SG_LOG( SG_IO, SG_WARN, "Error writing data." );
938 printf( "Error writing hz2 message\n" );
939 return false;
940 }
941 //printf( "Sent Wh, %d bytes\n", length );
942
944 if ( ! io->write( buf, length ) ) {
945 SG_LOG( SG_IO, SG_WARN, "Error writing data." );
946 printf( "Error writing hz2 message\n" );
947 return false;
948 }
949 //printf( "Sent Wx, %d bytes\n", length );
950 }
951
952 // read the device messages back again to make sure we don't miss anything
953 bufptr = buf;
954 templen = 0;
955 gotCr = false;
956 gotLf = false;
957 som1 = false;
958 som2 = false;
959 length = 0;
960
961 while ( ( templen = io->read( readbuf, 1 ) ) == 1 ) {
962 if ( !som1 && !som2 ) {
963 if ( *readbuf == '@' ) {
964 som1 = true;
965 }
966 else {
967 continue;
968 }
969 }
970 else if ( !som2 ) {
971 if ( *readbuf == '@' ) {
972 som2 = true;
973 }
974 else {
975 som1 = false;
976 continue;
977 }
978 }
979 else if ( som1 && som2 ) {
980 if ( *readbuf == '\n' && !gotCr ) { // check for a carriage return
981 gotCr = true;
982 }
983 else if ( *readbuf == '\n' && gotCr ) { // see if we got a cr/lf
984 gotLf = true;
985 }
986 else if ( gotCr ) { // we had a cr but the next char was not a lf, so just must be data
987 gotCr = false;
988 }
989 }
990
991 *bufptr++ = *readbuf;
992 length++;
993
994 if ( gotCr && gotLf ) { // message done
995 //std::string msg = buffer_to_string();
996 //std::string ascmsg = asciitize_message( msg );
997 //printf( "Received message\n" );
998 //printf( "%s\n", ascmsg.c_str() );
999 //printf( "got message\n" );
1000 if ( parse_message() ) {
1001 // SG_LOG( SG_IO, SG_ALERT, "Success parsing data." );
1002 } else {
1003 // SG_LOG( SG_IO, SG_ALERT, "Error parsing data." );
1004 }
1005 length = 0;
1006 break;
1007 }
1008 }
1009
1010
1011 outputctr++;
1012 if ( outputctr % 10 == 0 ) {
1013 //printf( "AV400WSimB::process finished\n" );
1014 }
1015
1016 return true;
1017}
1018
1019
1020// close the channel
1022 SGIOChannel *io = get_io_channel();
1023
1024 set_enabled( false );
1025
1026 if ( ! io->close() ) {
1027 return false;
1028 }
1029
1030 return true;
1031}
1032
1033
double latitude
Definition ADA.cxx:53
double longitude
Definition ADA.cxx:54
bool gen_message()
Definition AV400WSim.cxx:49
bool parse_message()
bool gen_Wh_message()
bool gen_Wv_message()
bool verify_checksum(std::string message, int datachars)
std::string asciitize_message(std::string message)
bool gen_Wt_message()
bool parse_message()
bool gen_sbas_message()
bool gen_Wx_message()
std::string buffer_to_string()
bool gen_hostid_message()
bool gen_Wm_message()
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
Encapsulate the FDM properties in some getter/setter helpers.
int fgGetInt(const char *name, int defaultValue)
Get an int value for a property.
Definition fg_props.cxx:532
static int atoi(const string &str)
Definition options.cxx:113
bool fgSetDouble(const char *name, double defaultValue)
Set a double value for a property.
Definition proptest.cpp:31
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
Definition proptest.cpp:24
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