]> git.mxchange.org Git - flightgear.git/blob - src/Network/garmin.cxx
ca00dd414f3d6accdac829e02cb07ec9fc76ad33
[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 - curt@flightgear.org
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., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #include <simgear/debug/logstream.hxx>
25 #include <simgear/math/sg_geodesy.hxx>
26 #include <simgear/io/iochannel.hxx>
27
28 #include <FDM/flight.hxx>
29 #include <Main/globals.hxx>
30
31 #include "garmin.hxx"
32
33
34 FGGarmin::FGGarmin() {
35 }
36
37 FGGarmin::~FGGarmin() {
38 }
39
40
41 // calculate the garmin check sum
42 static char calc_nmea_cksum(char *sentence) {
43     unsigned char sum = 0;
44     int i, len;
45
46     // cout << sentence << endl;
47
48     len = strlen(sentence);
49     sum = sentence[0];
50     for ( i = 1; i < len; i++ ) {
51         // cout << sentence[i];
52         sum ^= sentence[i];
53     }
54     // cout << endl;
55
56     // printf("sum = %02x\n", sum);
57     return sum;
58 }
59
60
61 // generate Garmin message
62 bool FGGarmin::gen_message() {
63     // cout << "generating garmin message" << endl;
64
65     char rmc[256], rmc_sum[256], rmz[256], rmz_sum[256];
66     char dir;
67     int deg;
68     double min;
69
70     SGTime *t = globals->get_time_params();
71
72     char utc[10];
73     sprintf( utc, "%02d%02d%02d", 
74              t->getGmt()->tm_hour, t->getGmt()->tm_min, t->getGmt()->tm_sec );
75
76     char lat[20];
77     double latd = cur_fdm_state->get_Latitude() * RAD_TO_DEG;
78     if ( latd < 0.0 ) {
79         latd *= -1.0;
80         dir = 'S';
81     } else {
82         dir = 'N';
83     }
84     deg = (int)(latd);
85     min = (latd - (double)deg) * 60.0;
86     sprintf( lat, "%02d%06.3f,%c", abs(deg), min, dir);
87
88     char lon[20];
89     double lond = cur_fdm_state->get_Longitude() * RAD_TO_DEG;
90     if ( lond < 0.0 ) {
91         lond *= -1.0;
92         dir = 'W';
93     } else {
94         dir = 'E';
95     }
96     deg = (int)(lond);
97     min = (lond - (double)deg) * 60.0;
98     sprintf( lon, "%03d%06.3f,%c", abs(deg), min, dir);
99
100     char speed[10];
101     sprintf( speed, "%05.1f", cur_fdm_state->get_V_equiv_kts() );
102
103     char heading[10];
104     sprintf( heading, "%05.1f", cur_fdm_state->get_Psi() * RAD_TO_DEG );
105
106     char altitude_m[10];
107     sprintf( altitude_m, "%02d", 
108              (int)(cur_fdm_state->get_Altitude() * FEET_TO_METER) );
109
110     char altitude_ft[10];
111     sprintf( altitude_ft, "%02d", (int)cur_fdm_state->get_Altitude() );
112
113     char date[10];
114     sprintf( date, "%02d%02d%02d", t->getGmt()->tm_mday, 
115              t->getGmt()->tm_mon+1, t->getGmt()->tm_year );
116
117     // $GPRMC,HHMMSS,A,DDMM.MMM,N,DDDMM.MMM,W,XXX.X,XXX.X,DDMMYY,XXX.X,E*XX
118     sprintf( rmc, "GPRMC,%s,A,%s,%s,%s,%s,%s,000.0,E",
119              utc, lat, lon, speed, heading, date );
120     sprintf( rmc_sum, "%02X", calc_nmea_cksum(rmc) );
121
122     // sprintf( gga, "$GPGGA,%s,%s,%s,1,04,0.0,%s,M,00.0,M,,*00\r\n",
123     //          utc, lat, lon, altitude_m );
124
125     sprintf( rmz, "PGRMZ,%s,f,3", altitude_ft );
126     sprintf( rmz_sum, "%02X", calc_nmea_cksum(rmz) );
127
128     FG_LOG( FG_IO, FG_DEBUG, rmc );
129     FG_LOG( FG_IO, FG_DEBUG, rmz );
130
131     string garmin_sentence;
132
133     // RMC sentence
134     garmin_sentence = "$";
135     garmin_sentence += rmc;
136     garmin_sentence += "*";
137     garmin_sentence += rmc_sum;
138     garmin_sentence += "\n";
139
140     // RMZ sentence (garmin proprietary)
141     garmin_sentence += "$";
142     garmin_sentence += rmz;
143     garmin_sentence += "*";
144     garmin_sentence += rmz_sum;
145     garmin_sentence += "\n";
146
147     cout << garmin_sentence;
148
149     length = garmin_sentence.length();
150     strncpy( buf, garmin_sentence.c_str(), length );
151
152     return true;
153 }
154
155
156 // parse Garmin message
157 bool FGGarmin::parse_message() {
158     FG_LOG( FG_IO, FG_INFO, "parse garmin message" );
159
160     string msg = buf;
161     msg = msg.substr( 0, length );
162     FG_LOG( FG_IO, FG_INFO, "entire message = " << msg );
163
164     string::size_type begin_line, end_line, begin, end;
165     begin_line = begin = 0;
166
167     // extract out each line
168     end_line = msg.find("\n", begin_line);
169     while ( end_line != string::npos ) {
170         string line = msg.substr(begin_line, end_line - begin_line);
171         begin_line = end_line + 1;
172         FG_LOG( FG_IO, FG_INFO, "  input line = " << line );
173
174         // leading character
175         string start = msg.substr(begin, 1);
176         ++begin;
177         FG_LOG( FG_IO, FG_INFO, "  start = " << start );
178
179         // sentence
180         end = msg.find(",", begin);
181         if ( end == string::npos ) {
182             return false;
183         }
184     
185         string sentence = msg.substr(begin, end - begin);
186         begin = end + 1;
187         FG_LOG( FG_IO, FG_INFO, "  sentence = " << sentence );
188
189         double lon_deg, lon_min, lat_deg, lat_min;
190         double lon, lat, speed, heading, altitude;
191
192         if ( sentence == "GPRMC" ) {
193             // time
194             end = msg.find(",", begin);
195             if ( end == string::npos ) {
196                 return false;
197             }
198     
199             string utc = msg.substr(begin, end - begin);
200             begin = end + 1;
201             FG_LOG( FG_IO, FG_INFO, "  utc = " << utc );
202
203             // junk
204             end = msg.find(",", begin);
205             if ( end == string::npos ) {
206                 return false;
207             }
208     
209             string junk = msg.substr(begin, end - begin);
210             begin = end + 1;
211             FG_LOG( FG_IO, FG_INFO, "  junk = " << junk );
212
213             // lat val
214             end = msg.find(",", begin);
215             if ( end == string::npos ) {
216                 return false;
217             }
218     
219             string lat_str = msg.substr(begin, end - begin);
220             begin = end + 1;
221
222             lat_deg = atof( lat_str.substr(0, 2).c_str() );
223             lat_min = atof( lat_str.substr(2).c_str() );
224
225             // lat dir
226             end = msg.find(",", begin);
227             if ( end == string::npos ) {
228                 return false;
229             }
230     
231             string lat_dir = msg.substr(begin, end - begin);
232             begin = end + 1;
233
234             lat = lat_deg + ( lat_min / 60.0 );
235             if ( lat_dir == "S" ) {
236                 lat *= -1;
237             }
238
239             cur_fdm_state->set_Latitude( lat * DEG_TO_RAD );
240             FG_LOG( FG_IO, FG_INFO, "  lat = " << lat );
241
242             // lon val
243             end = msg.find(",", begin);
244             if ( end == string::npos ) {
245                 return false;
246             }
247     
248             string lon_str = msg.substr(begin, end - begin);
249             begin = end + 1;
250
251             lon_deg = atof( lon_str.substr(0, 3).c_str() );
252             lon_min = atof( lon_str.substr(3).c_str() );
253
254             // lon dir
255             end = msg.find(",", begin);
256             if ( end == string::npos ) {
257                 return false;
258             }
259     
260             string lon_dir = msg.substr(begin, end - begin);
261             begin = end + 1;
262
263             lon = lon_deg + ( lon_min / 60.0 );
264             if ( lon_dir == "W" ) {
265                 lon *= -1;
266             }
267
268             cur_fdm_state->set_Longitude( lon * DEG_TO_RAD );
269             FG_LOG( FG_IO, FG_INFO, "  lon = " << lon );
270
271             double sl_radius, lat_geoc;
272             sgGeodToGeoc( cur_fdm_state->get_Latitude(), 
273                           cur_fdm_state->get_Altitude(), 
274                           &sl_radius, &lat_geoc );
275             cur_fdm_state->set_Geocentric_Position( lat_geoc, 
276                            cur_fdm_state->get_Longitude(), 
277                            sl_radius + cur_fdm_state->get_Altitude() );
278
279             // speed
280             end = msg.find(",", begin);
281             if ( end == string::npos ) {
282                 return false;
283             }
284     
285             string speed_str = msg.substr(begin, end - begin);
286             begin = end + 1;
287             speed = atof( speed_str.c_str() );
288             cur_fdm_state->set_V_equiv_kts( speed );
289             cur_fdm_state->set_V_ground_speed( speed );
290             FG_LOG( FG_IO, FG_INFO, "  speed = " << speed );
291
292             // heading
293             end = msg.find(",", begin);
294             if ( end == string::npos ) {
295                 return false;
296             }
297     
298             string hdg_str = msg.substr(begin, end - begin);
299             begin = end + 1;
300             heading = atof( hdg_str.c_str() );
301             cur_fdm_state->set_Euler_Angles( cur_fdm_state->get_Phi(), 
302                                              cur_fdm_state->get_Theta(), 
303                                              heading * DEG_TO_RAD );
304             FG_LOG( FG_IO, FG_INFO, "  heading = " << heading );
305         } else if ( sentence == "PGRMZ" ) {
306             // altitude
307             end = msg.find(",", begin);
308             if ( end == string::npos ) {
309                 return false;
310             }
311     
312             string alt_str = msg.substr(begin, end - begin);
313             altitude = atof( alt_str.c_str() );
314             begin = end + 1;
315
316             // altitude units
317             end = msg.find(",", begin);
318             if ( end == string::npos ) {
319                 return false;
320             }
321     
322             string alt_units = msg.substr(begin, end - begin);
323             begin = end + 1;
324
325             if ( alt_units != "F" && alt_units != "f" ) {
326                 altitude *= METER_TO_FEET;
327             }
328
329             cur_fdm_state->set_Altitude( altitude );
330     
331             FG_LOG( FG_IO, FG_INFO, " altitude  = " << altitude );
332
333         }
334
335         // printf("%.8f %.8f\n", lon, lat);
336
337         begin = begin_line;
338         end_line = msg.find("\n", begin_line);
339     }
340
341     return true;
342 }
343
344
345 // open hailing frequencies
346 bool FGGarmin::open() {
347     if ( is_enabled() ) {
348         FG_LOG( FG_IO, FG_ALERT, "This shouldn't happen, but the channel " 
349                 << "is already in use, ignoring" );
350         return false;
351     }
352
353     SGIOChannel *io = get_io_channel();
354
355     if ( ! io->open( get_direction() ) ) {
356         FG_LOG( FG_IO, FG_ALERT, "Error opening channel communication layer." );
357         return false;
358     }
359
360     set_enabled( true );
361
362     return true;
363 }
364
365
366 // process work for this port
367 bool FGGarmin::process() {
368     SGIOChannel *io = get_io_channel();
369
370     if ( get_direction() == SG_IO_OUT ) {
371         gen_message();
372         if ( ! io->write( buf, length ) ) {
373             FG_LOG( FG_IO, FG_ALERT, "Error writing data." );
374             return false;
375         }
376     } else if ( get_direction() == SG_IO_IN ) {
377         if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
378             FG_LOG( FG_IO, FG_ALERT, "Success reading data." );
379             if ( parse_message() ) {
380                 FG_LOG( FG_IO, FG_ALERT, "Success parsing data." );
381             } else {
382                 FG_LOG( FG_IO, FG_ALERT, "Error parsing data." );
383             }
384         } else {
385             FG_LOG( FG_IO, FG_ALERT, "Error reading data." );
386             return false;
387         }
388         if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
389             FG_LOG( FG_IO, FG_ALERT, "Success reading data." );
390             if ( parse_message() ) {
391                 FG_LOG( FG_IO, FG_ALERT, "Success parsing data." );
392             } else {
393                 FG_LOG( FG_IO, FG_ALERT, "Error parsing data." );
394             }
395         } else {
396             FG_LOG( FG_IO, FG_ALERT, "Error reading data." );
397             return false;
398         }
399     }
400
401     return true;
402 }
403
404
405 // close the channel
406 bool FGGarmin::close() {
407     SGIOChannel *io = get_io_channel();
408
409     set_enabled( false );
410
411     if ( ! io->close() ) {
412         return false;
413     }
414
415     return true;
416 }