]> git.mxchange.org Git - flightgear.git/blob - src/Network/garmin.cxx
Trivial cleanup commit, to test continuous integration server.
[flightgear.git] / src / Network / garmin.cxx
1 // garmin.cxx -- Garmin protocal class
2 //
3 // Written by Curtis Olson, started November 1999.
4 //
5 // Copyright (C) 1999  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 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include <iostream>
28
29 #include <simgear/debug/logstream.hxx>
30 #include <simgear/math/sg_geodesy.hxx>
31 #include <simgear/io/iochannel.hxx>
32 #include <simgear/timing/sg_time.hxx>
33
34 #include <FDM/flightProperties.hxx>
35 #include <Main/fg_props.hxx>
36 #include <Main/globals.hxx>
37
38 #include "garmin.hxx"
39
40 using std::string;
41
42 FGGarmin::FGGarmin() {
43   fdm = new FlightProperties;
44 }
45
46 FGGarmin::~FGGarmin() {
47   delete fdm;
48 }
49
50
51 // calculate the garmin check sum
52 static char calc_nmea_cksum(char *sentence) {
53     unsigned char sum = 0;
54     int i, len;
55
56     // cout << sentence << endl;
57
58     len = strlen(sentence);
59     sum = sentence[0];
60     for ( i = 1; i < len; i++ ) {
61         // cout << sentence[i];
62         sum ^= sentence[i];
63     }
64     // cout << endl;
65
66     // printf("sum = %02x\n", sum);
67     return sum;
68 }
69
70
71 // generate Garmin message
72 bool FGGarmin::gen_message() {
73     // cout << "generating garmin message" << endl;
74
75     char rmc[256], rmc_sum[256], rmz[256], rmz_sum[256], gsa[256];
76     char dir;
77     int deg;
78     double min;
79
80     SGTime *t = globals->get_time_params();
81
82     char utc[10];
83     sprintf( utc, "%02d%02d%02d", 
84              t->getGmt()->tm_hour, t->getGmt()->tm_min, t->getGmt()->tm_sec );
85
86     char rmc_lat[20];
87     double latd = fdm->get_Latitude() * SGD_RADIANS_TO_DEGREES;
88     if ( latd < 0.0 ) {
89         latd = -latd;
90         dir = 'S';
91     } else {
92         dir = 'N';
93     }
94     deg = (int)(latd);
95     min = (latd - (double)deg) * 60.0;
96     sprintf( rmc_lat, "%02d%07.4f,%c", abs(deg), min, dir);
97
98     char rmc_lon[20];
99     double lond = fdm->get_Longitude() * SGD_RADIANS_TO_DEGREES;
100     if ( lond < 0.0 ) {
101         lond = -lond;
102         dir = 'W';
103     } else {
104         dir = 'E';
105     }
106     deg = (int)(lond);
107     min = (lond - (double)deg) * 60.0;
108     sprintf( rmc_lon, "%03d%07.4f,%c", abs(deg), min, dir);
109
110     char speed[10];
111     sprintf( speed, "%05.1f", fdm->get_V_equiv_kts() );
112
113     char heading[10];
114     sprintf( heading, "%05.1f", fdm->get_Psi() * SGD_RADIANS_TO_DEGREES );
115
116     char altitude_m[10];
117     sprintf( altitude_m, "%02d", 
118              (int)(fdm->get_Altitude() * SG_FEET_TO_METER) );
119
120     char date[10];
121     int year = t->getGmt()->tm_year;
122     while ( year >= 100 ) { year -= 100; }
123     sprintf( date, "%02d%02d%02d", t->getGmt()->tm_mday, 
124              t->getGmt()->tm_mon+1, year );
125
126     char magvar[10];
127     float magdeg = fgGetDouble( "/environment/magnetic-variation-deg" );
128     if ( magdeg < 0.0 ) {
129         magdeg = -magdeg;
130         dir = 'W';
131     } else {
132         dir = 'E';
133     }
134     sprintf( magvar, "%05.1f,%c", magdeg, dir );
135
136     // $GPRMC,HHMMSS,A,DDMM.MMMM,N,DDDMM.MMMM,W,XXX.X,XXX.X,DDMMYY,XXX.X,E*XX
137     sprintf( rmc, "GPRMC,%s,A,%s,%s,%s,%s,%s,%s",
138              utc, rmc_lat, rmc_lon, speed, heading, date, magvar );
139     sprintf( rmc_sum, "%02X", calc_nmea_cksum(rmc) );
140
141     // sprintf( gga, "$GPGGA,%s,%s,%s,1,04,0.0,%s,M,00.0,M,,*00\r\n",
142     //          utc, lat, lon, altitude_m );
143
144     sprintf( rmz, "PGRMZ,%s,M,3", altitude_m );
145     sprintf( rmz_sum, "%02X", calc_nmea_cksum(rmz) );
146
147     sprintf( gsa, "%s",
148              "$GPGSA,A,3,01,02,03,,05,,07,,09,,11,12,0.9,0.9,2.0*38" );
149
150     SG_LOG( SG_IO, SG_DEBUG, rmc );
151     SG_LOG( SG_IO, SG_DEBUG, rmz );
152     SG_LOG( SG_IO, SG_DEBUG, gsa );
153
154     string garmin_sentence;
155
156     // RMC sentence
157     garmin_sentence = "$";
158     garmin_sentence += rmc;
159     garmin_sentence += "*";
160     garmin_sentence += rmc_sum;
161     garmin_sentence += "\r\n";
162
163     // RMZ sentence (garmin proprietary)
164     garmin_sentence += "$";
165     garmin_sentence += rmz;
166     garmin_sentence += "*";
167     garmin_sentence += rmz_sum;
168     garmin_sentence += "\r\n";
169
170     // GSA sentence (totally faked)
171     garmin_sentence += gsa;
172     garmin_sentence += "\r\n";
173
174     std::cout << garmin_sentence;
175
176     length = garmin_sentence.length();
177     strncpy( buf, garmin_sentence.c_str(), length );
178
179     return true;
180 }
181
182
183 // parse Garmin message
184 bool FGGarmin::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 FGGarmin::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 FGGarmin::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 FGGarmin::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 }