FlightGear next
TowerController.cxx
Go to the documentation of this file.
1// Extracted from trafficrecord.cxx - Implementation of AIModels ATC code.
2//
3// Written by Durk Talsma, started September 2006.
4//
5// Copyright (C) 2006 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 <algorithm>
26#include <cstdio>
27#include <random>
28
29#include <osg/Geode>
30#include <osg/Geometry>
31#include <osg/MatrixTransform>
32#include <osg/Shape>
33
34#include <simgear/scene/material/EffectGeode.hxx>
35#include <simgear/scene/material/matlib.hxx>
36#include <simgear/scene/material/mat.hxx>
37#include <simgear/scene/util/OsgMath.hxx>
38#include <simgear/timing/sg_time.hxx>
39
40#include <Scenery/scenery.hxx>
41
42#include "trafficcontrol.hxx"
43#include "atc_mgr.hxx"
49#include <Airports/dynamics.hxx>
50#include <Airports/airport.hxx>
51#include <Radio/radio.hxx>
52#include <signal.h>
53
54#include <ATC/atc_mgr.hxx>
55#include <ATC/ATCController.hxx>
58
59using std::sort;
60using std::string;
61
62/***************************************************************************
63 * class FGTowerController
64 * subclass of FGATCController
65 **************************************************************************/
66
72
76
77//
79 FGAIFlightPlan * intendedRoute,
80 int currentPosition, double lat,
81 double lon, double heading,
82 double speed, double alt,
83 double radius, int leg,
84 FGAIAircraft * ref)
85{
86 init();
87
88 // Search activeTraffic for a record matching our id
90
91 // Add a new TrafficRecord if no one exists for this aircraft.
92 if (i == activeTraffic.end() || (activeTraffic.empty())) {
94 rec->setId(id);
95
96 rec->setPositionAndHeading(lat, lon, heading, speed, alt, leg);
97 rec->setRunway(intendedRoute->getRunway());
98 rec->setLeg(leg);
99 rec->setCallsign(ref->getCallSign());
100 rec->setRadius(radius);
101 rec->setAircraft(ref);
102 SGSharedPtr<FGTrafficRecord> sharedRec = static_cast<FGTrafficRecord*>(rec);
103 activeTraffic.push_back(sharedRec);
104
105 if (leg<=AILeg::TAKEOFF) {
106 // Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway.
107 time_t now = globals->get_time_params()->get_cur_time();
108 ActiveRunwayQueue* rwy = parent->getRunwayQueue(intendedRoute->getRunway());
109 rwy->requestTimeSlot(sharedRec);
110 SG_LOG(SG_ATC, SG_DEBUG, ref->getTrafficRef()->getCallSign() << "(" << ref->getID() << ") You are number " << rwy->getrunwayQueueSize() << " for takeoff from " << parent->parent()->getId() << "/" << rwy->getRunwayName() << " " << ref);
111 airportGroundRadar->add(sharedRec);
112 } else if (leg<AILeg::CRUISE) {
113 SG_LOG(SG_ATC, SG_DEBUG, ref->getTrafficRef()->getCallSign() << "(" << ref->getID() << ") Goodbye from " << intendedRoute->departureAirport()->getId() << " " << ref->getTrafficRef() << " " << ref) ;
114 } else {
115 SG_LOG(SG_ATC, SG_DEBUG, ref->getTrafficRef()->getCallSign() << "(" << ref->getID() << ") Welcome to " << intendedRoute->arrivalAirport()->getId() << " " << ref->getTrafficRef() << " " << ref) ;
116 airportGroundRadar->add(sharedRec);
117 }
118 } else {
119 if (((*i)->getLeg() > AILeg::RUNWAY_TAXI) && ((*i)->getLeg() < AILeg::CRUISE ||
120 (*i)->getLeg() > AILeg::LANDING) ) {
121 bool moved = airportGroundRadar->move(SGRect<double>(lat, lon), *i);
122 if (!moved) {
123 SG_LOG(SG_ATC, SG_ALERT,
124 "Not moved " << (*i)->getCallsign() << "(" << (*i)->getId() << ")" << *i);
125
126 }
127 }
128 (*i)->setPositionAndHeading(lat, lon, heading, speed, alt, leg);
129 (*i)->setRunway(intendedRoute->getRunway());
130 if ((*i)->getLeg() > AILeg::RUNWAY_TAXI && (*i)->getLeg() < AILeg::CRUISE) {
131 auto blocker = airportGroundRadar->getBlockedBy(*i);
132 if (blocker!=nullptr) {
133 (*i)->setWaitsForId(blocker->getId());
134 double distM = SGGeodesy::distanceM((*i)->getPos(), blocker->getPos());
135 int newSpeed = blocker->getSpeed() * (distM / 100);
136 SG_LOG(SG_ATC, SG_DEBUG,
137 (*i)->getCallsign() << "(" << (*i)->getId() << ") is blocked for takeoff by " << blocker->getCallsign() << "(" << blocker->getId() << ") new speed " << newSpeed << " dist " << distM);
138 (*i)->setSpeedAdjustment(newSpeed);
139 } else {
140 int oldWaitsForId = (*i)->getWaitsForId();
141 if (oldWaitsForId>0) {
142 SG_LOG(SG_ATC, SG_DEBUG,
143 (*i)->getCallsign() << "(" << (*i)->getId() << ") cleared of blocker " << oldWaitsForId);
144 (*i)->setResumeTaxi(true);
145 }
146 (*i)->clearSpeedAdjustment();
147 (*i)->setWaitingSince(0);
148 (*i)->setWaitsForId(0);
149 }
150 }
151 }
152}
153
155 double heading, double speed, double alt,
156 double dt)
157{
158 // Search activeTraffic for a record matching our id
160
161 setDt(getDt() + dt);
162
163 time_t now = globals->get_time_params()->get_cur_time();
164 if (i == activeTraffic.end() || (activeTraffic.empty())) {
165 SG_LOG(SG_ATC, SG_ALERT,
166 "AI error: updating aircraft without traffic record at " <<
167 SG_ORIGIN);
168 return;
169 }
170
171 // Update the position of the current aircraft
172 (*i)->setPositionAndHeading(geod.getLatitudeDeg(), geod.getLongitudeDeg(), heading, speed, alt, AILeg::UNKNOWN);
173
174 if ((*i)->getLeg()<AILeg::CRUISE) {
175 // see if we already have a clearance record for the currently active runway
176 // NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is
177 // already exists here. So, we can simplify the current code.
178
179 ActiveRunwayQueue* rwy = parent->getRunwayQueue((*i)->getRunway());
180 //if (parent->getId() == fgGetString("/sim/presets/airport-id")) {
181 // for (rwy = parent->getRunwayQueue().begin(); rwy != parent->getRunwayQueue().end(); ++rwy) {
182 // rwy->printrunwayQueue();
183 // }
184 //}
185
186 // only bother running the following code if the current aircraft is the
187 // first in line for departure
188 /* if (current.getAircraft() == rwy->getFirstAircraftInrunwayQueue()) {
189 if (rwy->getCleared()) {
190 if (id == rwy->getCleared()) {
191 current.setHoldPosition(false);
192 } else {
193 current.setHoldPosition(true);
194 }
195 } else {
196 // For now. At later stages, this will probably be the place to check for inbound traffic.
197 rwy->setCleared(id);
198 }
199 } */
200 // only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control
201 auto ac = rwy->getFirstAircraftInDepartureQueue();
202 if (ac) {
203 //FIXME replace by ATCMessageState
204 if (ac->getTakeOffStatus() == AITakeOffStatus::QUEUED) {
205 // transmit takeoff clearance
206 ac->setTakeOffStatus(AITakeOffStatus::CLEARED_FOR_TAKEOFF);
207 TrafficVectorIterator first = searchActiveTraffic(ac->getId());
208 //FIXME use checkTransmissionState
209 if (first == activeTraffic.end() || activeTraffic.empty()) {
210 SG_LOG(SG_ATC, SG_ALERT,
211 "FGApproachController updating aircraft without traffic record at " << SG_ORIGIN);
212 } else {
213 (*first)->setState(ATCMessageState::CLEARED_TAKEOFF);
215 }
216 }
217 }
218 //FIXME Make it an member of traffic record
219 if ((*i)->getTakeOffStatus() == AITakeOffStatus::CLEARED_FOR_TAKEOFF &&
220 (*i)->getRunwaySlot() < now) {
221 (*i)->setHoldPosition(false);
223 (*i)->setState(ATCMessageState::ANNOUNCE_ARRIVAL);
224 }
225 } else {
226 (*i)->setHoldPosition(true);
227 SG_LOG(SG_ATC, SG_BULK,
228 (*i)->getCallsign() << "(" << (*i)->getId() << ") Waiting for " << ((*i)->getRunwaySlot() - now) << " seconds");
229 }
230 int clearanceId = rwy->getCleared();
231 if (clearanceId) {
232 if (id == clearanceId) {
233 if ((*i)->hasHoldPosition()) {
234 SG_LOG(SG_ATC, SG_BULK, (*i)->getCallsign() << "(" << (*i)->getId() << ") Unset Hold " << clearanceId << " for rwy " << rwy->getRunwayName());
235 }
236 (*i)->setHoldPosition(false);
237 } else {
238 SG_LOG(SG_ATC, SG_BULK, (*i)->getCallsign() << "(" << (*i)->getId() << ") Not cleared " << id << " Currently cleared " << clearanceId);
239 }
240 } else {
241 if ((*i) == rwy->getFirstAircraftInDepartureQueue()) {
242 SG_LOG(SG_ATC, SG_BULK,
243 (*i)->getCallsign() << "(" << (*i)->getId() << ") Cleared for runway " << getName() << " " << rwy->getRunwayName() << " Id " << id);
244 auto blocker = airportGroundRadar->getBlockedBy(*i);
245 if (blocker==nullptr) {
246 // FIXME presumably this can be replaced by ground radar
247 rwy->setCleared(id);
249 if (l_ac) {
250 l_ac->setTakeOffStatus(AITakeOffStatus::QUEUED);
251 // transmit takeoff clearance? But why twice?
252 }
253 } else {
254 (*i)->setWaitsForId(blocker->getId());
255 double distM = SGGeodesy::distanceM((*i)->getPos(), blocker->getPos());
256 int newSpeed = blocker->getSpeed() * (distM / 100);
257 SG_LOG(SG_ATC, SG_DEBUG,
258 (*i)->getCallsign() << "(" << (*i)->getId() << ") is blocked for takeoff by " << blocker->getCallsign() << "(" << blocker->getId() << ") new speed " << newSpeed);
259 (*i)->setSpeedAdjustment(newSpeed);
260 }
261 } else {
262 #if 0 // Ticket #2770 : ATC/TowerController floods log
263 SG_LOG(SG_ATC, SG_BULK,
264 "Not cleared " << current.getAircraft()->getCallSign() << " " << rwy->getFirstAircraftInDepartureQueue()->getCallSign());
265 #endif
266 }
267 }
268 } else {
269
270 }
271}
272
274{
275 // ensure we don't modify activeTraffic during destruction
276 if (_isDestroying)
277 return;
278
279 // Search activeTraffic for a record matching our id
281 if (i == activeTraffic.end() || (activeTraffic.empty())) {
282 SG_LOG(SG_ATC, SG_ALERT,
283 "AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN);
284 return;
285 }
286 SG_LOG(SG_ATC, SG_BULK, "Signing off " << (*i)->getCallsign() << "(" << id << ") from " << getName() << " Leg : " << (*i)->getLeg() );
287
288 if((*i)->getLeg() <= AILeg::CRUISE) {
289 const auto trafficRunway = (*i)->getRunway();
290 ActiveRunwayQueue* runwayIt = parent->getRunwayQueue(trafficRunway);
291
292 SG_LOG(SG_ATC, SG_BULK, (*i)->getCallsign() << "(" << (*i)->getId() << ") Cleared " << id << " from " << runwayIt->getRunwayName() << " cleared " << runwayIt->getCleared() );
293 runwayIt->removeFromQueue(id);
294
295 (*i)->resetTakeOffStatus();
296 } else {
297 time_t now = globals->get_time_params()->get_cur_time();
300 }
301 }
302
304}
305
306// Note:
307// if we make trafficrecord a member of the base class
308// the following three functions: signOff, hasInstruction and getInstruction can
309// become devirtualized and be a member of the base ATCController class
310// which would simplify code maintenance.
311// note that this function is probably obsolete
313{
314 // Search activeTraffic for a record matching our id
316
317 if (i == activeTraffic.end() || activeTraffic.empty()) {
318 SG_LOG(SG_ATC, SG_ALERT,
319 "AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
320 } else {
321 return (*i)->hasInstruction();
322 }
323 return false;
324}
325
326
328{
329 // Search activeTraffic for a record matching our id
331
332 if (i == activeTraffic.end() || activeTraffic.empty()) {
333 SG_LOG(SG_ATC, SG_ALERT,
334 "AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
335 } else {
336 return (*i)->getInstruction();
337 }
338 return FGATCInstruction();
339}
340
341void FGTowerController::render(bool visible) {
342 // this should be bulk, since its called quite often
343 SG_LOG(SG_ATC, SG_BULK, "FGTowerController::render function not yet implemented");
344}
345
347 return string(parent->parent()->getName() + "-tower");
348}
349
350
355
356int FGTowerController::getFrequency() {
357 int towerFreq = parent->getTowerFrequency(2);
358 return towerFreq;
359}
const double rec
#define i(x)
void setCleared(int number)
void requestTimeSlot(SGSharedPtr< FGTrafficRecord > eta)
Fetch next slot for the active runway.
const std::string & getRunwayName() const
const SGSharedPtr< FGTrafficRecord > getFirstAircraftInDepartureQueue() const
int getCleared() const
Get id of cleared AI Aircraft.
const SGSharedPtr< FGTrafficRecord > getFirstOfStatus(int stat) const
Fetch the first aircraft in the departure queue with a certain status.
void removeFromQueue(int id)
FGAISchedule * getTrafficRef()
const std::string & getCallSign() const
Definition AIBase.hxx:367
int getID() const
Definition AIBase.cxx:1092
const std::string & getRunway() const
FGAirportRef departureAirport() const
FGAirportRef arrivalAirport() const
std::string getCallSign()
Definition Schedule.cxx:552
TrafficVectorIterator searchActiveTraffic(int id) const
Search activeTraffic vector to find matching id.
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)
void transmit(FGTrafficRecord *rec, FGAirportDynamics *parent, AtcMsgId msgId, AtcMsgDir msgDir, bool audible)
bool checkTransmissionState(int minState, int MaxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId, AtcMsgDir msgDir)
int getTowerFrequency(unsigned nr)
Definition dynamics.cxx:950
virtual void render(bool)
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)
FGATCInstruction getInstruction(int id)
virtual void update(double dt)
bool hasInstruction(int id)
virtual std::string getName() const
virtual void updateAircraftInformation(int id, SGGeod geod, double heading, double speed, double alt, double dt)
FGTowerController(FGAirportDynamics *parent)
void signOff(int id)
Sign off the aircraft with the id from this controller.
FGGlobals * globals
Definition globals.cxx:142
@ RUNWAY_TAXI
std::list< SGSharedPtr< FGTrafficRecord > >::const_iterator TrafficVectorIterator