]> git.mxchange.org Git - flightgear.git/blob - src/Navaids/procedure.cxx
Expose procedure routing and fixes to Nasal.
[flightgear.git] / src / Navaids / procedure.cxx
1 // procedure.cxx - define route storing an approach, arrival or departure procedure
2 // Written by James Turner, started 2009.
3 //
4 // Copyright (C) 2009  Curtis L. Olson
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License as
8 // published by the Free Software Foundation; either version 2 of the
9 // License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
20 #include "procedure.hxx"
21
22 #include <cassert>
23 #include <algorithm> // for reverse_copy
24
25 #include <simgear/structure/exception.hxx>
26
27 #include <Navaids/waypoint.hxx>
28
29 using std::string;
30
31 namespace flightgear
32 {
33   
34 static void markWaypoints(WayptVec& wps, WayptFlag f)
35 {
36   for (unsigned int i=0; i<wps.size(); ++i) {
37     wps[i]->setFlag(f, true);
38   }
39 }
40
41 Procedure::Procedure(const string& aIdent) :
42   _ident(aIdent)
43 {
44 }
45
46 Approach::Approach(const string& aIdent, ProcedureType ty) : 
47   Procedure(aIdent),
48   _type(ty)
49 {
50
51 }
52
53 void Approach::setRunway(FGRunwayRef aRwy)
54 {
55   _runway = aRwy;
56 }
57
58 FGAirport* Approach::airport() const
59 {
60   return _runway->airport();
61 }
62
63 RunwayVec Approach::runways() const
64 {
65   RunwayVec r;
66   r.push_back(_runway);
67   return r;
68 }
69   
70 void Approach::setPrimaryAndMissed(const WayptVec& aPrimary, const WayptVec& aMissed)
71 {
72   _primary = aPrimary;
73   _primary[0]->setFlag(WPT_IAF, true);
74   _primary[_primary.size()-1]->setFlag(WPT_FAF, true);
75   markWaypoints(_primary, WPT_APPROACH);
76   
77   _missed = aMissed;
78   
79   if (!_missed.empty()) {
80     // mark the first point as the published missed-approach point
81     _missed[0]->setFlag(WPT_MAP, true);
82     markWaypoints(_missed, WPT_MISS);
83     markWaypoints(_missed, WPT_APPROACH);
84   }
85 }
86
87 void Approach::addTransition(Transition* aTrans)
88 {
89   WayptRef entry = aTrans->enroute();
90   _transitions[entry] = aTrans;
91   aTrans->mark(WPT_APPROACH);
92 }
93
94 bool Approach::route(WayptRef aIAF, WayptVec& aWps)
95 {
96   if (aIAF.valid()) {
97     WptTransitionMap::iterator it;
98     bool haveTrans = false;
99     for (it = _transitions.begin(); it != _transitions.end(); ++it) {
100       Transition* t= it->second;
101       if (t->enroute()->matches(aIAF)) {
102         t->route(aWps);
103         haveTrans = true;
104         break;
105       }
106     } // of transitions iteration
107     
108     if (!haveTrans) {
109       SG_LOG(SG_GENERAL, SG_INFO, "approach " << ident() << " has no transition " <<
110         "for IAF: " << aIAF->ident());
111       return false;
112     }
113   }
114   
115   return routeFromVectors(aWps);
116 }
117
118 bool Approach::routeFromVectors(WayptVec& aWps)
119 {
120   aWps.insert(aWps.end(), _primary.begin(), _primary.end());
121   aWps.push_back(new RunwayWaypt(_runway, NULL));
122   aWps.insert(aWps.end(), _missed.begin(), _missed.end());
123   return true;
124 }
125
126 bool Approach::isApproach(ProcedureType ty)
127 {
128   return (ty >= PROCEDURE_APPROACH_ILS) && (ty <= PROCEDURE_APPROACH_RNAV);
129 }
130   
131 //////////////////////////////////////////////////////////////////////////////
132
133 ArrivalDeparture::ArrivalDeparture(const string& aIdent, FGAirport* apt) :
134   Procedure(aIdent),
135   _airport(apt)
136 {
137 }
138
139 void ArrivalDeparture::addRunway(FGRunwayRef aWay)
140 {
141   assert(aWay->airport() == _airport);
142   _runways[aWay] = NULL;
143 }
144
145 bool ArrivalDeparture::isForRunway(const FGRunway* aWay) const
146 {
147   // null runway always passes
148   if (!aWay) {
149     return true;
150   }
151   
152   FGRunwayRef r(const_cast<FGRunway*>(aWay));
153   return (_runways.count(r));
154 }
155
156 RunwayVec ArrivalDeparture::runways() const
157 {
158   RunwayVec r;
159   RunwayTransitionMap::const_iterator it = _runways.begin();
160   for (; it != _runways.end(); ++it) {
161     r.push_back(it->first);
162   }
163   
164   return r;
165 }
166     
167 void ArrivalDeparture::addTransition(Transition* aTrans)
168 {
169   WayptRef entry = aTrans->enroute();
170   aTrans->mark(flagType());
171   _enrouteTransitions[entry] = aTrans;
172 }
173
174 string_list ArrivalDeparture::transitionIdents() const
175 {
176   string_list r;
177   WptTransitionMap::const_iterator eit;
178   for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
179     r.push_back(eit->second->ident());
180   }
181   return r;
182 }
183   
184 void ArrivalDeparture::addRunwayTransition(FGRunwayRef aWay, Transition* aTrans)
185 {
186   assert(aWay->ident() == aTrans->ident());
187   if (!isForRunway(aWay)) {
188     throw sg_io_exception("adding transition for unspecified runway:" + aWay->ident(), ident());
189   }
190   
191   aTrans->mark(flagType());
192   _runways[aWay] = aTrans;
193 }
194
195 void ArrivalDeparture::setCommon(const WayptVec& aWps)
196 {
197   _common = aWps;
198   markWaypoints(_common, flagType());
199 }
200
201 bool ArrivalDeparture::commonRoute(Transition* t, WayptVec& aPath, FGRunwayRef aRwy)
202 {
203   // assume we're routing from enroute, to the runway.
204   // for departures, we'll flip the result points
205
206   WayptVec::iterator firstCommon = _common.begin();
207   if (t) {
208     t->route(aPath);
209
210     Waypt* transEnd = t->procedureEnd();
211     for (; firstCommon != _common.end(); ++firstCommon) {
212       if ((*firstCommon)->matches(transEnd)) {
213         // found transition->common point, stop search
214         break;
215       }
216     } // of common points
217     
218     // if we hit this point, the transition doesn't end (start, for a SID) on
219     // a common point. We assume this means we should just append the entire
220     // common section after the transition.
221     firstCommon = _common.begin();
222   } else {
223     // no tranasition
224   } // of not using a transition
225   
226   // append (some) common points
227   aPath.insert(aPath.end(), firstCommon, _common.end());
228   
229   if (!aRwy) {
230     // no runway specified, we're done
231     return true;
232   }
233   
234   RunwayTransitionMap::iterator r = _runways.find(aRwy);
235   assert(r != _runways.end());
236   if (!r->second) {
237     // no transitions specified. Not great, but not
238     // much we can do about it. Calling code will insert VECTORS to the approach
239     // if required, or maybe there's an approach transition defined.
240     return true;
241   }
242   
243   SG_LOG(SG_GENERAL, SG_INFO, ident() << " using runway transition for " << r->first->ident());
244   r->second->route(aPath);
245   return true;
246 }
247
248 Transition* ArrivalDeparture::findTransitionByEnroute(Waypt* aEnroute) const
249 {
250   if (!aEnroute) {
251     return NULL;
252   }
253   
254   WptTransitionMap::const_iterator eit;
255   for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
256     if (eit->second->enroute()->matches(aEnroute)) {
257       SG_LOG(SG_GENERAL, SG_INFO, ident() << " using enroute transition " << eit->second->ident());
258       return eit->second;
259     }
260   } // of enroute transition iteration
261   
262   return NULL;
263 }
264
265 WayptRef ArrivalDeparture::findBestTransition(const SGGeod& aPos) const
266 {
267   // no transitions, that's easy
268   if (_enrouteTransitions.empty()) {
269     SG_LOG(SG_GENERAL, SG_INFO, "no enroute transitions for " << ident());
270     return _common.front();
271   }
272   
273   double d = 1e9;
274   WayptRef w;
275   WptTransitionMap::const_iterator eit;
276   for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
277     WayptRef c = eit->second->enroute();
278     SG_LOG(SG_GENERAL, SG_INFO, "findBestTransition for " << ident() << ", looking at " << c->ident());
279     // assert(c->hasFixedPosition());
280     double cd = SGGeodesy::distanceM(aPos, c->position());
281     
282     if (cd < d) { // distance to 'c' is less, new best match
283       d = cd;
284       w = c;
285     }
286   } // of transitions iteration
287   
288   assert(w);
289   return w;
290 }
291
292 Transition* ArrivalDeparture::findTransitionByName(const string& aIdent) const
293 {
294   WptTransitionMap::const_iterator eit;
295   for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
296     if (eit->second->ident() == aIdent) {
297       return eit->second;
298     }
299   }
300   
301   return NULL;
302 }
303
304 ////////////////////////////////////////////////////////////////////////////
305
306 SID::SID(const string& aIdent, FGAirport* apt) :
307     ArrivalDeparture(aIdent, apt)
308 {
309 }
310
311 bool SID::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath)
312 {
313   if (!isForRunway(aWay)) {
314     SG_LOG(SG_GENERAL, SG_WARN, "SID " << ident() << " not for runway " << aWay->ident());
315     return false;
316   }
317   
318   WayptVec path;
319   if (!commonRoute(trans, path, aWay)) {
320     return false;
321   }
322   
323   // SID waypoints (including transitions) are stored reversed, so we can
324   // re-use the routing code. This is where we fix the ordering for client code
325   std::back_insert_iterator<WayptVec> bi(aPath);
326   std::reverse_copy(path.begin(), path.end(), bi);
327
328   return true;
329 }
330
331 ////////////////////////////////////////////////////////////////////////////
332
333 STAR::STAR(const string& aIdent, FGAirport* apt) :
334     ArrivalDeparture(aIdent, apt)
335 {
336 }
337
338 bool STAR::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath)
339 {
340   if (aWay && !isForRunway(aWay)) {
341     return false;
342   }
343     
344   return commonRoute(trans, aPath, aWay);
345 }
346
347 /////////////////////////////////////////////////////////////////////////////
348
349 Transition::Transition(const std::string& aIdent, ProcedureType ty, Procedure* aPr) :
350   Procedure(aIdent),
351   _type(ty),
352   _parent(aPr)
353 {
354   assert(aPr);
355 }
356   
357 void Transition::setPrimary(const WayptVec& aWps)
358 {
359   _primary = aWps;
360   assert(!_primary.empty());
361   _primary[0]->setFlag(WPT_TRANSITION, true);
362 }
363
364 WayptRef Transition::enroute() const
365 {
366   assert(!_primary.empty());
367   return _primary[0];
368 }
369
370 WayptRef Transition::procedureEnd() const
371 {
372   assert(!_primary.empty());
373   return _primary[_primary.size() - 1];
374 }
375
376 bool Transition::route(WayptVec& aPath)
377 {
378   aPath.insert(aPath.end(), _primary.begin(), _primary.end());
379   return true;
380 }
381
382 FGAirport* Transition::airport() const
383 {
384   return _parent->airport();
385 }
386   
387 void Transition::mark(WayptFlag f)
388 {
389   markWaypoints(_primary, f);
390 }
391   
392 } // of namespace