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