diff --git a/src/Main/fg_io.cxx b/src/Main/fg_io.cxx index cd66cf0c6..0aa1fa377 100644 --- a/src/Main/fg_io.cxx +++ b/src/Main/fg_io.cxx @@ -49,6 +49,7 @@ #include #include #include +#include #ifdef FG_JPEG_SERVER # include #endif @@ -147,6 +148,9 @@ FGIO::parse_port_config( const string& config ) } else if ( protocol == "garmin" ) { FGGarmin *garmin = new FGGarmin; io = garmin; + } else if ( protocol == "igc" ) { + IGCProtocol *igc = new IGCProtocol; + io = igc; } else if ( protocol == "httpd" ) { // determine port string port = tokens[1]; diff --git a/src/Main/options.cxx b/src/Main/options.cxx index b5230d4e7..5ec12cdec 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -1458,6 +1458,7 @@ struct OptionDesc { {"AV400WSimA", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 }, {"AV400WSimB", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 }, {"garmin", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 }, + {"igc", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 }, {"nmea", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 }, {"generic", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 }, {"props", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 }, diff --git a/src/Network/CMakeLists.txt b/src/Network/CMakeLists.txt index 27d2e1b07..b4cc36ec8 100644 --- a/src/Network/CMakeLists.txt +++ b/src/Network/CMakeLists.txt @@ -12,6 +12,7 @@ set(SOURCES generic.cxx httpd.cxx HTTPClient.cxx + igc.cxx joyclient.cxx jsclient.cxx lfsglass.cxx @@ -40,6 +41,7 @@ set(HEADERS generic.hxx httpd.hxx HTTPClient.hxx + igc.hxx joyclient.hxx jsclient.hxx lfsglass.hxx diff --git a/src/Network/igc.cxx b/src/Network/igc.cxx new file mode 100644 index 000000000..9e74f4972 --- /dev/null +++ b/src/Network/igc.cxx @@ -0,0 +1,243 @@ +// igc.cxx -- International Glider Commission (IGC) protocol class +// +// Written by Thorsten Brehm, started October 2013. +// +// Copyright (C) 2013 Thorsten Brehm - brehmt (at) gmail com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +/////////////////////////////////////////////////////////////////////////////// + +/* Usage: + * "fgfs --igc=file,out,1,OutputFile.igc" + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if defined( HAVE_VERSION_H ) && HAVE_VERSION_H +# include +#else +# include +#endif + +#include // sprintf +#include +#include +#include +#include + +#include
+#include
+ +#include "igc.hxx" + +IGCProtocol::IGCProtocol() : + length(0) +{ +} + +IGCProtocol::~IGCProtocol() +{ +} + +// generate IGC header records +bool IGCProtocol::gen_Hrecords() +{ + const char* AircraftType = fgGetString("/sim/aircraft", "unknown");; + const char* Callsign = fgGetString("/sim/multiplay/callsign", ""); + + SGTime *t = globals->get_time_params(); + int Day = t->getGmt()->tm_mday; + int Month = t->getGmt()->tm_mon+1; + int Year = t->getGmt()->tm_year % 100; + +#ifdef FLIGHTGEAR_VERSION + const char* Version = FLIGHTGEAR_VERSION; +#else + const char* Version = "unknown version"; +#endif + + length = snprintf(buf, FG_MAX_MSG_SIZE, + "HFDTE%02d%02d%02d\r\n" // date: DDMMYY + "HFFXA001\r\n" // fix accuracy (1 meter) + "HFGTYGliderType:%s\r\n" // aircraft type + "HFGIDGliderID:%s\r\n" // callsign + "HFDTM100GPSDatum:WGS84\r\n" // GPS datum type + "HFRFWFirmwareVersion:FlightGear %s\r\n" // "firmware" version + "HFRHWHardwareVersion:FlightGear Flight Simulator\r\n" // "hardware" version + "HFFTYFRType:Flight Simulator\r\n", // logger type + Day, Month, Year, + AircraftType, + Callsign, + Version); + SGIOChannel *io = get_io_channel(); + io->write(buf, length); + + return true; +} + +// generate igc B record message +bool IGCProtocol::gen_message() +{ + + /* IGC B-record spec: + * B H H M M S S D D M MM MM N D D D M MM MM E V P P P P P G G G G G CR LF + * + * Description Size Element Remarks + * ------------------------------------------------------------------------------------------------------------------------------------------------ + * Time UTC 6 bytes HHMMSS Valid characters 0-9. When a valid GNSS fix is received, the UTC time + * in a B-record line must be obtained directly from the same GNSS data + * package that was the source of the Lat/long and GNSS altitude that is + * recorded in the same B-record line. Other sources for the time in a + * B-record line (such as the Real-Time Clock in the recorder) must only + * be used to provide time-continuity where GNSS fixes are not available. + * Latitude 8 bytes DDMMmmmN/S Valid characters N, S, 0-9. Obtained directly from the same GPS data + * package that was the source of the UTC time that is recorded in the + * same B-record line. If no latitude is obtained from satellite data, + * pressure altitude fixing must continue, using times from the RTC. + * In this case, in B record lines must repeat the last latitude that was + * obtained from satellite data, until GPS fixing is regained. + * Longitude 9 bytes DDDMMmmmE/W Valid characters E,W, 0-9. Obtained directly from the same GPS data + * package that was the source of UTC time that is recorded in the same + * B-record line. If no longitude is obtained from satellite data, + * pressure altitude fixing must continue, using times from the RTC. + * In this case, in B record lines must repeat the last longitude + * that was obtained from satellite data, until GPS fixing is regained. + * Fix validity 1 byte. A or V Use A for a 3D fix and V for a 2D fix (no GPS altitude) or for no + * GPS data (pressure altitude data must continue to be recorded using + * times from the RTC). + * Press Alt. 5 bytes PPPPP Altitude to the ICAO ISA above the 1013.25 hPa sea level datum, valid + * characters 0-9 and negative sign "-". Negative values to have negative + * sign instead of leading zero. + * GNSS Alt. 5 bytes GGGGG Altitude above the WGS84 ellipsoid, valid characters 0-9. + */ + + char lonDir = 'E', latDir = 'N'; + int lonDeg, latDeg, lonMin, latMin; + + SGTime *t = globals->get_time_params(); + + double deg = fdm.get_Latitude() * SGD_RADIANS_TO_DEGREES; + if (deg < 0.0) + { + deg = -deg; + latDir = 'S'; + } + + latDeg = (int)(deg); + latMin = (int)((deg - (double)latDeg) * 60.0 * 1000.0); + + deg = fdm.get_Longitude() * SGD_RADIANS_TO_DEGREES; + if (deg < 0.0) + { + deg = -deg; + lonDir = 'W'; + } + + lonDeg = (int)(deg); + lonMin = (int)((deg - (double)lonDeg) * 60.0 * 1000.0); + + int Altitude = fdm.get_Altitude() * SG_FEET_TO_METER; + if (Altitude < 0) + Altitude = 0; + + int h = t->getGmt()->tm_hour; + int m = t->getGmt()->tm_min; + int s = t->getGmt()->tm_sec; + + // write the B record + length = snprintf(buf,FG_MAX_MSG_SIZE, + "B" + "%02d%02d%02d" // UTC time: HHMMSS + "%02d%05d%c" // Latitude: DDMMmmmN (or ..S) + "%03d%05d%c" // Longitude: DDDMMmmmE (or ..W) + "A" // Fix validity: A for a 3D fix, V for 2D fix + "%05d" // Pressure Altitude: PPPPP (above 1013.2 hPa) + "%05d" // GNSS Altitude: AAAAA + "\r\n", // Line feed: CR LF + h, m, s, + latDeg, latMin, latDir, + lonDeg, lonMin, lonDir, + Altitude, // This should be standard pressure altitude instead. Hm, well :). + Altitude // GPS altitude + ); + + return (length > 0); +} + +// reading IGC files is not supported +bool IGCProtocol::parse_message() +{ + return false; +} + +// write header data +bool IGCProtocol::open() +{ + if ( is_enabled() ) + { + SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " + << "is already in use, ignoring" ); + return false; + } + + SGIOChannel *io = get_io_channel(); + + if (!io->open( get_direction() )) + { + SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." ); + return false; + } + + set_enabled( true ); + + gen_Hrecords(); + + return true; +} + +// process work +bool IGCProtocol::process() +{ + SGIOChannel *io = get_io_channel(); + if ( get_direction() == SG_IO_OUT ) + { + gen_message(); + if (!io->write( buf, length )) + { + SG_LOG( SG_IO, SG_WARN, "Error writing data." ); + return false; + } + } else + if ( get_direction() == SG_IO_IN ) + { + SG_LOG( SG_IO, SG_ALERT, "Error: IGC input is not supported."); + return false; + } + + return true; +} + +// close the channel +bool IGCProtocol::close() +{ + SGIOChannel *io = get_io_channel(); + + set_enabled(false); + + return io->close(); +} diff --git a/src/Network/igc.hxx b/src/Network/igc.hxx new file mode 100644 index 000000000..9cf76e02c --- /dev/null +++ b/src/Network/igc.hxx @@ -0,0 +1,56 @@ +// igc.hxx -- International Glider Commission (IGC) protocol class +// +// Written by Thorsten Brehm, started October 2013. +// +// Copyright (C) 2013 Thorsten Brehm - brehmt (at) gmail com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _FG_IGC_HXX +#define _FG_IGC_HXX + +#include +#include +#include "protocol.hxx" + +class IGCProtocol : public FGProtocol +{ + char buf[FG_MAX_MSG_SIZE]; + int length; + + bool gen_Hrecords(); + +public: + IGCProtocol(); + ~IGCProtocol(); + + bool gen_message(); + bool parse_message(); + + // open hailing frequencies + bool open(); + + // process work for this port + bool process(); + + // close the channel + bool close(); + + FlightProperties fdm; +}; + +#endif // _FG_IGC_HXX