a7ccae9fca
- Fixed a bug in AI aircraft ground steering code: When aircraft were not moving, the value of headingchangeRate kept increasing to insane levels. Although this was clamped to a maximum of 30 degrees per second, the initial rate could still push the aircraft in the wrong direction. In practice, this bug would be visible when an AI aicraft would be pushed back, when it tended to veer to the right. - Make sure that the aircraft slows down well ahead of the pushback point. This change ensures that the AC will actually reach the pushback point. It also ensures a slightly tighter steering range. - AI ground steering rate is tuned to 30 degrees per second at a nominal taxispeed of 15. I now modulate the heading adjustment rate by manipulating the adjustment using a non-linear function (the sqrt). This allows for a slightly tighter turn radius at speeds < 15 and slightly looser turns at speeds > 15. - The AI Flightplan generation code can return false. This can be used to determine whether any additional AI aircraft may be created. Currently, the function returns false when no more parkings are available. This should limit the build-up of huge AIAircraft tower stacks. - The ground network can now graphically display all aircraft actitivy on the ground network by using a virtual marker system.
311 lines
9.7 KiB
C++
311 lines
9.7 KiB
C++
/******************************************************************************
|
|
* AIFlightPlanCreateCruise.cxx
|
|
* Written by Durk Talsma, started February, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*
|
|
**************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <simgear/route/waypoint.hxx>
|
|
|
|
#include <Airports/simple.hxx>
|
|
#include <Airports/runways.hxx>
|
|
#include <Airports/dynamics.hxx>
|
|
|
|
#include <Environment/environment_mgr.hxx>
|
|
#include <Environment/environment.hxx>
|
|
|
|
#include "AIFlightPlan.hxx"
|
|
#include "AIAircraft.hxx"
|
|
#include "performancedata.hxx"
|
|
|
|
using std::iostream;
|
|
|
|
/*
|
|
void FGAIFlightPlan::evaluateRoutePart(double deplat,
|
|
double deplon,
|
|
double arrlat,
|
|
double arrlon)
|
|
{
|
|
// First do a prescan of all the waypoints that are within a reasonable distance of the
|
|
// ideal route.
|
|
intVec nodes;
|
|
int tmpNode, prevNode;
|
|
|
|
SGGeoc dep(SGGeoc::fromDegM(deplon, deplat, 100.0));
|
|
SGGeoc arr(SGGeoc::fromDegM(arrlon, arrlat, 100.0));
|
|
|
|
SGVec3d a = SGVec3d::fromGeoc(dep);
|
|
SGVec3d nb = normalize(SGVec3d::fromGeoc(arr));
|
|
SGVec3d na = normalize(a);
|
|
|
|
SGVec3d _cross = cross(nb, na);
|
|
|
|
double angle = acos(dot(na, nb));
|
|
const double angleStep = 0.05 * SG_DEGREES_TO_RADIANS;
|
|
tmpNode = 0;
|
|
for (double ang = 0.0; ang < angle; ang += angleStep)
|
|
{
|
|
SGQuatd q = SGQuatd::fromAngleAxis(ang, _cross);
|
|
SGGeod geod = SGGeod::fromCart(q.transform(a));
|
|
|
|
prevNode = tmpNode;
|
|
tmpNode = globals->get_airwaynet()->findNearestNode(geod);
|
|
|
|
FGNode* node = globals->get_airwaynet()->findNode(tmpNode);
|
|
|
|
if ((tmpNode != prevNode) && (SGGeodesy::distanceM(geod, node->getPosition()) < 25000)) {
|
|
nodes.push_back(tmpNode);
|
|
}
|
|
}
|
|
|
|
intVecIterator i = nodes.begin();
|
|
intVecIterator j = nodes.end();
|
|
while (i != nodes.end())
|
|
{
|
|
j = nodes.end();
|
|
while (j != i)
|
|
{
|
|
j--;
|
|
FGAirRoute routePart = globals->get_airwaynet()->findShortestRoute(*i, *j);
|
|
if (!(routePart.empty()))
|
|
{
|
|
airRoute.add(routePart);
|
|
i = j;
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
*/
|
|
/*
|
|
void FGAIFlightPlan::createCruise(bool firstFlight, FGAirport *dep,
|
|
FGAirport *arr, double latitude,
|
|
double longitude, double speed, double alt)
|
|
{
|
|
bool useInitialWayPoint = true;
|
|
bool useCurrentWayPoint = false;
|
|
double heading;
|
|
double lat, lon, az;
|
|
double lat2, lon2, az2;
|
|
double azimuth;
|
|
waypoint *wpt;
|
|
waypoint *init_waypoint;
|
|
intVec ids;
|
|
char buffer[32];
|
|
SGPath routefile = globals->get_fg_root();
|
|
init_waypoint = new waypoint;
|
|
init_waypoint->name = "Initial waypoint";
|
|
init_waypoint->latitude = latitude;
|
|
init_waypoint->longitude = longitude;;
|
|
//wpt->altitude = apt->getElevation(); // should maybe be tn->elev too
|
|
init_waypoint->altitude = alt;
|
|
init_waypoint->speed = 450; //speed;
|
|
init_waypoint->crossat = -10000;
|
|
init_waypoint->gear_down = false;
|
|
init_waypoint->flaps_down= false;
|
|
init_waypoint->finished = false;
|
|
init_waypoint->on_ground = false;
|
|
waypoints.push_back(init_waypoint);
|
|
routefile.append("Data/AI/FlightPlans");
|
|
snprintf(buffer, 32, "%s-%s.txt",
|
|
dep->getId().c_str(),
|
|
arr->getId().c_str());
|
|
routefile.append(buffer);
|
|
cerr << "trying to read " << routefile.c_str()<< endl;
|
|
//exit(1);
|
|
if (routefile.exists())
|
|
{
|
|
sg_gzifstream in( routefile.str() );
|
|
do {
|
|
in >> route;
|
|
} while (!(in.eof()));
|
|
}
|
|
else {
|
|
//int runwayId = apt->getDynamics()->getGroundNetwork()->findNearestNode(lat2, lon2);
|
|
//int startId = globals->get_airwaynet()->findNearestNode(dep->getLatitude(), dep->getLongitude());
|
|
//int endId = globals->get_airwaynet()->findNearestNode(arr->getLatitude(), arr->getLongitude());
|
|
//FGAirRoute route;
|
|
evaluateRoutePart(dep->getLatitude(), dep->getLongitude(),
|
|
arr->getLatitude(), arr->getLongitude());
|
|
//exit(1);
|
|
}
|
|
route.first();
|
|
int node;
|
|
if (route.empty()) {
|
|
// if no route could be found, create a direct gps route...
|
|
cerr << "still no route found from " << dep->getName() << " to << " << arr->getName() <<endl;
|
|
|
|
//exit(1);
|
|
} else {
|
|
while(route.next(&node))
|
|
{
|
|
FGNode *fn = globals->get_airwaynet()->findNode(node);
|
|
//cerr << "Checking status of each waypoint: " << fn->getIdent();
|
|
|
|
SGWayPoint first(init_waypoint->longitude,
|
|
init_waypoint->latitude,
|
|
alt);
|
|
SGWayPoint curr (fn->getLongitude(),
|
|
fn->getLatitude(),
|
|
alt);
|
|
SGWayPoint arr (arr->getLongitude(),
|
|
arr->getLatitude(),
|
|
alt);
|
|
|
|
double crse, crsDiff;
|
|
double dist;
|
|
first.CourseAndDistance(arr, &course, &distance);
|
|
first.CourseAndDistance(curr, &crse, &dist);
|
|
|
|
dist *= SG_METER_TO_NM;
|
|
|
|
// We're only interested in the absolute value of crsDiff
|
|
// wich should fall in the 0-180 deg range.
|
|
crsDiff = fabs(crse-course);
|
|
if (crsDiff > 180)
|
|
crsDiff = 360-crsDiff;
|
|
// These are the three conditions that we consider including
|
|
// in our flight plan:
|
|
// 1) current waypoint is less then 100 miles away OR
|
|
// 2) curren waypoint is ahead of us, at any distance
|
|
//cerr << " Distance : " << dist << " : Course diff " << crsDiff
|
|
// << " crs to dest : " << course
|
|
// << " crs to wpt : " << crse;
|
|
if ((dist > 20.0) && (crsDiff > 90.0))
|
|
{
|
|
//useWpt = false;
|
|
// Once we start including waypoints, we have to continue, even though
|
|
// one of the following way point would suffice.
|
|
// so once is the useWpt flag is set to true, we cannot reset it to false.
|
|
//cerr << " discarding " << endl;
|
|
// << ": Course difference = " << crsDiff
|
|
// << "Course = " << course
|
|
// << "crse = " << crse << endl;
|
|
}
|
|
else {
|
|
//i = ids.end()-1;
|
|
//cerr << " accepting " << endl;
|
|
|
|
//ids.pop_back();
|
|
wpt = new waypoint;
|
|
wpt->name = "Airway"; // fixme: should be the name of the waypoint
|
|
wpt->latitude = fn->getLatitude();
|
|
wpt->longitude = fn->getLongitude();
|
|
//wpt->altitude = apt->getElevation(); // should maybe be tn->elev too
|
|
wpt->altitude = alt;
|
|
wpt->speed = 450; //speed;
|
|
wpt->crossat = -10000;
|
|
wpt->gear_down = false;
|
|
wpt->flaps_down= false;
|
|
wpt->finished = false;
|
|
wpt->on_ground = false;
|
|
waypoints.push_back(wpt);
|
|
}
|
|
|
|
if (!(routefile.exists()))
|
|
{
|
|
route.first();
|
|
fstream outf( routefile.c_str(), fstream::out );
|
|
while (route.next(&node))
|
|
outf << node << endl;
|
|
}
|
|
}
|
|
}
|
|
arr->getDynamics()->getActiveRunway("com", 2, activeRunway);
|
|
if (!(globals->get_runways()->search(arr->getId(),
|
|
activeRunway,
|
|
&rwy)))
|
|
{
|
|
cout << "Failed to find runway for " << arr->getId() << endl;
|
|
// Hmm, how do we handle a potential error like this?
|
|
exit(1);
|
|
}
|
|
//string test;
|
|
//arr->getActiveRunway(string("com"), 1, test);
|
|
//exit(1);
|
|
|
|
//cerr << "Altitude = " << alt << endl;
|
|
//cerr << "Done" << endl;
|
|
//if (arr->getId() == "EHAM")
|
|
// {
|
|
// cerr << "Creating cruise to EHAM " << latitude << " " << longitude << endl;
|
|
// }
|
|
heading = rwy._heading;
|
|
azimuth = heading + 180.0;
|
|
while ( azimuth >= 360.0 ) { azimuth -= 360.0; }
|
|
|
|
|
|
// Note: This places us at the location of the active
|
|
// runway during initial cruise. This needs to be
|
|
// fixed later.
|
|
geo_direct_wgs_84 ( 0, rwy._lat, rwy._lon, azimuth,
|
|
110000,
|
|
&lat2, &lon2, &az2 );
|
|
wpt = new waypoint;
|
|
wpt->name = "BOD"; //wpt_node->getStringValue("name", "END");
|
|
wpt->latitude = lat2;
|
|
wpt->longitude = lon2;
|
|
wpt->altitude = alt;
|
|
wpt->speed = speed;
|
|
wpt->crossat = alt;
|
|
wpt->gear_down = false;
|
|
wpt->flaps_down= false;
|
|
wpt->finished = false;
|
|
wpt->on_ground = false;
|
|
waypoints.push_back(wpt);
|
|
}
|
|
*/
|
|
|
|
|
|
/*******************************************************************
|
|
* CreateCruise
|
|
* initialize the Aircraft at the parking location
|
|
*
|
|
* Note that this is the original version that does not
|
|
* do any dynamic route computation.
|
|
******************************************************************/
|
|
bool FGAIFlightPlan::createCruise(FGAIAircraft *ac, bool firstFlight, FGAirport *dep,
|
|
FGAirport *arr, double latitude,
|
|
double longitude, double speed,
|
|
double alt, const string& fltType)
|
|
{
|
|
double vCruise = ac->getPerformance()->vCruise();
|
|
waypoint *wpt;
|
|
wpt = createInAir(ac, "Cruise", SGGeod::fromDeg(longitude, latitude), alt, vCruise);
|
|
waypoints.push_back(wpt);
|
|
|
|
string rwyClass = getRunwayClassFromTrafficType(fltType);
|
|
double heading = ac->getTrafficRef()->getCourse();
|
|
arr->getDynamics()->getActiveRunway(rwyClass, 2, activeRunway, heading);
|
|
rwy = arr->getRunwayByIdent(activeRunway);
|
|
// begin descent 110km out
|
|
SGGeod beginDescentPoint = rwy->pointOnCenterline(0);
|
|
SGGeod secondaryDescentPoint = rwy->pointOnCenterline(-10000);
|
|
|
|
wpt = createInAir(ac, "BOD", beginDescentPoint, alt, vCruise);
|
|
waypoints.push_back(wpt);
|
|
wpt = createInAir(ac, "BOD2", secondaryDescentPoint, alt, vCruise);
|
|
waypoints.push_back(wpt);
|
|
}
|