CirclingWind: eliminate attributes min_vector, max_vector
[xcsoar.git] / src / Task / TaskFileSeeYou.cpp
blob2a6b632097d449af6cbc0fef6474e3b45bd3cb50
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 "Task/TaskFileSeeYou.hpp"
25 #include "Util/Macros.hpp"
26 #include "IO/FileLineReader.hpp"
27 #include "Engine/Waypoint/Waypoints.hpp"
28 #include "Waypoint/WaypointReaderSeeYou.hpp"
29 #include "Task/ObservationZones/LineSectorZone.hpp"
30 #include "Task/ObservationZones/AnnularSectorZone.hpp"
31 #include "Task/ObservationZones/KeyholeZone.hpp"
32 #include "Engine/Task/Ordered/OrderedTask.hpp"
33 #include "Engine/Task/Ordered/Points/StartPoint.hpp"
34 #include "Engine/Task/Ordered/Points/FinishPoint.hpp"
35 #include "Engine/Task/Ordered/Points/AATPoint.hpp"
36 #include "Engine/Task/Ordered/Points/ASTPoint.hpp"
37 #include "Engine/Task/Factory/AbstractTaskFactory.hpp"
38 #include "Operation/Operation.hpp"
39 #include "Units/System.hpp"
41 struct SeeYouTaskInformation {
42 /** True = RT, False = AAT */
43 bool wp_dis;
44 /** AAT task time in seconds */
45 fixed task_time;
46 /** MaxAltStart in meters */
47 fixed max_start_altitude;
49 SeeYouTaskInformation():
50 wp_dis(true), task_time(fixed(0)), max_start_altitude(fixed(0)) {}
53 struct SeeYouTurnpointInformation {
54 /** CUP file contained info for this OZ */
55 bool valid;
57 enum Style {
58 FIXED,
59 SYMMETRICAL,
60 TO_NEXT_POINT,
61 TO_PREVIOUS_POINT,
62 TO_START_POINT,
63 } style;
65 bool is_line;
66 bool reduce;
68 fixed radius1, radius2, max_altitude;
69 Angle angle1, angle2, angle12;
71 SeeYouTurnpointInformation():
72 valid(false), style(SYMMETRICAL), is_line(false), reduce(false),
73 radius1(fixed(500)), radius2(fixed(500)),
74 max_altitude(fixed(0)),
75 angle1(Angle::Zero()),
76 angle2(Angle::Zero()),
77 angle12(Angle::Zero()) {}
80 static fixed
81 ParseTaskTime(const TCHAR* str)
83 int hh = 0, mm = 0, ss = 0;
84 TCHAR* end;
85 hh = _tcstol(str, &end, 10);
86 if (str != end && _tcslen(str) > 3 && str[2] == _T(':')) {
87 mm = _tcstol(str + 3, &end, 10);
88 if (str != end && _tcslen(str + 3) > 3 && str[5] == _T(':'))
89 ss = _tcstol(str + 6, NULL, 10);
91 return fixed(ss + mm * 60 + hh * 3600);
94 static SeeYouTurnpointInformation::Style
95 ParseStyle(const TCHAR* str)
97 int style = 1;
98 TCHAR* end;
99 style = _tcstol(str, &end, 10);
100 if (str == end)
101 style = 1;
103 return (SeeYouTurnpointInformation::Style)style;
106 static Angle
107 ParseAngle(const TCHAR* str)
109 int angle = 0;
110 TCHAR* end;
111 angle = _tcstol(str, &end, 10);
112 if (str == end)
113 angle = 0;
115 return Angle::Degrees(angle);
118 static fixed
119 ParseRadius(const TCHAR* str)
121 int radius = 500;
122 TCHAR* end;
123 radius = _tcstol(str, &end, 10);
124 if (str == end)
125 radius = 500;
127 return fixed(radius);
130 static fixed
131 ParseMaxAlt(const TCHAR* str)
133 fixed maxalt = fixed(0);
134 TCHAR* end;
135 maxalt = fixed(_tcstod(str, &end));
136 if (str == end)
137 return fixed(0);
139 if (_tcslen(end) >= 2 && end[0] == _T('f') && end[1] == _T('t'))
140 maxalt = Units::ToSysUnit(maxalt, Unit::FEET);
142 return maxalt;
146 * Parses the Options parameters line from See You task file for one task
147 * @param task_info Updated with Options
148 * @param params Input array of parameters preparsed from See You task file
149 * @param n_params number parameters in the line
151 static void
152 ParseOptions(SeeYouTaskInformation *task_info, const TCHAR *params[],
153 const size_t n_params)
155 // Iterate through available task options
156 for (unsigned i = 1; i < n_params; i++) {
157 if (_tcsncmp(params[i], _T("WpDis"), 5) == 0) {
158 // Parse WpDis option
159 if (_tcslen(params[i]) > 6 &&
160 _tcsncmp(params[i] + 6, _T("False"), 5) == 0)
161 task_info->wp_dis = false;
162 } else if (_tcsncmp(params[i], _T("TaskTime"), 8) == 0) {
163 // Parse TaskTime option
164 if (_tcslen(params[i]) > 9)
165 task_info->task_time = ParseTaskTime(params[i] + 9);
171 * Parses one ObsZone line from the See You task file
172 * @param turnpoint_infos Updated with the OZ info
173 * @param params Input array of parameters preparsed from See You task file
174 * @param n_params Number parameters in the line
175 * @return OZ index from CU (0 to n-1) or -1 if no OZ found
177 static int
178 ParseOZs(SeeYouTurnpointInformation turnpoint_infos[], const TCHAR *params[],
179 unsigned n_params)
181 // Read OZ index
182 TCHAR* end;
183 const int oz_index = _tcstol(params[0] + 8, &end, 10);
184 if (params[0] + 8 == end || oz_index >= 30)
185 return -1;
187 turnpoint_infos[oz_index].valid = true;
188 // Iterate through available OZ options
189 for (unsigned i = 1; i < n_params; i++) {
190 const TCHAR *pair = params[i];
191 SeeYouTurnpointInformation &tp_info = turnpoint_infos[oz_index];
193 if (_tcsncmp(pair, _T("style"), 5) == 0) {
194 if (_tcslen(pair) > 6)
195 tp_info.style = ParseStyle(pair + 6);
196 } else if (_tcsncmp(pair, _T("R1="), 3) == 0) {
197 if (_tcslen(pair) > 3)
198 tp_info.radius1 = ParseRadius(pair + 3);
199 } else if (_tcsncmp(pair, _T("A1="), 3) == 0) {
200 if (_tcslen(pair) > 3)
201 tp_info.angle1 = ParseAngle(pair + 3);
202 } else if (_tcsncmp(pair, _T("R2="), 3) == 0) {
203 if (_tcslen(pair) > 3)
204 tp_info.radius2 = ParseRadius(pair + 3);
205 } else if (_tcsncmp(pair, _T("A2="), 3) == 0) {
206 if (_tcslen(pair) > 3)
207 tp_info.angle2 = ParseAngle(pair + 3);
208 } else if (_tcsncmp(pair, _T("A12="), 4) == 0) {
209 if (_tcslen(pair) > 3)
210 tp_info.angle12 = ParseAngle(pair + 4);
211 } else if (_tcsncmp(pair, _T("max_altitude="), 7) == 0) {
212 if (_tcslen(pair) > 7)
213 tp_info.max_altitude = ParseMaxAlt(pair + 7);
214 } else if (_tcsncmp(pair, _T("is_line"), 4) == 0) {
215 if (_tcslen(pair) > 5 && pair[5] == _T('1'))
216 tp_info.is_line = true;
217 } else if (_tcsncmp(pair, _T("reduce"), 6) == 0) {
218 if (_tcslen(pair) > 7 && pair[7] == _T('1'))
219 tp_info.reduce = true;
222 return oz_index;
226 * Parses Options and OZs from See You task file
227 * @param reader. Points to first line of task after task "Waypoint list" line
228 * @param task_info Loads this with CU task options info
229 * @param turnpoint_infos Loads this with CU task tp info
231 static void
232 ParseCUTaskDetails(FileLineReader &reader, SeeYouTaskInformation *task_info,
233 SeeYouTurnpointInformation turnpoint_infos[])
235 // Read options/observation zones
236 TCHAR params_buffer[1024];
237 const TCHAR *params[20];
238 TCHAR *line;
239 int TPIndex = 0;
240 const unsigned int max_params = ARRAY_SIZE(params);
241 while ((line = reader.ReadLine()) != NULL &&
242 line[0] != _T('\"') && line[0] != _T(',')) {
243 const size_t n_params = WaypointReaderBase::
244 ExtractParameters(line, params_buffer, params, max_params, true);
246 if (_tcscmp(params[0], _T("Options")) == 0) {
247 // Options line found
248 ParseOptions(task_info, params, n_params);
250 } else if (_tcsncmp(params[0], _T("ObsZone"), 7) == 0) {
251 // Observation zone line found
252 if (_tcslen(params[0]) <= 8)
253 continue;
255 TPIndex = ParseOZs(turnpoint_infos, params, n_params);
256 if (TPIndex == 0)
257 task_info->max_start_altitude = turnpoint_infos[TPIndex].max_altitude;
259 } // end while
262 static bool isKeyhole(const SeeYouTurnpointInformation &turnpoint_infos)
264 return (fabs(turnpoint_infos.angle1.Degrees() - fixed(45)) < fixed(2) &&
265 fabs(turnpoint_infos.radius1 - fixed(10000)) < fixed(2) &&
266 fabs(turnpoint_infos.angle2.Degrees() - fixed(180)) < fixed(2) &&
267 fabs(turnpoint_infos.radius2 - fixed(500)) < fixed(2));
270 static bool isBGAFixedCourseZone(const SeeYouTurnpointInformation &turnpoint_infos)
272 return (fabs(turnpoint_infos.angle1.Degrees() - fixed(45)) < fixed(2) &&
273 fabs(turnpoint_infos.radius1 - fixed(20000)) < fixed(2) &&
274 fabs(turnpoint_infos.angle2.Degrees() - fixed(180)) < fixed(2) &&
275 fabs(turnpoint_infos.radius2 - fixed(500)) < fixed(2));
278 static bool isBGAEnhancedOptionZone(const SeeYouTurnpointInformation
279 &turnpoint_infos)
281 return (fabs(turnpoint_infos.angle1.Degrees() - fixed(90)) < fixed(2) &&
282 fabs(turnpoint_infos.radius1 - fixed(10000)) < fixed(2) &&
283 fabs(turnpoint_infos.angle2.Degrees() - fixed(180)) < fixed(2) &&
284 fabs(turnpoint_infos.radius2 - fixed(500)) < fixed(2));
288 * Creates the correct XCSoar OZ type from the See You OZ options for the point
289 * Note: there are several rules enforced here related to the combinations
290 * and types of Zones supported by XCSoar. When XCSoar adds more zone types,
291 * the logic below will need to be updated.
292 * @param turnpoint_infos Contains the See You turnpoint and OZ info
293 * @param current point position
294 * @param number wps in task
295 * @param array of wps for each point in task
296 * @param factType The XCSoar factory type
297 * @return the XCSoar OZ
299 static ObservationZonePoint*
300 CreateOZ(const SeeYouTurnpointInformation &turnpoint_infos,
301 unsigned pos, unsigned size, const Waypoint *wps[],
302 TaskFactoryType factType)
304 ObservationZonePoint* oz = NULL;
305 const bool is_intermediate = (pos > 0) && (pos < (size - 1));
306 const Waypoint *wp = wps[pos];
308 if (!turnpoint_infos.valid)
309 return NULL;
311 if (factType == TaskFactoryType::RACING &&
312 is_intermediate && isKeyhole(turnpoint_infos))
313 oz = KeyholeZone::CreateDAeCKeyholeZone(wp->location);
315 else if (factType == TaskFactoryType::RACING &&
316 is_intermediate && isBGAEnhancedOptionZone(turnpoint_infos))
317 oz = KeyholeZone::CreateBGAEnhancedOptionZone(wp->location);
319 else if (factType == TaskFactoryType::RACING &&
320 is_intermediate && isBGAFixedCourseZone(turnpoint_infos))
321 oz = KeyholeZone::CreateBGAFixedCourseZone(wp->location);
323 else if (!is_intermediate && turnpoint_infos.is_line) // special case "is_line"
324 oz = new LineSectorZone(wp->location, turnpoint_infos.radius1);
326 // special case "Cylinder"
327 else if (fabs(turnpoint_infos.angle1.Degrees() - fixed(180)) < fixed(1) )
328 oz = new CylinderZone(wp->location, turnpoint_infos.radius1);
330 else if (factType == TaskFactoryType::RACING) {
332 // XCSoar does not support fixed sectors for RT
333 if (turnpoint_infos.style == SeeYouTurnpointInformation::FIXED)
334 oz = new CylinderZone(wp->location, turnpoint_infos.radius1);
335 else
336 oz = SymmetricSectorZone::CreateFAISectorZone(wp->location,
337 is_intermediate);
339 } else if (is_intermediate) { //AAT intermediate point
340 Angle A12adj;
341 assert(wps[pos + 1]);
342 assert(wps[pos - 1]);
344 switch (turnpoint_infos.style) {
345 case SeeYouTurnpointInformation::FIXED: {
346 A12adj = turnpoint_infos.angle12.Reciprocal();
347 break;
349 case SeeYouTurnpointInformation::SYMMETRICAL: {
350 const Angle ap = wps[pos - 1]->location.Bearing(wp->location);
351 const Angle an = wps[pos + 1]->location.Bearing(wp->location);
352 A12adj = ap.HalfAngle(an).Reciprocal();
353 break;
356 case SeeYouTurnpointInformation::TO_NEXT_POINT: {
357 A12adj = wps[pos + 1]->location.Bearing(wp->location);
358 break;
360 case SeeYouTurnpointInformation::TO_PREVIOUS_POINT: {
361 A12adj = wps[pos - 1]->location.Bearing(wp->location);
362 break;
364 case SeeYouTurnpointInformation::TO_START_POINT: {
365 A12adj = wps[0]->location.Bearing(wp->location);
366 break;
370 const Angle RadialStart = (A12adj - turnpoint_infos.angle1).AsBearing();
371 const Angle RadialEnd = (A12adj + turnpoint_infos.angle1).AsBearing();
373 if (turnpoint_infos.radius2 > fixed(0) &&
374 (turnpoint_infos.angle2.AsBearing().Degrees()) < fixed(1)) {
375 oz = new AnnularSectorZone(wp->location, turnpoint_infos.radius1,
376 RadialStart, RadialEnd, turnpoint_infos.radius2);
377 } else {
378 oz = new SectorZone(wp->location, turnpoint_infos.radius1,
379 RadialStart, RadialEnd);
382 } else { // catch-all
383 oz = new CylinderZone(wp->location, turnpoint_infos.radius1);
386 return oz;
390 * Creates the XCSoar turnpoint from the See You parameters for the point
391 * @param pos The position of the point in the XCSoar task
392 * @param n_waypoints Number of points in the XCSoar task
393 * @param wp The waypoint
394 * @param fact The XCSoar factory
395 * @param oz The XCSoar OZ for the point
396 * @param factType The XCSoar factory type
397 * @return The point
399 static OrderedTaskPoint*
400 CreatePoint(unsigned pos, unsigned n_waypoints, const Waypoint *wp,
401 AbstractTaskFactory& fact, ObservationZonePoint* oz,
402 const TaskFactoryType factType)
404 OrderedTaskPoint *pt = NULL;
406 if (pos == 0)
407 pt = (oz ? fact.CreateStart(oz, *wp) : fact.CreateStart(*wp));
409 else if (pos == n_waypoints - 1)
410 pt = (oz ? fact.CreateFinish(oz, *wp) : fact.CreateFinish(*wp));
412 else if (factType == TaskFactoryType::RACING)
413 pt = (oz ? fact.CreateASTPoint(oz, *wp) : fact.CreateIntermediate(*wp));
415 else
416 pt = (oz ? fact.CreateAATPoint(oz, *wp) : fact.CreateIntermediate(*wp));
418 return pt;
421 static TCHAR *
422 AdvanceReaderToTask(FileLineReader &reader, const unsigned index)
424 // Skip lines until n-th task
425 unsigned count = 0;
426 bool in_task_section = false;
427 TCHAR *line;
428 for (unsigned i = 0; (line = reader.ReadLine()) != NULL; i++) {
429 if (in_task_section) {
430 if (line[0] == _T('\"') || line[0] == _T(',')) {
431 if (count == index)
432 break;
434 count++;
436 } else if (StringIsEqualIgnoreCase(line, _T("-----Related Tasks-----"))) {
437 in_task_section = true;
440 return line;
443 OrderedTask*
444 TaskFileSeeYou::GetTask(const TaskBehaviour &task_behaviour,
445 const Waypoints *waypoints, unsigned index) const
447 // Create FileReader for reading the task
448 FileLineReader reader(path, ConvertLineReader::AUTO);
449 if (reader.error())
450 return NULL;
452 // Read waypoints from the CUP file
453 Waypoints file_waypoints;
455 WaypointReaderSeeYou waypoint_file(0);
456 NullOperationEnvironment operation;
457 waypoint_file.Parse(file_waypoints, reader, operation);
459 file_waypoints.Optimise();
461 if (!reader.Rewind())
462 return NULL;
464 TCHAR *line = AdvanceReaderToTask(reader, index);
465 if (line == NULL)
466 return NULL;
468 // Read waypoint list
469 // e.g. "Club day 4 Racing task","085PRI","083BOJ","170D_K","065SKY","0844YY", "0844YY"
470 // TASK NAME , TAKEOFF, START , TP1 , TP2 , FINISH , LANDING
471 TCHAR waypoints_buffer[1024];
472 const TCHAR *wps[30];
473 size_t n_waypoints = WaypointReaderBase::
474 ExtractParameters(line, waypoints_buffer, wps, 30, true, _T('"')) - 3;
476 SeeYouTaskInformation task_info;
477 SeeYouTurnpointInformation turnpoint_infos[30];
478 const Waypoint *waypoints_in_task[30];
480 ParseCUTaskDetails(reader, &task_info, turnpoint_infos);
482 OrderedTask *task = new OrderedTask(task_behaviour);
483 task->SetFactory(task_info.wp_dis ?
484 TaskFactoryType::RACING : TaskFactoryType::AAT);
485 AbstractTaskFactory& fact = task->GetFactory();
486 const TaskFactoryType factType = task->GetFactoryType();
488 OrderedTaskBehaviour beh = task->GetOrderedTaskBehaviour();
489 if (factType == TaskFactoryType::AAT) {
490 beh.aat_min_time = task_info.task_time;
492 if (factType == TaskFactoryType::AAT ||
493 factType == TaskFactoryType::RACING) {
494 beh.start_constraints.max_height = (unsigned)task_info.max_start_altitude;
495 beh.start_constraints.max_height_ref = AltitudeReference::MSL;
497 task->SetOrderedTaskBehaviour(beh);
499 // mark task waypoints. Skip takeoff and landing point
500 for (unsigned i = 0; i < n_waypoints; i++) {
501 const Waypoint* file_wp = file_waypoints.LookupName(wps[i + 2]);
502 if (file_wp == NULL)
503 return NULL;
505 // Try to find waypoint by name
506 const Waypoint* wp = waypoints->LookupName(file_wp->name);
508 // If waypoint by name found and closer than 10m to the original
509 if (wp != NULL &&
510 wp->location.Distance(file_wp->location) <= fixed(10)) {
511 // Use this waypoint for the task
512 waypoints_in_task[i] = wp;
513 continue;
516 // Try finding the closest waypoint to the original one
517 wp = waypoints->GetNearest(file_wp->location, fixed(10));
519 // If closest waypoint found and closer than 10m to the original
520 if (wp != NULL &&
521 wp->location.Distance(file_wp->location) <= fixed(10)) {
522 // Use this waypoint for the task
523 waypoints_in_task[i] = wp;
524 continue;
527 // Use the original waypoint
528 waypoints_in_task[i] = file_wp;
531 //now create TPs and OZs
532 for (unsigned i = 0; i < n_waypoints; i++) {
534 ObservationZonePoint* oz = CreateOZ(turnpoint_infos[i], i, n_waypoints,
535 waypoints_in_task, factType);
536 assert(waypoints_in_task[i]);
537 OrderedTaskPoint *pt = CreatePoint(i, n_waypoints, waypoints_in_task[i],
538 fact, oz, factType);
540 if (pt != NULL)
541 fact.Append(*pt, false);
543 delete pt;
545 return task;
548 unsigned
549 TaskFileSeeYou::Count()
551 // Reset internal task name memory
552 namesuffixes.clear();
554 // Open the CUP file
555 FileLineReader reader(path, ConvertLineReader::AUTO);
556 if (reader.error())
557 return 0;
559 unsigned count = 0;
560 bool in_task_section = false;
561 TCHAR *line;
562 while ((line = reader.ReadLine()) != NULL) {
563 if (in_task_section) {
564 // If the line starts with a string or "nothing" followed
565 // by a comma it is a new task definition line
566 if (line[0] == _T('\"') || line[0] == _T(',')) {
567 // If we still have space in the task name list
568 if (count < namesuffixes.capacity()) {
569 // If the task doesn't have a name inside the file
570 if (line[0] == _T(','))
571 namesuffixes.append(NULL);
572 else {
573 // Ignore starting quote (")
574 line++;
576 // Save pointer to first character
577 TCHAR *name = line;
578 // Skip characters until next quote (") or end of string
579 while (line[0] != _T('\"') && line[0] != _T('\0'))
580 line++;
582 // Replace quote (") by end of string (null)
583 line[0] = _T('\0');
585 // Append task name to the list
586 if (_tcslen(name) > 0)
587 namesuffixes.append(_tcsdup(name));
588 else
589 namesuffixes.append(NULL);
593 // Increase the task counter
594 count++;
596 } else if (StringIsEqualIgnoreCase(line, _T("-----Related Tasks-----"))) {
597 // Found the marker -> all following lines are task lines
598 in_task_section = true;
602 // Return number of tasks found in the CUP file
603 return count;