first commit
This commit is contained in:
477
src/ATC/ATCController.cxx
Normal file
477
src/ATC/ATCController.cxx
Normal 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
138
src/ATC/ATCController.hxx
Normal 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
592
src/ATC/ATISEncoder.cxx
Normal 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
133
src/ATC/ATISEncoder.hxx
Normal 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
|
||||
195
src/ATC/ApproachController.cxx
Normal file
195
src/ATC/ApproachController.cxx
Normal 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;
|
||||
}
|
||||
67
src/ATC/ApproachController.hxx
Normal file
67
src/ATC/ApproachController.hxx
Normal 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
31
src/ATC/CMakeLists.txt
Normal 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
38
src/ATC/CommStation.cxx
Normal 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
36
src/ATC/CommStation.hxx
Normal 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
|
||||
|
||||
145
src/ATC/CurrentWeatherATISInformationProvider.cxx
Normal file
145
src/ATC/CurrentWeatherATISInformationProvider.cxx
Normal 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";
|
||||
}
|
||||
|
||||
58
src/ATC/CurrentWeatherATISInformationProvider.hxx
Normal file
58
src/ATC/CurrentWeatherATISInformationProvider.hxx
Normal 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
1002
src/ATC/GroundController.cxx
Normal file
File diff suppressed because it is too large
Load Diff
90
src/ATC/GroundController.hxx
Normal file
90
src/ATC/GroundController.hxx
Normal 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
|
||||
130
src/ATC/MetarPropertiesATISInformationProvider.cxx
Normal file
130
src/ATC/MetarPropertiesATISInformationProvider.cxx
Normal 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";
|
||||
}
|
||||
|
||||
58
src/ATC/MetarPropertiesATISInformationProvider.hxx
Normal file
58
src/ATC/MetarPropertiesATISInformationProvider.hxx
Normal 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
|
||||
442
src/ATC/StartupController.cxx
Normal file
442
src/ATC/StartupController.cxx
Normal 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;
|
||||
}
|
||||
|
||||
71
src/ATC/StartupController.hxx
Normal file
71
src/ATC/StartupController.hxx
Normal 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
308
src/ATC/TowerController.cxx
Normal 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;
|
||||
}
|
||||
68
src/ATC/TowerController.hxx
Normal file
68
src/ATC/TowerController.hxx
Normal 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
414
src/ATC/atc_mgr.cxx
Normal 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
83
src/ATC/atc_mgr.hxx
Normal 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
579
src/ATC/trafficcontrol.cxx
Normal 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
377
src/ATC/trafficcontrol.hxx
Normal 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
|
||||
Reference in New Issue
Block a user