first commit

This commit is contained in:
Your Name
2022-10-20 20:29:11 +08:00
commit 4d531f8044
3238 changed files with 1387862 additions and 0 deletions

477
src/ATC/ATCController.cxx Normal file
View File

@@ -0,0 +1,477 @@
// Extracted from trafficrecord.cxx - Implementation of AIModels ATC code.
//
// Written by Durk Talsma, started September 2006.
//
// Copyright (C) 2006 Durk Talsma.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <config.h>
#include <algorithm>
#include <cstdio>
#include <random>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osg/Shape>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/material/mat.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/timing/sg_time.hxx>
#include <Scenery/scenery.hxx>
#include "trafficcontrol.hxx"
#include "atc_mgr.hxx"
#include <AIModel/AIAircraft.hxx>
#include <AIModel/AIFlightPlan.hxx>
#include <AIModel/performancedata.hxx>
#include <Traffic/TrafficMgr.hxx>
#include <Airports/groundnetwork.hxx>
#include <Airports/dynamics.hxx>
#include <Airports/airport.hxx>
#include <Radio/radio.hxx>
#include <signal.h>
#include <ATC/atc_mgr.hxx>
#include <ATC/trafficcontrol.hxx>
#include <ATC/ATCController.hxx>
using std::sort;
using std::string;
/***************************************************************************
* FGATCController
*
**************************************************************************/
FGATCController::FGATCController()
{
dt_count = 0;
available = true;
lastTransmission = 0;
initialized = false;
lastTransmissionDirection = ATC_AIR_TO_GROUND;
group = NULL;
}
FGATCController::~FGATCController()
{
if (initialized) {
auto mgr = globals->get_subsystem<FGATCManager>();
mgr->removeController(this);
}
_isDestroying = true;
clearTrafficControllers();
}
void FGATCController::init()
{
if (!initialized) {
auto mgr = globals->get_subsystem<FGATCManager>();
mgr->addController(this);
initialized = true;
}
}
string FGATCController::getGateName(FGAIAircraft * ref)
{
return ref->atGate();
}
bool FGATCController::isUserAircraft(FGAIAircraft* ac)
{
return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false;
};
void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, AtcMsgId msgId,
AtcMsgDir msgDir, bool audible)
{
string sender, receiver;
int stationFreq = 0;
int taxiFreq = 0;
int towerFreq = 0;
int freqId = 0;
string atisInformation;
string text;
string taxiFreqStr;
string towerFreqStr;
double heading = 0;
string activeRunway;
string fltType;
string rwyClass;
string SID;
string transponderCode;
FGAIFlightPlan *fp;
string fltRules;
string instructionText;
int ground_to_air=0;
//double commFreqD;
sender = rec->getCallsign();
if (rec->getAircraft()->getTaxiClearanceRequest()) {
instructionText = "push-back and taxi";
} else {
instructionText = "taxi";
}
SG_LOG(SG_ATC, SG_DEBUG, "transmitting for: " << sender << "Leg = " << rec->getLeg());
auto depApt = rec->getAircraft()->getTrafficRef()->getDepartureAirport();
if (!depApt) {
SG_LOG(SG_ATC, SG_DEV_ALERT, "TrafficRec has empty departure airport, can't transmit");
return;
}
stationFreq = getFrequency();
taxiFreq = depApt->getDynamics()->getGroundFrequency(2);
towerFreq = depApt->getDynamics()->getTowerFrequency(2);
receiver = getName();
atisInformation = depApt->getDynamics()->getAtisSequence();
// Swap sender and receiver value in case of a ground to air transmission
if (msgDir == ATC_GROUND_TO_AIR) {
string tmp = sender;
sender = receiver;
receiver = tmp;
ground_to_air = 1;
}
switch (msgId) {
case MSG_ANNOUNCE_ENGINE_START:
text = sender + ". Ready to Start up.";
break;
case MSG_REQUEST_ENGINE_START:
text =
receiver + ", This is " + sender + ". Position " +
getGateName(rec->getAircraft()) + ". Information " +
atisInformation + ". " +
rec->getAircraft()->getTrafficRef()->getFlightRules() +
" to " +
rec->getAircraft()->getTrafficRef()->getArrivalAirport()->
getName() + ". Request start-up.";
break;
// Acknowledge engine startup permission
// Assign departure runway
// Assign SID, if necessery (TODO)
case MSG_PERMIT_ENGINE_START:
taxiFreqStr = formatATCFrequency3_2(taxiFreq);
heading = rec->getAircraft()->getTrafficRef()->getCourse();
fltType = rec->getAircraft()->getTrafficRef()->getFlightType();
rwyClass =
rec->getAircraft()->GetFlightPlan()->
getRunwayClassFromTrafficType(fltType);
rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
heading);
rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
fp = NULL;
rec->getAircraft()->GetFlightPlan()->setSID(fp);
if (fp) {
SID = fp->getName() + " departure";
} else {
SID = "fly runway heading ";
}
//snprintf(buffer, 7, "%3.2f", heading);
fltRules = rec->getAircraft()->getTrafficRef()->getFlightRules();
transponderCode = genTransponderCode(fltRules);
rec->getAircraft()->SetTransponderCode(transponderCode);
text =
receiver + ". Start-up approved. " + atisInformation +
" correct, runway " + activeRunway + ", " + SID + ", squawk " +
transponderCode + ". " +
"For "+ instructionText + " clearance call " + taxiFreqStr + ". " +
sender + " control.";
break;
case MSG_DENY_ENGINE_START:
text = receiver + ". Standby.";
break;
case MSG_ACKNOWLEDGE_ENGINE_START:
fp = rec->getAircraft()->GetFlightPlan()->getSID();
if (fp) {
SID =
rec->getAircraft()->GetFlightPlan()->getSID()->getName() +
" departure";
} else {
SID = "fly runway heading ";
}
taxiFreqStr = formatATCFrequency3_2(taxiFreq);
activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
transponderCode = rec->getAircraft()->GetTransponderCode();
text =
receiver + ". Start-up approved. " + atisInformation +
" correct, runway " + activeRunway + ", " + SID + ", squawk " +
transponderCode + ". " +
"For " + instructionText + " clearance call " + taxiFreqStr + ". " +
sender + ".";
break;
case MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY:
taxiFreqStr = formatATCFrequency3_2(taxiFreq);
text = receiver + ". Switching to " + taxiFreqStr + ". " + sender + ".";
break;
case MSG_INITIATE_CONTACT:
text = receiver + ". With you. " + sender + ".";
break;
case MSG_ACKNOWLEDGE_INITIATE_CONTACT:
text = receiver + ". Roger. " + sender + ".";
break;
case MSG_REQUEST_PUSHBACK_CLEARANCE:
if (rec->getAircraft()->getTaxiClearanceRequest()) {
text = receiver + ". Request push-back. " + sender + ".";
} else {
text = receiver + ". Request Taxi clearance. " + sender + ".";
}
break;
case MSG_PERMIT_PUSHBACK_CLEARANCE:
if (rec->getAircraft()->getTaxiClearanceRequest()) {
text = receiver + ". Push-back approved. " + sender + ".";
} else {
text = receiver + ". Cleared to Taxi. " + sender + ".";
}
break;
case MSG_HOLD_PUSHBACK_CLEARANCE:
text = receiver + ". Standby. " + sender + ".";
break;
case MSG_REQUEST_TAXI_CLEARANCE:
text = receiver + ". Ready to Taxi. " + sender + ".";
break;
case MSG_ISSUE_TAXI_CLEARANCE:
text = receiver + ". Cleared to taxi. " + sender + ".";
break;
case MSG_ACKNOWLEDGE_TAXI_CLEARANCE:
text = receiver + ". Cleared to taxi. " + sender + ".";
break;
case MSG_HOLD_POSITION:
text = receiver + ". Hold Position. " + sender + ".";
break;
case MSG_ACKNOWLEDGE_HOLD_POSITION:
text = receiver + ". Holding Position. " + sender + ".";
break;
case MSG_RESUME_TAXI:
text = receiver + ". Resume Taxiing. " + sender + ".";
break;
case MSG_ACKNOWLEDGE_RESUME_TAXI:
text = receiver + ". Continuing Taxi. " + sender + ".";
break;
case MSG_REPORT_RUNWAY_HOLD_SHORT:
activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
//activeRunway = "test";
text = receiver + ". Holding short runway "
+ activeRunway
+ ". " + sender + ".";
//text = "test1";
SG_LOG(SG_ATC, SG_DEBUG, "1 Currently at leg " << rec->getLeg());
break;
case MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT:
activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
text = receiver + " Roger. Holding short runway "
// + activeRunway
+ ". " + sender + ".";
//text = "test2";
SG_LOG(SG_ATC, SG_DEBUG, "2 Currently at leg " << rec->getLeg());
break;
case MSG_CLEARED_FOR_TAKEOFF:
activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
//activeRunway = "test";
text = receiver + ". Cleared for takeoff runway " + activeRunway + ". " + sender + ".";
break;
case MSG_ACKNOWLEDGE_CLEARED_FOR_TAKEOFF:
activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
text = receiver + " Roger. Cleared for takeoff runway " + activeRunway + ". " + sender + ".";
//text = "test2";
break;
case MSG_SWITCH_TOWER_FREQUENCY:
towerFreqStr = formatATCFrequency3_2(towerFreq);
text = receiver + " Contact Tower at " + towerFreqStr + ". " + sender + ".";
//text = "test3";
SG_LOG(SG_ATC, SG_DEBUG, "3 Currently at leg " << rec->getLeg());
break;
case MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY:
towerFreqStr = formatATCFrequency3_2(towerFreq);
text = receiver + " Roger, switching to tower at " + towerFreqStr + ". " + sender + ".";
//text = "test4";
SG_LOG(SG_ATC, SG_DEBUG, "4 Currently at leg " << rec->getLeg());
break;
default:
//text = "test3";
text = text + sender + ". Transmitting unknown Message.";
break;
}
const bool atcAudioEnabled = fgGetBool("/sim/sound/atc/enabled", false);
if (audible && atcAudioEnabled) {
double onBoardRadioFreq0 =
fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz");
double onBoardRadioFreq1 =
fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz");
int onBoardRadioFreqI0 = (int) floor(onBoardRadioFreq0 * 100 + 0.5);
int onBoardRadioFreqI1 = (int) floor(onBoardRadioFreq1 * 100 + 0.5);
SG_LOG(SG_ATC, SG_DEBUG, "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl);
if( stationFreq == 0 ) {
SG_LOG(SG_ATC, SG_DEBUG, getName() << " stationFreq not found");
}
// Display ATC message only when one of the radios is tuned
// the relevant frequency.
// Note that distance attenuation is currently not yet implemented
if ((stationFreq > 0)&&
((onBoardRadioFreqI0 == stationFreq)||
(onBoardRadioFreqI1 == stationFreq))) {
if (rec->allowTransmissions()) {
if( fgGetBool( "/sim/radio/use-itm-attenuation", false ) ) {
SG_LOG(SG_ATC, SG_DEBUG, "Using ITM radio propagation");
FGRadioTransmission* radio = new FGRadioTransmission();
SGGeod sender_pos;
double sender_alt_ft, sender_alt;
if(ground_to_air) {
sender_pos = parent->parent()->geod();
}
else {
sender_pos= rec->getPos();
}
double frequency = ((double)stationFreq) / 100;
radio->receiveATC(sender_pos, frequency, text, ground_to_air);
delete radio;
}
else {
SG_LOG(SG_ATC, SG_BULK, "Transmitting " << text);
fgSetString("/sim/messages/atc", text.c_str());
}
}
}
} else {
//FGATCDialogNew::instance()->addEntry(1, text);
}
}
void FGATCController::signOff(int id)
{
TrafficVectorIterator i = searchActiveTraffic(id);
if (i == activeTraffic.end()) {
// Dead traffic should never reach here
SG_LOG(SG_ATC, SG_ALERT,
"AI error: Aircraft without traffic record is signing off from " << getName() << " at " << SG_ORIGIN << " list " << activeTraffic.empty());
return;
}
activeTraffic.erase(i);
SG_LOG(SG_ATC, SG_DEBUG, i->getCallsign() << " signing off from " << getName() );
}
bool FGATCController::hasInstruction(int id)
{
// Search activeTraffic for a record matching our id
TrafficVectorIterator i = searchActiveTraffic(id);
if (i == activeTraffic.end()) {
SG_LOG(SG_ATC, SG_ALERT,
"AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
} else {
return i->hasInstruction();
}
return false;
}
FGATCInstruction FGATCController::getInstruction(int id)
{
// Search activeTraffic for a record matching our id
TrafficVectorIterator i = searchActiveTraffic(id);
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
SG_LOG(SG_ATC, SG_ALERT,
"AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
} else {
return i->getInstruction();
}
return FGATCInstruction();
}
/*
* Format integer frequency xxxyy as xxx.yy
* @param freq - integer value
* @return the formatted string
*/
string FGATCController::formatATCFrequency3_2(int freq)
{
char buffer[7]; // does this ever need to be freed?
snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
return string(buffer);
}
// TODO: Set transponder codes according to real-world routes.
// The current version just returns a random string of four octal numbers.
string FGATCController::genTransponderCode(const string& fltRules)
{
if (fltRules == "VFR")
return string("1200");
std::default_random_engine generator;
std::uniform_int_distribution<unsigned> distribution(0, 7);
unsigned val = (
distribution(generator) * 1000 +
distribution(generator) * 100 +
distribution(generator) * 10 +
distribution(generator));
return std::to_string(val);
}
void FGATCController::eraseDeadTraffic()
{
auto it = std::remove_if(activeTraffic.begin(), activeTraffic.end(), [](const FGTrafficRecord& traffic)
{
if (traffic.isDead()) {
SG_LOG(SG_ATC, SG_DEBUG, "Remove dead " << traffic.getId() << " " << traffic.isDead());
}
return traffic.isDead();
});
activeTraffic.erase(it, activeTraffic.end());
}
/*
* Search activeTraffic vector to find matching id
* @param id integer to search for in the vector
* @return the matching item OR activeTraffic.end()
*/
TrafficVectorIterator FGATCController::searchActiveTraffic(int id)
{
return std::find_if(activeTraffic.begin(), activeTraffic.end(),
[id] (const FGTrafficRecord& rec)
{ return rec.getId() == id; }
);
}
void FGATCController::clearTrafficControllers()
{
for (const auto& traffic : activeTraffic) {
traffic.clearATCController();
}
}

138
src/ATC/ATCController.hxx Normal file
View File

@@ -0,0 +1,138 @@
// Extracted from trafficcontrol.hxx - classes to manage AIModels based air traffic control
// Written by Durk Talsma, started September 2006.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef ATC_CONTROLLER_HXX
#define ATC_CONTROLLER_HXX
#include <Airports/airports_fwd.hxx>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osg/Shape>
#include <simgear/compiler.h>
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <ATC/trafficcontrol.hxx>
/**
* class FGATCController
* NOTE: this class serves as an abstraction layer for all sorts of ATC controllers.
*************************************************************************************/
class FGATCController
{
private:
protected:
// guard variable to avoid modifying state during destruction
bool _isDestroying = false;
bool initialized;
bool available;
time_t lastTransmission;
TrafficVector activeTraffic;
double dt_count;
osg::Group* group;
FGAirportDynamics *parent = nullptr;
std::string formatATCFrequency3_2(int );
std::string genTransponderCode(const std::string& fltRules);
bool isUserAircraft(FGAIAircraft*);
void clearTrafficControllers();
TrafficVectorIterator searchActiveTraffic(int id);
void eraseDeadTraffic();
/**Returns the frequency to be used. */
virtual int getFrequency() = 0;
public:
typedef enum {
MSG_ANNOUNCE_ENGINE_START,
MSG_REQUEST_ENGINE_START,
MSG_PERMIT_ENGINE_START,
MSG_DENY_ENGINE_START,
MSG_ACKNOWLEDGE_ENGINE_START,
MSG_REQUEST_PUSHBACK_CLEARANCE,
MSG_PERMIT_PUSHBACK_CLEARANCE,
MSG_HOLD_PUSHBACK_CLEARANCE,
MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY,
MSG_INITIATE_CONTACT,
MSG_ACKNOWLEDGE_INITIATE_CONTACT,
MSG_REQUEST_TAXI_CLEARANCE,
MSG_ISSUE_TAXI_CLEARANCE,
MSG_ACKNOWLEDGE_TAXI_CLEARANCE,
MSG_HOLD_POSITION,
MSG_ACKNOWLEDGE_HOLD_POSITION,
MSG_RESUME_TAXI,
MSG_ACKNOWLEDGE_RESUME_TAXI,
MSG_REPORT_RUNWAY_HOLD_SHORT,
MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT,
MSG_CLEARED_FOR_TAKEOFF,
MSG_ACKNOWLEDGE_CLEARED_FOR_TAKEOFF,
MSG_SWITCH_TOWER_FREQUENCY,
MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY
} AtcMsgId;
typedef enum {
ATC_AIR_TO_GROUND,
ATC_GROUND_TO_AIR
} AtcMsgDir;
FGATCController();
virtual ~FGATCController();
void init();
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) = 0;
virtual void updateAircraftInformation(int id, SGGeod geod,
double heading, double speed, double alt, double dt) = 0;
virtual void signOff(int id);
bool hasInstruction(int id);
FGATCInstruction getInstruction(int id);
bool hasActiveTraffic() {
return ! activeTraffic.empty();
};
TrafficVector &getActiveTraffic() {
return activeTraffic;
};
double getDt() {
return dt_count;
};
void setDt(double dt) {
dt_count = dt;
};
void transmit(FGTrafficRecord *rec, FGAirportDynamics *parent, AtcMsgId msgId, AtcMsgDir msgDir, bool audible);
std::string getGateName(FGAIAircraft *aircraft);
virtual void render(bool) = 0;
virtual std::string getName() = 0;
virtual void update(double) = 0;
private:
AtcMsgDir lastTransmissionDirection;
};
#endif

592
src/ATC/ATISEncoder.cxx Normal file
View File

@@ -0,0 +1,592 @@
/*
Encode an ATIS into spoken words
Copyright (C) 2014 Torsten Dreyer
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "ATISEncoder.hxx"
#include <Airports/dynamics.hxx>
#include <Main/globals.hxx>
#include <Main/locale.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/props/props_io.hxx>
#include <map>
#include <vector>
using std::string;
using std::vector;
using simgear::PropertyList;
static string NO_ATIS("nil");
static string EMPTY("");
#define SPACE append(1,' ')
std::string ATCSpeech::getSpokenDigit( int i )
{
string key = "n" + std::to_string(i);
return globals->get_locale()->getLocalizedString(key.c_str(), "atc", "" );
}
string ATCSpeech::getSpokenNumber( string number )
{
string result;
for( string::iterator it = number.begin(); it != number.end(); ++it ) {
if( !result.empty() )
result.SPACE;
result.append( getSpokenDigit( (*it) - '0' ));
}
return result;
}
string ATCSpeech::getSpokenNumber( int number, bool leadingZero, int digits )
{
string_list spokenDigits;
bool negative = false;
if( number < 0 ) {
negative = true;
number = -number;
}
int n = 0;
while( number > 0 ) {
spokenDigits.push_back( getSpokenDigit(number%10) );
number /= 10;
n++;
}
if( digits > 0 ) {
while( n++ < digits ) {
spokenDigits.push_back( getSpokenDigit(0) );
}
}
string result;
if( negative ) {
result.append( globals->get_locale()->getLocalizedString("minus", "atc", "minus" ) );
}
while( !spokenDigits.empty() ) {
if( !result.empty() )
result.SPACE;
result.append( spokenDigits.back() );
spokenDigits.pop_back();
}
return result;
}
string ATCSpeech::getSpokenAltitude( int altitude )
{
string result;
int thousands = altitude / 1000;
int hundrets = (altitude % 1000) / 100;
if( thousands > 0 ) {
result.append( getSpokenNumber(thousands) );
result.SPACE;
result.append( getSpokenDigit(1000) );
result.SPACE;
}
if( hundrets > 0 )
result.append( getSpokenNumber(hundrets) )
.SPACE
.append( getSpokenDigit(100) );
return result;
}
ATISEncoder::ATISEncoder()
{
handlerMap.insert( std::make_pair( "text", &ATISEncoder::processTextToken ));
handlerMap.insert( std::make_pair( "token", &ATISEncoder::processTokenToken ));
handlerMap.insert( std::make_pair( "if", &ATISEncoder::processIfToken ));
handlerMap.insert( std::make_pair( "section", &ATISEncoder::processTokens ));
handlerMap.insert( std::make_pair( "id", &ATISEncoder::getAtisId ));
handlerMap.insert( std::make_pair( "airport-name", &ATISEncoder::getAirportName ));
handlerMap.insert( std::make_pair( "time", &ATISEncoder::getTime ));
handlerMap.insert( std::make_pair( "approach-type", &ATISEncoder::getApproachType ));
handlerMap.insert( std::make_pair( "rwy-land", &ATISEncoder::getLandingRunway ));
handlerMap.insert( std::make_pair( "rwy-to", &ATISEncoder::getTakeoffRunway ));
handlerMap.insert( std::make_pair( "transition-level", &ATISEncoder::getTransitionLevel ));
handlerMap.insert( std::make_pair( "wind-dir", &ATISEncoder::getWindDirection ));
handlerMap.insert( std::make_pair( "wind-from", &ATISEncoder::getWindMinDirection ));
handlerMap.insert( std::make_pair( "wind-to", &ATISEncoder::getWindMaxDirection ));
handlerMap.insert( std::make_pair( "wind-speed-kn", &ATISEncoder::getWindspeedKnots ));
handlerMap.insert( std::make_pair( "gusts", &ATISEncoder::getGustsKnots ));
handlerMap.insert( std::make_pair( "visibility-metric", &ATISEncoder::getVisibilityMetric ));
handlerMap.insert( std::make_pair( "visibility-miles", &ATISEncoder::getVisibilityMiles ));
handlerMap.insert( std::make_pair( "phenomena", &ATISEncoder::getPhenomena ));
handlerMap.insert( std::make_pair( "clouds", &ATISEncoder::getClouds ));
handlerMap.insert( std::make_pair( "clouds-brief", &ATISEncoder::getCloudsBrief ));
handlerMap.insert( std::make_pair( "cavok", &ATISEncoder::getCavok ));
handlerMap.insert( std::make_pair( "temperature-deg", &ATISEncoder::getTemperatureDeg ));
handlerMap.insert( std::make_pair( "dewpoint-deg", &ATISEncoder::getDewpointDeg ));
handlerMap.insert( std::make_pair( "qnh", &ATISEncoder::getQnh ));
handlerMap.insert( std::make_pair( "inhg", &ATISEncoder::getInhg ));
handlerMap.insert( std::make_pair( "inhg-integer", &ATISEncoder::getInhgInteger ));
handlerMap.insert( std::make_pair( "inhg-fraction", &ATISEncoder::getInhgFraction ));
handlerMap.insert( std::make_pair( "trend", &ATISEncoder::getTrend ));
}
ATISEncoder::~ATISEncoder()
{
}
SGPropertyNode_ptr findAtisTemplate( const std::string & stationId, SGPropertyNode_ptr atisSchemaNode )
{
using simgear::strutils::starts_with;
SGPropertyNode_ptr atisTemplate;
PropertyList schemaNodes = atisSchemaNode->getChildren("atis-schema");
for( PropertyList::iterator asit = schemaNodes.begin(); asit != schemaNodes.end(); ++asit ) {
SGPropertyNode_ptr ppp = (*asit)->getNode("station-starts-with", false );
atisTemplate = (*asit)->getNode("atis", false );
if( !atisTemplate.valid() ) continue; // no <atis> node - ignore entry
PropertyList startsWithNodes = (*asit)->getChildren("station-starts-with");
for( PropertyList::iterator swit = startsWithNodes.begin(); swit != startsWithNodes.end(); ++swit ) {
if( starts_with( stationId, (*swit)->getStringValue() ) ) {
return atisTemplate;
}
}
}
return atisTemplate;
}
string ATISEncoder::encodeATIS( ATISInformationProvider * atisInformation )
{
using simgear::strutils::lowercase;
if( !atisInformation->isValid() ) return NO_ATIS;
airport = FGAirport::getByIdent( atisInformation->airportId() );
if( !airport.valid() ) {
SG_LOG( SG_ATC, SG_WARN, "ATISEncoder: unknown airport id " << atisInformation->airportId() );
return NO_ATIS;
}
_atis = atisInformation;
// lazily load the schema file on the first call
if( !atisSchemaNode.valid() ) {
atisSchemaNode = new SGPropertyNode();
try
{
SGPath path = globals->resolve_maybe_aircraft_path("ATC/atis.xml");
readProperties( path, atisSchemaNode );
}
catch (const sg_exception& e)
{
SG_LOG( SG_ATC, SG_ALERT, "ATISEncoder: Failed to load atis schema definition: " << e.getMessage());
return NO_ATIS;
}
}
string stationId = lowercase( airport->ident() );
SGPropertyNode_ptr atisTemplate = findAtisTemplate( stationId, atisSchemaNode );;
if( !atisTemplate.valid() ) {
SG_LOG(SG_ATC, SG_WARN, "no matching atis template for station " << stationId );
return NO_ATIS; // no template for this station!?
}
return processTokens( atisTemplate );
}
string ATISEncoder::processTokens( SGPropertyNode_ptr node )
{
string result;
if( node.valid() ) {
for( int i = 0; i < node->nChildren(); i++ ) {
result.append(processToken( node->getChild(i) ));
}
}
return result;
}
string ATISEncoder::processToken( SGPropertyNode_ptr token )
{
HandlerMap::iterator it = handlerMap.find( token->getNameString());
if( it == handlerMap.end() ) {
SG_LOG(SG_ATC, SG_WARN, "ATISEncoder: unknown token: " << token->getNameString() );
return EMPTY;
}
handler_t h = it->second;
return (this->*h)( token );
}
string ATISEncoder::processTextToken( SGPropertyNode_ptr token )
{
return token->getStringValue();
}
string ATISEncoder::processTokenToken( SGPropertyNode_ptr token )
{
HandlerMap::iterator it = handlerMap.find( token->getStringValue());
if( it == handlerMap.end() ) {
SG_LOG(SG_ATC, SG_WARN, "ATISEncoder: unknown token: " << token->getStringValue() );
return EMPTY;
}
handler_t h = it->second;
return (this->*h)( token );
}
string ATISEncoder::processIfToken( SGPropertyNode_ptr token )
{
using namespace simgear::strutils;
SGPropertyNode_ptr n;
if( (n = token->getChild("empty", false )).valid() ) {
return checkEmptyCondition( n, true) ?
processTokens(token->getChild("then",false)) :
processTokens(token->getChild("else",false));
}
if( (n = token->getChild("not-empty", false )).valid() ) {
return checkEmptyCondition( n, false) ?
processTokens(token->getChild("then",false)) :
processTokens(token->getChild("else",false));
}
if( (n = token->getChild("contains", false )).valid() ) {
return checkCondition( n, true, &contains, "contains") ?
processTokens(token->getChild("then",false)) :
processTokens(token->getChild("else",false));
}
if( (n = token->getChild("not-contains", false )).valid() ) {
return checkCondition( n, false, &contains, "not-contains") ?
processTokens(token->getChild("then",false)) :
processTokens(token->getChild("else",false));
}
if( (n = token->getChild("ends-with", false )).valid() ) {
return checkCondition( n, true, &ends_with, "ends-with") ?
processTokens(token->getChild("then",false)) :
processTokens(token->getChild("else",false));
}
if( (n = token->getChild("not-ends-with", false )).valid() ) {
return checkCondition( n, false, &ends_with, "not-ends-with") ?
processTokens(token->getChild("then",false)) :
processTokens(token->getChild("else",false));
}
if( (n = token->getChild("equals", false )).valid() ) {
return checkCondition( n, true, &equals, "equals") ?
processTokens(token->getChild("then",false)) :
processTokens(token->getChild("else",false));
}
if( (n = token->getChild("not-equals", false )).valid() ) {
return checkCondition( n, false, &equals, "not-equals") ?
processTokens(token->getChild("then",false)) :
processTokens(token->getChild("else",false));
}
if( (n = token->getChild("starts-with", false )).valid() ) {
return checkCondition( n, true, &starts_with, "starts-with") ?
processTokens(token->getChild("then",false)) :
processTokens(token->getChild("else",false));
}
if( (n = token->getChild("not-starts-with", false )).valid() ) {
return checkCondition( n, false, &starts_with, "not-starts-with") ?
processTokens(token->getChild("then",false)) :
processTokens(token->getChild("else",false));
}
SG_LOG(SG_ATC, SG_WARN, "ATISEncoder: no valid token found for <if> element" );
return EMPTY;
}
bool ATISEncoder::checkEmptyCondition( SGPropertyNode_ptr node, bool isEmpty )
{
SGPropertyNode_ptr n1 = node->getNode( "token", false );
if( !n1.valid() ) {
SG_LOG(SG_ATC, SG_WARN, "missing <token> node for (not)-empty" );
return false;
}
return processToken( n1 ).empty() == isEmpty;
}
bool ATISEncoder::checkCondition( SGPropertyNode_ptr node, bool notInverted,
bool (*fp)(const string &, const string &), const string &name )
{
using namespace simgear::strutils;
SGPropertyNode_ptr n1 = node->getNode( "token", 0, false );
SGPropertyNode_ptr n2 = node->getNode( "token", 1, false );
if( n1.valid() && n2.valid() ) {
bool comp = fp( processToken( n1 ), processToken( n2 ) );
return comp == notInverted;
}
if( n1.valid() && !n2.valid() ) {
SGPropertyNode_ptr t1 = node->getNode( "text", 0, false );
if( t1.valid() ) {
string n1s = lowercase( strip( processToken( n1 ) ) );
string t1s = lowercase( strip( processTextToken( t1 ) ) );
return fp( n1s, t1s ) == notInverted;
}
SG_LOG(SG_ATC, SG_WARN, "missing <token> or <text> node for " << name);
return false;
}
SG_LOG(SG_ATC, SG_WARN, "missing <token> node for " << name);
return false;
}
string ATISEncoder::getAtisId( SGPropertyNode_ptr )
{
FGAirportDynamics * dynamics = airport->getDynamics();
if( NULL != dynamics ) {
dynamics->updateAtisSequence( 30*60, false );
return dynamics->getAtisSequence();
}
return EMPTY;
}
string ATISEncoder::getAirportName( SGPropertyNode_ptr )
{
return airport->getName();
}
string ATISEncoder::getTime( SGPropertyNode_ptr )
{
return getSpokenNumber( _atis->getTime() % (100*100), true, 4 );
}
static inline FGRunwayRef findBestRunwayForWind( FGAirportRef airport, int windDeg, int windKt )
{
struct FGAirport::FindBestRunwayForHeadingParams p;
//TODO: ramp down the heading weight with wind speed
p.ilsWeight = 4;
return airport->findBestRunwayForHeading( windDeg, &p );
}
string ATISEncoder::getApproachType( SGPropertyNode_ptr )
{
FGRunwayRef runway = findBestRunwayForWind( airport, _atis->getWindDeg(), _atis->getWindSpeedKt() );
if( runway.valid() ) {
if( NULL != runway->ILS() ) return globals->get_locale()->getLocalizedString("ils", "atc", "ils" );
//TODO: any chance to find other approach types? localizer-dme, vor-dme, vor, ndb?
}
return globals->get_locale()->getLocalizedString("visual", "atc", "visual" );
}
string ATISEncoder::getLandingRunway( SGPropertyNode_ptr )
{
FGRunwayRef runway = findBestRunwayForWind( airport, _atis->getWindDeg(), _atis->getWindSpeedKt() );
if( runway.valid() ) {
string runwayIdent = runway->ident();
if(runwayIdent != "NN") {
return getSpokenNumber(runwayIdent);
}
}
return EMPTY;
}
string ATISEncoder::getTakeoffRunway( SGPropertyNode_ptr p )
{
//TODO: if the airport has more than one runway, probably pick another one?
return getLandingRunway( p );
}
string ATISEncoder::getTransitionLevel(SGPropertyNode_ptr)
{
double hPa = _atis->getQnh();
/* Transition level is the flight level above which aircraft must use standard pressure and below
* which airport pressure settings must be used.
* Following definitions are taken from German ATIS:
* QNH <= 977 hPa: TRL 80
* QNH <= 1013 hPa: TRL 70
* QNH > 1013 hPa: TRL 60
* (maybe differs slightly for other countries...)
*/
int tl;
if (hPa <= 978) {
tl = 80;
} else if (hPa <= 1013) {
tl = 70;
} else if (hPa <= 1046) {
tl = 60;
} else {
tl = 50;
}
// add an offset to the transition level for high altitude airports (just guessing here,
// seems reasonable)
int e = int(airport->getElevation() / 1000.0);
if (e >= 3) {
// TL steps in 10(00)ft
tl += (e - 2) * 10;
}
return getSpokenNumber(tl);
}
string ATISEncoder::getWindDirection( SGPropertyNode_ptr )
{
string variable = globals->get_locale()->getLocalizedString("variable", "atc", "variable" );
bool vrb = _atis->getWindMinDeg() == 0 && _atis->getWindMaxDeg() == 359;
return vrb ? variable : getSpokenNumber( _atis->getWindDeg(), true, 3 );
}
string ATISEncoder::getWindMinDirection( SGPropertyNode_ptr )
{
return getSpokenNumber( _atis->getWindMinDeg(), true, 3 );
}
string ATISEncoder::getWindMaxDirection( SGPropertyNode_ptr )
{
return getSpokenNumber( _atis->getWindMaxDeg(), true, 3 );
}
string ATISEncoder::getWindspeedKnots( SGPropertyNode_ptr )
{
return getSpokenNumber( _atis->getWindSpeedKt() );
}
string ATISEncoder::getGustsKnots( SGPropertyNode_ptr )
{
int g = _atis->getGustsKt();
return g > 0 ? getSpokenNumber( g ) : EMPTY;
}
string ATISEncoder::getCavok( SGPropertyNode_ptr )
{
string CAVOK = globals->get_locale()->getLocalizedString("cavok", "atc", "cavok" );
return _atis->isCavok() ? CAVOK : EMPTY;
}
string ATISEncoder::getVisibilityMetric( SGPropertyNode_ptr )
{
string m = globals->get_locale()->getLocalizedString("meters", "atc", "meters" );
string km = globals->get_locale()->getLocalizedString("kilometers", "atc", "kilometers" );
string or_more = globals->get_locale()->getLocalizedString("ormore", "atc", "or more" );
int v = _atis->getVisibilityMeters();
string reply;
if( v < 5000 ) return reply.append( getSpokenAltitude( v ) ).SPACE.append( m );
if( v >= 9999 ) return reply.append( getSpokenNumber(10) ).SPACE.append( km ).SPACE.append(or_more);
return reply.append( getSpokenNumber( v/1000 ).SPACE.append( km ) );
}
string ATISEncoder::getVisibilityMiles( SGPropertyNode_ptr )
{
string feet = globals->get_locale()->getLocalizedString("feet", "atc", "feet" );
int v = _atis->getVisibilityMeters();
int vft = int( v * SG_METER_TO_FEET / 100 + 0.5 ) * 100; // Rounded to 100 ft
int vsm = int( v * SG_METER_TO_SM + 0.5 );
string reply;
if( vsm < 1 ) return reply.append( getSpokenAltitude( vft ) ).SPACE.append( feet );
if( vsm >= 10 ) return reply.append( getSpokenNumber(10) );
return reply.append( getSpokenNumber( vsm ) );
}
string ATISEncoder::getPhenomena( SGPropertyNode_ptr )
{
return _atis->getPhenomena();
}
string ATISEncoder::getClouds( SGPropertyNode_ptr )
{
string FEET = globals->get_locale()->getLocalizedString("feet", "atc", "feet" );
string reply;
ATISInformationProvider::CloudEntries cloudEntries = _atis->getClouds();
for (ATISInformationProvider::CloudEntries::iterator it = cloudEntries.begin(); it != cloudEntries.end(); ++it) {
if (!reply.empty())
reply.SPACE;
reply.append( it->second ).SPACE.append( getSpokenAltitude(it->first).SPACE.append( FEET ) );
}
return reply;
}
string ATISEncoder::getCloudsBrief( SGPropertyNode_ptr )
{
string reply;
ATISInformationProvider::CloudEntries cloudEntries = _atis->getClouds();
for (ATISInformationProvider::CloudEntries::iterator it = cloudEntries.begin(); it != cloudEntries.end(); ++it) {
if (!reply.empty())
reply.append(",").SPACE;
reply.append( it->second ).SPACE.append( getSpokenAltitude(it->first) );
}
return reply;
}
string ATISEncoder::getTemperatureDeg( SGPropertyNode_ptr )
{
return getSpokenNumber( _atis->getTemperatureDeg() );
}
string ATISEncoder::getDewpointDeg( SGPropertyNode_ptr )
{
return getSpokenNumber( _atis->getDewpointDeg() );
}
string ATISEncoder::getQnh( SGPropertyNode_ptr )
{
return getSpokenNumber( _atis->getQnh() );
}
string ATISEncoder::getInhgInteger( SGPropertyNode_ptr )
{
double qnh = _atis->getQnhInHg();
return getSpokenNumber( (int)qnh, true, 2 );
}
string ATISEncoder::getInhgFraction( SGPropertyNode_ptr )
{
double qnh = _atis->getQnhInHg();
int f = int(100 * (qnh - int(qnh)) + 0.5);
return getSpokenNumber( f, true, 2 );
}
string ATISEncoder::getInhg( SGPropertyNode_ptr node)
{
string DECIMAL = globals->get_locale()->getLocalizedString("dp", "atc", "decimal" );
return getInhgInteger(node)
.SPACE.append(DECIMAL).SPACE
.append(getInhgFraction(node));
}
string ATISEncoder::getTrend( SGPropertyNode_ptr )
{
return _atis->getTrend();
}

133
src/ATC/ATISEncoder.hxx Normal file
View File

@@ -0,0 +1,133 @@
/*
Encode an ATIS into spoken words
Copyright (C) 2014 Torsten Dreyer
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __ATIS_ENCODER_HXX
#define __ATIS_ENCODER_HXX
#include <string>
#include <Airports/airport.hxx>
#include <simgear/props/props.hxx>
#include <map>
class ATCSpeech {
public:
static std::string getSpokenDigit( int i );
static std::string getSpokenNumber( std::string number );
static std::string getSpokenNumber( int number, bool leadingZero = false, int digits = 1 );
static std::string getSpokenAltitude( int altitude );
};
class ATISInformationProvider {
public:
virtual ~ATISInformationProvider() {}
virtual bool isValid() = 0;
virtual std::string airportId() = 0;
static long makeAtisTime( int day, int hour, int minute ) {
return 100*100l* day + 100l * hour + minute;
}
inline int getAtisTimeDay( long atisTime ) { return atisTime / (100l*100l); }
inline int getAtisTimeHour( long atisTime ) { return (atisTime % (100l*100l)) / 100l; }
inline int getAtisTimeMinute( long atisTime ) { return atisTime % 100l; }
virtual long getTime() = 0; // see makeAtisTime
virtual int getWindDeg() = 0;
virtual int getWindMinDeg() = 0;
virtual int getWindMaxDeg() = 0;
virtual int getWindSpeedKt() = 0;
virtual int getGustsKt() = 0;
virtual int getQnh() = 0;
virtual double getQnhInHg() = 0;
virtual bool isCavok() = 0;
virtual int getVisibilityMeters() = 0;
virtual std::string getPhenomena() = 0;
typedef std::map<int,std::string> CloudEntries;
virtual CloudEntries getClouds() = 0;
virtual int getTemperatureDeg() = 0;
virtual int getDewpointDeg() = 0;
virtual std::string getTrend() = 0;
};
class ATISEncoder : public ATCSpeech {
public:
ATISEncoder();
virtual ~ATISEncoder();
virtual std::string encodeATIS( ATISInformationProvider * atisInformationProvider );
protected:
virtual std::string getAtisId( SGPropertyNode_ptr );
virtual std::string getAirportName( SGPropertyNode_ptr );
virtual std::string getTime( SGPropertyNode_ptr );
virtual std::string getApproachType( SGPropertyNode_ptr );
virtual std::string getLandingRunway( SGPropertyNode_ptr );
virtual std::string getTakeoffRunway( SGPropertyNode_ptr );
virtual std::string getTransitionLevel( SGPropertyNode_ptr );
virtual std::string getWindDirection( SGPropertyNode_ptr );
virtual std::string getWindMinDirection( SGPropertyNode_ptr );
virtual std::string getWindMaxDirection( SGPropertyNode_ptr );
virtual std::string getWindspeedKnots( SGPropertyNode_ptr );
virtual std::string getGustsKnots( SGPropertyNode_ptr );
virtual std::string getCavok( SGPropertyNode_ptr );
virtual std::string getVisibilityMetric( SGPropertyNode_ptr );
virtual std::string getVisibilityMiles( SGPropertyNode_ptr );
virtual std::string getPhenomena( SGPropertyNode_ptr );
virtual std::string getClouds( SGPropertyNode_ptr );
virtual std::string getCloudsBrief( SGPropertyNode_ptr );
virtual std::string getTemperatureDeg( SGPropertyNode_ptr );
virtual std::string getDewpointDeg( SGPropertyNode_ptr );
virtual std::string getQnh( SGPropertyNode_ptr );
virtual std::string getInhgInteger( SGPropertyNode_ptr );
virtual std::string getInhgFraction( SGPropertyNode_ptr );
virtual std::string getInhg( SGPropertyNode_ptr );
virtual std::string getTrend( SGPropertyNode_ptr );
typedef std::string (ATISEncoder::*handler_t)( SGPropertyNode_ptr baseNode );
typedef std::map<std::string, handler_t > HandlerMap;
HandlerMap handlerMap;
SGPropertyNode_ptr atisSchemaNode;
std::string processTokens( SGPropertyNode_ptr baseNode );
std::string processToken( SGPropertyNode_ptr baseNode );
std::string processTextToken( SGPropertyNode_ptr baseNode );
std::string processTokenToken( SGPropertyNode_ptr baseNode );
std::string processIfToken( SGPropertyNode_ptr baseNode );
bool checkEmptyCondition( SGPropertyNode_ptr node, bool isEmpty );
// Wrappers that can be passed as function pointers to checkCondition
// @see simgear::strutils::starts_with
// @see simgear::strutils::ends_with
static bool contains(const string &s, const string &substring)
{ return s.find(substring) != std::string::npos; };
static bool equals(const string &s1, const string &s2)
{ return s1 == s2; };
bool checkCondition( SGPropertyNode_ptr node, bool notInverted,
bool (*fp)(const std::string &, const std::string &),
const std::string &name );
FGAirportRef airport;
ATISInformationProvider * _atis;
};
#endif

View File

@@ -0,0 +1,195 @@
// Extracted from trafficrecord.cxx - Implementation of AIModels ATC code.
//
// Written by Durk Talsma, started September 2006.
//
// Copyright (C) 2006 Durk Talsma.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <config.h>
#include <algorithm>
#include <cstdio>
#include <random>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osg/Shape>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/material/mat.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/timing/sg_time.hxx>
#include <Scenery/scenery.hxx>
#include "trafficcontrol.hxx"
#include "atc_mgr.hxx"
#include <AIModel/AIAircraft.hxx>
#include <AIModel/AIFlightPlan.hxx>
#include <AIModel/performancedata.hxx>
#include <Traffic/TrafficMgr.hxx>
#include <Airports/groundnetwork.hxx>
#include <Airports/dynamics.hxx>
#include <Airports/airport.hxx>
#include <Radio/radio.hxx>
#include <signal.h>
#include <ATC/atc_mgr.hxx>
#include <ATC/trafficcontrol.hxx>
#include <ATC/ATCController.hxx>
#include <ATC/ApproachController.hxx>
using std::sort;
using std::string;
/***************************************************************************
* class FGApproachController
* subclass of FGATCController
**************************************************************************/
FGApproachController::FGApproachController(FGAirportDynamics *par):
FGATCController()
{
parent = par;
}
FGApproachController::~FGApproachController()
{
}
void FGApproachController::announcePosition(int id,
FGAIFlightPlan * intendedRoute,
int currentPosition,
double lat, double lon,
double heading, double speed,
double alt, double radius,
int leg, FGAIAircraft * ref)
{
init();
// Search activeTraffic for a record matching our id
TrafficVectorIterator i = FGATCController::searchActiveTraffic(id);
// Add a new TrafficRecord if no one exsists for this aircraft.
if (i == activeTraffic.end() || activeTraffic.empty()) {
FGTrafficRecord rec;
rec.setId(id);
rec.setPositionAndHeading(lat, lon, heading, speed, alt);
rec.setRunway(intendedRoute->getRunway());
rec.setLeg(leg);
rec.setCallsign(ref->getCallSign());
rec.setAircraft(ref);
activeTraffic.push_back(rec);
} else {
i->setPositionAndHeading(lat, lon, heading, speed, alt);
}
}
void FGApproachController::updateAircraftInformation(int id, SGGeod geod,
double heading, double speed, double alt,
double dt)
{
// Search activeTraffic for a record matching our id
TrafficVectorIterator i = FGATCController::searchActiveTraffic(id);
TrafficVectorIterator current;
// update position of the current aircraft
if (i == activeTraffic.end() || activeTraffic.empty()) {
SG_LOG(SG_ATC, SG_ALERT,
"AI error: updating aircraft without traffic record at " << SG_ORIGIN);
} else {
i->setPositionAndHeading(geod.getLatitudeDeg(), geod.getLongitudeDeg(), heading, speed, alt);
current = i;
if(current->getAircraft()) {
//FIXME No call to aircraft! -> set instruction
time_t time_diff =
current->getAircraft()->
checkForArrivalTime(string("final001"));
if (time_diff != 0) {
SG_LOG(SG_ATC, SG_BULK, current->getCallsign() << "|ApproachController: checking for speed " << time_diff);
}
if (time_diff > 15) {
current->setSpeedAdjustment(current->getAircraft()->
getPerformance()->vDescent() *
1.35);
} else if (time_diff > 5) {
current->setSpeedAdjustment(current->getAircraft()->
getPerformance()->vDescent() *
1.2);
} else if (time_diff < -15) {
current->setSpeedAdjustment(current->getAircraft()->
getPerformance()->vDescent() *
0.65);
} else if (time_diff < -5) {
current->setSpeedAdjustment(current->getAircraft()->
getPerformance()->vDescent() *
0.8);
} else {
current->clearSpeedAdjustment();
}
}
//current->setSpeedAdjustment(current->getAircraft()->getPerformance()->vDescent() + time_diff);
}
setDt(getDt() + dt);
}
/* Periodically check for and remove dead traffic records */
void FGApproachController::update(double dt)
{
FGATCController::eraseDeadTraffic();
}
ActiveRunway *FGApproachController::getRunway(const string& name)
{
ActiveRunwayVecIterator rwy = activeRunways.begin();
if (activeRunways.size()) {
while (rwy != activeRunways.end()) {
if (rwy->getRunwayName() == name) {
break;
}
rwy++;
}
}
if (rwy == activeRunways.end()) {
ActiveRunway aRwy(name, 0);
activeRunways.push_back(aRwy);
rwy = activeRunways.end() - 1;
}
return &(*rwy);
}
void FGApproachController::render(bool visible) {
// Must be BULK in order to prevent it being called each frame
SG_LOG(SG_ATC, SG_BULK, "FGApproachController::render function not yet implemented");
}
string FGApproachController::getName() {
return string(parent->parent()->getName() + "-approach");
}
int FGApproachController::getFrequency() {
int groundFreq = parent->getApproachFrequency(2);
int towerFreq = parent->getTowerFrequency(2);
return groundFreq>0?groundFreq:towerFreq;
}

View File

@@ -0,0 +1,67 @@
// Extracted from trafficcontrol.hxx - classes to manage AIModels based air traffic control
// Written by Durk Talsma, started September 2006.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef APPROACH_CONTROLLER_HXX
#define APPROACH_CONTROLLER_HXX
#include <Airports/airports_fwd.hxx>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osg/Shape>
#include <simgear/compiler.h>
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <ATC/ATCController.hxx>
#include <ATC/trafficcontrol.hxx>
/******************************************************************************
* class FGApproachController
*****************************************************************************/
class FGApproachController : public FGATCController
{
private:
ActiveRunwayVec activeRunways;
/**Returns the frequency to be used. */
int getFrequency();
public:
FGApproachController(FGAirportDynamics * parent);
virtual ~FGApproachController();
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 updateAircraftInformation(int id, SGGeod geod,
double heading, double speed, double alt, double dt);
virtual void render(bool);
virtual std::string getName();
virtual void update(double dt);
ActiveRunway* getRunway(const std::string& name);
};
#endif

31
src/ATC/CMakeLists.txt Normal file
View File

@@ -0,0 +1,31 @@
include(FlightGearComponent)
set(SOURCES
atc_mgr.cxx
trafficcontrol.cxx
CommStation.cxx
ATISEncoder.cxx
MetarPropertiesATISInformationProvider.cxx
CurrentWeatherATISInformationProvider.cxx
ATCController.cxx
ApproachController.cxx
GroundController.cxx
StartupController.cxx
TowerController.cxx
)
set(HEADERS
atc_mgr.hxx
trafficcontrol.hxx
CommStation.hxx
ATISEncoder.hxx
MetarPropertiesATISInformationProvider.hxx
CurrentWeatherATISInformationProvider.hxx
ATCController.hxx
ApproachController.hxx
GroundController.hxx
StartupController.hxx
TowerController.hxx
)
flightgear_component(ATC "${SOURCES}" "${HEADERS}")

38
src/ATC/CommStation.cxx Normal file
View File

@@ -0,0 +1,38 @@
#include "config.h"
#include "CommStation.hxx"
#include <Airports/airport.hxx>
#include <Navaids/NavDataCache.hxx>
namespace flightgear {
CommStation::CommStation(PositionedID aGuid, const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq) :
FGPositioned(aGuid, t, name, pos),
mRangeNM(range),
mFreqKhz(freq),
mAirport(0)
{
}
void CommStation::setAirport(PositionedID apt)
{
mAirport = apt;
}
FGAirportRef CommStation::airport() const
{
return FGPositioned::loadById<FGAirport>(mAirport);
}
double CommStation::freqMHz() const
{
return mFreqKhz / 1000.0;
}
CommStationRef
CommStation::findByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt)
{
return (CommStation*) NavDataCache::instance()->findCommByFreq(freqKhz, pos, filt).ptr();
}
} // of namespace flightgear

36
src/ATC/CommStation.hxx Normal file
View File

@@ -0,0 +1,36 @@
#ifndef FG_ATC_COMM_STATION_HXX
#define FG_ATC_COMM_STATION_HXX
#include <Airports/airports_fwd.hxx>
#include <Navaids/positioned.hxx>
namespace flightgear
{
class CommStation : public FGPositioned
{
public:
CommStation(PositionedID aGuid, const std::string& name, FGPositioned::Type t, const SGGeod& pos, int range, int freq);
void setAirport(PositionedID apt);
FGAirportRef airport() const;
int rangeNm() const
{ return mRangeNM; }
int freqKHz() const
{ return mFreqKhz; }
double freqMHz() const;
static CommStationRef findByFreq(int freqKhz, const SGGeod& pos, FGPositioned::Filter* filt = NULL);
private:
int mRangeNM;
int mFreqKhz;
PositionedID mAirport;
};
} // of namespace flightgear
#endif // of FG_ATC_COMM_STATION_HXX

View File

@@ -0,0 +1,145 @@
/*
Provide Data for the ATIS Encoder from metarproperties
Copyright (C) 2014 Torsten Dreyer
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "CurrentWeatherATISInformationProvider.hxx"
#include <Main/fg_props.hxx>
using std::string;
CurrentWeatherATISInformationProvider::CurrentWeatherATISInformationProvider( const std::string & airportId ) :
_airportId(airportId),
_environment(fgGetNode("/environment"))
{
}
static inline int roundToInt( double d )
{
return (static_cast<int>(10.0 * (d + .5))) / 10;
}
static inline int roundToInt( SGPropertyNode_ptr n )
{
return roundToInt( n->getDoubleValue() );
}
static inline int roundToInt( SGPropertyNode_ptr n, const char * child )
{
return roundToInt( n->getNode(child,true) );
}
CurrentWeatherATISInformationProvider::~CurrentWeatherATISInformationProvider()
{
}
bool CurrentWeatherATISInformationProvider::isValid()
{
return true;
}
string CurrentWeatherATISInformationProvider::airportId()
{
return _airportId;
}
long CurrentWeatherATISInformationProvider::getTime()
{
int h = fgGetInt( "/sim/time/utc/hour", 12 );
int m = 20 + fgGetInt( "/sim/time/utc/minute", 0 ) / 30 ; // fake twice per hour
return makeAtisTime( 0, h, m );
}
int CurrentWeatherATISInformationProvider::getWindDeg()
{
// round to 10 degs
int i = 5 + roundToInt( _environment->getNode("config/boundary/entry[0]/wind-from-heading-deg",true) );
i /= 10;
return i*10;
}
int CurrentWeatherATISInformationProvider::getWindSpeedKt()
{
return roundToInt( _environment, "config/boundary/entry[0]/wind-speed-kt" );
}
int CurrentWeatherATISInformationProvider::getGustsKt()
{
return 0;
}
int CurrentWeatherATISInformationProvider::getQnh()
{
// TODO: Calculate QNH correctly from environment
return roundToInt( _environment->getNode("pressure-sea-level-inhg",true)->getDoubleValue() * SG_INHG_TO_PA / 100 );
}
double CurrentWeatherATISInformationProvider::getQnhInHg()
{
// TODO: Calculate QNH correctly from environment
return _environment->getNode("pressure-sea-level-inhg",true)->getDoubleValue();
}
bool CurrentWeatherATISInformationProvider::isCavok()
{
return false;
}
int CurrentWeatherATISInformationProvider::getVisibilityMeters()
{
return roundToInt( _environment, "ground-visibility-m" );
}
string CurrentWeatherATISInformationProvider::getPhenomena()
{
return "";
}
ATISInformationProvider::CloudEntries CurrentWeatherATISInformationProvider::getClouds()
{
using simgear::PropertyList;
ATISInformationProvider::CloudEntries cloudEntries;
PropertyList layers = _environment->getNode("clouds",true)->getChildren("layer");
for( PropertyList::iterator it = layers.begin(); it != layers.end(); ++it ) {
string coverage = (*it)->getStringValue( "coverage", "clear" );
int alt = roundToInt( (*it)->getDoubleValue("elevation-ft", -9999 ) ) / 100;
alt *= 100;
if( coverage != "clear" && alt > 0 )
cloudEntries[alt] = coverage;
}
return cloudEntries;
}
int CurrentWeatherATISInformationProvider::getTemperatureDeg()
{
return roundToInt( _environment, "temperature-sea-level-degc" );
}
int CurrentWeatherATISInformationProvider::getDewpointDeg()
{
return roundToInt( _environment, "dewpoint-sea-level-degc" );
}
string CurrentWeatherATISInformationProvider::getTrend()
{
return "nosig";
}

View File

@@ -0,0 +1,58 @@
/*
Provide Data for the ATIS Encoder from metarproperties
Copyright (C) 2014 Torsten Dreyer
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __CURRENTWEATHER_ATIS_ENCODER_HXX
#define __CURRENTWEATHER_ATIS_ENCODER_HXX
/* ATIS encoder from current weather */
#include <string>
#include "ATISEncoder.hxx"
class CurrentWeatherATISInformationProvider : public ATISInformationProvider
{
public:
CurrentWeatherATISInformationProvider( const std::string & airportId );
virtual ~CurrentWeatherATISInformationProvider();
protected:
virtual bool isValid();
virtual std::string airportId();
virtual long getTime();
virtual int getWindDeg();
virtual int getWindMinDeg() { return getWindDeg(); }
virtual int getWindMaxDeg() { return getWindDeg(); }
virtual int getWindSpeedKt();
virtual int getGustsKt();
virtual int getQnh();
virtual double getQnhInHg();
virtual bool isCavok();
virtual int getVisibilityMeters();
virtual std::string getPhenomena();
virtual CloudEntries getClouds();
virtual int getTemperatureDeg();
virtual int getDewpointDeg();
virtual std::string getTrend();
private:
std::string _airportId;
SGPropertyNode_ptr _environment;
};
#endif

1002
src/ATC/GroundController.cxx Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,90 @@
// GroundController.hxx - forked from groundnetwork.cxx
//
// Written by Durk Talsma, started June 2005.
//
// Copyright (C) 2004 Durk Talsma.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef ATC_GROUND_CONTROLLER_HXX
#define ATC_GROUND_CONTROLLER_HXX
#include <simgear/compiler.h>
#include <string>
#include <ATC/trafficcontrol.hxx>
class FGAirportDynamics;
/**************************************************************************************
* class FGGroundController
*************************************************************************************/
class FGGroundController : public FGATCController
{
private:
bool hasNetwork;
bool networkInitialized;
int count;
int version;
FGTowerController *towerController;
/**Returns the frequency to be used. */
int getFrequency();
void checkSpeedAdjustment(int id, double lat, double lon,
double heading, double speed, double alt);
void checkHoldPosition(int id, double lat, double lon,
double heading, double speed, double alt);
void updateStartupTraffic(TrafficVectorIterator i, int& priority, time_t now);
bool updateActiveTraffic(TrafficVectorIterator i, int& priority, time_t now);
public:
FGGroundController(FGAirportDynamics *par);
~FGGroundController();
void setVersion (int v) { version = v;};
int getVersion() { return version; };
bool exists() {
return hasNetwork;
};
void setTowerController(FGTowerController *twrCtrlr) {
towerController = twrCtrlr;
};
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 updateAircraftInformation(int id, SGGeod geod, double heading, double speed, double alt, double dt);
bool checkTransmissionState(int minState, int MaxState, TrafficVectorIterator i, time_t now, AtcMsgId msgId,
AtcMsgDir msgDir);
bool checkForCircularWaits(int id);
virtual void render(bool);
virtual std::string getName();
virtual void update(double dt);
void addVersion(int v) {version = v; };
};
#endif

View File

@@ -0,0 +1,130 @@
/*
Provide Data for the ATIS Encoder from metarproperties
Copyright (C) 2014 Torsten Dreyer
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "MetarPropertiesATISInformationProvider.hxx"
#include <Main/globals.hxx>
#include <simgear/constants.h>
using std::string;
MetarPropertiesATISInformationProvider::MetarPropertiesATISInformationProvider( SGPropertyNode_ptr metar ) :
_metar( metar )
{
}
MetarPropertiesATISInformationProvider::~MetarPropertiesATISInformationProvider()
{
}
bool MetarPropertiesATISInformationProvider::isValid()
{
return _metar->getBoolValue( "valid", false );
}
string MetarPropertiesATISInformationProvider::airportId()
{
return _metar->getStringValue( "station-id", "xxxx" );
}
long MetarPropertiesATISInformationProvider::getTime()
{
return makeAtisTime( 0,
_metar->getIntValue( "hour" ) % 24,
_metar->getIntValue( "minute" ) % 60 );
}
int MetarPropertiesATISInformationProvider::getWindDeg()
{
return _metar->getIntValue( "base-wind-dir-deg" );
}
int MetarPropertiesATISInformationProvider::getWindMinDeg()
{
return _metar->getIntValue( "base-wind-range-from" );
}
int MetarPropertiesATISInformationProvider::getWindMaxDeg()
{
return _metar->getIntValue( "base-wind-range-to" );
}
int MetarPropertiesATISInformationProvider::getWindSpeedKt()
{
return _metar->getIntValue( "base-wind-speed-kt" );
}
int MetarPropertiesATISInformationProvider::getGustsKt()
{
return _metar->getIntValue( "gust-wind-speed-kt" );
}
int MetarPropertiesATISInformationProvider::getQnh()
{
return _metar->getDoubleValue("pressure-inhg") * SG_INHG_TO_PA / 100.0;
}
double MetarPropertiesATISInformationProvider::getQnhInHg()
{
return _metar->getDoubleValue("pressure-inhg");
}
bool MetarPropertiesATISInformationProvider::isCavok()
{
return _metar->getBoolValue( "cavok" );
}
int MetarPropertiesATISInformationProvider::getVisibilityMeters()
{
return _metar->getIntValue( "min-visibility-m" );
}
string MetarPropertiesATISInformationProvider::getPhenomena()
{
return _metar->getStringValue( "decoded" );
}
ATISInformationProvider::CloudEntries MetarPropertiesATISInformationProvider::getClouds()
{
CloudEntries reply;
using simgear::PropertyList;
PropertyList layers = _metar->getNode("clouds", true )->getChildren("layer");
for( PropertyList::iterator it = layers.begin(); it != layers.end(); ++it ) {
std::string coverage = (*it)->getStringValue("coverage", "clear");
double elevation = (*it)->getDoubleValue("elevation-ft", -9999 );
if( elevation > 0 ) {
reply[elevation] = coverage;
}
}
return reply;
}
int MetarPropertiesATISInformationProvider::getTemperatureDeg()
{
return _metar->getIntValue( "temperature-degc" );
}
int MetarPropertiesATISInformationProvider::getDewpointDeg()
{
return _metar->getIntValue( "dewpoint-degc" );
}
string MetarPropertiesATISInformationProvider::getTrend()
{
return "nosig";
}

View File

@@ -0,0 +1,58 @@
/*
Provide Data for the ATIS Encoder from metarproperties
Copyright (C) 2014 Torsten Dreyer
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __METARPROPERTIES_ATIS_ENCODER_HXX
#define __METARPROPERTIES_ATIS_ENCODER_HXX
/* ATIS encoder from metarproperties */
#include <simgear/props/props.hxx>
#include <string>
#include "ATISEncoder.hxx"
class MetarPropertiesATISInformationProvider : public ATISInformationProvider
{
public:
MetarPropertiesATISInformationProvider( SGPropertyNode_ptr metar );
virtual ~MetarPropertiesATISInformationProvider();
protected:
virtual bool isValid();
virtual std::string airportId();
virtual long getTime();
virtual int getWindDeg();
virtual int getWindMinDeg();
virtual int getWindMaxDeg();
virtual int getWindSpeedKt();
virtual int getGustsKt();
virtual int getQnh();
virtual double getQnhInHg();
virtual bool isCavok();
virtual int getVisibilityMeters();
virtual std::string getPhenomena();
virtual CloudEntries getClouds();
virtual int getTemperatureDeg();
virtual int getDewpointDeg();
virtual std::string getTrend();
private:
SGPropertyNode_ptr _metar;
};
#endif

View File

@@ -0,0 +1,442 @@
// Extracted from trafficrecord.cxx - Implementation of AIModels ATC code.
//
// Written by Durk Talsma, started September 2006.
//
// Copyright (C) 2006 Durk Talsma.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <config.h>
#include <algorithm>
#include <cstdio>
#include <random>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osg/Shape>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/material/mat.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/timing/sg_time.hxx>
#include <Scenery/scenery.hxx>
#include "trafficcontrol.hxx"
#include "atc_mgr.hxx"
#include <AIModel/AIAircraft.hxx>
#include <AIModel/AIFlightPlan.hxx>
#include <AIModel/performancedata.hxx>
#include <Traffic/TrafficMgr.hxx>
#include <Airports/groundnetwork.hxx>
#include <Airports/dynamics.hxx>
#include <Airports/airport.hxx>
#include <Radio/radio.hxx>
#include <signal.h>
#include <ATC/atc_mgr.hxx>
#include <ATC/trafficcontrol.hxx>
#include <ATC/ATCController.hxx>
#include <ATC/StartupController.hxx>
using std::sort;
using std::string;
/***************************************************************************
* class FGStartupController
* subclass of FGATCController
**************************************************************************/
FGStartupController::FGStartupController(FGAirportDynamics *par):
FGATCController()
{
parent = par;
}
FGStartupController::~FGStartupController()
{
}
void FGStartupController::announcePosition(int id,
FGAIFlightPlan * intendedRoute,
int currentPosition, double lat,
double lon, double heading,
double speed, double alt,
double radius, int leg,
FGAIAircraft * ref)
{
init();
// Search activeTraffic for a record matching our id
TrafficVectorIterator i = FGATCController::searchActiveTraffic(id);
// Add a new TrafficRecord if no one exsists for this aircraft.
if (i == activeTraffic.end() || activeTraffic.empty()) {
FGTrafficRecord rec;
rec.setId(id);
rec.setPositionAndHeading(lat, lon, heading, speed, alt);
rec.setRunway(intendedRoute->getRunway());
rec.setLeg(leg);
rec.setPositionAndIntentions(currentPosition, intendedRoute);
rec.setCallsign(ref->getCallSign());
rec.setAircraft(ref);
rec.setHoldPosition(true);
activeTraffic.push_back(rec);
} else {
i->setPositionAndIntentions(currentPosition, intendedRoute);
i->setPositionAndHeading(lat, lon, heading, speed, alt);
}
}
bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId,
AtcMsgDir msgDir)
{
int state = i->getState();
if ((state == st) && available) {
if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
SG_LOG(SG_ATC, SG_BULK, "Checking state " << st << " for " << i->getAircraft()->getCallSign());
SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
int n = trans_num->getIntValue();
if (n == 0) {
trans_num->setIntValue(-1);
// PopupCallback(n);
SG_LOG(SG_ATC, SG_BULK, "Selected transmission message " << n);
//FGATCDialogNew::instance()->removeEntry(1);
} else {
SG_LOG(SG_ATC, SG_BULK, "Creating message for " << i->getAircraft()->getCallSign());
transmit(&(*i), &(*parent), msgId, msgDir, false);
return false;
}
}
if (now > startTime) {
SG_LOG(SG_ATC, SG_BULK, "Transmitting startup msg");
transmit(&(*i), &(*parent), msgId, msgDir, true);
i->updateState();
lastTransmission = now;
available = false;
return true;
}
}
return false;
}
void FGStartupController::updateAircraftInformation(int id, SGGeod geod,
double heading, double speed, double alt,
double dt)
{
// Search activeTraffic for a record matching our id
TrafficVectorIterator i = FGATCController::searchActiveTraffic(id);
TrafficVectorIterator current, closest;
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
SG_LOG(SG_ATC, SG_ALERT,
"AI error: updating aircraft without traffic record at " << SG_ORIGIN);
return;
} else {
i->setPositionAndHeading(geod.getLatitudeDeg(), geod.getLongitudeDeg(), heading, speed, alt);
current = i;
}
setDt(getDt() + dt);
int state = i->getState();
// Sentry FLIGHTGEAR-2Q : don't crash on null TrafficRef
// Sentry FLIGHTGEAR-129: don't crash on null aircraft
if (!i->getAircraft() || !i->getAircraft()->getTrafficRef()) {
SG_LOG(SG_ATC, SG_ALERT, "AI traffic: updating aircraft without traffic ref");
return;
}
// The user controlled aircraft should have crased here, because it doesn't have a traffic reference.
// NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
time_t now = globals->get_time_params()->get_cur_time();
if ((startTime - now) > 0) {
SG_LOG(SG_ATC, SG_BULK, i->getAircraft()->getTrafficRef()->getCallSign() << " is scheduled to depart in " << startTime - now << " seconds. Available = " << available << " at parking " << getGateName(i->getAircraft()));
}
if ((now - lastTransmission) > 3 + (rand() % 15)) {
available = true;
}
checkTransmissionState(0, now, (startTime + 0 ), i, MSG_ANNOUNCE_ENGINE_START, ATC_AIR_TO_GROUND);
checkTransmissionState(1, now, (startTime + 60 ), i, MSG_REQUEST_ENGINE_START, ATC_AIR_TO_GROUND);
checkTransmissionState(2, now, (startTime + 80 ), i, MSG_PERMIT_ENGINE_START, ATC_GROUND_TO_AIR);
checkTransmissionState(3, now, (startTime + 100), i, MSG_ACKNOWLEDGE_ENGINE_START, ATC_AIR_TO_GROUND);
if (checkTransmissionState(4, now, (startTime + 130), i, MSG_ACKNOWLEDGE_SWITCH_GROUND_FREQUENCY, ATC_AIR_TO_GROUND)) {
i->nextFrequency();
}
checkTransmissionState(5, now, (startTime + 140), i, MSG_INITIATE_CONTACT, ATC_AIR_TO_GROUND);
checkTransmissionState(6, now, (startTime + 150), i, MSG_ACKNOWLEDGE_INITIATE_CONTACT, ATC_GROUND_TO_AIR);
checkTransmissionState(7, now, (startTime + 180), i, MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND);
if ((state == 8) && available) {
if (now > startTime + 200) {
if (i->pushBackAllowed()) {
i->allowRepeatedTransmissions();
transmit(&(*i), &(*parent), MSG_PERMIT_PUSHBACK_CLEARANCE,
ATC_GROUND_TO_AIR, true);
i->updateState();
} else {
transmit(&(*i), &(*parent), MSG_HOLD_PUSHBACK_CLEARANCE,
ATC_GROUND_TO_AIR, true);
i->suppressRepeatedTransmissions();
}
lastTransmission = now;
available = false;
}
}
if ((state == 9) && available) {
i->setHoldPosition(false);
}
}
// Note that this function is copied from simgear. for maintanance purposes, it's probabtl better to make a general function out of that.
static void WorldCoordinate(osg::Matrix& obj_pos, double lat,
double lon, double elev, double hdg, double slope)
{
SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
obj_pos = makeZUpFrame(geod);
// hdg is not a compass heading, but a counter-clockwise rotation
// around the Z axis
obj_pos.preMult(osg::Matrix::rotate(hdg * SGD_DEGREES_TO_RADIANS,
0.0, 0.0, 1.0));
obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
0.0, 1.0, 0.0));
}
void FGStartupController::render(bool visible)
{
SG_LOG(SG_ATC, SG_DEBUG, "Rendering startup controller");
SGMaterialLib *matlib = globals->get_matlib();
if (group) {
//int nr = ;
globals->get_scenery()->get_scene_graph()->removeChild(group);
//while (group->getNumChildren()) {
// SG_LOG(SG_ATC, SG_BULK, "Number of children: " << group->getNumChildren());
//simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
//osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
//geode->releaseGLObjects();
//group->removeChild(geode);
//delete geode;
group = 0;
}
if (visible) {
group = new osg::Group;
FGScenery * local_scenery = globals->get_scenery();
//double elevation_meters = 0.0;
//double elevation_feet = 0.0;
FGGroundNetwork* groundNet = parent->parent()->groundNetwork();
//for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
double dx = 0;
time_t now = globals->get_time_params()->get_cur_time();
for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
if (i->isActive(300)) {
// Handle start point
int pos = i->getCurrentPosition();
SG_LOG(SG_ATC, SG_BULK, "rendering for " << i->getAircraft()->getCallSign() << "pos = " << pos);
if (pos > 0) {
FGTaxiSegment *segment = groundNet->findSegment(pos);
SGGeod start = i->getPos();
SGGeod end (segment->getEnd()->geod());
double length = SGGeodesy::distanceM(start, end);
//heading = SGGeodesy::headingDeg(start->geod(), end->geod());
double az2, heading; //, distanceM;
SGGeodesy::inverse(start, end, heading, az2, length);
double coveredDistance = length * 0.5;
SGGeod center;
SGGeodesy::direct(start, heading, coveredDistance, center, az2);
SG_LOG(SG_ATC, SG_BULK, "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading);
///////////////////////////////////////////////////////////////////////////////
// Make a helper function out of this
osg::Matrix obj_pos;
osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
obj_trans->setDataVariance(osg::Object::STATIC);
// Experimental: Calculate slope here, based on length, and the individual elevations
double elevationStart;
if (isUserAircraft((i)->getAircraft())) {
elevationStart = fgGetDouble("/position/ground-elev-m");
} else {
elevationStart = ((i)->getAircraft()->_getAltitude() * SG_FEET_TO_METER);
}
double elevationEnd = segment->getEnd()->getElevationM();
if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
SGGeod center2 = end;
center2.setElevationM(SG_MAX_ELEVATION_M);
if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
//elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
//elevation_meters += 0.5;
}
else {
elevationEnd = parent->getElevation();
}
segment->getEnd()->setElevation(elevationEnd);
}
double elevationMean = (elevationStart + elevationEnd) / 2.0;
double elevDiff = elevationEnd - elevationStart;
double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
SG_LOG(SG_ATC, SG_BULK, "1. Using mean elevation : " << elevationMean << " and " << slope);
WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean + 0.5 + dx, -(heading), slope );
;
obj_trans->setMatrix( obj_pos );
//osg::Vec3 center(0, 0, 0)
float width = length /2.0;
osg::Vec3 corner(-width, 0, 0.25f);
osg::Vec3 widthVec(2*width + 1, 0, 0);
osg::Vec3 heightVec(0, 1, 0);
osg::Geometry* geometry;
geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
simgear::EffectGeode* geode = new simgear::EffectGeode;
geode->setName("test");
geode->addDrawable(geometry);
//osg::Node *custom_obj;
SGMaterial *mat;
if (segment->hasBlock(now)) {
mat = matlib->find("UnidirectionalTaperRed", center);
} else {
mat = matlib->find("UnidirectionalTaperGreen", center);
}
if (mat)
geode->setEffect(mat->get_effect());
obj_trans->addChild(geode);
// wire as much of the scene graph together as we can
//->addChild( obj_trans );
group->addChild( obj_trans );
/////////////////////////////////////////////////////////////////////
} else {
SG_LOG(SG_ATC, SG_DEBUG, "BIG FAT WARNING: current position is here : " << pos);
}
for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
osg::Matrix obj_pos;
int k = (*j);
if (k > 0) {
SG_LOG(SG_ATC, SG_BULK, "rendering for " << i->getAircraft()->getCallSign() << "intention = " << k);
osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
obj_trans->setDataVariance(osg::Object::STATIC);
FGTaxiSegment *segment = groundNet->findSegment(k);
double elevationStart = segment->getStart()->getElevationM();
double elevationEnd = segment->getEnd ()->getElevationM();
if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
SGGeod center2 = segment->getStart()->geod();
center2.setElevationM(SG_MAX_ELEVATION_M);
if (local_scenery->get_elevation_m( center2, elevationStart, NULL )) {
//elevation_feet = elevationStart * SG_METER_TO_FEET + 0.5;
//elevation_meters += 0.5;
}
else {
elevationStart = parent->getElevation();
}
segment->getStart()->setElevation(elevationStart);
}
if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
SGGeod center2 = segment->getEnd()->geod();
center2.setElevationM(SG_MAX_ELEVATION_M);
if (local_scenery->get_elevation_m( center2, elevationEnd, NULL )) {
//elevation_feet = elevationEnd * SG_METER_TO_FEET + 0.5;
//elevation_meters += 0.5;
}
else {
elevationEnd = parent->getElevation();
}
segment->getEnd()->setElevation(elevationEnd);
}
double elevationMean = (elevationStart + elevationEnd) / 2.0;
double elevDiff = elevationEnd - elevationStart;
double length = segment->getLength();
double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
SG_LOG(SG_ATC, SG_BULK, "2. Using mean elevation : " << elevationMean << " and " << slope);
SGGeod segCenter(segment->getCenter());
WorldCoordinate( obj_pos, segCenter.getLatitudeDeg(),
segCenter.getLongitudeDeg(), elevationMean + 0.5 + dx, -(segment->getHeading()), slope );
//WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), parent->getElevation()+8+dx, -(segment->getHeading()) );
obj_trans->setMatrix( obj_pos );
//osg::Vec3 center(0, 0, 0)
float width = segment->getLength() /2.0;
osg::Vec3 corner(-width, 0, 0.25f);
osg::Vec3 widthVec(2*width + 1, 0, 0);
osg::Vec3 heightVec(0, 1, 0);
osg::Geometry* geometry;
geometry = osg::createTexturedQuadGeometry(corner, widthVec, heightVec);
simgear::EffectGeode* geode = new simgear::EffectGeode;
geode->setName("test");
geode->addDrawable(geometry);
//osg::Node *custom_obj;
SGMaterial *mat;
if (segment->hasBlock(now)) {
mat = matlib->find("UnidirectionalTaperRed", segCenter);
} else {
mat = matlib->find("UnidirectionalTaperGreen", segCenter);
}
if (mat)
geode->setEffect(mat->get_effect());
obj_trans->addChild(geode);
// wire as much of the scene graph together as we can
//->addChild( obj_trans );
group->addChild( obj_trans );
} else {
SG_LOG(SG_ATC, SG_DEBUG, "BIG FAT WARNING: k is here : " << pos);
}
}
dx += 0.2;
}
}
globals->get_scenery()->get_scene_graph()->addChild(group);
}
}
string FGStartupController::getName() {
return string(parent->parent()->getName() + "-Startup");
}
void FGStartupController::update(double dt)
{
FGATCController::eraseDeadTraffic();
}
int FGStartupController::getFrequency() {
int groundFreq = parent->getGroundFrequency(2);
int towerFreq = parent->getTowerFrequency(2);
return groundFreq>0?groundFreq:towerFreq;
}

View File

@@ -0,0 +1,71 @@
// Extracted from trafficcontrol.hxx - classes to manage AIModels based air traffic control
// Written by Durk Talsma, started September 2006.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef STARTUP_CONTROLLER_HXX
#define STARTUP_CONTROLLER_HXX
#include <Airports/airports_fwd.hxx>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osg/Shape>
#include <simgear/compiler.h>
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <ATC/ATCController.hxx>
#include <ATC/trafficcontrol.hxx>
/******************************************************************************
* class FGStartupController
* handle
*****************************************************************************/
class FGStartupController : public FGATCController
{
private:
/**Returns the frequency to be used. */
int getFrequency();
public:
FGStartupController(FGAirportDynamics *parent);
virtual ~FGStartupController();
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 updateAircraftInformation(int id, SGGeod geod,
double heading, double speed, double alt, double dt);
virtual void render(bool);
virtual std::string getName();
virtual void update(double dt);
// Hpoefully, we can move this function to the base class, but I need to verify what is needed for the other controllers before doing so.
bool checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId,
AtcMsgDir msgDir);
};
#endif

308
src/ATC/TowerController.cxx Normal file
View File

@@ -0,0 +1,308 @@
// Extracted from trafficrecord.cxx - Implementation of AIModels ATC code.
//
// Written by Durk Talsma, started September 2006.
//
// Copyright (C) 2006 Durk Talsma.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <config.h>
#include <algorithm>
#include <cstdio>
#include <random>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osg/Shape>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/material/mat.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/timing/sg_time.hxx>
#include <Scenery/scenery.hxx>
#include "trafficcontrol.hxx"
#include "atc_mgr.hxx"
#include <AIModel/AIAircraft.hxx>
#include <AIModel/AIFlightPlan.hxx>
#include <AIModel/performancedata.hxx>
#include <Traffic/TrafficMgr.hxx>
#include <Airports/groundnetwork.hxx>
#include <Airports/dynamics.hxx>
#include <Airports/airport.hxx>
#include <Radio/radio.hxx>
#include <signal.h>
#include <ATC/atc_mgr.hxx>
#include <ATC/ATCController.hxx>
#include <ATC/trafficcontrol.hxx>
#include <ATC/TowerController.hxx>
using std::sort;
using std::string;
/***************************************************************************
* class FGTowerController
s * subclass of FGATCController
**************************************************************************/
FGTowerController::FGTowerController(FGAirportDynamics *par) :
FGATCController()
{
parent = par;
}
FGTowerController::~FGTowerController()
{
}
//
void FGTowerController::announcePosition(int id,
FGAIFlightPlan * intendedRoute,
int currentPosition, double lat,
double lon, double heading,
double speed, double alt,
double radius, int leg,
FGAIAircraft * ref)
{
init();
// Search activeTraffic for a record matching our id
TrafficVectorIterator i = FGATCController::searchActiveTraffic(id);
// Add a new TrafficRecord if no one exsists for this aircraft.
if (i == activeTraffic.end() || (activeTraffic.empty())) {
FGTrafficRecord rec;
rec.setId(id);
rec.setPositionAndHeading(lat, lon, heading, speed, alt);
rec.setRunway(intendedRoute->getRunway());
rec.setLeg(leg);
//rec.setCallSign(callsign);
rec.setRadius(radius);
rec.setAircraft(ref);
activeTraffic.push_back(rec);
// Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway.
ActiveRunwayVecIterator rwy = activeRunways.begin();
if (! activeRunways.empty()) {
while (rwy != activeRunways.end()) {
if (rwy->getRunwayName() == intendedRoute->getRunway()) {
break;
}
rwy++;
}
}
if (rwy == activeRunways.end()) {
ActiveRunway aRwy(intendedRoute->getRunway(), id);
aRwy.addToDepartureQueue(ref);
activeRunways.push_back(aRwy);
rwy = (activeRunways.end()-1);
} else {
rwy->addToDepartureQueue(ref);
}
SG_LOG(SG_ATC, SG_DEBUG, ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getdepartureQueueSize() << " for takeoff ");
} else {
i->setPositionAndHeading(lat, lon, heading, speed, alt);
}
}
void FGTowerController::updateAircraftInformation(int id, SGGeod geod,
double heading, double speed, double alt,
double dt)
{
// Search activeTraffic for a record matching our id
TrafficVectorIterator i = FGATCController::searchActiveTraffic(id);
setDt(getDt() + dt);
if (i == activeTraffic.end() || (activeTraffic.empty())) {
SG_LOG(SG_ATC, SG_ALERT,
"AI error: updating aircraft without traffic record at " <<
SG_ORIGIN);
return;
}
// Update the position of the current aircraft
FGTrafficRecord& current = *i;
current.setPositionAndHeading(geod.getLatitudeDeg(), geod.getLongitudeDeg(), heading, speed, alt);
// see if we already have a clearance record for the currently active runway
// NOTE: dd. 2011-08-07: Because the active runway has been constructed in the announcePosition function, we may safely assume that is
// already exists here. So, we can simplify the current code.
ActiveRunwayVecIterator rwy = activeRunways.begin();
//if (parent->getId() == fgGetString("/sim/presets/airport-id")) {
// for (rwy = activeRunways.begin(); rwy != activeRunways.end(); rwy++) {
// rwy->printdepartureQueue();
// }
//}
rwy = activeRunways.begin();
while (rwy != activeRunways.end()) {
if (rwy->getRunwayName() == current.getRunway()) {
break;
}
rwy++;
}
// only bother running the following code if the current aircraft is the
// first in line for depature
/* if (current.getAircraft() == rwy->getFirstAircraftIndepartureQueue()) {
if (rwy->getCleared()) {
if (id == rwy->getCleared()) {
current.setHoldPosition(false);
} else {
current.setHoldPosition(true);
}
} else {
// For now. At later stages, this will probably be the place to check for inbound traffc.
rwy->setCleared(id);
}
} */
// only bother with aircraft that have a takeoff status of 2, since those are essentially under tower control
auto ac = rwy->getFirstAircraftInDepartureQueue();
if (ac) {
if (ac->getTakeOffStatus() == 1) {
// transmit takeoff clearance
ac->setTakeOffStatus(2);
transmit(&(*i), &(*parent), MSG_CLEARED_FOR_TAKEOFF, ATC_GROUND_TO_AIR, true);
i->setState(10);
}
}
//FIXME Make it an member of traffic record
if (current.getAircraft()->getTakeOffStatus() == 2) {
current.setHoldPosition(false);
} else {
current.setHoldPosition(true);
}
int clearanceId = rwy->getCleared();
if (clearanceId) {
if (id == clearanceId) {
SG_LOG(SG_ATC, SG_BULK, "Unset Hold " << clearanceId << " for " << rwy->getRunwayName());
current.setHoldPosition(false);
} else {
SG_LOG(SG_ATC, SG_WARN, "Not cleared " << id << " " << clearanceId);
}
} else {
if (current.getAircraft() == rwy->getFirstAircraftInDepartureQueue()) {
SG_LOG(SG_ATC, SG_BULK,
"Cleared " << current.getAircraft()->getCallSign() << " for " << rwy->getRunwayName() << " Id " << id);
rwy->setCleared(id);
auto ac = rwy->getFirstOfStatus(1);
if (ac) {
ac->setTakeOffStatus(2);
// transmit takeoff clearacne? But why twice?
}
} else {
SG_LOG(SG_ATC, SG_BULK,
"Not cleared " << current.getAircraft()->getCallSign() << " " << rwy->getFirstAircraftInDepartureQueue()->getCallSign());
}
}
}
void FGTowerController::signOff(int id)
{
SG_LOG(SG_ATC, SG_BULK, "Signing off " << id << " from Tower");
// ensure we don't modify activeTraffic during destruction
if (_isDestroying)
return;
// Search activeTraffic for a record matching our id
TrafficVectorIterator i = FGATCController::searchActiveTraffic(id);
if (i == activeTraffic.end() || (activeTraffic.empty())) {
SG_LOG(SG_ATC, SG_ALERT,
"AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN);
return;
}
const auto trafficRunway = i->getRunway();
auto runwayIt = std::find_if(activeRunways.begin(), activeRunways.end(),
[&trafficRunway](const ActiveRunway& ar) {
return ar.getRunwayName() == trafficRunway;
});
if (runwayIt != activeRunways.end()) {
SG_LOG(SG_ATC, SG_BULK, "Cleared " << id << " from " << runwayIt->getRunwayName() );
runwayIt->setCleared(0);
runwayIt->updateDepartureQueue();
} else {
SG_LOG(SG_ATC, SG_ALERT,
"AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff at " << SG_ORIGIN);
}
i->getAircraft()->resetTakeOffStatus();
FGATCController::signOff(id);
}
// NOTE:
// IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
// THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
// BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
// WHICH WOULD SIMPLIFY CODE MAINTENANCE.
// Note that this function is probably obsolete
bool FGTowerController::hasInstruction(int id)
{
// Search activeTraffic for a record matching our id
TrafficVectorIterator i = FGATCController::searchActiveTraffic(id);
if (i == activeTraffic.end() || activeTraffic.empty()) {
SG_LOG(SG_ATC, SG_ALERT,
"AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
} else {
return i->hasInstruction();
}
return false;
}
FGATCInstruction FGTowerController::getInstruction(int id)
{
// Search activeTraffic for a record matching our id
TrafficVectorIterator i = FGATCController::searchActiveTraffic(id);
if (i == activeTraffic.end() || activeTraffic.empty()) {
SG_LOG(SG_ATC, SG_ALERT,
"AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
} else {
return i->getInstruction();
}
return FGATCInstruction();
}
void FGTowerController::render(bool visible) {
// this should be bulk, since its called quite often
SG_LOG(SG_ATC, SG_BULK, "FGTowerController::render function not yet implemented");
}
string FGTowerController::getName() {
return string(parent->getId() + "-tower");
}
void FGTowerController::update(double dt)
{
FGATCController::eraseDeadTraffic();
}
int FGTowerController::getFrequency() {
int towerFreq = parent->getTowerFrequency(2);
return towerFreq;
}

View File

@@ -0,0 +1,68 @@
// Extracted from trafficcontrol.hxx - classes to manage AIModels based air traffic control
// Written by Durk Talsma, started September 2006.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef TOWER_CONTROLLER_HXX
#define TOWER_CONTROLLER_HXX
#include <Airports/airports_fwd.hxx>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osg/Shape>
#include <simgear/compiler.h>
#include <simgear/constants.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <ATC/ATCController.hxx>
#include <ATC/trafficcontrol.hxx>
/******************************************************************************
* class FGTowerControl
*****************************************************************************/
class FGTowerController : public FGATCController
{
private:
ActiveRunwayVec activeRunways;
/**Returns the frequency to be used. */
int getFrequency();
public:
FGTowerController(FGAirportDynamics *parent);
virtual ~FGTowerController();
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);
void signOff(int id);
bool hasInstruction(int id);
FGATCInstruction getInstruction(int id);
virtual void updateAircraftInformation(int id, SGGeod geod,
double heading, double speed, double alt, double dt);
virtual void render(bool);
virtual std::string getName();
virtual void update(double dt);
};
#endif

414
src/ATC/atc_mgr.cxx Normal file
View File

@@ -0,0 +1,414 @@
/******************************************************************************
* atc_mgr.cxx
* Written by Durk Talsma, started August 1, 2010.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
**************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <Airports/dynamics.hxx>
#include <Airports/airportdynamicsmanager.hxx>
#include <Airports/airport.hxx>
#include <Scenery/scenery.hxx>
#include <Main/globals.hxx>
#include <Main/fg_props.hxx>
#include <AIModel/AIAircraft.hxx>
#include <AIModel/AIManager.hxx>
#include <Traffic/Schedule.hxx>
#include <Traffic/SchedFlight.hxx>
#include <AIModel/AIFlightPlan.hxx>
#include "atc_mgr.hxx"
using std::string;
/**
Constructer, initializes values to private boolean and FGATCController instances
*/
FGATCManager::FGATCManager() :
controller(NULL),
prevController(NULL),
networkVisible(false),
initSucceeded(false)
{
}
/**
Default destructor
*/
FGATCManager::~FGATCManager() {
}
/**
Sets up ATC subsystem parts depending on other subsystems
Override of SGSubsystem::postinit()
Will set private boolean flag "initSucceeded" to true upon conclusion
*/
void FGATCManager::postinit()
{
int leg = 0;
trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
// Assign a controller to the user's aircraft.
// Three scenarios are considered:
// - Starting on ground at a parking position
// - Starting on ground at the runway.
// - Starting in the air
bool onGround = fgGetBool("/sim/presets/onground");
string runway = fgGetString("/sim/atc/runway");
string curAirport = fgGetString("/sim/presets/airport-id");
string parking = fgGetString("/sim/presets/parkpos");
_routeManagerDestinationAirportNode = globals->get_props()->getNode("/autopilot/route-manager/destination/airport", true);
destination = _routeManagerDestinationAirportNode->getStringValue();
FGAIManager* aiManager = globals->get_subsystem<FGAIManager>();
auto userAircraft = aiManager->getUserAircraft();
string callsign = userAircraft->getCallSign();
double aircraftRadius = 40; // note that this is currently hardcoded to a one-size-fits all JumboJet value. Should change later.
// In case a destination is not set yet, make it equal to the current airport
if (destination.empty()) {
destination = curAirport;
}
// NEXT UP: Create a traffic schedule and fill that with appropriate information. This we can use for flight planning.
// Note that these are currently only defaults.
userAircraftTrafficRef.reset(new FGAISchedule);
userAircraftTrafficRef->setFlightType("gate");
userAircraftScheduledFlight.reset(new FGScheduledFlight);
userAircraftScheduledFlight->setDepartureAirport(curAirport);
userAircraftScheduledFlight->setArrivalAirport(destination);
userAircraftScheduledFlight->initializeAirports();
userAircraftScheduledFlight->setFlightRules("IFR");
userAircraftScheduledFlight->setCallSign(callsign);
userAircraftTrafficRef->assign(userAircraftScheduledFlight.get());
std::unique_ptr<FGAIFlightPlan> fp ;
userAircraft->setTrafficRef(userAircraftTrafficRef.get());
string flightPlanName = curAirport + "-" + _routeManagerDestinationAirportNode->getStringValue() + ".xml";
//double cruiseAlt = 100; // Doesn't really matter right now.
//double courseToDest = 180; // Just use something neutral; this value might affect the runway that is used though...
//time_t deptime = 0; // just make sure how flightplan processing is affected by this...
FGAirportDynamicsRef dcs(flightgear::AirportDynamicsManager::find(curAirport));
if (dcs && onGround) {// && !runway.empty()) {
ParkingAssignment pk;
if (parking == "AVAILABLE") {
double radius = fgGetDouble("/sim/dimensions/radius-m");
if (radius > 0) {
pk = dcs->getAvailableParking(radius, string(), string(), string());
if (pk.isValid()) {
fgGetString("/sim/presets/parkpos");
fgSetString("/sim/presets/parkpos", pk.parking()->getName());
}
}
if (!pk.isValid()) {
FGParkingList pkl(dcs->getParkings(true, "gate"));
if (!pkl.empty()) {
std::sort(pkl.begin(), pkl.end(), [](const FGParkingRef& a, const FGParkingRef& b) {
return a->getRadius() > b->getRadius();
});
pk = ParkingAssignment(pkl.front(), dcs);
fgSetString("/sim/presets/parkpos", pkl.front()->getName());
}
}
} else if (!parking.empty()) {
pk = dcs->getAvailableParkingByName(parking);
}
if (pk.isValid()) {
dcs->setParkingAvailable(pk.parking(), false);
fp.reset(new FGAIFlightPlan);
controller = dcs->getStartupController();
int stationFreq = dcs->getGroundFrequency(1);
if (stationFreq > 0)
{
SG_LOG(SG_ATC, SG_DEBUG, "Setting radio frequency to : " << stationFreq);
fgSetDouble("/instrumentation/comm[0]/frequencies/selected-mhz", ((double) stationFreq / 100.0));
}
leg = 1;
//double, lat, lon, head; // Unused variables;
//int getId = apt->getDynamics()->getParking(gateId, &lat, &lon, &head);
aircraftRadius = pk.parking()->getRadius();
string fltType = pk.parking()->getType(); // gate / ramp, ga, etc etc.
string aircraftType; // Unused.
string airline; // Currently used for gate selection, but a fallback mechanism will apply when not specified.
fp->setGate(pk);
if (!(fp->createPushBack(userAircraft,
false,
dcs->parent(),
aircraftRadius,
fltType,
aircraftType,
airline))) {
controller = 0;
return;
}
} else if (!runway.empty()) {
// on a runway
controller = dcs->getTowerController();
int stationFreq = dcs->getTowerFrequency(2);
if (stationFreq > 0)
{
SG_LOG(SG_ATC, SG_DEBUG, "Setting radio frequency to inair frequency : " << stationFreq);
fgSetDouble("/instrumentation/comm[0]/frequencies/selected-mhz", ((double) stationFreq / 100.0));
}
fp.reset(new FGAIFlightPlan);
leg = 3;
string fltType = "ga";
fp->setRunway(runway);
fp->createTakeOff(userAircraft, false, dcs->parent(), {}, 0, fltType);
userAircraft->setTakeOffStatus(2);
} else {
// We're on the ground somewhere. Handle this case later.
// important : we are on the ground, so reset the AIFlightPlan back to
// a dummy one. Otherwise, in the reposition case, we end up with a
// stale flight-plan which confuses other code (eg, PositionInit::finalizeForParking)
// see unit test: PosInitTests::testRepositionAtOccupied
fp.reset(FGAIFlightPlan::createDummyUserPlan());
userAircraft->FGAIBase::setFlightPlan(std::move(fp));
controller = nullptr;
initSucceeded = true; // should be false?
return;
}
if (fp && !fp->empty()) {
fp->getLastWaypoint()->setName( fp->getLastWaypoint()->getName() + string("legend"));
}
} else {
controller = 0;
}
// Create an initial flightplan and assign it to the ai_ac. We won't use this flightplan, but it is necessary to
// keep the ATC code happy.
// note in the reposition case, 'fp' is only the new FlightPlan; if we didn't create one here.
// we will continue using the existing flight plan (and not restart it, for example)
if (fp) {
fp->restart();
fp->setLeg(leg);
userAircraft->FGAIBase::setFlightPlan(std::move(fp));
}
if (controller) {
FGAIFlightPlan* plan = userAircraft->GetFlightPlan();
const int routeIndex = (plan && plan->getCurrentWaypoint()) ? plan->getCurrentWaypoint()->getRouteIndex() : 0;
controller->announcePosition(userAircraft->getID(), plan,
routeIndex,
userAircraft->_getLatitude(),
userAircraft->_getLongitude(),
userAircraft->_getHeading(),
userAircraft->getSpeed(),
userAircraft->getAltitude(),
aircraftRadius, leg, userAircraft);
}
initSucceeded = true;
}
/**
Shutdown method
Clears activeStations vector in preparation for clean shutdown
Override of SGSubsystem::shutdown()
*/
void FGATCManager::shutdown()
{
activeStations.clear();
userAircraftTrafficRef.reset();
userAircraftScheduledFlight.reset();
_routeManagerDestinationAirportNode.clear();
}
void FGATCManager::reposition()
{
prevController = controller = nullptr;
// remove any parking assignment form the user flight-plan, so it's
// available again. postinit() will recompute a new value if required
FGAIManager* aiManager = globals->get_subsystem<FGAIManager>();
auto userAircraft = aiManager->getUserAircraft();
if (userAircraft) {
if (userAircraft->GetFlightPlan()) {
auto userAIFP = userAircraft->GetFlightPlan();
userAIFP->setGate({}); // clear any assignment
}
userAircraft->clearATCController();
}
postinit(); // critical for position-init logic
}
/**
Adds FGATCController instance to std::vector activeStations.
FGATCController is a basic class for every controller
*/
void FGATCManager::addController(FGATCController *controller) {
activeStations.push_back(controller);
}
/**
Searches for and removes FGATCController instance from std::vector activeStations
*/
void FGATCManager::removeController(FGATCController *controller)
{
AtcVecIterator it;
it = std::find(activeStations.begin(), activeStations.end(), controller);
if (it != activeStations.end()) {
activeStations.erase(it);
}
}
/**
Update the subsystem.
FlightGear invokes this method every time the subsystem should
update its state.
@param time The delta time, in seconds, since the last
update. On first update, delta time will be 0.
*/
void FGATCManager::update ( double time ) {
// SG_LOG(SG_ATC, SG_BULK, "ATC update code is running at time: " << time);
// Test code: let my virtual co-pilot handle ATC
FGAIManager* aiManager = globals->get_subsystem<FGAIManager>();
FGAIAircraft* user_ai_ac = aiManager->getUserAircraft();
FGAIFlightPlan *fp = user_ai_ac->GetFlightPlan();
// Update destination
string result = _routeManagerDestinationAirportNode->getStringValue();
if (destination != result && result != "") {
destination = result;
userAircraftScheduledFlight->setArrivalAirport(destination);
userAircraftScheduledFlight->initializeAirports();
userAircraftTrafficRef->clearAllFlights();
userAircraftTrafficRef->assign(userAircraftScheduledFlight.get());
auto userAircraft = aiManager->getUserAircraft();
userAircraft->setTrafficRef(userAircraftTrafficRef.get());
}
/* test code : find out how the routing develops */
if (fp) {
int size = fp->getNrOfWayPoints();
//SG_LOG(SG_ATC, SG_DEBUG, "Setting pos" << pos << " ");
//SG_LOG(SG_ATC, SG_DEBUG, "Setting intentions");
// This indicates that we have run out of waypoints: Im future versions, the
// user should be able to select a new route, but for now just shut down the
// system.
if (size < 3) {
return;
}
#if 0
// Test code: Print how far we're progressing along the taxi route.
SG_LOG(SG_ATC, SG_DEBUG, "Size of waypoint queue " << size);
for (int i = 0; i < size; i++) {
int val = fp->getRouteIndex(i);
SG_LOG(SG_ATC, SG_BULK, fp->getWayPoint(i)->getName() << " ");
//if ((val) && (val != pos)) {
// intentions.push_back(val);
SG_LOG(SG_ATC, SG_BULK, "[done ]");
//}
}
SG_LOG(SG_ATC, SG_BULK, "[done ]");
#endif
}
if (fp) {
SG_LOG(SG_ATC, SG_DEBUG, "User aircraft currently at leg : " << fp->getLeg());
}
// Call getATCController method; returns what FGATCController presently controls the user aircraft
// - e.g. FGStartupController
controller = user_ai_ac->getATCController();
// Update the ATC dialog
//FGATCDialogNew::instance()->update(time);
// Controller manager - if controller is set, then will update controller
if (controller) {
SG_LOG(SG_ATC, SG_DEBUG, "name of previous waypoint : " << fp->getPreviousWaypoint()->getName());
SG_LOG(SG_ATC, SG_DEBUG, "Currently under control of " << controller->getName());
// update aircraft information (simulates transponder)
controller->updateAircraftInformation(user_ai_ac->getID(),
user_ai_ac->getGeodPos(),
user_ai_ac->_getHeading(),
user_ai_ac->getSpeed(),
user_ai_ac->getAltitude(), time);
//string airport = fgGetString("/sim/presets/airport-id");
//FGAirport *apt = FGAirport::findByIdent(airport);
// AT this stage we should update the flightplan, so that waypoint incrementing is conducted as well as leg loading.
// Ground network visibility:
// a) check to see if the message to toggle visibility was called
// b) if so, toggle network visibility and reset the transmission
// c) therafter disable rendering for the old controller (TODO: should this be earlier?)
// d) and render if enabled for the new controller
int n = trans_num->getIntValue();
if (n == 1) {
SG_LOG(SG_ATC, SG_DEBUG, "Toggling ground network visibility " << networkVisible);
networkVisible = !networkVisible;
trans_num->setIntValue(-1);
}
// stop rendering the old controller's groundnetwork
if ((controller != prevController) && (prevController)) {
prevController->render(false);
}
// render the path for the present controller if the ground network is set to visible
controller->render(networkVisible);
SG_LOG(SG_ATC, SG_BULK, "Adding ground network to the scenegraph::update");
// reset previous controller for next update() iteration
prevController = controller;
}
// update the active ATC stations
for (AtcVecIterator atc = activeStations.begin(); atc != activeStations.end(); ++atc) {
(*atc)->update(time);
}
}
// Register the subsystem.
SGSubsystemMgr::Registrant<FGATCManager> registrantFGATCManager(
SGSubsystemMgr::POST_FDM,
{{"FGAIManager", SGSubsystemMgr::Dependency::HARD}});

83
src/ATC/atc_mgr.hxx Normal file
View File

@@ -0,0 +1,83 @@
/* -*- Mode: C++ -*- *****************************************************
* atic.hxx
* Written by Durk Talsma. Started August 1, 2010; based on earlier work
* by David C. Luff
*
* Updated by Jonathan Redpath. Started June 12, 2019. Documenting and extending
* functionality of the ATC subsystem
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
**************************************************************************/
/**************************************************************************
* The ATC Manager interfaces the users aircraft within the AI traffic system
* and also monitors the ongoing AI traffic patterns for potential conflicts
* and interferes where necessary.
*************************************************************************/
#ifndef _ATC_MGR_HXX_
#define _ATC_MGR_HXX_
#include <simgear/structure/subsystem_mgr.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <ATC/trafficcontrol.hxx>
#include <AIModel/AIAircraft.hxx>
#include <Traffic/Schedule.hxx>
#include <Traffic/SchedFlight.hxx>
typedef std::vector<FGATCController*> AtcVec;
typedef std::vector<FGATCController*>::iterator AtcVecIterator;
class FGATCManager : public SGSubsystem
{
private:
AtcVec activeStations;
FGATCController *controller, *prevController; // The ATC controller that is responsible for the user's aircraft.
bool networkVisible;
bool initSucceeded;
SGPropertyNode_ptr trans_num;
string destination;
std::unique_ptr<FGAISchedule> userAircraftTrafficRef;
std::unique_ptr<FGScheduledFlight> userAircraftScheduledFlight;
SGPropertyNode_ptr _routeManagerDestinationAirportNode;
public:
FGATCManager();
virtual ~FGATCManager();
// Subsystem API.
void postinit() override;
void shutdown() override;
void update(double time) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "ATC"; }
void addController(FGATCController *controller);
void removeController(FGATCController* controller);
void reposition();
};
#endif // _ATC_MRG_HXX_

579
src/ATC/trafficcontrol.cxx Normal file
View File

@@ -0,0 +1,579 @@
// trafficrecord.cxx - Implementation of AIModels ATC code.
//
// Written by Durk Talsma, started September 2006.
//
// Copyright (C) 2006 Durk Talsma.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#include <config.h>
#include <algorithm>
#include <cstdio>
#include <random>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osg/Shape>
#include <simgear/scene/material/EffectGeode.hxx>
#include <simgear/scene/material/matlib.hxx>
#include <simgear/scene/material/mat.hxx>
#include <simgear/scene/util/OsgMath.hxx>
#include <simgear/timing/sg_time.hxx>
#include <simgear/math/sg_geodesy.hxx>
#include <Scenery/scenery.hxx>
#include "trafficcontrol.hxx"
#include "atc_mgr.hxx"
#include <AIModel/AIAircraft.hxx>
#include <AIModel/AIFlightPlan.hxx>
#include <AIModel/performancedata.hxx>
#include <ATC/atc_mgr.hxx>
#include <Traffic/TrafficMgr.hxx>
#include <Airports/groundnetwork.hxx>
#include <Airports/dynamics.hxx>
#include <Airports/airport.hxx>
#include <Radio/radio.hxx>
#include <signal.h>
using std::sort;
using std::string;
/***************************************************************************
* ActiveRunway
**************************************************************************/
ActiveRunway::ActiveRunway(const std::string& r, int cc) :
rwy(r)
{
currentlyCleared = cc;
distanceToFinal = 6.0 * SG_NM_TO_METER;
};
void ActiveRunway::updateDepartureQueue()
{
departureQueue.erase(departureQueue.begin());
}
/*
* Fetch next slot for the active runway
* @param eta time of slot requested
* @return newEta: next slot available; starts at eta paramater
* and adds separation as needed
*/
time_t ActiveRunway::requestTimeSlot(time_t eta)
{
time_t newEta = 0;
// default separation - 60 seconds
time_t separation = 60;
//if (wakeCategory == "heavy_jet") {
// SG_LOG(SG_ATC, SG_DEBUG, "Heavy jet, using extra separation");
// time_t separation = 120;
//}
bool found = false;
// if the aircraft is the first arrival, add to the vector and return eta directly
if (estimatedArrivalTimes.empty()) {
estimatedArrivalTimes.push_back(eta);
SG_LOG(SG_ATC, SG_DEBUG, getRunwayName() << "Checked eta slots, using " << eta);
return eta;
} else {
// First check the already assigned slots to see where we need to fit the flight in
TimeVectorIterator i = estimatedArrivalTimes.begin();
SG_LOG(SG_ATC, SG_DEBUG, getRunwayName() << " Checking eta slots " << eta << " : " << estimatedArrivalTimes.size() << " Timediff " << (eta - globals->get_time_params()->get_cur_time()));
// is this needed - just a debug output?
for (i = estimatedArrivalTimes.begin();
i != estimatedArrivalTimes.end(); i++) {
SG_LOG(SG_ATC, SG_BULK, "Stored time : " << (*i));
}
// if the flight is before the first scheduled slot + separation
i = estimatedArrivalTimes.begin();
if ((eta + separation) < (*i)) {
newEta = eta;
SG_LOG(SG_ATC, SG_BULK, "Storing at beginning");
SG_LOG(SG_ATC, SG_DEBUG, "Done. New ETA : " << newEta);
slotHousekeeping(newEta);
return newEta;
}
// else, look through the rest of the slots
while ((i != estimatedArrivalTimes.end()) && (!found)) {
TimeVectorIterator j = i + 1;
// if the flight is after the last scheduled slot check if separation is needed
if (j == estimatedArrivalTimes.end()) {
if (((*i) + separation) < eta) {
SG_LOG(SG_ATC, SG_BULK, "Storing at end");
newEta = eta;
} else {
newEta = (*i) + separation;
SG_LOG(SG_ATC, SG_BULK, "Storing at end + separation");
}
SG_LOG(SG_ATC, SG_DEBUG, "Done. New ETA : " << newEta);
slotHousekeeping(newEta);
return newEta;
} else {
// potential slot found
// check the distance between the previous and next slots
// distance msut be greater than 2* separation
if ((((*j) - (*i)) > (separation * 2))) {
// now check whether this slot is usable:
// eta should fall between the two points
// i.e. eta > i AND eta < j
SG_LOG(SG_ATC, SG_DEBUG, "Found potential slot after " << (*i));
if (eta > (*i) && (eta < (*j))) {
found = true;
if (eta < ((*i) + separation)) {
newEta = (*i) + separation;
SG_LOG(SG_ATC, SG_BULK, "Using original" << (*i) << " + separation ");
} else {
newEta = eta;
SG_LOG(SG_ATC, SG_BULK, "Using original after " << (*i));
}
} else if (eta < (*i)) {
found = true;
newEta = (*i) + separation;
SG_LOG(SG_ATC, SG_BULK, "Using delayed slot after " << (*i));
}
/*
if (((*j) - separation) < eta) {
found = true;
if (((*i) + separation) < eta) {
newEta = eta;
SG_LOG(SG_ATC, SG_BULK, "Using original after " << (*i));
} else {
newEta = (*i) + separation;
SG_LOG(SG_ATC, SG_BULK, "Using " << (*i) << " + separation ");
}
} */
}
}
i++;
}
}
SG_LOG(SG_ATC, SG_DEBUG, "Done. New ETA : " << newEta);
slotHousekeeping(newEta);
return newEta;
}
void ActiveRunway::slotHousekeeping(time_t newEta)
{
// add the slot to the vector and resort the vector
estimatedArrivalTimes.push_back(newEta);
sort(estimatedArrivalTimes.begin(), estimatedArrivalTimes.end());
// do some housekeeping : remove any slots that are past
time_t now = globals->get_time_params()->get_cur_time();
TimeVectorIterator i = estimatedArrivalTimes.begin();
while (i != estimatedArrivalTimes.end()) {
if ((*i) < now) {
SG_LOG(SG_ATC, SG_BULK, "Deleting timestamp " << (*i) << " (now = " << now << "). ");
estimatedArrivalTimes.erase(i);
i = estimatedArrivalTimes.begin();
} else {
i++;
}
}
}
/* Output the contents of the departure queue vector nicely formatted*/
void ActiveRunway::printDepartureQueue()
{
SG_LOG(SG_ATC, SG_DEBUG, "Departure queue for " << rwy << ": ");
for (auto acft : departureQueue) {
SG_LOG(SG_ATC, SG_DEBUG, " " << acft->getCallSign() << " " << acft->getTakeOffStatus());
SG_LOG(SG_ATC, SG_DEBUG, " " << acft->_getLatitude() << " " << acft->_getLongitude() << acft->getSpeed() << " " << acft->getAltitude());
}
}
/* Fetch the first aircraft in the departure queue with a certain status */
SGSharedPtr<FGAIAircraft>ActiveRunway::getFirstOfStatus(int stat) const
{
auto it = std::find_if(departureQueue.begin(), departureQueue.end(), [stat](const SGSharedPtr<FGAIAircraft>& acft) {
return acft->getTakeOffStatus() == stat;
});
if (it == departureQueue.end()) {
return {};
}
return *it;
}
SGSharedPtr<FGAIAircraft> ActiveRunway::getFirstAircraftInDepartureQueue() const
{
if (departureQueue.empty()) {
return {};
}
return departureQueue.front();
};
void ActiveRunway::addToDepartureQueue(FGAIAircraft *ac)
{
assert(ac);
assert(!ac->getDie());
departureQueue.push_back(ac);
};
/***************************************************************************
* FGTrafficRecord
**************************************************************************/
FGTrafficRecord::FGTrafficRecord():
id(0), waitsForId(0),
currentPos(0),
leg(0),
frequencyId(0),
state(0),
allowTransmission(true),
allowPushback(true),
priority(0),
timer(0),
heading(0), speed(0), altitude(0), radius(0)
{
}
FGTrafficRecord::~FGTrafficRecord()
{
}
void FGTrafficRecord::setPositionAndIntentions(int pos,
FGAIFlightPlan * route)
{
SG_LOG(SG_ATC, SG_DEBUG, "Position: " << pos);
currentPos = pos;
if (!intentions.empty()) {
intVecIterator i = intentions.begin();
if ((*i) != currentPos) {
SG_LOG(SG_ATC, SG_ALERT,
"Error in FGTrafficRecord::setPositionAndIntentions at " << SG_ORIGIN << ", " << (*i));
}
intentions.erase(i);
} else {
//FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
int size = route->getNrOfWayPoints();
SG_LOG(SG_ATC, SG_DEBUG, "Setting pos" << currentPos);
SG_LOG(SG_ATC, SG_DEBUG, "Setting intentions");
for (int i = 2; i < size; i++) {
int val = route->getRouteIndex(i);
intentions.push_back(val);
}
}
}
void FGTrafficRecord::setAircraft(FGAIAircraft *ref)
{
aircraft = ref;
}
bool FGTrafficRecord::isDead() const {
if (!aircraft) {
return true;
}
return aircraft->getDie();
}
void FGTrafficRecord::clearATCController() const {
if (aircraft) {
aircraft->clearATCController();
}
}
FGAIAircraft* FGTrafficRecord::getAircraft() const
{
if(aircraft.valid()) {
return aircraft.ptr();
}
return 0;
}
/**
* Check if another aircraft is ahead of the current one, and on the same taxiway
* @return true / false if this is/isn't the case.
*/
bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other)
{
bool result = false;
SG_LOG(SG_ATC, SG_BULK, getCallsign() << "|checkPositionAndIntentions");
if (currentPos == other.currentPos && getId() != other.getId() ) {
SG_LOG(SG_ATC, SG_BULK, getCallsign() << "|Check Position and intentions: " << other.getCallsign() << " we are on the same taxiway; Index = " << currentPos);
result = true;
}
// else if (! other.intentions.empty())
// {
// SG_LOG(SG_ATC, SG_BULK, "Start check 2");
// intVecIterator i = other.intentions.begin();
// while (!((i == other.intentions.end()) || ((*i) == currentPos)))
// i++;
// if (i != other.intentions.end()) {
// SG_LOG(SG_ATC, SG_BULK, "Check Position and intentions: current matches other.intentions");
// result = true;
// }
else if (! intentions.empty()) {
SG_LOG(SG_ATC, SG_BULK, getCallsign() << "|Itentions " << intentions.size());
intVecIterator i = intentions.begin();
//while (!((i == intentions.end()) || ((*i) == other.currentPos)))
while (i != intentions.end()) {
if ((*i) == other.currentPos) {
break;
}
i++;
}
if (i != intentions.end()) {
SG_LOG(SG_ATC, SG_BULK, getCallsign() << "| Check Position and intentions: " << other.getCallsign()<< " matches Index = " << (*i));
result = true;
}
}
return result;
}
void FGTrafficRecord::setPositionAndHeading(double lat, double lon,
double hdg, double spd,
double alt)
{
this->pos = SGGeod::fromDegFt(lon, lat, alt);
heading = hdg;
speed = spd;
altitude = alt;
}
int FGTrafficRecord::crosses(FGGroundNetwork * net,
FGTrafficRecord & other)
{
if (checkPositionAndIntentions(other)
|| (other.checkPositionAndIntentions(*this)))
return -1;
intVecIterator i, j;
int currentTargetNode = 0, otherTargetNode = 0;
if (currentPos > 0)
currentTargetNode = net->findSegment(currentPos)->getEnd()->getIndex(); // OKAY,...
if (other.currentPos > 0)
otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,...
if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
return currentTargetNode;
if (! intentions.empty()) {
for (i = intentions.begin(); i != intentions.end(); i++) {
if ((*i) > 0) {
if (currentTargetNode ==
net->findSegment(*i)->getEnd()->getIndex()) {
SG_LOG(SG_ATC, SG_BULK, "Current crosses at " << currentTargetNode);
return currentTargetNode;
}
}
}
}
if (! other.intentions.empty()) {
for (i = other.intentions.begin(); i != other.intentions.end();
i++) {
if ((*i) > 0) {
if (otherTargetNode ==
net->findSegment(*i)->getEnd()->getIndex()) {
SG_LOG(SG_ATC, SG_BULK, "Other crosses at " << currentTargetNode);
return otherTargetNode;
}
}
}
}
if (! intentions.empty() && ! other.intentions.empty()) {
for (i = intentions.begin(); i != intentions.end(); i++) {
for (j = other.intentions.begin(); j != other.intentions.end();
j++) {
SG_LOG(SG_ATC, SG_BULK, "finding segment " << *i << " and " << *j);
if (((*i) > 0) && ((*j) > 0)) {
currentTargetNode =
net->findSegment(*i)->getEnd()->getIndex();
otherTargetNode =
net->findSegment(*j)->getEnd()->getIndex();
if (currentTargetNode == otherTargetNode) {
SG_LOG(SG_ATC, SG_BULK, "Routes will cross at " << currentTargetNode);
return currentTargetNode;
}
}
}
}
}
return -1;
}
bool FGTrafficRecord::onRoute(FGGroundNetwork * net,
FGTrafficRecord & other)
{
int node = -1, othernode = -1;
if (currentPos > 0)
node = net->findSegment(currentPos)->getEnd()->getIndex();
if (other.currentPos > 0)
othernode =
net->findSegment(other.currentPos)->getEnd()->getIndex();
if ((node == othernode) && (node != -1))
return true;
if (! other.intentions.empty()) {
for (intVecIterator i = other.intentions.begin();
i != other.intentions.end(); i++) {
if (*i > 0) {
othernode = net->findSegment(*i)->getEnd()->getIndex();
if ((node == othernode) && (node > -1))
return true;
}
}
}
//if (other.currentPos > 0)
// othernode = net->findSegment(other.currentPos)->getEnd()->getIndex();
//if (! intentions.empty())
// {
// for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
// {
// if (*i > 0)
// {
// node = net->findSegment(*i)->getEnd()->getIndex();
// if ((node == othernode) && (node > -1))
// return true;
// }
// }
// }
return false;
}
bool FGTrafficRecord::isOpposing(FGGroundNetwork * net,
FGTrafficRecord & other, int node)
{
// Check if current segment is the reverse segment for the other aircraft
FGTaxiSegment *opp;
SG_LOG(SG_ATC, SG_BULK, "Current segment " << currentPos);
if ((currentPos > 0) && (other.currentPos > 0)) {
opp = net->findSegment(currentPos)->opposite();
if (opp) {
if (opp->getIndex() == other.currentPos)
return true;
}
for (intVecIterator i = intentions.begin(); i != intentions.end();
i++) {
if ((opp = net->findSegment(other.currentPos)->opposite())) {
if ((*i) > 0)
if (opp->getIndex() ==
net->findSegment(*i)->getIndex()) {
if (net->findSegment(*i)->getStart()->getIndex() ==
node) {
{
SG_LOG(SG_ATC, SG_BULK, "Found the node " << node);
return true;
}
}
}
}
if (! other.intentions.empty()) {
for (intVecIterator j = other.intentions.begin();
j != other.intentions.end(); j++) {
SG_LOG(SG_ATC, SG_BULK, "Current segment 1 " << (*i));
if ((*i) > 0) {
if ((opp = net->findSegment(*i)->opposite())) {
if (opp->getIndex() ==
net->findSegment(*j)->getIndex()) {
SG_LOG(SG_ATC, SG_BULK, "Nodes " << net->findSegment(*i)->getIndex()
<< " and " << net->findSegment(*j)->getIndex()
<< " are opposites ");
if (net->findSegment(*i)->getStart()->
getIndex() == node) {
{
SG_LOG(SG_ATC, SG_BULK, "Found the node " << node);
return true;
}
}
}
}
}
}
}
}
}
return false;
}
bool FGTrafficRecord::isActive(int margin) const
{
if (aircraft->getDie()) {
return false;
}
time_t now = globals->get_time_params()->get_cur_time();
time_t deptime = aircraft->getTrafficRef()->getDepartureTime();
return ((now + margin) > deptime);
}
void FGTrafficRecord::setSpeedAdjustment(double spd)
{
instruction.setChangeSpeed(true);
instruction.setSpeed(spd);
}
void FGTrafficRecord::setHeadingAdjustment(double heading)
{
instruction.setChangeHeading(true);
instruction.setHeading(heading);
}
bool FGTrafficRecord::pushBackAllowed() const
{
return allowPushback;
}
/***************************************************************************
* FGATCInstruction
*
**************************************************************************/
FGATCInstruction::FGATCInstruction()
{
holdPattern = false;
holdPosition = false;
changeSpeed = false;
changeHeading = false;
changeAltitude = false;
resolveCircularWait = false;
speed = 0;
heading = 0;
alt = 0;
}
bool FGATCInstruction::hasInstruction() const
{
return (holdPattern || holdPosition || changeSpeed || changeHeading
|| changeAltitude || resolveCircularWait);
}

377
src/ATC/trafficcontrol.hxx Normal file
View File

@@ -0,0 +1,377 @@
// trafficcontrol.hxx - classes to manage AIModels based air traffic control
// Written by Durk Talsma, started September 2006.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// $Id$
#ifndef _TRAFFIC_CONTROL_HXX_
#define _TRAFFIC_CONTROL_HXX_
#include <Airports/airports_fwd.hxx>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/MatrixTransform>
#include <osg/Shape>
#include <simgear/compiler.h>
// There is probably a better include than sg_geodesy to get the SG_NM_TO_METER...
#include <simgear/math/sg_geodesy.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
class FGAIAircraft;
typedef std::vector<FGAIAircraft*> AircraftVec;
typedef std::vector<FGAIAircraft*>::iterator AircraftVecIterator;
class FGAIFlightPlan;
typedef std::vector<FGAIFlightPlan*> FlightPlanVec;
typedef std::vector<FGAIFlightPlan*>::iterator FlightPlanVecIterator;
typedef std::map<std::string, FlightPlanVec> FlightPlanVecMap;
class FGTrafficRecord;
typedef std::list<FGTrafficRecord> TrafficVector;
typedef std::list<FGTrafficRecord>::iterator TrafficVectorIterator;
class ActiveRunway;
typedef std::vector<ActiveRunway> ActiveRunwayVec;
typedef std::vector<ActiveRunway>::iterator ActiveRunwayVecIterator;
typedef std::vector<int> intVec;
typedef std::vector<int>::iterator intVecIterator;
/**************************************************************************************
* class FGATCInstruction
* like class FGATC Controller, this class definition should go into its own file
* and or directory... For now, just testing this stuff out though...
*************************************************************************************/
class FGATCInstruction
{
private:
bool holdPattern;
bool holdPosition;
bool changeSpeed;
bool changeHeading;
bool changeAltitude;
bool resolveCircularWait;
double speed;
double heading;
double alt;
public:
FGATCInstruction();
bool hasInstruction () const;
bool getHoldPattern () const {
return holdPattern;
};
bool getHoldPosition () const {
return holdPosition;
};
bool getChangeSpeed () const {
return changeSpeed;
};
bool getChangeHeading () const {
return changeHeading;
};
bool getChangeAltitude() const {
return changeAltitude;
};
bool getCheckForCircularWait() const {
return resolveCircularWait;
};
double getSpeed () const {
return speed;
};
double getHeading () const {
return heading;
};
double getAlt () const {
return alt;
};
void setHoldPattern (bool val) {
holdPattern = val;
};
void setHoldPosition (bool val) {
holdPosition = val;
};
void setChangeSpeed (bool val) {
changeSpeed = val;
};
void setChangeHeading (bool val) {
changeHeading = val;
};
void setChangeAltitude(bool val) {
changeAltitude = val;
};
void setResolveCircularWait (bool val) {
resolveCircularWait = val;
};
void setSpeed (double val) {
speed = val;
};
void setHeading (double val) {
heading = val;
};
void setAlt (double val) {
alt = val;
};
};
/**************************************************************************************
* class FGTrafficRecord
* Represents the interaction of an AI Aircraft and ATC
*************************************************************************************/
class FGTrafficRecord
{
private:
int id;
int waitsForId;
int currentPos;
int leg;
int frequencyId;
int state;
bool allowTransmission;
bool allowPushback;
int priority;
time_t timer;
intVec intentions;
FGATCInstruction instruction;
SGGeod pos;
double heading, speed, altitude, radius;
std::string callsign;
std::string runway;
SGSharedPtr<FGAIAircraft> aircraft;
public:
FGTrafficRecord();
virtual ~FGTrafficRecord();
void setId(int val) {
id = val;
};
void setRadius(double rad) {
radius = rad;
};
void setPositionAndIntentions(int pos, FGAIFlightPlan *route);
void setRunway(const std::string& rwy) {
runway = rwy;
};
void setLeg(int lg) {
leg = lg;
};
int getId() const {
return id;
};
int getState() const {
return state;
};
void setState(int s) {
state = s;
}
FGATCInstruction getInstruction() const {
return instruction;
};
bool hasInstruction() const {
return instruction.hasInstruction();
};
void setPositionAndHeading(double lat, double lon, double hdg, double spd, double alt);
bool checkPositionAndIntentions(FGTrafficRecord &other);
int crosses (FGGroundNetwork *, FGTrafficRecord &other);
bool isOpposing (FGGroundNetwork *, FGTrafficRecord &other, int node);
bool isActive(int margin) const;
bool isDead() const;
void clearATCController() const;
bool onRoute(FGGroundNetwork *, FGTrafficRecord &other);
bool getSpeedAdjustment() const {
return instruction.getChangeSpeed();
};
SGGeod getPos() {
return pos;
}
double getHeading () const {
return heading ;
};
double getSpeed () const {
return speed ;
};
double getFAltitude () const {
return altitude ;
};
double getRadius () const {
return radius ;
};
int getWaitsForId () const {
return waitsForId;
};
void setSpeedAdjustment(double spd);
void setHeadingAdjustment(double heading);
void clearSpeedAdjustment () {
instruction.setChangeSpeed (false);
};
void clearHeadingAdjustment() {
instruction.setChangeHeading(false);
};
bool hasHeadingAdjustment() const {
return instruction.getChangeHeading();
};
bool hasHoldPosition() const {
return instruction.getHoldPosition();
};
void setHoldPosition (bool inst) {
instruction.setHoldPosition(inst);
};
void setWaitsForId(int id) {
waitsForId = id;
};
void setResolveCircularWait() {
instruction.setResolveCircularWait(true);
};
void clearResolveCircularWait() {
instruction.setResolveCircularWait(false);
};
void setCallsign(std::string clsgn) { callsign = clsgn; };
const std::string& getCallsign() const {
return callsign;
};
const std::string& getRunway() const {
return runway;
};
void setAircraft(FGAIAircraft *ref);
void updateState() {
state++;
allowTransmission=true;
};
//string getCallSign() { return callsign; };
FGAIAircraft *getAircraft() const;
int getTime() const {
return timer;
};
int getLeg() const {
return leg;
};
void setTime(time_t time) {
timer = time;
};
bool pushBackAllowed() const;
bool allowTransmissions() const {
return allowTransmission;
};
void allowPushBack() { allowPushback =true;};
void denyPushBack () { allowPushback = false;};
void suppressRepeatedTransmissions () {
allowTransmission=false;
};
void allowRepeatedTransmissions () {
allowTransmission=true;
};
void nextFrequency() {
frequencyId++;
};
int getNextFrequency() const {
return frequencyId;
};
intVec& getIntentions() {
return intentions;
};
int getCurrentPosition() const {
return currentPos;
};
void setPriority(int p) { priority = p; };
int getPriority() const { return priority; };
};
/***********************************************************************
* Active runway, a utility class to keep track of which aircraft has
* clearance for a given runway.
**********************************************************************/
class ActiveRunway
{
private:
const std::string rwy;
int currentlyCleared;
double distanceToFinal;
TimeVector estimatedArrivalTimes;
using AircraftRefVec = std::vector<SGSharedPtr<FGAIAircraft>>;
AircraftRefVec departureQueue;
public:
ActiveRunway(const std::string& r, int cc);
const std::string& getRunwayName() const
{
return rwy;
};
int getCleared() const
{
return currentlyCleared;
};
double getApproachDistance() const
{
return distanceToFinal;
};
//time_t getEstApproachTime() { return estimatedArrival; };
//void setEstApproachTime(time_t time) { estimatedArrival = time; };
void addToDepartureQueue(FGAIAircraft *ac);
void setCleared(int number) {
currentlyCleared = number;
};
time_t requestTimeSlot(time_t eta);
//time_t requestTimeSlot(time_t eta, std::string wakeCategory);
void slotHousekeeping(time_t newEta);
int getdepartureQueueSize() {
return departureQueue.size();
};
SGSharedPtr<FGAIAircraft> getFirstAircraftInDepartureQueue() const;
SGSharedPtr<FGAIAircraft> getFirstOfStatus(int stat) const;
void updateDepartureQueue();
void printDepartureQueue();
};
#endif // _TRAFFIC_CONTROL_HXX