Android release v6.7_preview1
[xcsoar.git] / src / Waypoint / WaypointReaderWinPilot.cpp
bloba79a419a94068c093ed6b194f6b93d6486d7aa1a
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 "WaypointReaderWinPilot.hpp"
25 #include "Units/System.hpp"
26 #include "Waypoint/Waypoints.hpp"
27 #include "Util/NumberParser.hpp"
28 #include "Util/Macros.hpp"
30 static bool
31 ParseAngle(const TCHAR* src, Angle& dest, const bool lat)
33 // Two format variants:
34 // 51:47.841N (DD:MM.mmm // the usual)
35 // 51:23:45N (DD:MM:SS)
37 TCHAR *endptr;
39 long deg = _tcstol(src, &endptr, 10);
40 if (endptr == src || *endptr != _T(':') || deg < 0)
41 return false;
43 // Limit angle to +/- 90 degrees for Latitude or +/- 180 degrees for Longitude
44 deg = std::min(deg, lat ? 90L : 180L);
46 src = endptr + 1;
47 long min = _tcstol(src, &endptr, 10);
48 if (endptr == src || min < 0 || min >= 60)
49 return false;
51 src = endptr + 1;
52 fixed sec;
53 if (*endptr == ':') {
54 /* 00..59 */
55 long l = _tcstol(src, &endptr, 10);
56 if (endptr == src || l < 0 || l >= 60)
57 return false;
59 sec = fixed(l) / 3600;
60 } else if (*endptr == '.') {
61 /* 000..999 */
62 long l = _tcstol(src, &endptr, 10);
63 if (endptr == src + 1 && l >= 0 && l < 100)
64 sec = fixed(l) / (60 * 10);
65 else if (endptr == src + 2 && l >= 0 && l < 1000)
66 sec = fixed(l) / (60 * 100);
67 else if (endptr == src + 3 && l >= 0 && l < 10000)
68 sec = fixed(l) / (60 * 1000);
69 else
70 return false;
71 } else
72 return false;
74 fixed value = fixed(deg) + fixed(min) / 60 + sec;
76 TCHAR sign = *endptr;
77 if (sign == 'W' || sign == 'w' || sign == 'S' || sign == 's')
78 value = -value;
80 // Save angle
81 dest = Angle::Degrees(value);
82 return true;
85 static bool
86 ParseRunwayDirection(const TCHAR* src, Runway &dest)
88 // WELT2000 written files contain a 4-digit runway direction specification
89 // at the end of the comment, e.g. "123.50 0927"
91 TCHAR const *start = _tcsrchr(src, _T(' '));
92 if (start)
93 start++;
94 else
95 start = src;
97 TCHAR *end;
98 int value = _tcstol(start, &end, 10);
99 if (start == end || *end != _T('\0') || end - start != 4 )
100 return false;
102 int a1 = (value / 100) * 10;
103 int a2 = (value % 100) * 10;
104 if (a1 < 0 || a1 > 360 || a2 < 0 || a2 > 360)
105 return false;
106 if (a1 == 360)
107 a1 = 0;
108 if (a2 == 360)
109 a2 = 0;
111 // TODO: WELT2000 generates quite a few entries where
112 // a) a1 == a2 (accept those) and
113 // b) the angles are neither equal or reciprocal (discard those)
114 // Try finding a logic behind this behaviour.
115 if (a1 != a2 && (a1 + 180) % 360 != a2)
116 return false;
118 dest.SetDirectionDegrees(a1);
119 return true;
122 static bool
123 ParseAltitude(const TCHAR* src, fixed& dest)
125 // Parse string
126 TCHAR *endptr;
127 double value = _tcstod(src, &endptr);
128 if (endptr == src)
129 return false;
131 dest = fixed(value);
133 // Convert to system unit if necessary
134 TCHAR unit = *endptr;
135 if (unit == 'F' || unit == 'f')
136 dest = Units::ToSysUnit(dest, Unit::FEET);
138 // Save altitude
139 return true;
142 static bool
143 ParseFlags(const TCHAR* src, Waypoint &dest)
145 // A = Airport
146 // T = Turnpoint
147 // L = Landable
148 // H = Home
149 // S = Start point
150 // F = Finish point
151 // R = Restricted
152 // W = Waypoint flag
154 for (unsigned i = 0; src[i] != 0; i++) {
155 switch (src[i]) {
156 case 'A':
157 case 'a':
158 dest.type = Waypoint::Type::AIRFIELD;
159 break;
160 case 'T':
161 case 't':
162 dest.flags.turn_point = true;
163 break;
164 case 'L':
165 case 'l':
166 // Don't downgrade!
167 if (dest.type != Waypoint::Type::AIRFIELD)
168 dest.type = Waypoint::Type::OUTLANDING;
169 break;
170 case 'H':
171 case 'h':
172 dest.flags.home = true;
173 break;
174 case 'S':
175 case 's':
176 dest.flags.start_point = true;
177 break;
178 case 'F':
179 case 'f':
180 dest.flags.finish_point = true;
181 break;
185 return true;
188 bool
189 WaypointReaderWinPilot::ParseLine(const TCHAR* line, const unsigned linenum,
190 Waypoints &waypoints)
192 TCHAR ctemp[4096];
193 const TCHAR *params[20];
194 static constexpr unsigned int max_params = ARRAY_SIZE(params);
195 static bool welt2000_format = false;
196 size_t n_params;
198 if (linenum == 0)
199 welt2000_format = false;
201 // If (end-of-file)
202 if (line[0] == '\0')
203 // -> return without error condition
204 return true;
206 // If comment
207 if (line[0] == _T('*')) {
208 if (linenum == 0)
209 welt2000_format = (_tcsstr(line, _T("WRITTEN BY WELT2000")) != NULL);
210 // -> return without error condition
211 return true;
214 if (_tcslen(line) >= ARRAY_SIZE(ctemp))
215 /* line too long for buffer */
216 return false;
218 GeoPoint location;
220 // Get fields
221 n_params = ExtractParameters(line, ctemp, params, max_params, true);
222 if (n_params < 6)
223 return false;
225 // Latitude (e.g. 51:15.900N)
226 if (!ParseAngle(params[1], location.latitude, true))
227 return false;
229 // Longitude (e.g. 00715.900W)
230 if (!ParseAngle(params[2], location.longitude, false))
231 return false;
232 location.Normalize(); // ensure longitude is within -180:180
234 Waypoint new_waypoint(location);
235 new_waypoint.file_num = file_num;
236 new_waypoint.original_id = ParseUnsigned(params[0], NULL, 0);
238 // Name (e.g. KAMPLI)
239 if (*params[5] == _T('\0'))
240 return false;
241 new_waypoint.name=params[5];
243 // Altitude (e.g. 458M)
244 /// @todo configurable behaviour
245 if (!ParseAltitude(params[3], new_waypoint.elevation) &&
246 !CheckAltitude(new_waypoint))
247 return false;
249 if (n_params > 6) {
250 // Description (e.g. 119.750 Airport)
251 new_waypoint.comment=params[6];
252 if (welt2000_format)
253 ParseRunwayDirection(params[6], new_waypoint.runway);
256 // Waypoint Flags (e.g. AT)
257 ParseFlags(params[4], new_waypoint);
259 waypoints.Append(std::move(new_waypoint));
260 return true;