Makefile: remove spurious tab
[xcsoar.git] / src / IGC / IGCWriter.cpp
blobb0d7d0b1a4796cc7e107cc3ee861aacd19054051
1 /*
2 Copyright_License {
4 XCSoar Glide Computer - http://www.xcsoar.org/
5 Copyright (C) 2000-2013 The XCSoar Project
6 A detailed list of copyright holders can be found in the file "AUTHORS".
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "IGC/IGCWriter.hpp"
25 #include "IGCString.hpp"
26 #include "NMEA/Info.hpp"
27 #include "Version.hpp"
29 #include <assert.h>
30 #include <windef.h> /* for MAX_PATH */
32 static char *
33 FormatIGCLocation(char *buffer, const GeoPoint &location)
35 char latitude_suffix = negative(location.latitude.Native())
36 ? 'S' : 'N';
37 unsigned latitude =
38 (unsigned)uround(fabs(location.latitude.Degrees() * 60000));
40 char longitude_suffix = negative(location.longitude.Native())
41 ? 'W' : 'E';
42 unsigned longitude =
43 (unsigned)uround(fabs(location.longitude.Degrees() * 60000));
45 sprintf(buffer, "%02u%05u%c%03u%05u%c",
46 latitude / 60000, latitude % 60000, latitude_suffix,
47 longitude / 60000, longitude % 60000, longitude_suffix);
49 return buffer + strlen(buffer);
52 IGCWriter::IGCWriter(const TCHAR *path)
53 :file(path)
55 fix.Clear();
57 grecord.Initialize();
60 bool
61 IGCWriter::CommitLine(char *line)
63 if (!file.WriteLine(line))
64 return false;
66 grecord.AppendRecordToBuffer(line);
67 return true;
70 bool
71 IGCWriter::WriteLine(const char *line)
73 assert(strchr(line, '\r') == NULL);
74 assert(strchr(line, '\n') == NULL);
76 char *const dest = BeginLine();
77 if (dest == nullptr)
78 return false;
80 char *const end = dest + MAX_IGC_BUFF - 1, *p = dest;
82 p = CopyIGCString(dest, end, line);
83 *p = '\0';
85 return CommitLine(dest);
88 bool
89 IGCWriter::WriteLine(const char *a, const TCHAR *b)
91 size_t a_length = strlen(a);
92 assert(a_length < MAX_IGC_BUFF);
94 char *const dest = BeginLine();
95 if (dest == nullptr)
96 return false;
98 char *const end = dest + MAX_IGC_BUFF - 1, *p = dest;
100 p = std::copy(a, a + a_length, p);
101 p = CopyIGCString(p, end, b);
102 *p = '\0';
104 return CommitLine(dest);
107 void
108 IGCWriter::WriteHeader(const BrokenDateTime &date_time,
109 const TCHAR *pilot_name, const TCHAR *aircraft_model,
110 const TCHAR *aircraft_registration,
111 const TCHAR *competition_id,
112 const char *logger_id, const TCHAR *driver_name,
113 bool simulator)
116 * HFDTE141203 <- should be UTC, same as time in filename
117 * HFFXA100
118 * HFPLTPILOT:JOHN WHARINGTON
119 * HFGTYGLIDERTYPE:LS 3
120 * HFGIDGLIDERID:VH-WUE
121 * HFDTM100GPSDATUM:WGS84
122 * HFRFWFIRMWAREVERSION:3.6
123 * HFRHWHARDWAREVERSION:3.4
124 * HFFTYFR TYPE:GARRECHT INGENIEURGESELLSCHAFT,VOLKSLOGGER 1.0
125 * HFCIDCOMPETITIONID:WUE
126 * HFCCLCOMPETITIONCLASS:FAI
129 assert(date_time.Plausible());
130 assert(logger_id != NULL);
131 assert(strlen(logger_id) == 3);
133 char buffer[100];
135 // Flight recorder ID number MUST go first..
136 sprintf(buffer, "AXCS%s", logger_id);
137 WriteLine(buffer);
139 sprintf(buffer, "HFDTE%02u%02u%02u",
140 date_time.day, date_time.month, date_time.year % 100);
141 WriteLine(buffer);
143 if (!simulator)
144 WriteLine(GetHFFXARecord());
146 WriteLine("HFPLTPILOT:", pilot_name);
147 WriteLine("HFGTYGLIDERTYPE:", aircraft_model);
148 WriteLine("HFGIDGLIDERID:", aircraft_registration);
149 WriteLine("HFCIDCOMPETITIONID:", competition_id);
150 WriteLine("HFFTYFRTYPE:XCSOAR,XCSOAR ", XCSoar_VersionStringOld);
151 WriteLine("HFGPS:", driver_name);
153 WriteLine("HFDTM100DATUM:WGS-84");
155 WriteLine(GetIRecord());
158 void
159 IGCWriter::StartDeclaration(const BrokenDateTime &date_time,
160 const int number_of_turnpoints)
162 assert(date_time.Plausible());
164 // IGC GNSS specification 3.6.1
165 char buffer[64];
166 sprintf(buffer, "C%02u%02u%02u%02u%02u%02u0000000000%02d",
167 // DD MM YY HH MM SS DD MM YY IIII TT
168 date_time.day,
169 date_time.month,
170 date_time.year % 100,
171 date_time.hour,
172 date_time.minute,
173 date_time.second,
174 number_of_turnpoints - 2);
176 WriteLine(buffer);
178 // takeoff line
179 // IGC GNSS specification 3.6.3
180 WriteLine("C0000000N00000000ETAKEOFF");
183 void
184 IGCWriter::EndDeclaration()
186 // TODO bug: this is causing problems with some analysis software
187 // maybe it's because the date and location fields are bogus
188 WriteLine("C0000000N00000000ELANDING");
191 void
192 IGCWriter::AddDeclaration(const GeoPoint &location, const TCHAR *id)
194 char c_record[64];
196 char *p = c_record;
197 *p++ = 'C';
198 p = FormatIGCLocation(p, location);
199 CopyASCIIUppper(p, id);
201 WriteLine(c_record);
204 void
205 IGCWriter::LoggerNote(const TCHAR *text)
207 WriteLine("LPLT", text);
211 * Applies range checks to the specified altitude value and converts
212 * it to an integer suitable for printing in the IGC file.
214 static int
215 NormalizeIGCAltitude(int value)
217 if (value < -9999)
218 /* for negative values, there are only 4 characters left (after
219 the minus sign), and besides that, IGC does not support a
220 journey towards the center of the earth */
221 return -9999;
223 if (value >= 99999)
224 /* hooray, new world record! .. or just some invalid value; we
225 have only 5 characters for the altitude, so we must clip it at
226 99999 */
227 return 99999;
229 return value;
232 void
233 IGCWriter::LogPoint(const IGCFix &fix, int epe, int satellites)
235 char b_record[128];
236 char *p = b_record;
238 sprintf(p, "B%02d%02d%02d", fix.time.hour, fix.time.minute, fix.time.second);
239 p += strlen(p);
241 p = FormatIGCLocation(p, fix.location);
243 sprintf(p, "%c%05d%05d%03d%02d",
244 fix.gps_valid ? 'A' : 'V',
245 NormalizeIGCAltitude(fix.pressure_altitude),
246 NormalizeIGCAltitude(fix.gps_altitude),
247 epe, satellites);
249 WriteLine(b_record);
250 Flush();
253 void
254 IGCWriter::LogPoint(const NMEAInfo& gps_info)
256 if (fix.Apply(gps_info))
257 LogPoint(fix, (int)GetEPE(gps_info.gps), GetSIU(gps_info.gps));
260 void
261 IGCWriter::LogEvent(const BrokenTime &time, const char *event)
263 char e_record[30];
264 sprintf(e_record, "E%02d%02d%02d%s",
265 time.hour, time.minute, time.second, event);
267 WriteLine(e_record);
270 void
271 IGCWriter::LogEvent(const IGCFix &fix, int epe, int satellites,
272 const char *event)
274 LogEvent(fix.time, event);
276 // tech_spec_gnss.pdf says we need a B record immediately after an E record
277 LogPoint(fix, epe, satellites);
280 void
281 IGCWriter::LogEvent(const NMEAInfo &gps_info, const char *event)
283 LogEvent(gps_info.date_time_utc, event);
285 // tech_spec_gnss.pdf says we need a B record immediately after an E record
286 LogPoint(gps_info);
289 void
290 IGCWriter::LogEmptyFRecord(const BrokenTime &time)
292 char f_record[32];
293 sprintf(f_record, "F%02u%02u%02u", time.hour, time.minute, time.second);
294 WriteLine(f_record);
297 void
298 IGCWriter::LogFRecord(const BrokenTime &time, const int *satellite_ids)
300 char f_record[32];
301 sprintf(f_record, "F%02u%02u%02u", time.hour, time.minute, time.second);
303 for (unsigned i = 0, length = 7; i < GPSState::MAXSATELLITES; ++i) {
304 if (satellite_ids[i] > 0) {
305 sprintf(f_record + length, "%02d", satellite_ids[i]);
306 length += 2;
310 WriteLine(f_record);
313 void
314 IGCWriter::Sign()
316 assert(file.IsOpen());
318 grecord.FinalizeBuffer();
319 grecord.WriteTo(file);