]> git.mxchange.org Git - flightgear.git/blob - src/Navaids/procedure.cxx
Create a real FlightPlan (and Leg) class
[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   WptTransitionMap::iterator it;
97   bool haveTrans = false;
98   for (it = _transitions.begin(); it != _transitions.end(); ++it) {
99     Transition* t= it->second;
100     if (t->enroute()->matches(aIAF)) {
101       t->route(aWps);
102       haveTrans = true;
103       break;
104     }
105   } // of transitions iteration
106   
107   if (!haveTrans) {
108     SG_LOG(SG_GENERAL, SG_INFO, "approach " << ident() << " has no transition " <<
109       "for IAF: " << aIAF->ident());
110     return false;
111   }
112   
113   return routeFromVectors(aWps);
114 }
115
116 bool Approach::routeFromVectors(WayptVec& aWps)
117 {
118   aWps.insert(aWps.end(), _primary.begin(), _primary.end());
119   aWps.push_back(new RunwayWaypt(_runway, NULL));
120   aWps.insert(aWps.end(), _missed.begin(), _missed.end());
121   return true;
122 }
123
124 bool Approach::isApproach(ProcedureType ty)
125 {
126   return (ty >= PROCEDURE_APPROACH_ILS) && (ty <= PROCEDURE_APPROACH_RNAV);
127 }
128   
129 //////////////////////////////////////////////////////////////////////////////
130
131 ArrivalDeparture::ArrivalDeparture(const string& aIdent, FGAirport* apt) :
132   Procedure(aIdent),
133   _airport(apt)
134 {
135 }
136
137 void ArrivalDeparture::addRunway(FGRunwayRef aWay)
138 {
139   assert(aWay->airport() == _airport);
140   _runways[aWay] = NULL;
141 }
142
143 bool ArrivalDeparture::isForRunway(const FGRunway* aWay) const
144 {
145   // null runway always passes
146   if (!aWay) {
147     return true;
148   }
149   
150   FGRunwayRef r(const_cast<FGRunway*>(aWay));
151   return (_runways.count(r));
152 }
153
154 RunwayVec ArrivalDeparture::runways() const
155 {
156   RunwayVec r;
157   RunwayTransitionMap::const_iterator it = _runways.begin();
158   for (; it != _runways.end(); ++it) {
159     r.push_back(it->first);
160   }
161   
162   return r;
163 }
164     
165 void ArrivalDeparture::addTransition(Transition* aTrans)
166 {
167   WayptRef entry = aTrans->enroute();
168   aTrans->mark(flagType());
169   _enrouteTransitions[entry] = aTrans;
170 }
171
172 string_list ArrivalDeparture::transitionIdents() const
173 {
174   string_list r;
175   WptTransitionMap::const_iterator eit;
176   for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
177     r.push_back(eit->second->ident());
178   }
179   return r;
180 }
181   
182 void ArrivalDeparture::addRunwayTransition(FGRunwayRef aWay, Transition* aTrans)
183 {
184   assert(aWay->ident() == aTrans->ident());
185   if (!isForRunway(aWay)) {
186     throw sg_io_exception("adding transition for unspecified runway:" + aWay->ident(), ident());
187   }
188   
189   aTrans->mark(flagType());
190   _runways[aWay] = aTrans;
191 }
192
193 void ArrivalDeparture::setCommon(const WayptVec& aWps)
194 {
195   _common = aWps;
196   markWaypoints(_common, flagType());
197 }
198
199 bool ArrivalDeparture::commonRoute(Transition* t, WayptVec& aPath, FGRunwayRef aRwy)
200 {
201   // assume we're routing from enroute, to the runway.
202   // for departures, we'll flip the result points
203
204   WayptVec::iterator firstCommon = _common.begin();
205   if (t) {
206     t->route(aPath);
207
208     Waypt* transEnd = t->procedureEnd();
209     for (; firstCommon != _common.end(); ++firstCommon) {
210       if ((*firstCommon)->matches(transEnd)) {
211         // found transition->common point, stop search
212         break;
213       }
214     } // of common points
215     
216     // if we hit this point, the transition doesn't end (start, for a SID) on
217     // a common point. We assume this means we should just append the entire
218     // common section after the transition.
219     firstCommon = _common.begin();
220   } else {
221     // no tranasition
222   } // of not using a transition
223   
224   // append (some) common points
225   aPath.insert(aPath.end(), firstCommon, _common.end());
226   
227   if (!aRwy) {
228     // no runway specified, we're done
229     return true;
230   }
231   
232   RunwayTransitionMap::iterator r = _runways.find(aRwy);
233   assert(r != _runways.end());
234   if (!r->second) {
235     // no transitions specified. Not great, but not
236     // much we can do about it. Calling code will insert VECTORS to the approach
237     // if required, or maybe there's an approach transition defined.
238     return true;
239   }
240   
241   SG_LOG(SG_GENERAL, SG_INFO, ident() << " using runway transition for " << r->first->ident());
242   r->second->route(aPath);
243   return true;
244 }
245
246 Transition* ArrivalDeparture::findTransitionByEnroute(Waypt* aEnroute) const
247 {
248   if (!aEnroute) {
249     return NULL;
250   }
251   
252   WptTransitionMap::const_iterator eit;
253   for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
254     if (eit->second->enroute()->matches(aEnroute)) {
255       SG_LOG(SG_GENERAL, SG_INFO, ident() << " using enroute transition " << eit->second->ident());
256       return eit->second;
257     }
258   } // of enroute transition iteration
259   
260   return NULL;
261 }
262
263 WayptRef ArrivalDeparture::findBestTransition(const SGGeod& aPos) const
264 {
265   // no transitions, that's easy
266   if (_enrouteTransitions.empty()) {
267     SG_LOG(SG_GENERAL, SG_INFO, "no enroute transitions for " << ident());
268     return _common.front();
269   }
270   
271   double d = 1e9;
272   WayptRef w;
273   WptTransitionMap::const_iterator eit;
274   for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
275     WayptRef c = eit->second->enroute();
276     SG_LOG(SG_GENERAL, SG_INFO, "findBestTransition for " << ident() << ", looking at " << c->ident());
277     // assert(c->hasFixedPosition());
278     double cd = SGGeodesy::distanceM(aPos, c->position());
279     
280     if (cd < d) { // distance to 'c' is less, new best match
281       d = cd;
282       w = c;
283     }
284   } // of transitions iteration
285   
286   assert(w);
287   return w;
288 }
289
290 Transition* ArrivalDeparture::findTransitionByName(const string& aIdent) const
291 {
292   WptTransitionMap::const_iterator eit;
293   for (eit = _enrouteTransitions.begin(); eit != _enrouteTransitions.end(); ++eit) {
294     if (eit->second->ident() == aIdent) {
295       return eit->second;
296     }
297   }
298   
299   return NULL;
300 }
301
302 ////////////////////////////////////////////////////////////////////////////
303
304 SID::SID(const string& aIdent, FGAirport* apt) :
305     ArrivalDeparture(aIdent, apt)
306 {
307 }
308
309 bool SID::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath)
310 {
311   if (!isForRunway(aWay)) {
312     SG_LOG(SG_GENERAL, SG_WARN, "SID " << ident() << " not for runway " << aWay->ident());
313     return false;
314   }
315   
316   WayptVec path;
317   if (!commonRoute(trans, path, aWay)) {
318     return false;
319   }
320   
321   // SID waypoints (including transitions) are stored reversed, so we can
322   // re-use the routing code. This is where we fix the ordering for client code
323   std::back_insert_iterator<WayptVec> bi(aPath);
324   std::reverse_copy(path.begin(), path.end(), bi);
325
326   return true;
327 }
328
329 ////////////////////////////////////////////////////////////////////////////
330
331 STAR::STAR(const string& aIdent, FGAirport* apt) :
332     ArrivalDeparture(aIdent, apt)
333 {
334 }
335
336 bool STAR::route(FGRunwayRef aWay, Transition* trans, WayptVec& aPath)
337 {
338   if (aWay && !isForRunway(aWay)) {
339     return false;
340   }
341     
342   return commonRoute(trans, aPath, aWay);
343 }
344
345 /////////////////////////////////////////////////////////////////////////////
346
347 Transition::Transition(const std::string& aIdent, ProcedureType ty, Procedure* aPr) :
348   Procedure(aIdent),
349   _type(ty),
350   _parent(aPr)
351 {
352   assert(aPr);
353 }
354   
355 void Transition::setPrimary(const WayptVec& aWps)
356 {
357   _primary = aWps;
358   assert(!_primary.empty());
359   _primary[0]->setFlag(WPT_TRANSITION, true);
360 }
361
362 WayptRef Transition::enroute() const
363 {
364   assert(!_primary.empty());
365   return _primary[0];
366 }
367
368 WayptRef Transition::procedureEnd() const
369 {
370   assert(!_primary.empty());
371   return _primary[_primary.size() - 1];
372 }
373
374 bool Transition::route(WayptVec& aPath)
375 {
376   aPath.insert(aPath.end(), _primary.begin(), _primary.end());
377   return true;
378 }
379
380 FGAirport* Transition::airport() const
381 {
382   return _parent->airport();
383 }
384   
385 void Transition::mark(WayptFlag f)
386 {
387   markWaypoints(_primary, f);
388 }
389   
390 } // of namespace