1 // fg_serial.cxx -- higher level serial port management routines
3 // Written by Curtis Olson, started November 1998.
5 // Copyright (C) 1998 Curtis L. Olson - curt@flightgear.org
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.
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.
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.
22 // (Log is kept at end of this file)
25 #include <stdlib.h> // atoi()
28 #include <Aircraft/aircraft.hxx>
29 #include <Debug/logstream.hxx>
30 #include <Include/fg_constants.h>
31 #include <Serial/serial.hxx>
32 #include <Time/fg_time.hxx>
34 #include "options.hxx"
36 #include "fg_serial.hxx"
39 // support up to four serial channels. Each channel can be assigned
40 // to an arbitrary port. Bi-directional communication is supported by
41 // the underlying layer.
43 // define the four channels
49 // the type of each channel
50 fgSerialPortKind port_a_kind = FG_SERIAL_DISABLED;
51 fgSerialPortKind port_b_kind = FG_SERIAL_DISABLED;
52 fgSerialPortKind port_c_kind = FG_SERIAL_DISABLED;
53 fgSerialPortKind port_d_kind = FG_SERIAL_DISABLED;
56 // configure a port based on the config string
57 static bool config_port(fgSERIAL& s, fgSerialPortKind& kind,
60 string::size_type begin, end;
70 end = config.find(",", begin);
71 if ( end == string::npos ) {
75 device = config.substr(begin, end - begin);
77 cout << " device = " << device << endl;
80 end = config.find(",", begin);
81 if ( end == string::npos ) {
85 format = config.substr(begin, end - begin);
87 cout << " format = " << format << endl;
90 end = config.find(",", begin);
91 if ( end == string::npos ) {
95 baud = config.substr(begin, end - begin);
97 cout << " baud = " << baud << endl;
100 direction = config.substr(begin);
101 cout << " direction = " << direction << endl;
103 if ( s.is_enabled() ) {
104 FG_LOG( FG_SERIAL, FG_ALERT, "This shouldn't happen, but the port "
105 << "is already in use, ignoring" );
109 if ( ! s.open_port( device ) ) {
110 FG_LOG( FG_SERIAL, FG_ALERT, "Error opening device: " << device );
113 if ( ! s.set_baud( atoi( baud.c_str() ) ) ) {
114 FG_LOG( FG_SERIAL, FG_ALERT, "Error setting baud: " << baud );
117 if ( format == "nmea" ) {
118 if ( direction == "out" ) {
119 kind = FG_SERIAL_NMEA_OUT;
120 } else if ( direction == "in" ) {
121 kind = FG_SERIAL_NMEA_IN;
123 FG_LOG( FG_SERIAL, FG_ALERT, "Unknown direction" );
126 } else if ( format == "garman" ) {
127 if ( direction == "out" ) {
128 kind = FG_SERIAL_GARMAN_OUT;
129 } else if ( direction == "in" ) {
130 kind = FG_SERIAL_GARMAN_IN;
132 FG_LOG( FG_SERIAL, FG_ALERT, "Unknown direction" );
135 } else if ( format == "fgfs" ) {
136 if ( direction == "out" ) {
137 kind = FG_SERIAL_FGFS_OUT;
138 } else if ( direction == "in" ) {
139 kind = FG_SERIAL_FGFS_IN;
141 FG_LOG( FG_SERIAL, FG_ALERT, "Unknown direction" );
145 FG_LOG( FG_SERIAL, FG_ALERT, "Unknown format" );
153 // initialize serial ports based on command line options (if any)
154 void fgSerialInit() {
155 if ( current_options.get_port_a_config() != "" ) {
156 config_port(port_a, port_a_kind, current_options.get_port_a_config() );
159 if ( current_options.get_port_b_config() != "" ) {
160 config_port(port_b, port_b_kind, current_options.get_port_b_config() );
163 if ( current_options.get_port_c_config() != "" ) {
164 config_port(port_c, port_c_kind, current_options.get_port_c_config() );
167 if ( current_options.get_port_d_config() != "" ) {
168 config_port(port_d, port_d_kind, current_options.get_port_d_config() );
173 char calc_nmea_cksum(char *sentence) {
174 unsigned char sum = 0;
177 // printf("%s\n", sentence);
179 len = strlen(sentence);
181 for ( i = 1; i < len; i++ ) {
182 // printf("%c", sentence[i]);
187 // printf("sum = %02x\n", sum);
192 static void send_nmea_out( fgSERIAL& s ) {
193 char rmc[256], gga[256];
194 char rmc_sum[10], gga_sum[10];
201 f = current_aircraft.flight;
202 t = &cur_time_params;
205 sprintf( utc, "%02d%02d%02d",
206 t->gmt->tm_hour, t->gmt->tm_min, t->gmt->tm_sec );
209 double latd = FG_Latitude * RAD_TO_DEG;
217 min = (latd - (double)deg) * 60.0;
218 sprintf( lat, "%02d%06.3f,%c", abs(deg), min, dir);
221 double lond = FG_Longitude * RAD_TO_DEG;
229 min = (lond - (double)deg) * 60.0;
230 sprintf( lon, "%02d%06.3f,%c", abs(deg), min, dir);
233 sprintf( speed, "%05.1f", FG_V_equiv_kts );
236 sprintf( heading, "%05.1f", FG_Psi * RAD_TO_DEG );
239 sprintf( altitude_m, "%02d", (int)(FG_Altitude * FEET_TO_METER) );
241 char altitude_ft[10];
242 sprintf( altitude_ft, "%02d", (int)FG_Altitude );
245 sprintf( date, "%02d%02d%02d",
246 t->gmt->tm_mday, t->gmt->tm_mon+1, t->gmt->tm_year );
248 // $GPRMC,HHMMSS,A,DDMM.MMM,N,DDDMM.MMM,W,XXX.X,XXX.X,DDMMYY,XXX.X,E*XX
249 sprintf( rmc, "GPRMC,%s,A,%s,%s,%s,%s,%s,0.000,E",
250 utc, lat, lon, speed, heading, date );
251 sprintf( rmc_sum, "%02X", 0 /*calc_nmea_cksum(rmc)*/ );
253 sprintf( gga, "GPGGA,%s,%s,%s,1,,,%s,M,,,,",
254 utc, lat, lon, altitude_m );
255 sprintf( gga_sum, "%02X", 0 /*calc_nmea_cksum(gga)*/ );
258 FG_LOG( FG_SERIAL, FG_DEBUG, rmc );
259 FG_LOG( FG_SERIAL, FG_DEBUG, gga );
261 // one full frame every 2 seconds according to the standard
262 if ( cur_time_params.cur_time % 2 == 0 ) {
263 // rmc on even seconds
264 string rmc_sentence = "$";
267 rmc_sentence += rmc_sum;
268 rmc_sentence += "\r\n";
269 s.write_port(rmc_sentence);
271 // gga on odd seconds
272 string gga_sentence = "$";
275 gga_sentence += gga_sum;
276 gga_sentence += "\n";
277 // s.write_port(gga_sentence);
281 static void read_nmea_in( fgSERIAL& s ) {
284 static void send_garman_out( fgSERIAL& s ) {
285 char rmc[256], rmz[256];
292 f = current_aircraft.flight;
293 t = &cur_time_params;
296 sprintf( utc, "%02d%02d%02d",
297 t->gmt->tm_hour, t->gmt->tm_min, t->gmt->tm_sec );
300 double latd = FG_Latitude * RAD_TO_DEG;
308 min = (latd - (double)deg) * 60.0;
309 sprintf( lat, "%02d%06.3f,%c", abs(deg), min, dir);
312 double lond = FG_Longitude * RAD_TO_DEG;
320 min = (lond - (double)deg) * 60.0;
321 sprintf( lon, "%02d%06.3f,%c", abs(deg), min, dir);
324 sprintf( speed, "%05.1f", FG_V_equiv_kts );
327 sprintf( heading, "%05.1f", FG_Psi * RAD_TO_DEG );
330 sprintf( altitude_m, "%02d", (int)(FG_Altitude * FEET_TO_METER) );
332 char altitude_ft[10];
333 sprintf( altitude_ft, "%02d", (int)FG_Altitude );
336 sprintf( date, "%02d%02d%02d",
337 t->gmt->tm_mday, t->gmt->tm_mon+1, t->gmt->tm_year );
339 // $GPRMC,HHMMSS,A,DDMM.MMM,N,DDDMM.MMM,W,XXX.X,XXX.X,DDMMYY,XXX.X,E*XX
340 sprintf( rmc, "$GPRMC,%s,A,%s,%s,%s,%s,%s,000.0,E*00\r\n",
341 utc, lat, lon, speed, heading, date );
343 // sprintf( gga, "$GPGGA,%s,%s,%s,1,04,0.0,%s,M,00.0,M,,*00\r\n",
344 // utc, lat, lon, altitude_m );
346 sprintf( rmz, "$PGRMZ,%s,f,3*00\r\n", altitude_ft );
348 FG_LOG( FG_SERIAL, FG_DEBUG, rmc );
349 FG_LOG( FG_SERIAL, FG_DEBUG, rmz );
352 // one full frame every 2 seconds according to the standard
353 if ( cur_time_params.cur_time % 2 == 0 ) {
354 // rmc on even seconds
357 // gga on odd seconds
362 static void read_garman_in( fgSERIAL& s ) {
365 static void send_fgfs_out( fgSERIAL& s ) {
368 static void read_fgfs_in( fgSERIAL& s ) {
372 // one more level of indirection ...
373 static void process_port( fgSERIAL& s, const fgSerialPortKind kind ) {
374 static long last_time;
375 if ( kind == FG_SERIAL_NMEA_OUT ) {
376 if (cur_time_params.cur_time > last_time ) {
379 last_time = cur_time_params.cur_time;
380 } else if ( kind == FG_SERIAL_NMEA_IN ) {
382 } else if ( kind == FG_SERIAL_GARMAN_OUT ) {
383 if (cur_time_params.cur_time > last_time ) {
386 last_time = cur_time_params.cur_time;
387 } else if ( kind == FG_SERIAL_GARMAN_IN ) {
389 } else if ( kind == FG_SERIAL_FGFS_OUT ) {
391 } else if ( kind == FG_SERIAL_FGFS_IN ) {
397 // process any serial port work
398 void fgSerialProcess() {
399 if ( port_a_kind != FG_SERIAL_DISABLED ) {
400 process_port(port_a, port_a_kind);
403 if ( port_b_kind != FG_SERIAL_DISABLED ) {
404 process_port(port_b, port_b_kind);
407 if ( port_c_kind != FG_SERIAL_DISABLED ) {
408 process_port(port_c, port_c_kind);
411 if ( port_d_kind != FG_SERIAL_DISABLED ) {
412 process_port(port_d, port_d_kind);
418 // Revision 1.3 1998/11/23 20:51:51 curt
419 // Tweaking serial stuff.
421 // Revision 1.2 1998/11/19 13:53:25 curt
422 // Added a "Garman" mode.
424 // Revision 1.1 1998/11/16 13:57:42 curt