]> git.mxchange.org Git - flightgear.git/blob - src/Network/AV400Sim.cxx
Merge branch 'topic/gcintersect' into next
[flightgear.git] / src / Network / AV400Sim.cxx
1 // AV400Sim.cxx -- Garmin 400 series protocal class.  This AV400Sim
2 // protocol generates the set of "simulator" commands a garmin 400
3 // series gps would expect as input in simulator mode.  The AV400
4 // protocol generates the set of commands that a garmin 400 series gps
5 // would emit.
6 //
7 // Written by Curtis Olson, started Janauary 2009.
8 //
9 // Copyright (C) 2009  Curtis L. Olson - http://www.flightgear.org/~curt
10 //
11 // This program is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU General Public License as
13 // published by the Free Software Foundation; either version 2 of the
14 // License, or (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful, but
17 // WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 // General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
24 //
25 // $Id$
26
27
28 #ifdef HAVE_CONFIG_H
29 #  include "config.h"
30 #endif
31
32 #include <simgear/debug/logstream.hxx>
33 #include <simgear/math/sg_geodesy.hxx>
34 #include <simgear/io/iochannel.hxx>
35 #include <simgear/timing/sg_time.hxx>
36
37 #include <FDM/flight.hxx>
38 #include <Main/fg_props.hxx>
39 #include <Main/globals.hxx>
40
41 #include "AV400Sim.hxx"
42
43 FGAV400Sim::FGAV400Sim() {
44 }
45
46 FGAV400Sim::~FGAV400Sim() {
47 }
48
49
50 // generate AV400Sim message
51 bool FGAV400Sim::gen_message() {
52     // cout << "generating garmin message" << endl;
53
54     char msg_a[32], msg_b[32], msg_c[32], msg_d[32], msg_e[32];
55     char msg_f[32], msg_i[32], msg_j[32], msg_k[32], msg_l[32], msg_r[32];
56     char msg_type2[256];
57
58     char dir;
59     int deg;
60     double min;
61
62     // create msg_a
63     double latd = cur_fdm_state->get_Latitude() * SGD_RADIANS_TO_DEGREES;
64     if ( latd < 0.0 ) {
65         latd = -latd;
66         dir = 'S';
67     } else {
68         dir = 'N';
69     }
70     deg = (int)latd;
71     min = (latd - (double)deg) * 60.0 * 100.0;
72     sprintf( msg_a, "a%c %03d %04.0f\r\n", dir, deg, min);
73
74     // create msg_b
75     double lond = cur_fdm_state->get_Longitude() * SGD_RADIANS_TO_DEGREES;
76     if ( lond < 0.0 ) {
77         lond = -lond;
78         dir = 'W';
79     } else {
80         dir = 'E';
81     }
82     deg = (int)lond;
83     min = (lond - (double)deg) * 60.0 * 100.0;
84     sprintf( msg_b, "b%c %03d %04.0f\r\n", dir, deg, min);
85
86     // create msg_c
87     double alt = cur_fdm_state->get_Altitude();
88     if ( alt > 99999.0 ) { alt = 99999.0; }
89     sprintf( msg_c, "c%05.0f\r\n", alt );
90
91     // create msg_d
92     double ve_kts = fgGetDouble( "/velocities/speed-east-fps" ) * SG_FPS_TO_KT;
93     if ( ve_kts < 0.0 ) {
94         ve_kts = -ve_kts;
95         dir = 'W';
96     } else {
97         dir = 'E';
98     }
99     if ( ve_kts > 999.0 ) { ve_kts = 999.0; }
100     sprintf( msg_d, "d%c%03.0f\r\n", dir, ve_kts );
101
102     // create msg_e
103     double vn_kts = fgGetDouble( "/velocities/speed-north-fps" ) * SG_FPS_TO_KT;
104     if ( vn_kts < 0.0 ) {
105         vn_kts = -vn_kts;
106         dir = 'S';
107     } else {
108         dir = 'N';
109     }
110     if ( vn_kts > 999.0 ) { vn_kts = 999.0; }
111     sprintf( msg_e, "e%c%03.0f\r\n", dir, vn_kts );
112
113     // create msg_f
114     double climb_fpm = fgGetDouble( "/velocities/vertical-speed-fps" ) * 60;
115     if ( climb_fpm < 0.0 ) {
116         climb_fpm = -climb_fpm;
117         dir = 'D';
118     } else {
119         dir = 'U';
120     }
121     if ( climb_fpm > 9999.0 ) { climb_fpm = 9999.0; }
122     sprintf( msg_f, "f%c%04.0f\r\n", dir, climb_fpm );
123
124     // create msg_i
125     double fuel = fgGetDouble( "/consumables/fuel/total-fuel-gals" );
126     if ( fuel > 999.9 ) { fuel = 999.9; }
127     sprintf( msg_i, "i%04.0f\r\n", fuel*10.0 );
128
129     // create msg_j
130     double gph = fgGetDouble( "/engines/engine[0]/fuel-flow-gph" );
131     gph += fgGetDouble( "/engines/engine[1]/fuel-flow-gph" );
132     gph += fgGetDouble( "/engines/engine[2]/fuel-flow-gph" );
133     gph += fgGetDouble( "/engines/engine[3]/fuel-flow-gph" );
134     if ( gph > 999.9 ) { gph = 999.9; }
135     sprintf( msg_j, "j%04.0f\r\n", gph*10.0 );
136
137     // create msg_k
138     sprintf( msg_k, "k%04d%02d%02d%02d%02d%02d\r\n",
139              fgGetInt( "/sim/time/utc/year"),
140              fgGetInt( "/sim/time/utc/month"),
141              fgGetInt( "/sim/time/utc/day"),
142              fgGetInt( "/sim/time/utc/hour"),
143              fgGetInt( "/sim/time/utc/minute"),
144              fgGetInt( "/sim/time/utc/second") );
145
146     // create msg_l
147     alt = fgGetDouble( "/instrumentation/pressure-alt-ft" );
148     if ( alt > 99999.0 ) { alt = 99999.0; }
149     sprintf( msg_l, "l%05.0f\r\n", alt );
150
151     // create msg_r
152     sprintf( msg_r, "rA\r\n" );
153
154     // sentence type 2
155     sprintf( msg_type2, "w01%c\r\n", (char)65 );
156
157     // assemble message
158     string sentence;
159     sentence += '\002';         // STX
160     sentence += msg_a;          // latitude
161     sentence += msg_b;          // longitude
162     sentence += msg_c;          // gps altitude
163     sentence += msg_d;          // ve kts
164     sentence += msg_e;          // vn kts
165     sentence += msg_f;          // climb fpm
166     sentence += msg_i;          // total fuel in gal (*10)
167     sentence += msg_j;          // fuel flow gph (*10)
168     sentence += msg_k;          // date/time (UTC)
169     sentence += msg_l;          // pressure altitude
170     sentence += msg_r;          // RAIM available
171     sentence += msg_type2;      // type2 message
172     sentence += '\003';         // ETX
173
174     // cout << sentence;
175     length = sentence.length();
176     // cout << endl << "length = " << length << endl;
177     strncpy( buf, sentence.c_str(), length );
178
179     return true;
180 }
181
182
183 // parse AV400Sim message
184 bool FGAV400Sim::parse_message() {
185     SG_LOG( SG_IO, SG_INFO, "parse garmin message" );
186
187     string msg = buf;
188     msg = msg.substr( 0, length );
189     SG_LOG( SG_IO, SG_INFO, "entire message = " << msg );
190
191     string::size_type begin_line, end_line, begin, end;
192     begin_line = begin = 0;
193
194     // extract out each line
195     end_line = msg.find("\n", begin_line);
196     while ( end_line != string::npos ) {
197         string line = msg.substr(begin_line, end_line - begin_line);
198         begin_line = end_line + 1;
199         SG_LOG( SG_IO, SG_INFO, "  input line = " << line );
200
201         // leading character
202         string start = msg.substr(begin, 1);
203         ++begin;
204         SG_LOG( SG_IO, SG_INFO, "  start = " << start );
205
206         // sentence
207         end = msg.find(",", begin);
208         if ( end == string::npos ) {
209             return false;
210         }
211     
212         string sentence = msg.substr(begin, end - begin);
213         begin = end + 1;
214         SG_LOG( SG_IO, SG_INFO, "  sentence = " << sentence );
215
216         double lon_deg, lon_min, lat_deg, lat_min;
217         double lon, lat, speed, heading, altitude;
218
219         if ( sentence == "GPRMC" ) {
220             // time
221             end = msg.find(",", begin);
222             if ( end == string::npos ) {
223                 return false;
224             }
225     
226             string utc = msg.substr(begin, end - begin);
227             begin = end + 1;
228             SG_LOG( SG_IO, SG_INFO, "  utc = " << utc );
229
230             // junk
231             end = msg.find(",", begin);
232             if ( end == string::npos ) {
233                 return false;
234             }
235     
236             string junk = msg.substr(begin, end - begin);
237             begin = end + 1;
238             SG_LOG( SG_IO, SG_INFO, "  junk = " << junk );
239
240             // lat val
241             end = msg.find(",", begin);
242             if ( end == string::npos ) {
243                 return false;
244             }
245     
246             string lat_str = msg.substr(begin, end - begin);
247             begin = end + 1;
248
249             lat_deg = atof( lat_str.substr(0, 2).c_str() );
250             lat_min = atof( lat_str.substr(2).c_str() );
251
252             // lat dir
253             end = msg.find(",", begin);
254             if ( end == string::npos ) {
255                 return false;
256             }
257     
258             string lat_dir = msg.substr(begin, end - begin);
259             begin = end + 1;
260
261             lat = lat_deg + ( lat_min / 60.0 );
262             if ( lat_dir == "S" ) {
263                 lat *= -1;
264             }
265
266             cur_fdm_state->set_Latitude( lat * SGD_DEGREES_TO_RADIANS );
267             SG_LOG( SG_IO, SG_INFO, "  lat = " << lat );
268
269             // lon val
270             end = msg.find(",", begin);
271             if ( end == string::npos ) {
272                 return false;
273             }
274     
275             string lon_str = msg.substr(begin, end - begin);
276             begin = end + 1;
277
278             lon_deg = atof( lon_str.substr(0, 3).c_str() );
279             lon_min = atof( lon_str.substr(3).c_str() );
280
281             // lon dir
282             end = msg.find(",", begin);
283             if ( end == string::npos ) {
284                 return false;
285             }
286     
287             string lon_dir = msg.substr(begin, end - begin);
288             begin = end + 1;
289
290             lon = lon_deg + ( lon_min / 60.0 );
291             if ( lon_dir == "W" ) {
292                 lon *= -1;
293             }
294
295             cur_fdm_state->set_Longitude( lon * SGD_DEGREES_TO_RADIANS );
296             SG_LOG( SG_IO, SG_INFO, "  lon = " << lon );
297
298 #if 0
299             double sl_radius, lat_geoc;
300             sgGeodToGeoc( cur_fdm_state->get_Latitude(), 
301                           cur_fdm_state->get_Altitude(), 
302                           &sl_radius, &lat_geoc );
303             cur_fdm_state->set_Geocentric_Position( lat_geoc, 
304                            cur_fdm_state->get_Longitude(), 
305                            sl_radius + cur_fdm_state->get_Altitude() );
306 #endif
307
308             // speed
309             end = msg.find(",", begin);
310             if ( end == string::npos ) {
311                 return false;
312             }
313     
314             string speed_str = msg.substr(begin, end - begin);
315             begin = end + 1;
316             speed = atof( speed_str.c_str() );
317             cur_fdm_state->set_V_calibrated_kts( speed );
318             // cur_fdm_state->set_V_ground_speed( speed );
319             SG_LOG( SG_IO, SG_INFO, "  speed = " << speed );
320
321             // heading
322             end = msg.find(",", begin);
323             if ( end == string::npos ) {
324                 return false;
325             }
326     
327             string hdg_str = msg.substr(begin, end - begin);
328             begin = end + 1;
329             heading = atof( hdg_str.c_str() );
330             cur_fdm_state->set_Euler_Angles( cur_fdm_state->get_Phi(), 
331                                              cur_fdm_state->get_Theta(), 
332                                              heading * SGD_DEGREES_TO_RADIANS );
333             SG_LOG( SG_IO, SG_INFO, "  heading = " << heading );
334         } else if ( sentence == "PGRMZ" ) {
335             // altitude
336             end = msg.find(",", begin);
337             if ( end == string::npos ) {
338                 return false;
339             }
340     
341             string alt_str = msg.substr(begin, end - begin);
342             altitude = atof( alt_str.c_str() );
343             begin = end + 1;
344
345             // altitude units
346             end = msg.find(",", begin);
347             if ( end == string::npos ) {
348                 return false;
349             }
350     
351             string alt_units = msg.substr(begin, end - begin);
352             begin = end + 1;
353
354             if ( alt_units != "F" && alt_units != "f" ) {
355                 altitude *= SG_METER_TO_FEET;
356             }
357
358             cur_fdm_state->set_Altitude( altitude );
359     
360             SG_LOG( SG_IO, SG_INFO, " altitude  = " << altitude );
361
362         }
363
364         // printf("%.8f %.8f\n", lon, lat);
365
366         begin = begin_line;
367         end_line = msg.find("\n", begin_line);
368     }
369
370     return true;
371 }
372
373
374 // open hailing frequencies
375 bool FGAV400Sim::open() {
376     if ( is_enabled() ) {
377         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
378                 << "is already in use, ignoring" );
379         return false;
380     }
381
382     SGIOChannel *io = get_io_channel();
383
384     if ( ! io->open( get_direction() ) ) {
385         SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
386         return false;
387     }
388
389     set_enabled( true );
390
391     return true;
392 }
393
394
395 // process work for this port
396 bool FGAV400Sim::process() {
397     SGIOChannel *io = get_io_channel();
398
399     if ( get_direction() == SG_IO_OUT ) {
400         gen_message();
401         if ( ! io->write( buf, length ) ) {
402             SG_LOG( SG_IO, SG_WARN, "Error writing data." );
403             return false;
404         }
405     } else if ( get_direction() == SG_IO_IN ) {
406         if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
407             SG_LOG( SG_IO, SG_ALERT, "Success reading data." );
408             if ( parse_message() ) {
409                 SG_LOG( SG_IO, SG_ALERT, "Success parsing data." );
410             } else {
411                 SG_LOG( SG_IO, SG_ALERT, "Error parsing data." );
412             }
413         } else {
414             SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
415             return false;
416         }
417         if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
418             SG_LOG( SG_IO, SG_ALERT, "Success reading data." );
419             if ( parse_message() ) {
420                 SG_LOG( SG_IO, SG_ALERT, "Success parsing data." );
421             } else {
422                 SG_LOG( SG_IO, SG_ALERT, "Error parsing data." );
423             }
424         } else {
425             SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
426             return false;
427         }
428     }
429
430     return true;
431 }
432
433
434 // close the channel
435 bool FGAV400Sim::close() {
436     SGIOChannel *io = get_io_channel();
437
438     set_enabled( false );
439
440     if ( ! io->close() ) {
441         return false;
442     }
443
444     return true;
445 }