FlightGear next
AV400Sim.cxx
Go to the documentation of this file.
1/*
2 * SPDX-FileName: AV400Sim.cxx
3 * SPDX-FileComment: <text>Garmin 400 series protocal class. This AV400Sim
4 * protocol generates the set of "simulator" commands a garmin 400
5 * series gps would expect as input in simulator mode. The AV400
6 * protocol generates the set of commands that a garmin 400 series gps
7 * would emit.</text>
8 * SPDX-FileCopyrightText: Copyright (C) 2009 Curtis L. Olson - http://www.flightgear.org/~curt
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12#ifdef HAVE_CONFIG_H
13# include "config.h"
14#endif
15
16#include <cstdlib>
17#include <cstring>
18#include <cstdio>
19
20#include <simgear/debug/logstream.hxx>
21#include <simgear/math/sg_geodesy.hxx>
22#include <simgear/io/iochannel.hxx>
23#include <simgear/timing/sg_time.hxx>
24
26#include <Main/fg_props.hxx>
27#include <Main/globals.hxx>
28
29#include "AV400Sim.hxx"
30
34
36 delete fdm;
37}
38
39
40// generate AV400Sim message
42 // cout << "generating garmin message" << endl;
43
44 char msg_a[32], msg_b[32], msg_c[32], msg_d[32], msg_e[32];
45 char msg_f[32], msg_h[32], msg_i[32], msg_j[32], msg_k[32], msg_l[32], msg_r[32];
46 char msg_type2[256];
47
48 char dir;
49 int deg;
50 double min;
51
52 // create msg_a
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 %03d %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 double alt = fdm->get_Altitude();
78 if ( alt > 99999.0 ) { alt = 99999.0; }
79 snprintf( msg_c, 32, "c%05.0f\r\n", alt );
80
81 // create msg_d
82 double ve_kts = fgGetDouble( "/velocities/speed-east-fps" ) * SG_FPS_TO_KT;
83 if ( ve_kts < 0.0 ) {
84 ve_kts = -ve_kts;
85 dir = 'W';
86 } else {
87 dir = 'E';
88 }
89 if ( ve_kts > 999.0 ) { ve_kts = 999.0; }
90 snprintf( msg_d, 32, "d%c%03.0f\r\n", dir, ve_kts );
91
92 // create msg_e
93 double vn_kts = fgGetDouble( "/velocities/speed-north-fps" ) * SG_FPS_TO_KT;
94 if ( vn_kts < 0.0 ) {
95 vn_kts = -vn_kts;
96 dir = 'S';
97 } else {
98 dir = 'N';
99 }
100 if ( vn_kts > 999.0 ) { vn_kts = 999.0; }
101 snprintf( msg_e, 32, "e%c%03.0f\r\n", dir, vn_kts );
102
103 // create msg_f
104 double climb_fpm = fgGetDouble( "/velocities/vertical-speed-fps" ) * 60;
105 if ( climb_fpm < 0.0 ) {
106 climb_fpm = -climb_fpm;
107 dir = 'D';
108 } else {
109 dir = 'U';
110 }
111 if ( climb_fpm > 9999.0 ) { climb_fpm = 9999.0; }
112 snprintf( msg_f, 32, "f%c%04.0f\r\n", dir, climb_fpm );
113
114 // create msg_h
115 double obs = fgGetDouble( "/instrumentation/nav[0]/radials/selected-deg" );
116 snprintf( msg_h, sizeof(msg_h), "h%04d\r\n", (int)(obs*10) );
117
118 // create msg_i
119 double fuel = fgGetDouble( "/consumables/fuel/total-fuel-gals" );
120 if ( fuel > 999.9 ) { fuel = 999.9; }
121 snprintf( msg_i, sizeof(msg_i), "i%04.0f\r\n", fuel*10.0 );
122
123 // create msg_j
124 double gph = fgGetDouble( "/engines/engine[0]/fuel-flow-gph" );
125 gph += fgGetDouble( "/engines/engine[1]/fuel-flow-gph" );
126 gph += fgGetDouble( "/engines/engine[2]/fuel-flow-gph" );
127 gph += fgGetDouble( "/engines/engine[3]/fuel-flow-gph" );
128 if ( gph > 999.9 ) { gph = 999.9; }
129 snprintf( msg_j, 32, "j%04.0f\r\n", gph*10.0 );
130
131 // create msg_k
132 snprintf( msg_k, 32, "k%04d%02d%02d%02d%02d%02d\r\n",
133 fgGetInt( "/sim/time/utc/year"),
134 fgGetInt( "/sim/time/utc/month"),
135 fgGetInt( "/sim/time/utc/day"),
136 fgGetInt( "/sim/time/utc/hour"),
137 fgGetInt( "/sim/time/utc/minute"),
138 fgGetInt( "/sim/time/utc/second") );
139
140 // create msg_l
141 alt = fgGetDouble( "/instrumentation/pressure-alt-ft" );
142 if ( alt > 99999.0 ) { alt = 99999.0; }
143 snprintf( msg_l, 32, "l%05.0f\r\n", alt );
144
145 // create msg_r
146 snprintf( msg_r, 32, "rA\r\n" );
147
148 // sentence type 2
149 snprintf( msg_type2, 256, "w01%c\r\n", (char)65 );
150
151 // assemble message
152 std::string sentence;
153 sentence += '\002'; // STX
154 sentence += msg_a; // latitude
155 sentence += msg_b; // longitude
156 sentence += msg_c; // gps altitude
157 sentence += msg_d; // ve kts
158 sentence += msg_e; // vn kts
159 sentence += msg_f; // climb fpm
160 sentence += msg_h; // obs heading in deg (*10)
161 sentence += msg_i; // total fuel in gal (*10)
162 sentence += msg_j; // fuel flow gph (*10)
163 sentence += msg_k; // date/time (UTC)
164 sentence += msg_l; // pressure altitude
165 sentence += msg_r; // RAIM available
166 sentence += msg_type2; // type2 message
167 sentence += '\003'; // ETX
168
169 // cout << sentence;
170 length = sentence.length();
171 // cout << endl << "length = " << length << endl;
172 strncpy( buf, sentence.c_str(), length );
173
174 return true;
175}
176
177
178// parse AV400Sim message
180 SG_LOG( SG_IO, SG_INFO, "parse AV400Sim message" );
181
182 std::string msg = buf;
183 msg = msg.substr( 0, length );
184 SG_LOG( SG_IO, SG_INFO, "entire message = " << msg );
185
186 std::string ident = msg.substr(0, 1);
187 if ( ident == "i" ) {
188 std::string side = msg.substr(1,1);
189 std::string num = msg.substr(2,3);
190 if ( side == "-" ) {
191 fgSetDouble("/instrumentation/gps/cdi-deflection", 0.0);
192 } else {
193 int pos = atoi(num.c_str());
194 if ( side == "L" ) {
195 pos *= -1;
196 }
197 fgSetDouble("/instrumentation/gps/cdi-deflection",
198 (double)pos / 8.0);
199 fgSetBool("/instrumentation/gps/has-gs", false);
200 }
201 } else if ( ident == "k" ) {
202 std::string ind = msg.substr(1,1);
203 if ( ind == "T" ) {
204 fgSetBool("/instrumentation/gps/to-flag", true);
205 fgSetBool("/instrumentation/gps/from-flag", false);
206 } else if ( ind == "F" ) {
207 fgSetBool("/instrumentation/gps/to-flag", false);
208 fgSetBool("/instrumentation/gps/from-flag", true);
209 } else {
210 fgSetBool("/instrumentation/gps/to-flag", false);
211 fgSetBool("/instrumentation/gps/from-flag", false);
212 }
213 } else {
214 // SG_LOG( SG_IO, SG_ALERT, "unknown AV400Sim message = " << msg );
215 }
216
217 return true;
218}
219
220
221// open hailing frequencies
223 if ( is_enabled() ) {
224 SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
225 << "is already in use, ignoring" );
226 return false;
227 }
228
229 SGIOChannel *io = get_io_channel();
230
231 if ( ! io->open( get_direction() ) ) {
232 SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
233 return false;
234 }
235
236 set_enabled( true );
237
238 return true;
239}
240
241
242// process work for this port
244 SGIOChannel *io = get_io_channel();
245
246 // until we have parsers/generators for the reverse direction,
247 // this is hardwired to expect that the physical GPS is slaving
248 // from FlightGear.
249
250 // Send FlightGear data to the external device
251 gen_message();
252 if ( ! io->write( buf, length ) ) {
253 SG_LOG( SG_IO, SG_WARN, "Error writing data." );
254 return false;
255 }
256
257 // read the device messages back
258 while ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
259 // SG_LOG( SG_IO, SG_ALERT, "Success reading data." );
260 if ( parse_message() ) {
261 // SG_LOG( SG_IO, SG_ALERT, "Success parsing data." );
262 } else {
263 // SG_LOG( SG_IO, SG_ALERT, "Error parsing data." );
264 }
265 }
266
267 return true;
268}
269
270
271// close the channel
273 SGIOChannel *io = get_io_channel();
274
275 set_enabled( false );
276
277 if ( ! io->close() ) {
278 return false;
279 }
280
281 return true;
282}
#define min(X, Y)
bool close()
Definition AV400Sim.cxx:272
bool parse_message()
Definition AV400Sim.cxx:179
bool process()
Definition AV400Sim.cxx:243
bool open()
Definition AV400Sim.cxx:222
bool gen_message()
Definition AV400Sim.cxx:41
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