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 static void send_nmea_out( fgSERIAL& s ) {
174 char rmc[256], gga[256], rmz[256];
181 f = current_aircraft.flight;
182 t = &cur_time_params;
185 sprintf( utc, "%02d%02d%02d",
186 t->gmt->tm_hour, t->gmt->tm_min, t->gmt->tm_sec );
189 double latd = FG_Latitude * RAD_TO_DEG;
197 min = (latd - (double)deg) * 60.0;
198 sprintf( lat, "%02d%06.3f,%c", abs(deg), min, dir);
201 double lond = FG_Longitude * RAD_TO_DEG;
209 min = (lond - (double)deg) * 60.0;
210 sprintf( lon, "%02d%06.3f,%c", abs(deg), min, dir);
213 sprintf( speed, "%05.1f", FG_V_equiv_kts );
216 sprintf( heading, "%05.1f", FG_Psi * RAD_TO_DEG );
219 sprintf( altitude_m, "%02d", (int)(FG_Altitude * FEET_TO_METER) );
221 char altitude_ft[10];
222 sprintf( altitude_ft, "%02d", (int)FG_Altitude );
225 sprintf( date, "%02d%02d%02d",
226 t->gmt->tm_mday, t->gmt->tm_mon+1, t->gmt->tm_year );
228 // $GPRMC,HHMMSS,A,DDMM.MMM,N,DDDMM.MMM,W,XXX.X,XXX.X,DDMMYY,XXX.X,E*XX
229 sprintf( rmc, "$GPRMC,%s,A,%s,%s,%s,%s,%s,000.0,E*00\r\n",
230 utc, lat, lon, speed, heading, date );
232 sprintf( gga, "$GPGGA,%s,%s,%s,1,04,0.0,%s,M,00.0,M,,*00\r\n",
233 utc, lat, lon, altitude_m );
235 // sprintf( rmz, "$PGRMZ,%s,f,3*00\r\n", altitude_ft );
237 FG_LOG( FG_SERIAL, FG_DEBUG, rmc );
238 FG_LOG( FG_SERIAL, FG_DEBUG, gga );
239 // FG_LOG( FG_SERIAL, FG_DEBUG, rmz );
242 // one full frame every 2 seconds according to the standard
243 if ( cur_time_params.cur_time % 2 == 0 ) {
244 // rmc on even seconds
247 // gga on odd seconds
250 // s.write_port(rmz);
253 static void read_nmea_in( fgSERIAL& s ) {
256 static void send_garman_out( fgSERIAL& s ) {
257 char rmc[256], rmz[256];
264 f = current_aircraft.flight;
265 t = &cur_time_params;
268 sprintf( utc, "%02d%02d%02d",
269 t->gmt->tm_hour, t->gmt->tm_min, t->gmt->tm_sec );
272 double latd = FG_Latitude * RAD_TO_DEG;
280 min = (latd - (double)deg) * 60.0;
281 sprintf( lat, "%02d%06.3f,%c", abs(deg), min, dir);
284 double lond = FG_Longitude * RAD_TO_DEG;
292 min = (lond - (double)deg) * 60.0;
293 sprintf( lon, "%02d%06.3f,%c", abs(deg), min, dir);
296 sprintf( speed, "%05.1f", FG_V_equiv_kts );
299 sprintf( heading, "%05.1f", FG_Psi * RAD_TO_DEG );
302 sprintf( altitude_m, "%02d", (int)(FG_Altitude * FEET_TO_METER) );
304 char altitude_ft[10];
305 sprintf( altitude_ft, "%02d", (int)FG_Altitude );
308 sprintf( date, "%02d%02d%02d",
309 t->gmt->tm_mday, t->gmt->tm_mon+1, t->gmt->tm_year );
311 // $GPRMC,HHMMSS,A,DDMM.MMM,N,DDDMM.MMM,W,XXX.X,XXX.X,DDMMYY,XXX.X,E*XX
312 sprintf( rmc, "$GPRMC,%s,A,%s,%s,%s,%s,%s,000.0,E*00\r\n",
313 utc, lat, lon, speed, heading, date );
315 // sprintf( gga, "$GPGGA,%s,%s,%s,1,04,0.0,%s,M,00.0,M,,*00\r\n",
316 // utc, lat, lon, altitude_m );
318 sprintf( rmz, "$PGRMZ,%s,f,3*00\r\n", altitude_ft );
320 FG_LOG( FG_SERIAL, FG_DEBUG, rmc );
321 FG_LOG( FG_SERIAL, FG_DEBUG, rmz );
324 // one full frame every 2 seconds according to the standard
325 if ( cur_time_params.cur_time % 2 == 0 ) {
326 // rmc on even seconds
329 // gga on odd seconds
334 static void read_garman_in( fgSERIAL& s ) {
337 static void send_fgfs_out( fgSERIAL& s ) {
340 static void read_fgfs_in( fgSERIAL& s ) {
344 // one more level of indirection ...
345 static void process_port( fgSERIAL& s, const fgSerialPortKind kind ) {
346 static long last_time;
347 if ( kind == FG_SERIAL_NMEA_OUT ) {
348 if (cur_time_params.cur_time > last_time ) {
351 last_time = cur_time_params.cur_time;
352 } else if ( kind == FG_SERIAL_NMEA_IN ) {
354 } else if ( kind == FG_SERIAL_GARMAN_OUT ) {
355 if (cur_time_params.cur_time > last_time ) {
358 last_time = cur_time_params.cur_time;
359 } else if ( kind == FG_SERIAL_GARMAN_IN ) {
361 } else if ( kind == FG_SERIAL_FGFS_OUT ) {
363 } else if ( kind == FG_SERIAL_FGFS_IN ) {
369 // process any serial port work
370 void fgSerialProcess() {
371 if ( port_a_kind != FG_SERIAL_DISABLED ) {
372 process_port(port_a, port_a_kind);
375 if ( port_b_kind != FG_SERIAL_DISABLED ) {
376 process_port(port_b, port_b_kind);
379 if ( port_c_kind != FG_SERIAL_DISABLED ) {
380 process_port(port_c, port_c_kind);
383 if ( port_d_kind != FG_SERIAL_DISABLED ) {
384 process_port(port_d, port_d_kind);
390 // Revision 1.2 1998/11/19 13:53:25 curt
391 // Added a "Garman" mode.
393 // Revision 1.1 1998/11/16 13:57:42 curt