]> git.mxchange.org Git - flightgear.git/blob - src/Network/AV400.cxx
Create TimeManager subsystem, and collect the time related code out of main.cxx and...
[flightgear.git] / src / Network / AV400.cxx
1 // AV400.cxx -- Garmin 400 series protocal class
2 //
3 // Written by Curtis Olson, started August 2006.
4 //
5 // Copyright (C) 2006  Curtis L. Olson - http://www.flightgear.org/~curt
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include <cstring>
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
35 #include <Main/fg_props.hxx>
36 #include <Main/globals.hxx>
37
38 #include "AV400.hxx"
39
40 FGAV400::FGAV400() {
41 }
42
43 FGAV400::~FGAV400() {
44 }
45
46
47 #if 0
48 // calculate the garmin check sum
49 static char calc_nmea_cksum(char *sentence) {
50     unsigned char sum = 0;
51     int i, len;
52
53     // cout << sentence << endl;
54
55     len = strlen(sentence);
56     sum = sentence[0];
57     for ( i = 1; i < len; i++ ) {
58         // cout << sentence[i];
59         sum ^= sentence[i];
60     }
61     // cout << endl;
62
63     // printf("sum = %02x\n", sum);
64     return sum;
65 }
66 #endif
67
68
69 // generate AV400 message
70 bool FGAV400::gen_message() {
71     // cout << "generating garmin message" << endl;
72
73     char msg_z[32], msg_A[32], msg_B[32], msg_C[32], msg_D[32];
74     char msg_Q[32], msg_T[32], msg_type2[256];
75     // the following could be implemented, but currently are unused
76     // char msg_E[32], msg_G[32], msg_I[32], msg_K[32], msg_L[32], msg_S[32];
77     // char msg_l[32];
78
79     char dir;
80     int deg;
81     double min;
82
83     // create msg_z
84     sprintf( msg_z, "z%05.0f\r\n", fdm.get_Altitude() );
85
86     // create msg_A
87     sprintf( msg_A, "A");
88
89     double latd = fdm.get_Latitude() * SGD_RADIANS_TO_DEGREES;
90     if ( latd < 0.0 ) {
91         latd = -latd;
92         dir = 'S';
93     } else {
94         dir = 'N';
95     }
96     deg = (int)latd;
97     min = (latd - (double)deg) * 60.0 * 100.0;
98     sprintf( msg_A, "A%c %02d %04.0f\r\n", dir, deg, min);
99
100     // create msg_B
101     double lond = fdm.get_Longitude() * SGD_RADIANS_TO_DEGREES;
102     if ( lond < 0.0 ) {
103         lond = -lond;
104         dir = 'W';
105     } else {
106         dir = 'E';
107     }
108     deg = (int)lond;
109     min = (lond - (double)deg) * 60.0 * 100.0;
110     sprintf( msg_B, "B%c %03d %04.0f\r\n", dir, deg, min);
111
112     // create msg_C
113     float magdeg = fgGetDouble( "/environment/magnetic-variation-deg" );
114     double vn = fgGetDouble( "/velocities/speed-north-fps" );
115     double ve = fgGetDouble( "/velocities/speed-east-fps" );
116     double gnd_trk_true = atan2( ve, vn ) * SGD_RADIANS_TO_DEGREES;
117     double gnd_trk_mag = gnd_trk_true - magdeg;
118     if ( gnd_trk_mag < 0.0 ) { gnd_trk_mag += 360.0; }
119     if ( gnd_trk_mag >= 360.0 ) { gnd_trk_mag -= 360.0; }
120     sprintf( msg_C, "C%03.0f\r\n", gnd_trk_mag);
121
122     // create msg_D
123     double speed_kt = sqrt( vn*vn + ve*ve ) * SG_FPS_TO_KT;
124     if ( speed_kt > 999.0 ) {
125         speed_kt = 999.0;
126     }
127     sprintf( msg_D, "D%03.0f\r\n", speed_kt);
128
129     // create msg_E (not implemented)
130     // create msg_G (not implemented)
131     // create msg_I (not implemented)
132     // create msg_K (not implemented)
133     // create msg_L (not implemented)
134
135     // create msg_Q
136     if ( magdeg < 0.0 ) {
137         magdeg = -magdeg;
138         dir = 'W';
139     } else {
140         dir = 'E';
141     }
142     sprintf( msg_Q, "Q%c%03.0f\r\n", dir, magdeg * 10.0 );
143
144     // create msg_S (not implemented)
145
146     // create msg_T
147     sprintf( msg_T, "T---------\r\n" );
148
149     // create msg_l (not implemented)
150
151     // sentence type 2
152     sprintf( msg_type2, "w01%c\r\n", (char)65 );
153
154     // assemble message
155     string sentence;
156     sentence += '\002';         // STX
157     sentence += msg_z;          // altitude
158     sentence += msg_A;          // latitude
159     sentence += msg_B;          // longitude
160     sentence += msg_C;          // ground track
161     sentence += msg_D;          // ground speed (kt)
162     // sentence += "E-----\r\n";
163     // sentence += "G-----\r\n";
164     // sentence += "I----\r\n";
165     // sentence += "K-----\r\n";
166     // sentence += "L----\r\n";
167     sentence += msg_Q;          // magvar
168     // sentence += "S-----\r\n";
169     sentence += msg_T;          // end of type 1 messages (must be sent)
170     sentence += msg_type2;      // type2 message
171     // sentence += "l------\r\n";
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 AV400 message
184 bool FGAV400::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             fdm.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             fdm.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( fdm.get_Latitude(), 
301                           fdm.get_Altitude(), 
302                           &sl_radius, &lat_geoc );
303             fdm.set_Geocentric_Position( lat_geoc, 
304                            fdm.get_Longitude(), 
305                            sl_radius + fdm.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             fdm.set_V_calibrated_kts( speed );
318             // fdm.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             fdm.set_Euler_Angles( fdm.get_Phi(), 
331                                              fdm.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             fdm.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 FGAV400::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 FGAV400::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 FGAV400::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 }