FlightGear next
GroundController.cxx
Go to the documentation of this file.
1// GroundController.hxx - forked from groundnetwork.cxx
2
3// Written by Durk Talsma, started June 2005.
4//
5// Copyright (C) 2004 Durk Talsma.
6//
7// This program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public License as
9// published by the Free Software Foundation; either version 2 of the
10// License, or (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful, but
13// WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15// General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program; if not, write to the Free Software
19// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20//
21// $Id$
22
23#include <config.h>
24
25#include <cmath>
26#include <algorithm>
27#include <fstream>
28#include <map>
29#include <algorithm>
30
31#include <osg/Geode>
32#include <osg/Geometry>
33#include <osg/MatrixTransform>
34#include <osg/Shape>
35
36#include <simgear/debug/logstream.hxx>
37#include <simgear/scene/material/EffectGeode.hxx>
38#include <simgear/scene/material/matlib.hxx>
39#include <simgear/scene/material/mat.hxx>
40#include <simgear/scene/util/OsgMath.hxx>
41#include <simgear/structure/exception.hxx>
42#include <simgear/timing/timestamp.hxx>
43#include <simgear/timing/sg_time.hxx>
44
45#include <Airports/airport.hxx>
46#include <Airports/dynamics.hxx>
47#include <Airports/runways.hxx>
49
50#include <Main/globals.hxx>
51#include <Main/fg_props.hxx>
56
57#include <ATC/atc_mgr.hxx>
58
59#include <Scenery/scenery.hxx>
60
62#include <ATC/ATCController.hxx>
64
65using std::string;
66
67
68/***************************************************************************
69 * FGGroundController()
70 **************************************************************************/
72{
73 hasNetwork = false;
74 count = 0;
75 group = 0;
76 version = 0;
77 networkInitialized = false;
79
80 parent = par;
81 hasNetwork = true;
82 networkInitialized = true;
83}
84
88
90{
91 return (a.getIntentions().size() < b.getIntentions().size());
92}
93
94void FGGroundController::signOff(int id)
95{
97}
98
100 FGAIFlightPlan * intendedRoute,
101 int currentPosition, double lat,
102 double lon, double heading,
103 double speed, double alt,
104 double radius, int leg,
105 FGAIAircraft * aircraft)
106{
107 if (!aircraft || !aircraft->getPerformance()) {
108 SG_LOG(SG_ATC, SG_ALERT, "announcePosition: missing aircraft performance");
109 return;
110 }
111
112 // Search the activeTraffic vector to find a traffic vector with our id
114
115 // Add a new TrafficRecord if none exists for this aircraft
116 // otherwise set the information for the TrafficRecord
117 if (i == activeTraffic.end() || (activeTraffic.empty())) {
118 SG_LOG(SG_ATC, SG_BULK, "Adding " << aircraft->getCallSign() << "(" << id << ")" );
120 rec->setId(id);
121 rec->setLeg(leg);
122 rec->setPositionAndIntentions(currentPosition, intendedRoute);
123 rec->setPositionAndHeading(lat, lon, heading, speed, alt, leg);
124 rec->setRadius(radius); // only need to do this when creating the record.
125 rec->setCallsign(aircraft->getCallSign());
126 rec->setAircraft(aircraft);
127 // add to the front of the list of activeTraffic if the aircraft is already taxiing
128 SGSharedPtr<FGTrafficRecord> sharedRec = static_cast<FGTrafficRecord*>(rec);
129 if (leg == AILeg::RUNWAY_TAXI) {
130 activeTraffic.push_front(sharedRec);
131 } else {
132 activeTraffic.push_back(sharedRec);
133 }
134 SG_LOG(SG_ATC, SG_DEBUG,
135 "Added " << sharedRec->getCallsign() << "(" << sharedRec->getId() << ") " << sharedRec);
136 airportGroundRadar->add(sharedRec);
137 } else {
138 bool moved = airportGroundRadar->move(SGRect<double>(lat, lon), *i);
139 if (!moved) {
140 SG_LOG(SG_ATC, SG_ALERT,
141 "Not moved " << (*i)->getCallsign() << "(" << (*i)->getId() << ")");
142
143 }
144 (*i)->setPositionAndIntentions(currentPosition, intendedRoute);
145 (*i)->setPositionAndHeading(lat, lon, heading, speed, alt, leg);
146
147 parent->getRunwayQueue((*i)->getRunway())->updateFirst((*i), intendedRoute->getArrivalTime());
148 }
149}
150
164
166 double heading, double speed, double alt,
167 double dt)
168{
169 // Check whether aircraft are on hold due to a preceding pushback. If so, make sure to
170 // Transmit air-to-ground "Ready to taxi request:
171 // Transmit ground to air approval / hold
172 // Transmit confirmation ...
173 // Probably use a status mechanism similar to the Engine start procedure in the startup controller.
174
175 // Search the activeTraffic vector to find a traffic vector with our id
177
178 // update position of the current aircraft
179 if (i == activeTraffic.end() || activeTraffic.empty()) {
180 SG_LOG(SG_ATC, SG_DEV_WARN,
181 "AI error: updating aircraft without traffic record at " << ", id=" << id);
182 return;
183 }
184
185 SG_LOG(SG_ATC, SG_BULK, "Moving " << (*i)->getCallsign() << "(" << (*i)->getId() << ") Speed : " << speed
186 << " Speed 2 : " << (*i)->getSpeed());
187
188 airportGroundRadar->move(SGRect<double>(geod.getLatitudeDeg(), geod.getLongitudeDeg()), *i);
189 (*i)->setPositionAndHeading(geod.getLatitudeDeg(), geod.getLongitudeDeg(), heading, speed, alt, AILeg::UNKNOWN);
190 TrafficVectorIterator current = i;
191
192 setDt(getDt() + dt);
193
194 // Update every three secs, but add some randomness
195 // to prevent all IA objects doing this in synchrony
196 //if (getDt() < (3.0) + (rand() % 10))
197 // return;
198 //else
199 // setDt(0);
200 (*current)->clearResolveCircularWait();
201 // (*current)->setWaitsForId(0);
202 checkSpeedAdjustment(id, geod.getLatitudeDeg(), geod.getLongitudeDeg(), heading, speed, alt);
203 bool needsTaxiClearance = (*current)->getAircraft()->getTaxiClearanceRequest();
204 time_t now = globals->get_time_params()->get_cur_time();
205 if ((now - lastTransmission) > 10) {
206 available = true;
207 }
208 if (!needsTaxiClearance) {
209 checkHoldPosition(id, geod.getLatitudeDeg(), geod.getLongitudeDeg(), heading, speed, alt);
210 if (checkForCircularWaits(id)) {
211 (*i)->setResolveCircularWait();
212 }
213 if ((*i)->getLeg() >= AILeg::APPROACH) {
216 }
217 }
218 } else {
219 (*current)->setHoldPosition(true);
220 int state = (*current)->getState();
221
223 (*current)->setState(ATCMessageState::TAXI_CLEARED);
224 }
226 (*current)->setState(ATCMessageState::ACK_TAXI_CLEARED);
227 }
229 (*current)->setState(ATCMessageState::START_TAXI);
230 }
231 if ((state == ATCMessageState::START_TAXI) && available) {
232 (*current)->setState(ATCMessageState::NORMAL);
233 (*current)->getAircraft()->setTaxiClearanceRequest(false);
234 (*current)->setHoldPosition(false);
235 available = false;
236 }
237 }
238}
239
253
254void FGGroundController::checkSpeedAdjustment(int id, double lat,
255 double lon, double heading,
256 double speed, double alt) {
257 TrafficVectorIterator current;
258 // bool previousInstruction;
260 if (!activeTraffic.size()) {
261 return;
262 }
263 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
264 SG_LOG(SG_ATC, SG_ALERT,
265 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkSpeedAdjustment (" << id << ")" );
266 }
267 current = i;
268
269 auto blocker = airportGroundRadar->getBlockedBy(*i);
270 time_t now = globals->get_time_params()->get_cur_time();
271 if (blocker!=nullptr) {
272 int oldWaitsForId = (*i)->getWaitsForId();
273 (*i)->setWaitsForId(blocker->getId());
274 if(oldWaitsForId!=blocker->getId()) {
275 (*i)->setWaitingSince(now);
276 }
277 // https://wiki.flightgear.org/AI_Traffic#Braking
278 double distM = SGGeodesy::distanceM((*i)->getPos(), blocker->getPos());
279 double sizeA = (*i)->getRadius()*2;
280 double sizeB = blocker->getRadius();
281 double distanceSlowdown = std::min((distM-20-sizeB), sizeA); // At 20 m we want to correct to zero
282 double speedCorrection = std::min(std::max((distanceSlowdown/sizeA),0.0),1.0);
283 int newSpeed = blocker->getSpeed() * speedCorrection; // clamp to max speed of other aircraft
284 newSpeed = newSpeed>2?newSpeed:0; // ensure we don't crawl
285 int waittime = (now-(*i)->getWaitingSince());
286 const sgDebugPriority level = waittime > 6000?SG_DEV_WARN:SG_DEBUG;
287 if (blocker->getWaitsForId()) {
288 SG_LOG(SG_ATC, level,
289 (*i)->getCallsign() << "(" << (*i)->getId() << ") is blocked by " << blocker->getCallsign() << "(" << blocker->getId() << ") for " << waittime << " seconds which is blocked by (" << blocker->getWaitsForId() << ") new speed " << newSpeed << " Dist : " << distM << " Other speed : " << blocker->getSpeed());
290 if (blocker->getWaitsForId() == (*i)->getId()) {
291 SG_LOG(SG_ATC, level,
292 (*i)->getCallsign() << "(" << (*i)->getId() << ") circular ");
293 }
294 } else {
295 SG_LOG(SG_ATC, level,
296 (*i)->getCallsign() << "(" << (*i)->getId() << ") is blocked by " << blocker->getCallsign() << "(" << blocker->getId() << ") for " << waittime << " seconds new speed " << newSpeed << " Dist : " << distM << " Other speed : " << blocker->getSpeed());
297 }
298 if (newSpeed!=0) {
299 (*i)->setSpeedAdjustment(newSpeed);
300 } else {
301 if (oldWaitsForId!=blocker->getId()) {
302 (*i)->setState(ATCMessageState::NORMAL);
303 (*i)->setRequestHoldPosition(true);
304 }
305 }
306 return;
307 } else {
308 int oldWaitsForId = (*i)->getWaitsForId();
309 int waitTime = now - (*i)->getWaitingSince();
310 if (oldWaitsForId>0 && waitTime > 5) {
311 SG_LOG(SG_ATC, SG_DEBUG,
312 (*i)->getCallsign() << "(" << (*i)->getId() << ") cleared of blocker (" << oldWaitsForId << ")");
313 (*i)->setResumeTaxi(true);
314 (*i)->clearSpeedAdjustment();
315 (*i)->setWaitingSince(0);
316 (*i)->setWaitsForId(0);
317 }
318 return;
319 }
320}
321
329
330void FGGroundController::checkHoldPosition(int id, double lat,
331 double lon, double heading,
332 double speed, double alt)
333{
334 TrafficVectorIterator current;
336 if (activeTraffic.size()) {
337 //while (((*i)->getId() != id) && i != activeTraffic.end())
338 while (i != activeTraffic.end()) {
339 if ((*i)->getId() == id) {
340 break;
341 }
342 i++;
343 }
344 } else {
345 return;
346 }
347
348 time_t now = globals->get_time_params()->get_cur_time();
349 if ((now - lastTransmission) > 10) {
350 available = true;
351 }
352 if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
353 SG_LOG(SG_ATC, SG_ALERT,
354 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkHoldPosition at " );
355 }
356 current = i;
357 if ((*current)->getTakeOffStatus() == AITakeOffStatus::QUEUED) {
358 (*current)->setHoldPosition(true);
359 return;
360 }
361
362 if ((*current)->getTakeOffStatus() == AITakeOffStatus::CLEARED_FOR_TAKEOFF) {
363 (*current)->setHoldPosition(false);
364 (*current)->clearSpeedAdjustment();
365 return;
366 }
367 if ((now - lastTransmission) > 2) {
368 available = true;
369 }
370 if ((*current)->getState() == ATCMessageState::NORMAL && available) {
371 if ((*current)->getRequestHoldPosition()) { // No has a hold short instruction
373 SG_LOG(SG_ATC, SG_DEBUG, "Transmitting hold short instruction ");
374 (*current)->setState(ATCMessageState::ACK_HOLD);
375 (*current)->setRequestHoldPosition(false);
376 (*current)->setHoldPosition(true);
377 lastTransmission = now;
378 available = false;
379 // Don't act on the changed instruction until the transmission is confirmed
380 // So set back to original status
381 SG_LOG(SG_ATC, SG_BULK, "Current transmit state " << (*current)->getState());
382 }
383 if ((*current)->getResumeTaxi()) { // No has a hold short instruction
385 SG_LOG(SG_ATC, SG_DEBUG, "Transmitting resume instruction ");
386 (*current)->setState(ATCMessageState::ACK_RESUME_TAXI);
387 (*current)->setResumeTaxi(false);
388 (*current)->setHoldPosition(false);
389 lastTransmission = now;
390 available = false;
391 // Don't act on the changed instruction until the transmission is confirmed
392 // So set back to original status
393 SG_LOG(SG_ATC, SG_BULK, "Current transmit state " << (*current)->getState());
394 }
395 }
396 // 6 = Report runway
397 // 7 = Acknowledge report runway
398 // 8 = Switch tower frequency
399 // 9 = Acknowledge switch tower frequency
400
401 //int state = (*current)->getState();
403 (*current)->setState(ATCMessageState::NORMAL);
404 (*current)->setHoldPosition(true);
405 }
407 (*current)->setState(ATCMessageState::NORMAL);
408 (*current)->setHoldPosition(false);
409 }
410 if ((*current)->getTakeOffStatus() && ((*current)->getState() == 0)) {
411 SG_LOG(SG_ATC, SG_DEBUG, "Scheduling " << (*current)->getAircraft()->getCallSign() << " for hold short");
412 (*current)->setState(ATCMessageState::REPORT_RUNWAY);
413 }
415 }
417 }
419 }
421 }
422
423 //(*current)->setState(0);
424}
425
440{
441 SG_LOG(SG_ATC, SG_BULK, "Performing circular check for " << id);
442 int target = 0;
443 TrafficVectorIterator current, other;
445 int trafficSize = activeTraffic.size();
446 if (trafficSize) {
447 while (i != activeTraffic.end()) {
448 if ((*i)->getId() == id) {
449 break;
450 }
451 i++;
452 }
453 } else {
454 return false;
455 }
456
457 if (i == activeTraffic.end()) {
458 // Presumably in towercontroller
459 SG_LOG(SG_ATC, SG_BULK,
460 "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits at " );
461 }
462
463 current = i;
464 target = (*current)->getWaitsForId();
465 //bool printed = false; // Note that this variable is for debugging purposes only.
466 int counter = 0;
467
468 if (id == target) {
469 SG_LOG(SG_ATC, SG_DEBUG, "aircraft is waiting for user");
470 return false;
471 }
472
473 while ((target > 0) && (target != id) && counter++ < trafficSize) {
474 //printed = true;
475 //FIXME Move to GroundRadar
477 if (trafficSize) {
478 while (iter != activeTraffic.end()) {
479 if ((*iter)->getId() == target) {
480 break;
481 }
482 ++iter;
483 }
484 } else {
485 return false;
486 }
487
488 if (iter == activeTraffic.end()) {
489 // The target id is not found on the current network, which means it's at the tower
490 SG_LOG(SG_ATC, SG_BULK, "AI error: Trying to access non-existing aircraft in FGGroundNetwork::checkForCircularWaits Id : " << target);
491 return false;
492 }
493
494 other = iter;
495 target = (*other)->getWaitsForId();
496
497 // actually this trap isn't as impossible as it first seemed:
498 // the setWaitsForID(id) is set to current when the aircraft
499 // is waiting for the user controlled aircraft.
500 if ((*current)->getId() == (*other)->getId())
501 return false;
502 }
503
504 //if (printed)
505 SG_LOG(SG_ATC, SG_BULK, "[done] ");
506 if (id == target) {
507 SG_LOG(SG_ATC, SG_WARN,
508 "Detected circular wait condition: Id = " << id <<
509 "target = " << target);
510 return true;
511 } else {
512 return false;
513 }
514}
515
522void FGGroundController::handover(SGSharedPtr<FGTrafficRecord> aiObject, int leg) {
523 FGATCController::handover(aiObject, leg);
524 if (leg == AILeg::PARKING_TAXI) {
525 // The first contact
526 SG_LOG(SG_ATC, SG_DEBUG,
527 "Added " << (aiObject)->getCallsign() << "(" << (aiObject)->getId() << ") " << aiObject);
528 parent->getRunwayQueue(aiObject->getRunway())->removeFromQueue(aiObject->getId());
529 }
530}
531
532
533// Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
534static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
535 double lon, double elev, double hdg, double slope)
536{
537 SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
538 obj_pos = makeZUpFrame(geod);
539 // hdg is not a compass heading, but a counter-clockwise rotation
540 // around the Z axis
541 obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
542 0.0, 0.0, 1.0));
543 obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
544 0.0, 1.0, 0.0));
545}
546
549{
550 SGMaterialLib *matlib = globals->get_matlib();
551 FGGroundNetwork* network = parent->parent()->groundNetwork();
552
553 if (group) {
554 //int nr = ;
555 globals->get_scenery()->get_scene_graph()->removeChild(group);
556 //while (group->getNumChildren()) {
557 // SG_LOG(SG_ATC, SG_DEBUG, "Number of children: " << group->getNumChildren());
558 //simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
559 //osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
560 //geode->releaseGLObjects();
561 //group->removeChild(geode);
562 //delete geode;
563 group = 0;
564 }
565 if (visible) {
566 group = new osg::Group;
567 FGScenery * local_scenery = globals->get_scenery();
568 // double elevation_meters = 0.0;
569// double elevation_feet = 0.0;
570 time_t now = globals->get_time_params()->get_cur_time();
571
572 //for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
573 //double dx = 0;
574
575 for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
576 // Handle start point i.e. the segment that is connected to the aircraft itself on the starting end
577 // and to the the first "real" taxi segment on the other end.
578 const int pos = (*i)->getCurrentPosition();
579 if (pos > 0) {
580 FGTaxiSegment* segment = network->findSegment(pos);
581 SGGeod start = (*i)->getPos();
582 SGGeod end (segment->getEnd()->geod());
583
584 double length = SGGeodesy::distanceM(start, end);
585 //heading = SGGeodesy::headingDeg(start->geod(), end->geod());
586
587 double az2, heading; //, distanceM;
588 SGGeodesy::inverse(start, end, heading, az2, length);
589 double coveredDistance = length * 0.5;
590 SGGeod center;
591 SGGeodesy::direct(start, heading, coveredDistance, center, az2);
592 SG_LOG(SG_ATC, SG_BULK, "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading);
594 // Make a helper function out of this
595 osg::Matrix obj_pos;
596 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
597 obj_trans->setDataVariance(osg::Object::STATIC);
598 // Experimental: Calculate slope here, based on length, and the individual elevations
599 double elevationStart;
600 if (isUserAircraft((*i)->getAircraft())) {
601 elevationStart = fgGetDouble("/position/ground-elev-m");
602 } else {
603 elevationStart = ((*i)->getAircraft()->_getAltitude());
604 }
605 double elevationEnd = segment->getEnd()->getElevationM();
606 SG_LOG(SG_ATC, SG_DEBUG, "Using elevation " << elevationEnd);
607
608 if ((elevationEnd == 0) || (elevationEnd = parent->getElevation())) {
609 SGGeod center2 = end;
610 center2.setElevationM(SG_MAX_ELEVATION_M);
611 if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
612// elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
613 //elevation_meters += 0.5;
614 }
615 else {
616 elevationEnd = parent->getElevation();
617 }
618 segment->getEnd()->setElevation(elevationEnd);
619 }
620 double elevationMean = (elevationStart + elevationEnd) / 2.0;
621 double elevDiff = elevationEnd - elevationStart;
622
623 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
624
625 SG_LOG(SG_ATC, SG_DEBUG, "1. Using mean elevation : " << elevationMean << " and " << slope);
626
627 WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean+ 0.5, -(heading), slope );
628
629 obj_trans->setMatrix( obj_pos );
630 //osg::Vec3 center(0, 0, 0)
631
632 float width = length /2.0;
633 osg::Vec3 corner(-width, 0, 0.25f);
634 osg::Vec3 widthVec(2*width + 1, 0, 0);
635 osg::Vec3 heightVec(0, 1, 0);
636 osg::Geometry* geometry;
637 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
638 simgear::EffectGeode* geode = new simgear::EffectGeode;
639 geode->setName("test");
640 geode->addDrawable(geometry);
641 //osg::Node *custom_obj;
642 SGMaterial *mat;
643 if (segment->hasBlock(now)) {
644 mat = matlib->find("UnidirectionalTaperRed", center);
645 } else {
646 mat = matlib->find("UnidirectionalTaperGreen", center);
647 }
648 if (mat)
649 geode->setEffect(mat->get_effect());
650 obj_trans->addChild(geode);
651 // wire as much of the scene graph together as we can
652 //->addChild( obj_trans );
653 group->addChild( obj_trans );
655 } else {
656 SG_LOG(SG_ATC, SG_INFO, "BIG FAT WARNING: current position is here : " << pos);
657 }
658 // Next: Draw the other taxi segments.
659 for (intVecIterator j = (*i)->getIntentions().begin(); j != (*i)->getIntentions().end(); j++) {
660 osg::Matrix obj_pos;
661 const int k = (*j);
662 if (k > 0) {
663 osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
664 obj_trans->setDataVariance(osg::Object::STATIC);
665 FGTaxiSegment* segmentK = network->findSegment(k);
666 // Experimental: Calculate slope here, based on length, and the individual elevations
667 double elevationStart = segmentK->getStart()->getElevationM();
668 double elevationEnd = segmentK->getEnd ()->getElevationM();
669 if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
670 SGGeod center2 = segmentK->getStart()->geod();
671 center2.setElevationM(SG_MAX_ELEVATION_M);
672 if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
673// elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
674 //elevation_meters += 0.5;
675 }
676 else {
677 elevationStart = parent->getElevation();
678 }
679 segmentK->getStart()->setElevation(elevationStart);
680 }
681 if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
682 SGGeod center2 = segmentK->getEnd()->geod();
683 center2.setElevationM(SG_MAX_ELEVATION_M);
684 if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
685// elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
686 //elevation_meters += 0.5;
687 }
688 else {
689 elevationEnd = parent->getElevation();
690 }
691 segmentK->getEnd()->setElevation(elevationEnd);
692 }
693
694 double elevationMean = (elevationStart + elevationEnd) / 2.0;
695 double elevDiff = elevationEnd - elevationStart;
696 double length = segmentK->getLength();
697 double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
698
699 SG_LOG(SG_ATC, SG_DEBUG, "2. Using mean elevation : " << elevationMean << " and " << slope);
700
701 SGGeod segCenter = segmentK->getCenter();
702 WorldCoordinate( obj_pos, segCenter.getLatitudeDeg(), segCenter.getLongitudeDeg(),
703 elevationMean+ 0.5, -(segmentK->getHeading()), slope );
704
705 obj_trans->setMatrix( obj_pos );
706 //osg::Vec3 center(0, 0, 0)
707
708 float width = segmentK->getLength() /2.0;
709 osg::Vec3 corner(-width, 0, 0.25f);
710 osg::Vec3 widthVec(2*width + 1, 0, 0);
711 osg::Vec3 heightVec(0, 1, 0);
712 osg::Geometry* geometry;
713 geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
714 simgear::EffectGeode* geode = new simgear::EffectGeode;
715 geode->setName("test");
716 geode->addDrawable(geometry);
717 //osg::Node *custom_obj;
718 SGMaterial *mat;
719 if (segmentK->hasBlock(now)) {
720 mat = matlib->find("UnidirectionalTaperRed", segCenter);
721 } else {
722 mat = matlib->find("UnidirectionalTaperGreen", segCenter);
723 }
724 if (mat)
725 geode->setEffect(mat->get_effect());
726 obj_trans->addChild(geode);
727 // wire as much of the scene graph together as we can
728 //->addChild( obj_trans );
729 group->addChild( obj_trans );
730 }
731 }
732 //dx += 0.1;
733 }
734 globals->get_scenery()->get_scene_graph()->addChild(group);
735 }
736}
737
739 return string(parent->parent()->getName() + "-ground");
740}
741
743{
744 time_t now = globals->get_time_params()->get_cur_time();
745 FGGroundNetwork* network = parent->parent()->groundNetwork();
746 network->unblockAllSegments(now);
747 int priority = 1;
748
749 TrafficVector& startupTraffic(parent->getStartupController()->getActiveTraffic());
751
752 //sort(activeTraffic.begin(), activeTraffic.end(), compare_trafficrecords);
753 // Handle traffic that is under ground control first; this way we'll prevent clutter at the gate areas.
754 // Don't allow an aircraft to pushback when a taxiing aircraft is currently using part of the intended route.
755 for (i = startupTraffic.begin(); i != startupTraffic.end(); ++i) {
756 updateStartupTraffic(i, priority, now);
757 }
758
759 for (i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
760 updateActiveTraffic(i, priority, now);
761 }
762
764}
765
766void FGGroundController::updateStartupTraffic(TrafficVectorIterator i,
767 int& priority,
768 time_t now)
769{
770 if (!(*i)->getAircraft()) {
771 SG_LOG(SG_ATC, SG_ALERT, "updateStartupTraffic: missing aircraft");
772 return;
773 }
774
775 if (!(*i)->getAircraft()->getPerformance()) {
776 SG_LOG(SG_ATC, SG_ALERT, "updateStartupTraffic: missing aircraft performance");
777 return;
778 }
779
780 if( airportGroundRadar->isBlockedForPushback(*i) ) {
781 return;
782 }
783
784
785 (*i)->allowPushBack();
786 (*i)->setPriority(priority++);
787}
788
789bool FGGroundController::updateActiveTraffic(TrafficVectorIterator i,
790 int& priority,
791 time_t now)
792{
793 if (!(*i)->getAircraft()) {
794 SG_LOG(SG_ATC, SG_ALERT, "updateActiveTraffic: missing aircraft");
795 return false;
796 }
797
798 if ((*i)->getAircraft()->getDie()) {
799 // aircraft has died
800 return false;
801 }
802
803 if (!(*i)->getAircraft()->getPerformance()) {
804 SG_LOG(SG_ATC, SG_ALERT, "updateActiveTraffic: missing aircraft performance");
805 return false;
806 }
807
808 (*i)->setPriority(priority++);
809 return true;
810}
811
812int FGGroundController::getFrequency() {
813 int groundFreq = parent->getGroundFrequency(2);
814 int towerFreq = parent->getTowerFrequency(2);
815 return groundFreq>0?groundFreq:towerFreq;
816}
const double rec
static void WorldCoordinate(osg::Matrix &obj_pos, double lat, double lon, double elev, double hdg, double slope)
bool compare_trafficrecords(FGTrafficRecord a, FGTrafficRecord b)
#define i(x)
PerformanceData * getPerformance()
const std::string & getCallSign() const
Definition AIBase.hxx:367
time_t getArrivalTime() const
TrafficVectorIterator searchActiveTraffic(int id) const
Search activeTraffic vector to find matching id.
bool isUserAircraft(FGAIAircraft *)
virtual void handover(SGSharedPtr< FGTrafficRecord > aiObject, int leg)
We share the traffic record much like real life.
virtual void signOff(int id)
Sign off the aircraft with the id from this controller.
FGAirportDynamics * parent
SGSharedPtr< AirportGroundRadar > airportGroundRadar
TrafficVector activeTraffic
void setDt(double dt)
osg::Group * group
void transmit(FGTrafficRecord *rec, FGAirportDynamics *parent, AtcMsgId msgId, AtcMsgDir msgDir, bool audible)
@ MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT
@ MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY
bool checkTransmissionState(int minState, int MaxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId, AtcMsgDir msgDir)
SGTime * get_time_params() const
Definition globals.hxx:311
FGGroundController(FGAirportDynamics *par)
virtual std::string getName() const
virtual void announcePosition(int id, FGAIFlightPlan *intendedRoute, int currentRoute, double lat, double lon, double hdg, double spd, double alt, double radius, int leg, FGAIAircraft *aircraft)
virtual void handover(SGSharedPtr< FGTrafficRecord > aiObject, int leg)
We share the traffic record much like real life.
virtual void updateAircraftInformation(int id, SGGeod geod, double heading, double speed, double alt, double dt)
The ground network can deal with the following states: 0 = Normal; no action required 1 = "Acknowledg...
virtual void render(bool)
Draw visible taxi routes.
bool checkForCircularWaits(int id)
Check whether situations occur where the current aircraft is waiting for itself due to higher order i...
virtual void update(double dt)
void unblockAllSegments(time_t now)
FGTaxiSegment * findSegment(const FGTaxiNode *from, const FGTaxiNode *to) const
Find the taxiway segment joining two (ground-net) nodes.
bool get_elevation_m(const SGGeod &geod, double &alt, const simgear::BVHMaterial **material, const osg::Node *butNotFrom=0)
Compute the elevation of the scenery at geodetic latitude lat, geodetic longitude lon and not higher ...
Definition scenery.cxx:527
FGTaxiNodeRef getEnd() const
double getLength() const
bool hasBlock(time_t now)
FGTaxiNodeRef getStart() const
double getHeading() const
SGGeod getCenter() const
intVec & getIntentions()
FGGlobals * globals
Definition globals.cxx:142
std::vector< int >::iterator intVecIterator
@ RUNWAY_TAXI
@ PARKING_TAXI
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
Definition proptest.cpp:30
std::list< SGSharedPtr< FGTrafficRecord > >::const_iterator TrafficVectorIterator
std::list< SGSharedPtr< FGTrafficRecord > > TrafficVector