]> git.mxchange.org Git - flightgear.git/blob - src/Network/igc.cxx
Fix for bug 1304 - crash loading XML route
[flightgear.git] / src / Network / igc.cxx
1 // igc.cxx -- International Glider Commission (IGC) protocol class
2 //
3 // Written by Thorsten Brehm, started October 2013.
4 //
5 // Copyright (C) 2013 Thorsten Brehm - brehmt (at) gmail com
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 St, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 ///////////////////////////////////////////////////////////////////////////////
22
23 /* Usage:
24  *  "fgfs --igc=file,out,1,OutputFile.igc"
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #  include "config.h"
29 #endif
30
31 #if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
32 #  include <Include/version.h>
33 #else
34 #  include <Include/no_version.h>
35 #endif
36
37 #include <stdio.h>  // sprintf
38 #include <simgear/debug/logstream.hxx>
39 #include <simgear/math/sg_geodesy.hxx>
40 #include <simgear/io/iochannel.hxx>
41 #include <simgear/timing/sg_time.hxx>
42
43 #include <Main/fg_props.hxx>
44 #include <Main/globals.hxx>
45
46 #include "igc.hxx"
47
48 IGCProtocol::IGCProtocol() :
49     length(0)
50 {
51 }
52
53 IGCProtocol::~IGCProtocol()
54 {
55 }
56
57 // generate IGC header records
58 bool IGCProtocol::gen_Hrecords()
59 {
60     const char* AircraftType = fgGetString("/sim/aircraft", "unknown");;
61     const char* Callsign     = fgGetString("/sim/multiplay/callsign", "");
62
63     SGTime *t = globals->get_time_params();
64     int Day   = t->getGmt()->tm_mday;
65     int Month = t->getGmt()->tm_mon+1;
66     int Year  = t->getGmt()->tm_year % 100;
67
68 #ifdef FLIGHTGEAR_VERSION
69     const char* Version = FLIGHTGEAR_VERSION;
70 #else
71     const char* Version = "unknown version";
72 #endif
73
74     length = snprintf(buf, FG_MAX_MSG_SIZE,
75                     "HFDTE%02d%02d%02d\r\n"         // date: DDMMYY
76                     "HFFXA001\r\n"                  // fix accuracy (1 meter)
77                     "HFGTYGliderType:%s\r\n"        // aircraft type
78                     "HFGIDGliderID:%s\r\n"          // callsign
79                     "HFDTM100GPSDatum:WGS84\r\n"    // GPS datum type
80                     "HFRFWFirmwareVersion:FlightGear %s\r\n" // "firmware" version
81                     "HFRHWHardwareVersion:FlightGear Flight Simulator\r\n" // "hardware" version
82                     "HFFTYFRType:Flight Simulator\r\n", // logger type
83                     Day, Month, Year,
84                     AircraftType,
85                     Callsign,
86                     Version);
87     SGIOChannel *io = get_io_channel();
88     io->write(buf, length);
89
90     return true;
91 }
92
93 // generate igc B record message
94 bool IGCProtocol::gen_message()
95 {
96
97     /* IGC B-record spec:
98      *  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
99      *
100      *  Description     Size    Element     Remarks
101      *  ------------------------------------------------------------------------------------------------------------------------------------------------
102      *  Time UTC        6 bytes HHMMSS      Valid characters 0-9. When a valid GNSS fix is received, the UTC time
103      *                                      in a B-record line must be obtained directly from the same GNSS data
104      *                                      package that was the source of the Lat/long and GNSS altitude that is
105      *                                      recorded in the same B-record line. Other sources for the time in a
106      *                                      B-record line (such as the Real-Time Clock in the recorder) must only
107      *                                      be used to provide time-continuity where GNSS fixes are not available.
108      *  Latitude        8 bytes DDMMmmmN/S  Valid characters N, S, 0-9. Obtained directly from the same GPS data
109      *                                      package that was the source of the UTC time that is recorded in the
110      *                                      same B-record line. If no latitude is obtained from satellite data,
111      *                                      pressure altitude fixing must continue, using times from the RTC.
112      *                                      In this case, in B record lines must repeat the last latitude that was
113      *                                      obtained from satellite data, until GPS fixing is regained.
114      *  Longitude       9 bytes DDDMMmmmE/W Valid characters E,W, 0-9. Obtained directly from the same GPS data
115      *                                      package that was the source of UTC time that is recorded in the same
116      *                                      B-record line. If no longitude is obtained from satellite data,
117      *                                      pressure altitude fixing must continue, using times from the RTC.
118      *                                      In this case, in B record lines must repeat the last longitude
119      *                                      that was obtained from satellite data, until GPS fixing is regained.
120      *  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
121      *                                      GPS data (pressure altitude data must continue to be recorded using
122      *                                      times from the RTC).
123      *  Press Alt.      5 bytes PPPPP       Altitude to the ICAO ISA above the 1013.25 hPa sea level datum, valid
124      *                                      characters 0-9 and negative sign "-". Negative values to have negative
125      *                                      sign instead of leading zero.
126      *  GNSS Alt.       5 bytes GGGGG       Altitude above the WGS84 ellipsoid, valid characters 0-9.
127      */
128
129      char lonDir = 'E', latDir = 'N';
130      int lonDeg, latDeg, lonMin, latMin;
131
132      SGTime *t = globals->get_time_params();
133
134      double deg = fdm.get_Latitude() * SGD_RADIANS_TO_DEGREES;
135      if (deg < 0.0)
136      {
137          deg = -deg;
138          latDir = 'S';
139      }
140
141      latDeg = (int)(deg);
142      latMin = (int)((deg - (double)latDeg) * 60.0 * 1000.0);
143
144      deg = fdm.get_Longitude() * SGD_RADIANS_TO_DEGREES;
145      if (deg < 0.0)
146      {
147          deg = -deg;
148          lonDir = 'W';
149      }
150
151      lonDeg = (int)(deg);
152      lonMin = (int)((deg - (double)lonDeg) * 60.0 * 1000.0);
153
154      int Altitude = fdm.get_Altitude() * SG_FEET_TO_METER;
155      if (Altitude < 0)
156          Altitude = 0;
157
158      int h = t->getGmt()->tm_hour;
159      int m = t->getGmt()->tm_min;
160      int s = t->getGmt()->tm_sec;
161
162      // write the B record
163      length = snprintf(buf,FG_MAX_MSG_SIZE,
164                   "B"
165                   "%02d%02d%02d" // UTC time:           HHMMSS
166                   "%02d%05d%c"   // Latitude:           DDMMmmmN (or ..S)
167                   "%03d%05d%c"   // Longitude:          DDDMMmmmE (or ..W)
168                   "A"            // Fix validity:       A for a 3D fix, V for 2D fix
169                   "%05d"         // Pressure Altitude:  PPPPP (above 1013.2 hPa)
170                   "%05d"         // GNSS Altitude:      AAAAA
171                   "\r\n",        // Line feed:          CR LF
172                   h, m, s,
173                   latDeg, latMin, latDir,
174                   lonDeg, lonMin, lonDir,
175                   Altitude, // This should be standard pressure altitude instead. Hm, well :).
176                   Altitude  // GPS altitude
177              );
178
179      return (length > 0);
180 }
181
182 // reading IGC files is not supported
183 bool IGCProtocol::parse_message()
184 {
185     return false;
186 }
187
188 // write header data
189 bool IGCProtocol::open()
190 {
191     if ( is_enabled() )
192     {
193         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
194                 << "is already in use, ignoring" );
195         return false;
196     }
197
198     SGIOChannel *io = get_io_channel();
199
200     if (!io->open( get_direction() ))
201     {
202         SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
203         return false;
204     }
205
206     set_enabled( true );
207
208     gen_Hrecords();
209
210     return true;
211 }
212
213 // process work
214 bool IGCProtocol::process()
215 {
216     SGIOChannel *io = get_io_channel();
217     if ( get_direction() == SG_IO_OUT )
218     {
219         gen_message();
220         if (!io->write( buf, length ))
221         {
222             SG_LOG( SG_IO, SG_WARN, "Error writing data." );
223             return false;
224         }
225     } else
226     if ( get_direction() == SG_IO_IN )
227     {
228         SG_LOG( SG_IO, SG_ALERT, "Error: IGC input is not supported.");
229         return false;
230     }
231
232     return true;
233 }
234
235 // close the channel
236 bool IGCProtocol::close()
237 {
238     SGIOChannel *io = get_io_channel();
239
240     set_enabled(false);
241
242     return io->close();
243 }