2008-11-16 13:43:44 +00:00
|
|
|
// trafficrecord.cxx - Implementation of AIModels ATC code.
|
2008-07-13 12:51:06 +00:00
|
|
|
//
|
|
|
|
// 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$
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include <config.h>
|
|
|
|
#endif
|
|
|
|
|
2010-08-29 17:25:34 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2011-04-26 17:18:28 +00:00
|
|
|
#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 <Scenery/scenery.hxx>
|
|
|
|
|
2008-07-13 12:51:06 +00:00
|
|
|
#include "trafficcontrol.hxx"
|
2011-04-03 15:58:16 +00:00
|
|
|
#include "atc_mgr.hxx"
|
2008-07-13 12:51:06 +00:00
|
|
|
#include <AIModel/AIAircraft.hxx>
|
|
|
|
#include <AIModel/AIFlightPlan.hxx>
|
2010-08-29 17:25:34 +00:00
|
|
|
#include <AIModel/performancedata.hxx>
|
|
|
|
#include <AIModel/performancedb.hxx>
|
2011-04-15 17:32:21 +00:00
|
|
|
#include <ATC/atc_mgr.hxx>
|
2008-07-13 12:51:06 +00:00
|
|
|
#include <Traffic/TrafficMgr.hxx>
|
2008-08-14 18:13:39 +00:00
|
|
|
#include <Airports/groundnetwork.hxx>
|
|
|
|
#include <Airports/dynamics.hxx>
|
2010-10-20 16:54:16 +00:00
|
|
|
#include <Airports/simple.hxx>
|
2011-11-24 04:39:54 +00:00
|
|
|
#include <Radio/radio.hxx>
|
2011-12-30 14:27:21 +00:00
|
|
|
#include <signal.h>
|
2011-09-04 06:18:13 +00:00
|
|
|
|
2010-08-29 17:25:34 +00:00
|
|
|
using std::sort;
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* ActiveRunway
|
|
|
|
**************************************************************************/
|
|
|
|
time_t ActiveRunway::requestTimeSlot(time_t eta)
|
|
|
|
{
|
|
|
|
time_t newEta;
|
|
|
|
time_t separation = 90;
|
|
|
|
bool found = false;
|
|
|
|
if (estimatedArrivalTimes.size() == 0) {
|
|
|
|
estimatedArrivalTimes.push_back(eta);
|
|
|
|
return eta;
|
|
|
|
} else {
|
|
|
|
TimeVectorIterator i = estimatedArrivalTimes.begin();
|
|
|
|
//cerr << "Checking eta slots " << eta << ": " << endl;
|
|
|
|
for (i = estimatedArrivalTimes.begin();
|
2011-10-03 18:54:58 +00:00
|
|
|
i != estimatedArrivalTimes.end(); i++) {
|
2010-08-29 17:25:34 +00:00
|
|
|
//cerr << "Stored time : " << (*i) << endl;
|
|
|
|
}
|
|
|
|
i = estimatedArrivalTimes.begin();
|
|
|
|
if ((eta + separation) < (*i)) {
|
|
|
|
newEta = eta;
|
|
|
|
found = true;
|
|
|
|
//cerr << "Storing at beginning" << endl;
|
|
|
|
}
|
|
|
|
while ((i != estimatedArrivalTimes.end()) && (!found)) {
|
|
|
|
TimeVectorIterator j = i + 1;
|
|
|
|
if (j == estimatedArrivalTimes.end()) {
|
|
|
|
if (((*i) + separation) < eta) {
|
|
|
|
//cerr << "Storing at end" << endl;
|
|
|
|
newEta = eta;
|
|
|
|
} else {
|
|
|
|
newEta = (*i) + separation;
|
|
|
|
//cerr << "Storing at end + separation" << endl;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ((((*j) - (*i)) > (separation * 2))) { // found a potential slot
|
2011-08-03 21:09:52 +00:00
|
|
|
// now check whether this slot is usable:
|
2010-08-29 17:25:34 +00:00
|
|
|
// 1) eta should fall between the two points
|
|
|
|
// i.e. eta > i AND eta < j
|
|
|
|
//
|
|
|
|
//cerr << "Found potential slot after " << (*i) << endl;
|
|
|
|
if (eta > (*i) && (eta < (*j))) {
|
|
|
|
found = true;
|
|
|
|
if (eta < ((*i) + separation)) {
|
|
|
|
newEta = (*i) + separation;
|
|
|
|
//cerr << "Using original" << (*i) << " + separation " << endl;
|
|
|
|
} else {
|
|
|
|
newEta = eta;
|
|
|
|
//cerr << "Using original after " << (*i) << endl;
|
|
|
|
}
|
|
|
|
} else if (eta < (*i)) {
|
|
|
|
found = true;
|
|
|
|
newEta = (*i) + separation;
|
|
|
|
//cerr << "Using delayed slot after " << (*i) << endl;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
if (((*j) - separation) < eta) {
|
|
|
|
found = true;
|
|
|
|
if (((*i) + separation) < eta) {
|
|
|
|
newEta = eta;
|
|
|
|
cerr << "Using original after " << (*i) << endl;
|
|
|
|
} else {
|
|
|
|
newEta = (*i) + separation;
|
|
|
|
cerr << "Using " << (*i) << " + separation " << endl;
|
|
|
|
}
|
|
|
|
} */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//cerr << ". done. New ETA : " << newEta << endl;
|
|
|
|
|
|
|
|
estimatedArrivalTimes.push_back(newEta);
|
|
|
|
sort(estimatedArrivalTimes.begin(), estimatedArrivalTimes.end());
|
|
|
|
// do some housekeeping : remove any timestamps that are past
|
|
|
|
time_t now = time(NULL) + fgGetLong("/sim/time/warp");
|
|
|
|
TimeVectorIterator i = estimatedArrivalTimes.begin();
|
|
|
|
while (i != estimatedArrivalTimes.end()) {
|
|
|
|
if ((*i) < now) {
|
|
|
|
//cerr << "Deleting timestamp " << (*i) << " (now = " << now << "). " << endl;
|
|
|
|
estimatedArrivalTimes.erase(i);
|
|
|
|
i = estimatedArrivalTimes.begin();
|
|
|
|
} else {
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newEta;
|
|
|
|
}
|
|
|
|
|
2011-10-08 22:25:04 +00:00
|
|
|
void ActiveRunway::printDepartureCue()
|
|
|
|
{
|
|
|
|
cout << "Departure cue for " << rwy << ": " << endl;
|
|
|
|
for (AircraftVecIterator atc = departureCue.begin(); atc != departureCue.end(); atc++) {
|
|
|
|
cout << " " << (*atc)->getCallSign() << " " << (*atc)->getTakeOffStatus();
|
|
|
|
cout << " " << (*atc)->_getLatitude() << " " << (*atc)->_getLongitude() << (*atc)-> getSpeed() << " " << (*atc)->getAltitude() << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
FGAIAircraft* ActiveRunway::getFirstOfStatus(int stat)
|
|
|
|
{
|
|
|
|
for (AircraftVecIterator atc =departureCue.begin(); atc != departureCue.end(); atc++) {
|
|
|
|
if ((*atc)->getTakeOffStatus() == stat)
|
|
|
|
return (*atc);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2008-07-13 12:51:06 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* FGTrafficRecord
|
|
|
|
**************************************************************************/
|
2010-08-29 17:25:34 +00:00
|
|
|
FGTrafficRecord::FGTrafficRecord():
|
2011-10-03 18:54:58 +00:00
|
|
|
id(0), waitsForId(0),
|
|
|
|
currentPos(0),
|
|
|
|
leg(0),
|
|
|
|
frequencyId(0),
|
|
|
|
state(0),
|
|
|
|
allowTransmission(true),
|
|
|
|
allowPushback(true),
|
|
|
|
priority(0),
|
|
|
|
latitude(0), longitude(0), heading(0), speed(0), altitude(0), radius(0)
|
2010-08-29 17:25:34 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGTrafficRecord::setPositionAndIntentions(int pos,
|
2011-10-03 18:54:58 +00:00
|
|
|
FGAIFlightPlan * route)
|
2010-08-29 17:25:34 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
currentPos = pos;
|
|
|
|
if (intentions.size()) {
|
|
|
|
intVecIterator i = intentions.begin();
|
|
|
|
if ((*i) != pos) {
|
2012-01-02 21:23:24 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
|
|
|
"Error in FGTrafficRecord::setPositionAndIntentions at " << SG_ORIGIN);
|
2010-08-29 17:25:34 +00:00
|
|
|
}
|
2012-01-02 21:23:24 +00:00
|
|
|
intentions.erase(i);
|
2010-08-29 17:25:34 +00:00
|
|
|
} else {
|
|
|
|
//FGAIFlightPlan::waypoint* const wpt= route->getCurrentWaypoint();
|
|
|
|
int size = route->getNrOfWayPoints();
|
|
|
|
//cerr << "Setting pos" << pos << " ";
|
|
|
|
//cerr << "setting intentions ";
|
2012-01-02 21:23:24 +00:00
|
|
|
for (int i = 2; i < size; i++) {
|
2010-08-29 17:25:34 +00:00
|
|
|
int val = route->getRouteIndex(i);
|
2012-01-02 21:23:24 +00:00
|
|
|
intentions.push_back(val);
|
2010-08-29 17:25:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-08-07 19:38:50 +00:00
|
|
|
/**
|
2011-10-03 18:54:58 +00:00
|
|
|
* Check if another aircraft is ahead of the current one, and on the same
|
2011-08-07 19:38:50 +00:00
|
|
|
* return true / false is the is/isn't the case.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
2010-08-29 17:25:34 +00:00
|
|
|
|
|
|
|
bool FGTrafficRecord::checkPositionAndIntentions(FGTrafficRecord & other)
|
|
|
|
{
|
|
|
|
bool result = false;
|
|
|
|
//cerr << "Start check 1" << endl;
|
|
|
|
if (currentPos == other.currentPos) {
|
|
|
|
//cerr << callsign << ": Check Position and intentions: we are on the same taxiway" << other.callsign << "Index = " << currentPos << endl;
|
|
|
|
result = true;
|
|
|
|
}
|
2011-10-03 18:54:58 +00:00
|
|
|
// else if (other.intentions.size())
|
2010-08-29 17:25:34 +00:00
|
|
|
// {
|
|
|
|
// cerr << "Start check 2" << endl;
|
2011-10-03 18:54:58 +00:00
|
|
|
// intVecIterator i = other.intentions.begin();
|
2010-08-29 17:25:34 +00:00
|
|
|
// while (!((i == other.intentions.end()) || ((*i) == currentPos)))
|
|
|
|
// i++;
|
|
|
|
// if (i != other.intentions.end()) {
|
|
|
|
// cerr << "Check Position and intentions: current matches other.intentions" << endl;
|
|
|
|
// result = true;
|
|
|
|
// }
|
|
|
|
else if (intentions.size()) {
|
|
|
|
//cerr << "Start check 3" << endl;
|
|
|
|
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()) {
|
|
|
|
//cerr << callsign << ": Check Position and intentions: .other.current matches" << other.callsign << "Index = " << (*i) << endl;
|
|
|
|
result = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//cerr << "Done !!" << endl;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGTrafficRecord::setPositionAndHeading(double lat, double lon,
|
2011-10-03 18:54:58 +00:00
|
|
|
double hdg, double spd,
|
|
|
|
double alt)
|
2010-08-29 17:25:34 +00:00
|
|
|
{
|
|
|
|
latitude = lat;
|
|
|
|
longitude = lon;
|
|
|
|
heading = hdg;
|
|
|
|
speed = spd;
|
|
|
|
altitude = alt;
|
|
|
|
}
|
|
|
|
|
|
|
|
int FGTrafficRecord::crosses(FGGroundNetwork * net,
|
|
|
|
FGTrafficRecord & other)
|
|
|
|
{
|
|
|
|
if (checkPositionAndIntentions(other)
|
2011-10-03 18:54:58 +00:00
|
|
|
|| (other.checkPositionAndIntentions(*this)))
|
2010-08-29 17:25:34 +00:00
|
|
|
return -1;
|
|
|
|
intVecIterator i, j;
|
|
|
|
int currentTargetNode = 0, otherTargetNode = 0;
|
|
|
|
if (currentPos > 0)
|
2011-10-03 18:54:58 +00:00
|
|
|
currentTargetNode = net->findSegment(currentPos)->getEnd()->getIndex(); // OKAY,...
|
2010-08-29 17:25:34 +00:00
|
|
|
if (other.currentPos > 0)
|
|
|
|
otherTargetNode = net->findSegment(other.currentPos)->getEnd()->getIndex(); // OKAY,...
|
|
|
|
if ((currentTargetNode == otherTargetNode) && currentTargetNode > 0)
|
|
|
|
return currentTargetNode;
|
|
|
|
if (intentions.size()) {
|
|
|
|
for (i = intentions.begin(); i != intentions.end(); i++) {
|
|
|
|
if ((*i) > 0) {
|
|
|
|
if ((currentTargetNode ==
|
2011-10-03 18:54:58 +00:00
|
|
|
net->findSegment(*i)->getEnd()->getIndex())) {
|
2010-08-29 17:25:34 +00:00
|
|
|
//cerr << "Current crosses at " << currentTargetNode <<endl;
|
|
|
|
return currentTargetNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (other.intentions.size()) {
|
|
|
|
for (i = other.intentions.begin(); i != other.intentions.end();
|
2011-10-03 18:54:58 +00:00
|
|
|
i++) {
|
2010-08-29 17:25:34 +00:00
|
|
|
if ((*i) > 0) {
|
|
|
|
if (otherTargetNode ==
|
2011-10-03 18:54:58 +00:00
|
|
|
net->findSegment(*i)->getEnd()->getIndex()) {
|
2010-08-29 17:25:34 +00:00
|
|
|
//cerr << "Other crosses at " << currentTargetNode <<endl;
|
|
|
|
return otherTargetNode;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (intentions.size() && other.intentions.size()) {
|
|
|
|
for (i = intentions.begin(); i != intentions.end(); i++) {
|
|
|
|
for (j = other.intentions.begin(); j != other.intentions.end();
|
2011-10-03 18:54:58 +00:00
|
|
|
j++) {
|
2010-08-29 17:25:34 +00:00
|
|
|
//cerr << "finding segment " << *i << " and " << *j << endl;
|
|
|
|
if (((*i) > 0) && ((*j) > 0)) {
|
|
|
|
currentTargetNode =
|
|
|
|
net->findSegment(*i)->getEnd()->getIndex();
|
|
|
|
otherTargetNode =
|
|
|
|
net->findSegment(*j)->getEnd()->getIndex();
|
|
|
|
if (currentTargetNode == otherTargetNode) {
|
|
|
|
//cerr << "Routes will cross at " << currentTargetNode << endl;
|
|
|
|
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.size()) {
|
|
|
|
for (intVecIterator i = other.intentions.begin();
|
2011-10-03 18:54:58 +00:00
|
|
|
i != other.intentions.end(); i++) {
|
2010-08-29 17:25:34 +00:00
|
|
|
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.size())
|
|
|
|
// {
|
|
|
|
// for (intVecIterator i = intentions.begin(); i != intentions.end(); i++)
|
|
|
|
// {
|
2011-10-03 18:54:58 +00:00
|
|
|
// if (*i > 0)
|
2010-08-29 17:25:34 +00:00
|
|
|
// {
|
|
|
|
// 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;
|
|
|
|
//cerr << "Current segment " << currentPos << endl;
|
|
|
|
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();
|
2011-10-03 18:54:58 +00:00
|
|
|
i++) {
|
2010-08-29 17:25:34 +00:00
|
|
|
if ((opp = net->findSegment(other.currentPos)->opposite())) {
|
|
|
|
if ((*i) > 0)
|
|
|
|
if (opp->getIndex() ==
|
2011-10-03 18:54:58 +00:00
|
|
|
net->findSegment(*i)->getIndex()) {
|
2010-08-29 17:25:34 +00:00
|
|
|
if (net->findSegment(*i)->getStart()->getIndex() ==
|
2011-10-03 18:54:58 +00:00
|
|
|
node) {
|
2010-08-29 17:25:34 +00:00
|
|
|
{
|
|
|
|
//cerr << "Found the node " << node << endl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (other.intentions.size()) {
|
|
|
|
for (intVecIterator j = other.intentions.begin();
|
2011-10-03 18:54:58 +00:00
|
|
|
j != other.intentions.end(); j++) {
|
2010-08-29 17:25:34 +00:00
|
|
|
// cerr << "Current segment 1 " << (*i) << endl;
|
|
|
|
if ((*i) > 0) {
|
|
|
|
if ((opp = net->findSegment(*i)->opposite())) {
|
|
|
|
if (opp->getIndex() ==
|
2011-10-03 18:54:58 +00:00
|
|
|
net->findSegment(*j)->getIndex()) {
|
2010-08-29 17:25:34 +00:00
|
|
|
//cerr << "Nodes " << net->findSegment(*i)->getIndex()
|
|
|
|
// << " and " << net->findSegment(*j)->getIndex()
|
|
|
|
// << " are opposites " << endl;
|
|
|
|
if (net->findSegment(*i)->getStart()->
|
2011-10-03 18:54:58 +00:00
|
|
|
getIndex() == node) {
|
2010-08-29 17:25:34 +00:00
|
|
|
{
|
|
|
|
//cerr << "Found the node " << node << endl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
bool FGTrafficRecord::isActive(int margin)
|
|
|
|
{
|
|
|
|
time_t now = time(NULL) + fgGetLong("/sim/time/warp");
|
|
|
|
time_t deptime = aircraft->getTrafficRef()->getDepartureTime();
|
|
|
|
return ((now + margin) > deptime);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-29 17:25:34 +00:00
|
|
|
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()
|
|
|
|
{
|
2011-10-03 18:54:58 +00:00
|
|
|
return allowPushback;
|
2010-01-30 15:40:33 +00:00
|
|
|
}
|
|
|
|
|
2008-07-13 12:51:06 +00:00
|
|
|
|
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
|
2008-07-13 12:51:06 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* FGATCInstruction
|
|
|
|
*
|
|
|
|
**************************************************************************/
|
|
|
|
FGATCInstruction::FGATCInstruction()
|
|
|
|
{
|
2010-08-29 17:25:34 +00:00
|
|
|
holdPattern = false;
|
|
|
|
holdPosition = false;
|
|
|
|
changeSpeed = false;
|
|
|
|
changeHeading = false;
|
|
|
|
changeAltitude = false;
|
|
|
|
resolveCircularWait = false;
|
2008-07-13 12:51:06 +00:00
|
|
|
|
2010-08-29 17:25:34 +00:00
|
|
|
speed = 0;
|
|
|
|
heading = 0;
|
|
|
|
alt = 0;
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
|
2010-04-25 08:32:53 +00:00
|
|
|
|
2008-07-13 12:51:06 +00:00
|
|
|
bool FGATCInstruction::hasInstruction()
|
|
|
|
{
|
2010-08-29 17:25:34 +00:00
|
|
|
return (holdPattern || holdPosition || changeSpeed || changeHeading
|
|
|
|
|| changeAltitude || resolveCircularWait);
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
|
2010-04-25 08:32:53 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* FGATCController
|
|
|
|
*
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-08-29 17:25:34 +00:00
|
|
|
FGATCController::FGATCController()
|
2010-04-25 08:32:53 +00:00
|
|
|
{
|
2011-07-24 10:48:13 +00:00
|
|
|
//cerr << "running FGATController constructor" << endl;
|
2010-08-29 17:25:34 +00:00
|
|
|
dt_count = 0;
|
|
|
|
available = true;
|
|
|
|
lastTransmission = 0;
|
2011-04-10 06:58:48 +00:00
|
|
|
initialized = false;
|
2011-04-03 15:58:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FGATCController::~FGATCController()
|
|
|
|
{
|
2011-10-03 18:54:58 +00:00
|
|
|
//cerr << "running FGATController destructor" << endl;
|
2010-04-25 08:32:53 +00:00
|
|
|
}
|
|
|
|
|
2010-08-29 17:25:34 +00:00
|
|
|
string FGATCController::getGateName(FGAIAircraft * ref)
|
2008-07-13 12:51:06 +00:00
|
|
|
{
|
|
|
|
return ref->atGate();
|
|
|
|
}
|
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
bool FGATCController::isUserAircraft(FGAIAircraft* ac)
|
|
|
|
{
|
|
|
|
return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false;
|
2011-04-11 20:23:53 +00:00
|
|
|
};
|
|
|
|
|
2011-09-04 06:18:13 +00:00
|
|
|
void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, AtcMsgId msgId,
|
2011-04-15 17:32:21 +00:00
|
|
|
AtcMsgDir msgDir, bool audible)
|
2008-07-13 12:51:06 +00:00
|
|
|
{
|
|
|
|
string sender, receiver;
|
2008-07-27 09:59:52 +00:00
|
|
|
int stationFreq = 0;
|
2010-08-29 17:25:34 +00:00
|
|
|
int taxiFreq = 0;
|
2011-08-10 19:39:28 +00:00
|
|
|
int towerFreq = 0;
|
2010-08-29 17:25:34 +00:00
|
|
|
int freqId = 0;
|
2009-02-07 17:17:07 +00:00
|
|
|
string atisInformation;
|
2008-07-13 12:51:06 +00:00
|
|
|
string text;
|
2009-02-07 17:17:07 +00:00
|
|
|
string taxiFreqStr;
|
2011-08-10 19:39:28 +00:00
|
|
|
string towerFreqStr;
|
2009-02-15 15:29:56 +00:00
|
|
|
double heading = 0;
|
|
|
|
string activeRunway;
|
|
|
|
string fltType;
|
|
|
|
string rwyClass;
|
2009-03-08 17:14:05 +00:00
|
|
|
string SID;
|
2009-03-22 13:49:51 +00:00
|
|
|
string transponderCode;
|
2009-03-08 17:14:05 +00:00
|
|
|
FGAIFlightPlan *fp;
|
2009-03-22 13:49:51 +00:00
|
|
|
string fltRules;
|
2011-08-07 19:38:50 +00:00
|
|
|
string instructionText;
|
2011-09-04 07:00:36 +00:00
|
|
|
int ground_to_air=0;
|
2010-02-21 14:15:18 +00:00
|
|
|
|
|
|
|
//double commFreqD;
|
|
|
|
sender = rec->getAircraft()->getTrafficRef()->getCallSign();
|
2011-08-07 19:38:50 +00:00
|
|
|
if (rec->getAircraft()->getTaxiClearanceRequest()) {
|
|
|
|
instructionText = "push-back and taxi";
|
|
|
|
} else {
|
|
|
|
instructionText = "taxi";
|
|
|
|
}
|
2010-04-25 08:32:53 +00:00
|
|
|
//cerr << "transmitting for: " << sender << "Leg = " << rec->getLeg() << endl;
|
2010-02-21 14:15:18 +00:00
|
|
|
switch (rec->getLeg()) {
|
2011-07-31 17:27:44 +00:00
|
|
|
case 1:
|
2010-08-29 17:25:34 +00:00
|
|
|
case 2:
|
|
|
|
freqId = rec->getNextFrequency();
|
|
|
|
stationFreq =
|
|
|
|
rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
|
|
|
|
getDynamics()->getGroundFrequency(rec->getLeg() + freqId);
|
|
|
|
taxiFreq =
|
|
|
|
rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
|
2011-07-31 17:27:44 +00:00
|
|
|
getDynamics()->getGroundFrequency(2);
|
2011-10-03 18:54:58 +00:00
|
|
|
towerFreq =
|
2011-08-10 19:39:28 +00:00
|
|
|
rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
|
|
|
|
getDynamics()->getTowerFrequency(2);
|
2010-08-29 17:25:34 +00:00
|
|
|
receiver =
|
|
|
|
rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
|
|
|
|
getName() + "-Ground";
|
|
|
|
atisInformation =
|
|
|
|
rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
|
2011-06-02 22:28:40 +00:00
|
|
|
getDynamics()->getAtisSequence();
|
2010-08-29 17:25:34 +00:00
|
|
|
break;
|
2011-07-31 17:27:44 +00:00
|
|
|
case 3:
|
2010-08-29 17:25:34 +00:00
|
|
|
receiver =
|
|
|
|
rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
|
|
|
|
getName() + "-Tower";
|
|
|
|
break;
|
2010-02-21 14:15:18 +00:00
|
|
|
}
|
|
|
|
// Swap sender and receiver value in case of a ground to air transmission
|
|
|
|
if (msgDir == ATC_GROUND_TO_AIR) {
|
2010-08-29 17:25:34 +00:00
|
|
|
string tmp = sender;
|
|
|
|
sender = receiver;
|
|
|
|
receiver = tmp;
|
2011-09-04 07:00:36 +00:00
|
|
|
ground_to_air=1;
|
2010-02-21 14:15:18 +00:00
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
switch (msgId) {
|
2010-08-29 17:25:34 +00:00
|
|
|
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()->
|
2011-10-03 18:54:58 +00:00
|
|
|
getDynamics()->getActiveRunway(rwyClass, 1, activeRunway,
|
|
|
|
heading);
|
2010-08-29 17:25:34 +00:00
|
|
|
rec->getAircraft()->GetFlightPlan()->setRunway(activeRunway);
|
|
|
|
fp = rec->getAircraft()->getTrafficRef()->getDepartureAirport()->
|
2011-10-03 18:54:58 +00:00
|
|
|
getDynamics()->getSID(activeRunway, heading);
|
2010-08-29 17:25:34 +00:00
|
|
|
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 + ". " +
|
2011-08-07 19:38:50 +00:00
|
|
|
"For "+ instructionText + " clearance call " + taxiFreqStr + ". " +
|
2010-08-29 17:25:34 +00:00
|
|
|
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();
|
2011-08-07 19:38:50 +00:00
|
|
|
|
2010-08-29 17:25:34 +00:00
|
|
|
text =
|
|
|
|
receiver + ". Start-up approved. " + atisInformation +
|
|
|
|
" correct, runway " + activeRunway + ", " + SID + ", squawk " +
|
|
|
|
transponderCode + ". " +
|
2011-08-07 19:38:50 +00:00
|
|
|
"For " + instructionText + " clearance call " + taxiFreqStr + ". " +
|
2010-08-29 17:25:34 +00:00
|
|
|
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:
|
2011-08-07 19:38:50 +00:00
|
|
|
if (rec->getAircraft()->getTaxiClearanceRequest()) {
|
|
|
|
text = receiver + ". Request push-back. " + sender;
|
|
|
|
} else {
|
|
|
|
text = receiver + ". Request Taxi clearance. " + sender;
|
|
|
|
}
|
2010-08-29 17:25:34 +00:00
|
|
|
break;
|
|
|
|
case MSG_PERMIT_PUSHBACK_CLEARANCE:
|
2011-08-07 19:38:50 +00:00
|
|
|
if (rec->getAircraft()->getTaxiClearanceRequest()) {
|
|
|
|
text = receiver + ". Push-back approved. " + sender;
|
|
|
|
} else {
|
|
|
|
text = receiver + ". Cleared to Taxi." + sender;
|
|
|
|
}
|
2010-08-29 17:25:34 +00:00
|
|
|
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;
|
2011-08-10 19:39:28 +00:00
|
|
|
case MSG_REPORT_RUNWAY_HOLD_SHORT:
|
|
|
|
activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
|
|
|
|
//activeRunway = "test";
|
2011-10-03 18:54:58 +00:00
|
|
|
text = receiver + ". Holding short runway "
|
|
|
|
+ activeRunway
|
|
|
|
+ ". " + sender;
|
2011-08-10 19:39:28 +00:00
|
|
|
//text = "test1";
|
|
|
|
//cerr << "1 Currently at leg " << rec->getLeg() << endl;
|
|
|
|
break;
|
|
|
|
case MSG_ACKNOWLEDGE_REPORT_RUNWAY_HOLD_SHORT:
|
|
|
|
activeRunway = rec->getAircraft()->GetFlightPlan()->getRunway();
|
2011-10-03 18:54:58 +00:00
|
|
|
text = receiver + "Roger. Holding short runway "
|
|
|
|
// + activeRunway
|
|
|
|
+ ". " + sender;
|
2011-08-10 19:39:28 +00:00
|
|
|
//text = "test2";
|
|
|
|
//cerr << "2 Currently at leg " << rec->getLeg() << endl;
|
|
|
|
break;
|
|
|
|
case MSG_SWITCH_TOWER_FREQUENCY:
|
|
|
|
towerFreqStr = formatATCFrequency3_2(towerFreq);
|
|
|
|
text = receiver + "Contact Tower at " + towerFreqStr + ". " + sender;
|
|
|
|
//text = "test3";
|
|
|
|
//cerr << "3 Currently at leg " << rec->getLeg() << endl;
|
|
|
|
break;
|
|
|
|
case MSG_ACKNOWLEDGE_SWITCH_TOWER_FREQUENCY:
|
|
|
|
towerFreqStr = formatATCFrequency3_2(towerFreq);
|
|
|
|
text = receiver + "Roger, switching to tower at " + towerFreqStr + ". " + sender;
|
|
|
|
//text = "test4";
|
|
|
|
//cerr << "4 Currently at leg " << rec->getLeg() << endl;
|
|
|
|
break;
|
2010-08-29 17:25:34 +00:00
|
|
|
default:
|
2011-08-10 19:39:28 +00:00
|
|
|
//text = "test3";
|
2010-08-29 17:25:34 +00:00
|
|
|
text = text + sender + ". Transmitting unknown Message";
|
|
|
|
break;
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
2011-04-15 17:32:21 +00:00
|
|
|
if (audible) {
|
|
|
|
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);
|
|
|
|
//cerr << "Using " << onBoardRadioFreq0 << ", " << onBoardRadioFreq1 << " and " << stationFreq << " for " << text << endl;
|
|
|
|
|
|
|
|
// Display ATC message only when one of the radios is tuned
|
|
|
|
// the relevant frequency.
|
|
|
|
// Note that distance attenuation is currently not yet implemented
|
2011-09-04 06:18:13 +00:00
|
|
|
|
2012-02-10 22:41:33 +00:00
|
|
|
if ((stationFreq > 0)&&
|
|
|
|
((onBoardRadioFreqI0 == stationFreq)||
|
|
|
|
(onBoardRadioFreqI1 == stationFreq))) {
|
2011-09-04 11:20:41 +00:00
|
|
|
if (rec->allowTransmissions()) {
|
2011-09-06 07:29:54 +00:00
|
|
|
|
2011-11-24 08:00:28 +00:00
|
|
|
if( fgGetBool( "/sim/radio/use-itm-attenuation", false ) ) {
|
|
|
|
//cerr << "Using ITM radio propagation" << endl;
|
2011-11-28 08:38:58 +00:00
|
|
|
FGRadioTransmission* radio = new FGRadioTransmission();
|
2011-11-24 04:20:59 +00:00
|
|
|
SGGeod sender_pos;
|
|
|
|
double sender_alt_ft, sender_alt;
|
|
|
|
if(ground_to_air) {
|
|
|
|
sender_alt_ft = parent->getElevation();
|
|
|
|
sender_alt = sender_alt_ft * SG_FEET_TO_METER;
|
|
|
|
sender_pos= SGGeod::fromDegM( parent->getLongitude(),
|
|
|
|
parent->getLatitude(), sender_alt );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sender_alt_ft = rec->getAltitude();
|
|
|
|
sender_alt = sender_alt_ft * SG_FEET_TO_METER;
|
|
|
|
sender_pos= SGGeod::fromDegM( rec->getLongitude(),
|
|
|
|
rec->getLatitude(), sender_alt );
|
|
|
|
}
|
2011-11-24 08:00:28 +00:00
|
|
|
double frequency = ((double)stationFreq) / 100;
|
2011-11-24 15:25:49 +00:00
|
|
|
radio->receiveATC(sender_pos, frequency, text, ground_to_air);
|
2011-11-24 04:39:54 +00:00
|
|
|
delete radio;
|
2011-11-24 04:20:59 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
fgSetString("/sim/messages/atc", text.c_str());
|
|
|
|
}
|
2011-04-15 17:32:21 +00:00
|
|
|
}
|
2010-01-30 15:40:33 +00:00
|
|
|
}
|
2011-04-15 17:32:21 +00:00
|
|
|
} else {
|
2011-09-22 18:52:05 +00:00
|
|
|
FGATCDialogNew::instance()->addEntry(1, text);
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-04 06:18:13 +00:00
|
|
|
|
2010-08-29 17:25:34 +00:00
|
|
|
string FGATCController::formatATCFrequency3_2(int freq)
|
|
|
|
{
|
2009-02-07 17:17:07 +00:00
|
|
|
char buffer[7];
|
2010-08-29 17:25:34 +00:00
|
|
|
snprintf(buffer, 7, "%3.2f", ((float) freq / 100.0));
|
2009-02-07 17:17:07 +00:00
|
|
|
return string(buffer);
|
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
|
2009-12-30 14:11:16 +00:00
|
|
|
// TODO: Set transponder codes according to real-world routes.
|
2011-10-03 18:54:58 +00:00
|
|
|
// The current version just returns a random string of four octal numbers.
|
2010-08-29 17:25:34 +00:00
|
|
|
string FGATCController::genTransponderCode(string fltRules)
|
|
|
|
{
|
2009-03-22 13:49:51 +00:00
|
|
|
if (fltRules == "VFR") {
|
|
|
|
return string("1200");
|
|
|
|
} else {
|
|
|
|
char buffer[5];
|
2010-08-29 17:25:34 +00:00
|
|
|
snprintf(buffer, 5, "%d%d%d%d", rand() % 8, rand() % 8, rand() % 8,
|
|
|
|
rand() % 8);
|
2009-03-22 13:49:51 +00:00
|
|
|
return string(buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
void FGATCController::init()
|
2011-04-10 06:58:48 +00:00
|
|
|
{
|
2011-10-03 18:54:58 +00:00
|
|
|
if (!initialized) {
|
|
|
|
FGATCManager *mgr = (FGATCManager*) globals->get_subsystem("ATC");
|
|
|
|
mgr->addController(this);
|
|
|
|
initialized = true;
|
2011-04-10 06:58:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-13 12:51:06 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* class FGTowerController
|
|
|
|
*
|
|
|
|
**************************************************************************/
|
2011-07-31 17:27:44 +00:00
|
|
|
FGTowerController::FGTowerController(FGAirportDynamics *par) :
|
2011-10-03 18:54:58 +00:00
|
|
|
FGATCController()
|
2008-07-13 12:51:06 +00:00
|
|
|
{
|
2011-07-31 17:27:44 +00:00
|
|
|
parent = par;
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
//
|
2010-08-29 17:25:34 +00:00
|
|
|
void FGTowerController::announcePosition(int id,
|
2011-10-03 18:54:58 +00:00
|
|
|
FGAIFlightPlan * intendedRoute,
|
|
|
|
int currentPosition, double lat,
|
|
|
|
double lon, double heading,
|
|
|
|
double speed, double alt,
|
|
|
|
double radius, int leg,
|
|
|
|
FGAIAircraft * ref)
|
2008-07-13 12:51:06 +00:00
|
|
|
{
|
2011-04-10 06:58:48 +00:00
|
|
|
init();
|
2008-07-13 12:51:06 +00:00
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
2010-08-29 17:25:34 +00:00
|
|
|
// Search whether the current id alread has an entry
|
2008-07-13 12:51:06 +00:00
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
|
|
|
if (activeTraffic.size()) {
|
2010-08-29 17:25:34 +00:00
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Add a new TrafficRecord if no one exsists for this aircraft.
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
|
|
|
FGTrafficRecord rec;
|
|
|
|
rec.setId(id);
|
|
|
|
|
|
|
|
rec.setPositionAndHeading(lat, lon, heading, speed, alt);
|
|
|
|
rec.setRunway(intendedRoute->getRunway());
|
|
|
|
rec.setLeg(leg);
|
|
|
|
//rec.setCallSign(callsign);
|
2011-08-07 19:38:50 +00:00
|
|
|
rec.setRadius(radius);
|
2010-08-29 17:25:34 +00:00
|
|
|
rec.setAircraft(ref);
|
|
|
|
activeTraffic.push_back(rec);
|
2011-10-03 18:54:58 +00:00
|
|
|
// Don't just schedule the aircraft for the tower controller, also assign if to the correct active runway.
|
2011-08-07 19:38:50 +00:00
|
|
|
ActiveRunwayVecIterator rwy = activeRunways.begin();
|
|
|
|
if (activeRunways.size()) {
|
|
|
|
while (rwy != activeRunways.end()) {
|
|
|
|
if (rwy->getRunwayName() == intendedRoute->getRunway()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rwy++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rwy == activeRunways.end()) {
|
|
|
|
ActiveRunway aRwy(intendedRoute->getRunway(), id);
|
|
|
|
aRwy.addToDepartureCue(ref);
|
|
|
|
activeRunways.push_back(aRwy);
|
|
|
|
rwy = (activeRunways.end()-1);
|
|
|
|
} else {
|
|
|
|
rwy->addToDepartureCue(ref);
|
|
|
|
}
|
|
|
|
|
2011-08-10 19:39:28 +00:00
|
|
|
//cerr << ref->getTrafficRef()->getCallSign() << " You are number " << rwy->getDepartureCueSize() << " for takeoff " << endl;
|
2010-08-29 17:25:34 +00:00
|
|
|
} else {
|
|
|
|
i->setPositionAndHeading(lat, lon, heading, speed, alt);
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
2010-08-29 17:25:34 +00:00
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
|
2011-04-03 15:58:16 +00:00
|
|
|
void FGTowerController::updateAircraftInformation(int id, double lat, double lon,
|
2011-10-03 18:54:58 +00:00
|
|
|
double heading, double speed, double alt,
|
|
|
|
double dt)
|
2010-08-29 17:25:34 +00:00
|
|
|
{
|
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search whether the current id has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
|
|
|
TrafficVectorIterator current, closest;
|
|
|
|
if (activeTraffic.size()) {
|
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
// // update position of the current aircraft
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: updating aircraft without traffic record at " << SG_ORIGIN);
|
2008-07-13 12:51:06 +00:00
|
|
|
} else {
|
2010-08-29 17:25:34 +00:00
|
|
|
i->setPositionAndHeading(lat, lon, heading, speed, alt);
|
|
|
|
current = i;
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
setDt(getDt() + dt);
|
|
|
|
|
2011-08-07 19:38:50 +00:00
|
|
|
// 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
|
2011-10-03 18:54:58 +00:00
|
|
|
// already exists here. So, we can simplify the current code.
|
2011-10-08 22:25:04 +00:00
|
|
|
|
2008-07-13 12:51:06 +00:00
|
|
|
ActiveRunwayVecIterator rwy = activeRunways.begin();
|
2011-10-26 20:26:37 +00:00
|
|
|
//if (parent->getId() == fgGetString("/sim/presets/airport-id")) {
|
|
|
|
// for (rwy = activeRunways.begin(); rwy != activeRunways.end(); rwy++) {
|
|
|
|
// rwy->printDepartureCue();
|
|
|
|
// }
|
|
|
|
//}
|
2011-10-08 22:25:04 +00:00
|
|
|
|
|
|
|
rwy = activeRunways.begin();
|
2011-08-07 19:38:50 +00:00
|
|
|
while (rwy != activeRunways.end()) {
|
|
|
|
if (rwy->getRunwayName() == current->getRunway()) {
|
|
|
|
break;
|
2010-08-29 17:25:34 +00:00
|
|
|
}
|
2011-08-07 19:38:50 +00:00
|
|
|
rwy++;
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
2011-08-07 19:38:50 +00:00
|
|
|
|
|
|
|
// only bother running the following code if the current aircraft is the
|
|
|
|
// first in line for depature
|
|
|
|
/* if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
|
|
|
|
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
|
2011-10-08 22:25:04 +00:00
|
|
|
FGAIAircraft* ac= rwy->getFirstAircraftInDepartureCue();
|
|
|
|
if (ac->getTakeOffStatus() == 1) {
|
|
|
|
ac->setTakeOffStatus(2);
|
|
|
|
}
|
2011-08-07 19:38:50 +00:00
|
|
|
if (current->getAircraft()->getTakeOffStatus() == 2) {
|
2011-10-08 22:25:04 +00:00
|
|
|
current -> setHoldPosition(false);
|
|
|
|
} else {
|
2011-08-07 19:38:50 +00:00
|
|
|
current->setHoldPosition(true);
|
2011-10-08 22:25:04 +00:00
|
|
|
}
|
|
|
|
int clearanceId = rwy->getCleared();
|
|
|
|
if (clearanceId) {
|
|
|
|
if (id == clearanceId) {
|
|
|
|
current->setHoldPosition(false);
|
2010-08-29 17:25:34 +00:00
|
|
|
}
|
2011-10-03 18:54:58 +00:00
|
|
|
} else {
|
2011-10-08 22:25:04 +00:00
|
|
|
if (current->getAircraft() == rwy->getFirstAircraftInDepartureCue()) {
|
|
|
|
rwy->setCleared(id);
|
|
|
|
FGAIAircraft *ac = rwy->getFirstOfStatus(1);
|
|
|
|
if (ac)
|
|
|
|
ac->setTakeOffStatus(2);
|
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
2011-10-08 22:25:04 +00:00
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
|
|
|
|
|
2010-08-29 17:25:34 +00:00
|
|
|
void FGTowerController::signOff(int id)
|
2008-07-13 12:51:06 +00:00
|
|
|
{
|
2010-08-29 17:25:34 +00:00
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search search if the current id alread has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
2008-07-13 12:51:06 +00:00
|
|
|
if (activeTraffic.size()) {
|
2010-08-29 17:25:34 +00:00
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
// If this aircraft has left the runway, we can clear the departure record for this runway
|
|
|
|
ActiveRunwayVecIterator rwy = activeRunways.begin();
|
|
|
|
if (activeRunways.size()) {
|
2010-08-29 17:25:34 +00:00
|
|
|
//while ((rwy->getRunwayName() != i->getRunway()) && (rwy != activeRunways.end())) {
|
|
|
|
while (rwy != activeRunways.end()) {
|
|
|
|
if (rwy->getRunwayName() == i->getRunway()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rwy++;
|
|
|
|
}
|
|
|
|
if (rwy != activeRunways.end()) {
|
2011-08-07 19:38:50 +00:00
|
|
|
rwy->setCleared(0);
|
|
|
|
rwy->updateDepartureCue();
|
2010-08-29 17:25:34 +00:00
|
|
|
} else {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: Attempting to erase non-existing runway clearance record in FGTowerController::signoff at " << SG_ORIGIN);
|
2010-08-29 17:25:34 +00:00
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN);
|
2008-07-13 12:51:06 +00:00
|
|
|
} else {
|
2011-08-07 19:38:50 +00:00
|
|
|
i->getAircraft()->resetTakeOffStatus();
|
2010-08-29 17:25:34 +00:00
|
|
|
i = activeTraffic.erase(i);
|
2011-07-27 09:01:37 +00:00
|
|
|
//cerr << "Signing off from tower controller" << endl;
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE:
|
|
|
|
// IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
|
2011-10-03 18:54:58 +00:00
|
|
|
// THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
|
2008-07-13 12:51:06 +00:00
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search search if the current id has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
2010-08-29 17:25:34 +00:00
|
|
|
if (activeTraffic.size()) {
|
2008-07-13 12:51:06 +00:00
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
2010-08-29 17:25:34 +00:00
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
|
2008-07-13 12:51:06 +00:00
|
|
|
} else {
|
2010-08-29 17:25:34 +00:00
|
|
|
return i->hasInstruction();
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
2010-08-29 17:25:34 +00:00
|
|
|
return false;
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FGATCInstruction FGTowerController::getInstruction(int id)
|
|
|
|
{
|
2010-08-29 17:25:34 +00:00
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search search if the current id has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
|
|
|
if (activeTraffic.size()) {
|
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
|
2010-08-29 17:25:34 +00:00
|
|
|
} else {
|
|
|
|
return i->getInstruction();
|
|
|
|
}
|
|
|
|
return FGATCInstruction();
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
|
2011-07-24 10:48:13 +00:00
|
|
|
void FGTowerController::render(bool visible) {
|
|
|
|
//cerr << "FGTowerController::render function not yet implemented" << endl;
|
2011-04-26 17:18:28 +00:00
|
|
|
}
|
|
|
|
|
2011-07-31 17:27:44 +00:00
|
|
|
string FGTowerController::getName() {
|
|
|
|
return string(parent->getId() + "-tower");
|
|
|
|
}
|
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
void FGTowerController::update(double dt)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-07-31 17:27:44 +00:00
|
|
|
|
2011-04-26 17:18:28 +00:00
|
|
|
|
2008-07-13 12:51:06 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* class FGStartupController
|
|
|
|
*
|
|
|
|
**************************************************************************/
|
2011-04-26 17:18:28 +00:00
|
|
|
FGStartupController::FGStartupController(FGAirportDynamics *par):
|
2011-10-03 18:54:58 +00:00
|
|
|
FGATCController()
|
2010-08-29 17:25:34 +00:00
|
|
|
{
|
2011-04-26 17:18:28 +00:00
|
|
|
parent = par;
|
2010-08-29 17:25:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FGStartupController::announcePosition(int id,
|
2011-10-03 18:54:58 +00:00
|
|
|
FGAIFlightPlan * intendedRoute,
|
|
|
|
int currentPosition, double lat,
|
|
|
|
double lon, double heading,
|
|
|
|
double speed, double alt,
|
|
|
|
double radius, int leg,
|
|
|
|
FGAIAircraft * ref)
|
2010-08-29 17:25:34 +00:00
|
|
|
{
|
2011-04-10 06:58:48 +00:00
|
|
|
init();
|
2010-08-29 17:25:34 +00:00
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search whether the current id alread has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
|
|
|
if (activeTraffic.size()) {
|
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Add a new TrafficRecord if no one exsists for this aircraft.
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
|
|
|
FGTrafficRecord rec;
|
|
|
|
rec.setId(id);
|
|
|
|
|
|
|
|
rec.setPositionAndHeading(lat, lon, heading, speed, alt);
|
|
|
|
rec.setRunway(intendedRoute->getRunway());
|
|
|
|
rec.setLeg(leg);
|
2011-04-26 17:18:28 +00:00
|
|
|
rec.setPositionAndIntentions(currentPosition, intendedRoute);
|
2010-08-29 17:25:34 +00:00
|
|
|
//rec.setCallSign(callsign);
|
|
|
|
rec.setAircraft(ref);
|
|
|
|
rec.setHoldPosition(true);
|
|
|
|
activeTraffic.push_back(rec);
|
2008-07-13 12:51:06 +00:00
|
|
|
} else {
|
2011-04-26 17:18:28 +00:00
|
|
|
i->setPositionAndIntentions(currentPosition, intendedRoute);
|
2008-07-13 12:51:06 +00:00
|
|
|
i->setPositionAndHeading(lat, lon, heading, speed, alt);
|
2010-08-29 17:25:34 +00:00
|
|
|
|
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE:
|
|
|
|
// IF WE MAKE TRAFFICRECORD A MEMBER OF THE BASE CLASS
|
2011-10-03 18:54:58 +00:00
|
|
|
// THE FOLLOWING THREE FUNCTIONS: SIGNOFF, HAS INSTRUCTION AND GETINSTRUCTION CAN
|
2008-07-13 12:51:06 +00:00
|
|
|
// BECOME DEVIRTUALIZED AND BE A MEMBER OF THE BASE ATCCONTROLLER CLASS
|
|
|
|
// WHICH WOULD SIMPLIFY CODE MAINTENANCE.
|
|
|
|
// Note that this function is probably obsolete
|
|
|
|
bool FGStartupController::hasInstruction(int id)
|
|
|
|
{
|
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search search if the current id has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
2010-08-29 17:25:34 +00:00
|
|
|
if (activeTraffic.size()) {
|
2008-07-13 12:51:06 +00:00
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
2010-08-29 17:25:34 +00:00
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
|
2008-07-13 12:51:06 +00:00
|
|
|
} else {
|
2010-08-29 17:25:34 +00:00
|
|
|
return i->hasInstruction();
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
2010-08-29 17:25:34 +00:00
|
|
|
return false;
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FGATCInstruction FGStartupController::getInstruction(int id)
|
|
|
|
{
|
2010-08-29 17:25:34 +00:00
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search search if the current id has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
2008-07-13 12:51:06 +00:00
|
|
|
if (activeTraffic.size()) {
|
2010-08-29 17:25:34 +00:00
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
|
2008-07-13 12:51:06 +00:00
|
|
|
} else {
|
2010-08-29 17:25:34 +00:00
|
|
|
return i->getInstruction();
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
2010-08-29 17:25:34 +00:00
|
|
|
return FGATCInstruction();
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
|
2010-08-29 17:25:34 +00:00
|
|
|
void FGStartupController::signOff(int id)
|
|
|
|
{
|
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search search if the current id alread has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
|
|
|
if (activeTraffic.size()) {
|
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: Aircraft without traffic record is signing off from tower at " << SG_ORIGIN);
|
2010-08-29 17:25:34 +00:00
|
|
|
} else {
|
2011-07-24 10:48:13 +00:00
|
|
|
//cerr << i->getAircraft()->getCallSign() << " signing off from startupcontroller" << endl;
|
2010-08-29 17:25:34 +00:00
|
|
|
i = activeTraffic.erase(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-14 15:51:46 +00:00
|
|
|
bool FGStartupController::checkTransmissionState(int st, time_t now, time_t startTime, TrafficVectorIterator i, AtcMsgId msgId,
|
2011-10-03 18:54:58 +00:00
|
|
|
AtcMsgDir msgDir)
|
2011-04-14 15:51:46 +00:00
|
|
|
{
|
|
|
|
int state = i->getState();
|
|
|
|
if ((state == st) && available) {
|
|
|
|
if ((msgDir == ATC_AIR_TO_GROUND) && isUserAircraft(i->getAircraft())) {
|
2011-10-03 18:54:58 +00:00
|
|
|
|
2011-07-24 10:48:13 +00:00
|
|
|
//cerr << "Checking state " << st << " for " << i->getAircraft()->getCallSign() << endl;
|
2011-04-14 15:51:46 +00:00
|
|
|
static SGPropertyNode_ptr trans_num = globals->get_props()->getNode("/sim/atc/transmission-num", true);
|
|
|
|
int n = trans_num->getIntValue();
|
2011-07-24 10:48:13 +00:00
|
|
|
if (n == 0) {
|
2011-04-14 15:51:46 +00:00
|
|
|
trans_num->setIntValue(-1);
|
2011-10-03 18:54:58 +00:00
|
|
|
// PopupCallback(n);
|
|
|
|
//cerr << "Selected transmission message " << n << endl;
|
|
|
|
FGATCDialogNew::instance()->removeEntry(1);
|
2011-04-14 15:51:46 +00:00
|
|
|
} else {
|
2011-07-24 10:48:13 +00:00
|
|
|
//cerr << "creading message for " << i->getAircraft()->getCallSign() << endl;
|
2011-09-04 07:00:36 +00:00
|
|
|
transmit(&(*i), &(*parent), msgId, msgDir, false);
|
2011-04-14 15:51:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (now > startTime) {
|
|
|
|
//cerr << "Transmitting startup msg" << endl;
|
2011-09-04 07:00:36 +00:00
|
|
|
transmit(&(*i), &(*parent), msgId, msgDir, true);
|
2011-04-14 15:51:46 +00:00
|
|
|
i->updateState();
|
|
|
|
lastTransmission = now;
|
|
|
|
available = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-04-03 15:58:16 +00:00
|
|
|
void FGStartupController::updateAircraftInformation(int id, double lat, double lon,
|
2011-10-03 18:54:58 +00:00
|
|
|
double heading, double speed, double alt,
|
|
|
|
double dt)
|
2008-07-13 12:51:06 +00:00
|
|
|
{
|
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search search if the current id has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
|
|
|
TrafficVectorIterator current, closest;
|
|
|
|
if (activeTraffic.size()) {
|
2010-08-29 17:25:34 +00:00
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
// // update position of the current aircraft
|
2011-04-11 20:23:53 +00:00
|
|
|
|
2008-07-13 12:51:06 +00:00
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: updating aircraft without traffic record at " << SG_ORIGIN);
|
2008-07-13 12:51:06 +00:00
|
|
|
} else {
|
2010-08-29 17:25:34 +00:00
|
|
|
i->setPositionAndHeading(lat, lon, heading, speed, alt);
|
|
|
|
current = i;
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
|
|
|
setDt(getDt() + dt);
|
|
|
|
|
|
|
|
int state = i->getState();
|
2011-04-11 20:23:53 +00:00
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
// The user controlled aircraft should have crased here, because it doesn't have a traffic reference.
|
2011-04-11 20:23:53 +00:00
|
|
|
// NOTE: if we create a traffic schedule for the user aircraft, we can use this to plan a flight.
|
2011-04-12 21:28:48 +00:00
|
|
|
time_t startTime = i->getAircraft()->getTrafficRef()->getDepartureTime();
|
2010-08-29 17:25:34 +00:00
|
|
|
time_t now = time(NULL) + fgGetLong("/sim/time/warp");
|
2011-10-03 18:54:58 +00:00
|
|
|
//cerr << i->getAircraft()->getTrafficRef()->getCallSign()
|
2008-11-16 13:43:44 +00:00
|
|
|
// << " is scheduled to depart in " << startTime-now << " seconds. Available = " << available
|
|
|
|
// << " at parking " << getGateName(i->getAircraft()) << endl;
|
2008-07-13 12:51:06 +00:00
|
|
|
|
|
|
|
if ((now - lastTransmission) > 3 + (rand() % 15)) {
|
|
|
|
available = true;
|
|
|
|
}
|
|
|
|
|
2011-04-14 15:51:46 +00:00
|
|
|
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);
|
2011-04-17 08:19:58 +00:00
|
|
|
checkTransmissionState(7, now, (startTime + 180), i, MSG_REQUEST_PUSHBACK_CLEARANCE, ATC_AIR_TO_GROUND);
|
2011-04-14 15:51:46 +00:00
|
|
|
|
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
|
2010-08-29 17:25:34 +00:00
|
|
|
if ((state == 8) && available) {
|
|
|
|
if (now > startTime + 200) {
|
2010-01-30 15:40:33 +00:00
|
|
|
if (i->pushBackAllowed()) {
|
2010-08-29 17:25:34 +00:00
|
|
|
i->allowRepeatedTransmissions();
|
2011-09-04 07:00:36 +00:00
|
|
|
transmit(&(*i), &(*parent), MSG_PERMIT_PUSHBACK_CLEARANCE,
|
2011-04-15 17:32:21 +00:00
|
|
|
ATC_GROUND_TO_AIR, true);
|
2010-08-29 17:25:34 +00:00
|
|
|
i->updateState();
|
2010-01-30 15:40:33 +00:00
|
|
|
} else {
|
2011-09-04 07:00:36 +00:00
|
|
|
transmit(&(*i), &(*parent), MSG_HOLD_PUSHBACK_CLEARANCE,
|
2011-04-15 17:32:21 +00:00
|
|
|
ATC_GROUND_TO_AIR, true);
|
2010-08-29 17:25:34 +00:00
|
|
|
i->suppressRepeatedTransmissions();
|
2010-01-30 15:40:33 +00:00
|
|
|
}
|
|
|
|
lastTransmission = now;
|
2010-08-29 17:25:34 +00:00
|
|
|
available = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((state == 9) && available) {
|
|
|
|
i->setHoldPosition(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-26 17:18:28 +00:00
|
|
|
// 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,
|
2011-09-03 09:26:17 +00:00
|
|
|
double lon, double elev, double hdg, double slope)
|
2011-04-26 17:18:28 +00:00
|
|
|
{
|
|
|
|
SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
|
2012-03-03 12:37:43 +00:00
|
|
|
obj_pos = makeZUpFrame(geod);
|
2011-04-26 17:18:28 +00:00
|
|
|
// 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));
|
2011-09-03 09:26:17 +00:00
|
|
|
obj_pos.preMult(osg::Matrix::rotate(slope * SGD_DEGREES_TO_RADIANS,
|
|
|
|
0.0, 1.0, 0.0));
|
2011-04-26 17:18:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-24 10:48:13 +00:00
|
|
|
void FGStartupController::render(bool visible)
|
2011-04-26 17:18:28 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
SGMaterialLib *matlib = globals->get_matlib();
|
|
|
|
if (group) {
|
|
|
|
//int nr = ;
|
|
|
|
globals->get_scenery()->get_scene_graph()->removeChild(group);
|
|
|
|
//while (group->getNumChildren()) {
|
|
|
|
// cerr << "Number of children: " << group->getNumChildren() << endl;
|
2011-09-03 09:26:17 +00:00
|
|
|
//simgear::EffectGeode* geode = (simgear::EffectGeode*) group->getChild(0);
|
2011-10-03 18:54:58 +00:00
|
|
|
//osg::MatrixTransform *obj_trans = (osg::MatrixTransform*) group->getChild(0);
|
|
|
|
//geode->releaseGLObjects();
|
|
|
|
//group->removeChild(geode);
|
|
|
|
//delete geode;
|
2011-07-24 10:48:13 +00:00
|
|
|
group = 0;
|
2011-04-26 17:18:28 +00:00
|
|
|
}
|
2011-07-24 10:48:13 +00:00
|
|
|
if (visible) {
|
|
|
|
group = new osg::Group;
|
2011-09-03 09:26:17 +00:00
|
|
|
FGScenery * local_scenery = globals->get_scenery();
|
2011-12-19 15:50:21 +00:00
|
|
|
//double elevation_meters = 0.0;
|
2011-09-03 09:26:17 +00:00
|
|
|
double elevation_feet = 0.0;
|
|
|
|
|
2011-07-24 10:48:13 +00:00
|
|
|
|
|
|
|
//for ( FGTaxiSegmentVectorIterator i = segments.begin(); i != segments.end(); i++) {
|
|
|
|
double dx = 0;
|
2011-10-26 20:26:37 +00:00
|
|
|
time_t now = time(NULL) + fgGetLong("/sim/time/warp");
|
2011-07-24 10:48:13 +00:00
|
|
|
for (TrafficVectorIterator i = activeTraffic.begin(); i != activeTraffic.end(); i++) {
|
2011-10-03 18:54:58 +00:00
|
|
|
if (i->isActive(300)) {
|
|
|
|
// Handle start point
|
|
|
|
int pos = i->getCurrentPosition();
|
|
|
|
//cerr << "rendering for " << i->getAircraft()->getCallSign() << "pos = " << pos << endl;
|
|
|
|
if (pos > 0) {
|
|
|
|
FGTaxiSegment *segment = parent->getGroundNetwork()->findSegment(pos);
|
|
|
|
SGGeod start(SGGeod::fromDeg((i->getLongitude()), (i->getLatitude())));
|
|
|
|
SGGeod end (SGGeod::fromDeg(segment->getEnd()->getLongitude(), segment->getEnd()->getLatitude()));
|
|
|
|
|
|
|
|
double length = SGGeodesy::distanceM(start, end);
|
|
|
|
//heading = SGGeodesy::headingDeg(start->getGeod(), end->getGeod());
|
|
|
|
|
|
|
|
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);
|
|
|
|
//cerr << "Active Aircraft : Centerpoint = (" << center.getLatitudeDeg() << ", " << center.getLongitudeDeg() << "). Heading = " << heading << endl;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Make a helper function out of this
|
|
|
|
osg::Matrix obj_pos;
|
2011-07-24 10:48:13 +00:00
|
|
|
osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
|
|
|
|
obj_trans->setDataVariance(osg::Object::STATIC);
|
2011-10-03 18:54:58 +00:00
|
|
|
// 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);
|
2011-09-03 09:26:17 +00:00
|
|
|
}
|
2011-10-08 22:25:04 +00:00
|
|
|
double elevationEnd = segment->getEnd()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
|
A number of cosmetic and/or infrastructural changes.
Traffic Manager:
* Just continue routing until we run out of flights. This change removes one of the major requirements for setting the "Home port" field.
* Add a time restriction requirement for the aircraft scheduler; this became necessary after removing the limited-to-home-port routing restriction.
* Added a new field to the heuristics calculation: take into account whether an aircraft has already been used in a previous session. Rotate aircraft assignments for greater variability across sessions.
* Added a revision number to the cache files, so that old cache results, which are no longer compatible with the new file format, are discarded.
Groundnetwork and traffic control:
* Added a revision number to the cache files, so that old and incompatible results are discarded.
* The caching algorithm probably didn't store the correct data for airports that were processed while the user was quite far away. This is now corrected by checking whether the cached elevation data are equal to the generic airport elevation.
AIAircraft:
* I've been searching for the infamous aircraft bend-over-backward bug, that can occur during initialization, but to no avail yet. The only variable potentially responsible (tgt_vs) wich can explain the irregular jumping behavior, as well as the weird pitch results is initialized in AIAircraft's only constructor (through AIBase), and I can't find any situation in the ground handling code where this variable could get bizarre values. But,
* a couple of tgt_vs. calculations appear to be completely redundant. This value was calculated twice inside the ProcessFlightplan function, and subsequently again in the updateSecondaryTargetValues function. I have removed the calculations in the process flightplan function, without any apparent side effect.
2011-09-04 18:27:36 +00:00
|
|
|
if ((elevationEnd == 0) || (elevationEnd == parent->getElevation())) {
|
2011-10-03 18:54:58 +00:00
|
|
|
SGGeod center2 = end;
|
2011-09-03 09:26:17 +00:00
|
|
|
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;
|
|
|
|
}
|
2011-10-03 18:54:58 +00:00
|
|
|
else {
|
A number of cosmetic and/or infrastructural changes.
Traffic Manager:
* Just continue routing until we run out of flights. This change removes one of the major requirements for setting the "Home port" field.
* Add a time restriction requirement for the aircraft scheduler; this became necessary after removing the limited-to-home-port routing restriction.
* Added a new field to the heuristics calculation: take into account whether an aircraft has already been used in a previous session. Rotate aircraft assignments for greater variability across sessions.
* Added a revision number to the cache files, so that old cache results, which are no longer compatible with the new file format, are discarded.
Groundnetwork and traffic control:
* Added a revision number to the cache files, so that old and incompatible results are discarded.
* The caching algorithm probably didn't store the correct data for airports that were processed while the user was quite far away. This is now corrected by checking whether the cached elevation data are equal to the generic airport elevation.
AIAircraft:
* I've been searching for the infamous aircraft bend-over-backward bug, that can occur during initialization, but to no avail yet. The only variable potentially responsible (tgt_vs) wich can explain the irregular jumping behavior, as well as the weird pitch results is initialized in AIAircraft's only constructor (through AIBase), and I can't find any situation in the ground handling code where this variable could get bizarre values. But,
* a couple of tgt_vs. calculations appear to be completely redundant. This value was calculated twice inside the ProcessFlightplan function, and subsequently again in the updateSecondaryTargetValues function. I have removed the calculations in the process flightplan function, without any apparent side effect.
2011-09-04 18:27:36 +00:00
|
|
|
elevationEnd = parent->getElevation();
|
2011-09-03 09:26:17 +00:00
|
|
|
}
|
|
|
|
segment->getEnd()->setElevation(elevationEnd);
|
|
|
|
}
|
2011-10-03 18:54:58 +00:00
|
|
|
|
2011-09-03 09:26:17 +00:00
|
|
|
double elevationMean = (elevationStart + elevationEnd) / 2.0;
|
|
|
|
double elevDiff = elevationEnd - elevationStart;
|
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
double slope = atan2(elevDiff, length) * SGD_RADIANS_TO_DEGREES;
|
2011-09-03 09:26:17 +00:00
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
//cerr << "1. Using mean elevation : " << elevationMean << " and " << slope << endl;
|
2011-09-03 09:26:17 +00:00
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
WorldCoordinate( obj_pos, center.getLatitudeDeg(), center.getLongitudeDeg(), elevationMean + 0.5 + dx, -(heading), slope );
|
|
|
|
;
|
2011-07-24 10:48:13 +00:00
|
|
|
|
|
|
|
obj_trans->setMatrix( obj_pos );
|
|
|
|
//osg::Vec3 center(0, 0, 0)
|
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
float width = length /2.0;
|
2011-07-24 10:48:13 +00:00
|
|
|
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;
|
2011-10-03 18:54:58 +00:00
|
|
|
SGMaterial *mat;
|
2011-10-26 20:26:37 +00:00
|
|
|
if (segment->hasBlock(now)) {
|
2011-10-03 18:54:58 +00:00
|
|
|
mat = matlib->find("UnidirectionalTaperRed");
|
|
|
|
} else {
|
|
|
|
mat = matlib->find("UnidirectionalTaperGreen");
|
|
|
|
}
|
2011-07-24 10:48:13 +00:00
|
|
|
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 );
|
2011-10-03 18:54:58 +00:00
|
|
|
/////////////////////////////////////////////////////////////////////
|
2011-07-24 10:48:13 +00:00
|
|
|
} else {
|
2011-10-03 18:54:58 +00:00
|
|
|
//cerr << "BIG FAT WARNING: current position is here : " << pos << endl;
|
2011-07-24 10:48:13 +00:00
|
|
|
}
|
2011-10-03 18:54:58 +00:00
|
|
|
for (intVecIterator j = (i)->getIntentions().begin(); j != (i)->getIntentions().end(); j++) {
|
|
|
|
osg::Matrix obj_pos;
|
|
|
|
int k = (*j);
|
|
|
|
if (k > 0) {
|
|
|
|
//cerr << "rendering for " << i->getAircraft()->getCallSign() << "intention = " << k << endl;
|
|
|
|
osg::MatrixTransform *obj_trans = new osg::MatrixTransform;
|
|
|
|
obj_trans->setDataVariance(osg::Object::STATIC);
|
|
|
|
FGTaxiSegment *segment = parent->getGroundNetwork()->findSegment(k);
|
|
|
|
|
2011-10-08 22:25:04 +00:00
|
|
|
double elevationStart = segment->getStart()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
|
|
|
|
double elevationEnd = segment->getEnd ()->getElevationM(parent->getElevation()*SG_FEET_TO_METER);
|
2011-10-03 18:54:58 +00:00
|
|
|
if ((elevationStart == 0) || (elevationStart == parent->getElevation())) {
|
|
|
|
SGGeod center2 = segment->getStart()->getGeod();
|
|
|
|
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()->getGeod();
|
|
|
|
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;
|
|
|
|
|
|
|
|
//cerr << "2. Using mean elevation : " << elevationMean << " and " << slope << endl;
|
|
|
|
|
|
|
|
|
|
|
|
WorldCoordinate( obj_pos, segment->getLatitude(), segment->getLongitude(), 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;
|
2011-10-26 20:26:37 +00:00
|
|
|
if (segment->hasBlock(now)) {
|
2011-10-03 18:54:58 +00:00
|
|
|
mat = matlib->find("UnidirectionalTaperRed");
|
|
|
|
} else {
|
|
|
|
mat = matlib->find("UnidirectionalTaperGreen");
|
|
|
|
}
|
|
|
|
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 {
|
|
|
|
//cerr << "BIG FAT WARNING: k is here : " << pos << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dx += 0.2;
|
2011-07-24 10:48:13 +00:00
|
|
|
}
|
2011-04-26 17:18:28 +00:00
|
|
|
}
|
2011-07-24 10:48:13 +00:00
|
|
|
globals->get_scenery()->get_scene_graph()->addChild(group);
|
2011-04-26 17:18:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-31 17:27:44 +00:00
|
|
|
string FGStartupController::getName() {
|
|
|
|
return string(parent->getId() + "-startup");
|
|
|
|
}
|
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
void FGStartupController::update(double dt)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-31 17:27:44 +00:00
|
|
|
|
2010-08-29 17:25:34 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* class FGApproachController
|
|
|
|
*
|
|
|
|
**************************************************************************/
|
2011-07-31 17:27:44 +00:00
|
|
|
FGApproachController::FGApproachController(FGAirportDynamics *par):
|
2011-10-03 18:54:58 +00:00
|
|
|
FGATCController()
|
2010-08-29 17:25:34 +00:00
|
|
|
{
|
2011-07-31 17:27:44 +00:00
|
|
|
parent = par;
|
2010-08-29 17:25:34 +00:00
|
|
|
}
|
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
//
|
2010-08-29 17:25:34 +00:00
|
|
|
void FGApproachController::announcePosition(int id,
|
2011-10-03 18:54:58 +00:00
|
|
|
FGAIFlightPlan * intendedRoute,
|
|
|
|
int currentPosition,
|
|
|
|
double lat, double lon,
|
|
|
|
double heading, double speed,
|
|
|
|
double alt, double radius,
|
|
|
|
int leg, FGAIAircraft * ref)
|
2010-08-29 17:25:34 +00:00
|
|
|
{
|
2011-04-10 06:58:48 +00:00
|
|
|
init();
|
2010-08-29 17:25:34 +00:00
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search whether the current id alread has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
|
|
|
if (activeTraffic.size()) {
|
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Add a new TrafficRecord if no one exsists for this aircraft.
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
|
|
|
FGTrafficRecord rec;
|
|
|
|
rec.setId(id);
|
|
|
|
|
|
|
|
rec.setPositionAndHeading(lat, lon, heading, speed, alt);
|
|
|
|
rec.setRunway(intendedRoute->getRunway());
|
|
|
|
rec.setLeg(leg);
|
|
|
|
//rec.setCallSign(callsign);
|
|
|
|
rec.setAircraft(ref);
|
|
|
|
activeTraffic.push_back(rec);
|
|
|
|
} else {
|
|
|
|
i->setPositionAndHeading(lat, lon, heading, speed, alt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-03 15:58:16 +00:00
|
|
|
void FGApproachController::updateAircraftInformation(int id, double lat, double lon,
|
2011-10-03 18:54:58 +00:00
|
|
|
double heading, double speed, double alt,
|
|
|
|
double dt)
|
2010-08-29 17:25:34 +00:00
|
|
|
{
|
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search search if the current id has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
|
|
|
TrafficVectorIterator current, closest;
|
|
|
|
if (activeTraffic.size()) {
|
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// // update position of the current aircraft
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: updating aircraft without traffic record at " << SG_ORIGIN);
|
2010-08-29 17:25:34 +00:00
|
|
|
} else {
|
|
|
|
i->setPositionAndHeading(lat, lon, heading, speed, alt);
|
|
|
|
current = i;
|
|
|
|
//cerr << "ApproachController: checking for speed" << endl;
|
|
|
|
time_t time_diff =
|
|
|
|
current->getAircraft()->
|
|
|
|
checkForArrivalTime(string("final001"));
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGApproachController::signOff(int id)
|
|
|
|
{
|
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search search if the current id alread has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
|
|
|
if (activeTraffic.size()) {
|
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: Aircraft without traffic record is signing off from approach at " << SG_ORIGIN);
|
2010-08-29 17:25:34 +00:00
|
|
|
} else {
|
|
|
|
i = activeTraffic.erase(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-03 18:54:58 +00:00
|
|
|
void FGApproachController::update(double dt)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
2010-08-29 17:25:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool FGApproachController::hasInstruction(int id)
|
|
|
|
{
|
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search search if the current id has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
|
|
|
if (activeTraffic.size()) {
|
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
2010-01-30 15:40:33 +00:00
|
|
|
}
|
2010-08-29 17:25:34 +00:00
|
|
|
}
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: checking ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
|
2010-08-29 17:25:34 +00:00
|
|
|
} else {
|
|
|
|
return i->hasInstruction();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
FGATCInstruction FGApproachController::getInstruction(int id)
|
|
|
|
{
|
|
|
|
TrafficVectorIterator i = activeTraffic.begin();
|
|
|
|
// Search search if the current id has an entry
|
|
|
|
// This might be faster using a map instead of a vector, but let's start by taking a safe route
|
|
|
|
if (activeTraffic.size()) {
|
|
|
|
//while ((i->getId() != id) && i != activeTraffic.end()) {
|
|
|
|
while (i != activeTraffic.end()) {
|
|
|
|
if (i->getId() == id) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i == activeTraffic.end() || (activeTraffic.size() == 0)) {
|
2011-12-11 12:55:56 +00:00
|
|
|
SG_LOG(SG_ATC, SG_ALERT,
|
2011-10-08 22:25:04 +00:00
|
|
|
"AI error: requesting ATC instruction for aircraft without traffic record at " << SG_ORIGIN);
|
2010-08-29 17:25:34 +00:00
|
|
|
} else {
|
|
|
|
return i->getInstruction();
|
|
|
|
}
|
|
|
|
return FGATCInstruction();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ActiveRunway *FGApproachController::getRunway(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);
|
2008-07-13 12:51:06 +00:00
|
|
|
}
|
2011-04-26 17:18:28 +00:00
|
|
|
|
2011-07-24 10:48:13 +00:00
|
|
|
void FGApproachController::render(bool visible) {
|
|
|
|
//cerr << "FGApproachController::render function not yet implemented" << endl;
|
2011-04-26 17:18:28 +00:00
|
|
|
}
|
2011-07-31 17:27:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string FGApproachController::getName() {
|
|
|
|
return string(parent->getId() + "-approach");
|
|
|
|
}
|