2004-10-28 08:33:55 +00:00
|
|
|
// FGAICarrier - FGAIShip-derived class creates an AI aircraft carrier
|
|
|
|
//
|
|
|
|
// Written by David Culp, started October 2004.
|
|
|
|
// - davidculp2@comcast.net
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
|
2004-11-26 10:24:48 +00:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
|
|
|
|
2004-11-30 12:34:11 +00:00
|
|
|
#include <simgear/math/point3d.hxx>
|
|
|
|
#include <simgear/math/sg_geodesy.hxx>
|
|
|
|
#include <math.h>
|
|
|
|
#include <Main/util.hxx>
|
|
|
|
#include <Main/viewer.hxx>
|
|
|
|
|
2004-10-28 08:33:55 +00:00
|
|
|
#include "AICarrier.hxx"
|
|
|
|
|
Mathias Fröhlich:
I have introduced the posibility to start directly on the carrier.
With that patch you will have a --carrrier=id argument where id can either be
the pennant number configured in the nimitz scenario or the carriers name
also configured in the carriers scenario.
Additionaly you can use --parkpos=id to select different positions on the
carrier. They are also configured in the scenario file.
That includes the switch of the whole FGInterface class to make use of the
groundcache.
That means that an aircraft no longer uses the current elevation value from
the scenery class. It rather has its own local cache of the aircrafts
environment which is setup in the common_init method of FGInterface and
updated either manually by calling
FGInterface::get_groundlevel_m(lat, lon, alt_m);
or implicitly by calling the above method in the
FGInterface::_updateGeo*Position(lat, lon, alt);
methods.
A call get_groundlevel_m rebuilds the groundcache if the request is outside
the range of the cache.
Note that for the real usage of the groundcache including the correct
information about the movement of objects and the velocity information, you
still need to set up the groundcache in the usual way like YASim and JSBSim
currently does.
If you use the native interface, you will get only static objects correctly.
But for FDM's only using one single ground level for a whole step this is IMO
sufficient.
The AIManager gets a way to return the location of a object which is placed
wrt an AI Object. At the moment it only honours AICarriers for that.
That method is a static one, which loads the scenario file for that reason and
throws it away afterwards. This looked like the aprioriate way, because the
AIManager is initialized much later in flightgears bootstrap, and I did not
find an easy way to reorder that for my needs. Since this additional load is
very small and does only happen if such a relative location is required, I
think that this is ok.
Note that moving on the carrier will only work correctly for JSBSim and YASim,
but you should now be able to start and move on every not itself moving
object with any FDM.
2005-07-03 09:39:14 +00:00
|
|
|
#include "AIScenario.hxx"
|
|
|
|
|
2005-08-16 09:37:23 +00:00
|
|
|
/** Value of earth radius (meters) */
|
|
|
|
#define RADIUS_M SG_EQUATORIAL_RADIUS_M
|
|
|
|
|
|
|
|
|
Mathias Fröhlich:
I have introduced the posibility to start directly on the carrier.
With that patch you will have a --carrrier=id argument where id can either be
the pennant number configured in the nimitz scenario or the carriers name
also configured in the carriers scenario.
Additionaly you can use --parkpos=id to select different positions on the
carrier. They are also configured in the scenario file.
That includes the switch of the whole FGInterface class to make use of the
groundcache.
That means that an aircraft no longer uses the current elevation value from
the scenery class. It rather has its own local cache of the aircrafts
environment which is setup in the common_init method of FGInterface and
updated either manually by calling
FGInterface::get_groundlevel_m(lat, lon, alt_m);
or implicitly by calling the above method in the
FGInterface::_updateGeo*Position(lat, lon, alt);
methods.
A call get_groundlevel_m rebuilds the groundcache if the request is outside
the range of the cache.
Note that for the real usage of the groundcache including the correct
information about the movement of objects and the velocity information, you
still need to set up the groundcache in the usual way like YASim and JSBSim
currently does.
If you use the native interface, you will get only static objects correctly.
But for FDM's only using one single ground level for a whole step this is IMO
sufficient.
The AIManager gets a way to return the location of a object which is placed
wrt an AI Object. At the moment it only honours AICarriers for that.
That method is a static one, which loads the scenario file for that reason and
throws it away afterwards. This looked like the aprioriate way, because the
AIManager is initialized much later in flightgears bootstrap, and I did not
find an easy way to reorder that for my needs. Since this additional load is
very small and does only happen if such a relative location is required, I
think that this is ok.
Note that moving on the carrier will only work correctly for JSBSim and YASim,
but you should now be able to start and move on every not itself moving
object with any FDM.
2005-07-03 09:39:14 +00:00
|
|
|
|
2004-10-28 08:33:55 +00:00
|
|
|
FGAICarrier::FGAICarrier(FGAIManager* mgr) : FGAIShip(mgr) {
|
2005-03-19 09:57:18 +00:00
|
|
|
_type_str = "carrier";
|
|
|
|
_otype = otCarrier;
|
2005-08-16 09:37:23 +00:00
|
|
|
|
|
|
|
|
2004-10-28 08:33:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FGAICarrier::~FGAICarrier() {
|
|
|
|
}
|
|
|
|
|
2005-08-16 09:37:23 +00:00
|
|
|
void FGAICarrier::setWind_from_east(double fps) {
|
|
|
|
wind_from_east = fps;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGAICarrier::setWind_from_north(double fps) {
|
|
|
|
wind_from_north = fps;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGAICarrier::setMaxLat(double deg) {
|
|
|
|
max_lat = fabs(deg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGAICarrier::setMinLat(double deg) {
|
|
|
|
min_lat = fabs(deg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGAICarrier::setMaxLong(double deg) {
|
|
|
|
max_long = fabs(deg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGAICarrier::setMinLong(double deg) {
|
|
|
|
min_long = fabs(deg);
|
|
|
|
}
|
|
|
|
|
2004-11-26 10:24:48 +00:00
|
|
|
void FGAICarrier::setSolidObjects(const list<string>& so) {
|
|
|
|
solid_objects = so;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGAICarrier::setWireObjects(const list<string>& wo) {
|
|
|
|
wire_objects = wo;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGAICarrier::setCatapultObjects(const list<string>& co) {
|
|
|
|
catapult_objects = co;
|
|
|
|
}
|
|
|
|
|
Mathias Fröhlich:
I have introduced the posibility to start directly on the carrier.
With that patch you will have a --carrrier=id argument where id can either be
the pennant number configured in the nimitz scenario or the carriers name
also configured in the carriers scenario.
Additionaly you can use --parkpos=id to select different positions on the
carrier. They are also configured in the scenario file.
That includes the switch of the whole FGInterface class to make use of the
groundcache.
That means that an aircraft no longer uses the current elevation value from
the scenery class. It rather has its own local cache of the aircrafts
environment which is setup in the common_init method of FGInterface and
updated either manually by calling
FGInterface::get_groundlevel_m(lat, lon, alt_m);
or implicitly by calling the above method in the
FGInterface::_updateGeo*Position(lat, lon, alt);
methods.
A call get_groundlevel_m rebuilds the groundcache if the request is outside
the range of the cache.
Note that for the real usage of the groundcache including the correct
information about the movement of objects and the velocity information, you
still need to set up the groundcache in the usual way like YASim and JSBSim
currently does.
If you use the native interface, you will get only static objects correctly.
But for FDM's only using one single ground level for a whole step this is IMO
sufficient.
The AIManager gets a way to return the location of a object which is placed
wrt an AI Object. At the moment it only honours AICarriers for that.
That method is a static one, which loads the scenario file for that reason and
throws it away afterwards. This looked like the aprioriate way, because the
AIManager is initialized much later in flightgears bootstrap, and I did not
find an easy way to reorder that for my needs. Since this additional load is
very small and does only happen if such a relative location is required, I
think that this is ok.
Note that moving on the carrier will only work correctly for JSBSim and YASim,
but you should now be able to start and move on every not itself moving
object with any FDM.
2005-07-03 09:39:14 +00:00
|
|
|
void FGAICarrier::setParkingPositions(const list<ParkPosition>& p) {
|
2005-03-19 09:57:18 +00:00
|
|
|
ppositions = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FGAICarrier::setSign(const string& s) {
|
|
|
|
sign = s;
|
|
|
|
}
|
|
|
|
|
2005-08-16 09:37:23 +00:00
|
|
|
void FGAICarrier::setTACANChannelID(const string& id) {
|
|
|
|
TACAN_channel_id = id;
|
|
|
|
}
|
|
|
|
|
2005-03-19 09:57:18 +00:00
|
|
|
void FGAICarrier::setFlolsOffset(const Point3D& off) {
|
|
|
|
flols_off = off;
|
|
|
|
}
|
|
|
|
|
2005-10-25 13:49:55 +00:00
|
|
|
void FGAICarrier::getVelocityWrtEarth(sgdVec3& v, sgdVec3& omega, sgdVec3& pivot) {
|
2005-08-16 09:37:23 +00:00
|
|
|
sgdCopyVec3(v, vel_wrt_earth );
|
|
|
|
sgdCopyVec3(omega, rot_wrt_earth );
|
|
|
|
sgdCopyVec3(pivot, rot_pivot_wrt_earth );
|
2004-11-26 10:24:48 +00:00
|
|
|
}
|
2004-10-28 08:33:55 +00:00
|
|
|
|
|
|
|
void FGAICarrier::update(double dt) {
|
2005-08-16 09:37:23 +00:00
|
|
|
|
|
|
|
// For computation of rotation speeds we just use finite differences her.
|
|
|
|
// That is perfectly valid since this thing is not driven by accelerations
|
|
|
|
// but by just apply discrete changes at its velocity variables.
|
|
|
|
double old_hdg = hdg;
|
|
|
|
double old_roll = roll;
|
|
|
|
double old_pitch = pitch;
|
2004-11-26 10:24:48 +00:00
|
|
|
|
|
|
|
// Update the velocity information stored in those nodes.
|
|
|
|
double v_north = 0.51444444*speed*cos(hdg * SGD_DEGREES_TO_RADIANS);
|
|
|
|
double v_east = 0.51444444*speed*sin(hdg * SGD_DEGREES_TO_RADIANS);
|
|
|
|
|
|
|
|
double sin_lat = sin(pos.lat() * SGD_DEGREES_TO_RADIANS);
|
|
|
|
double cos_lat = cos(pos.lat() * SGD_DEGREES_TO_RADIANS);
|
|
|
|
double sin_lon = sin(pos.lon() * SGD_DEGREES_TO_RADIANS);
|
|
|
|
double cos_lon = cos(pos.lon() * SGD_DEGREES_TO_RADIANS);
|
2005-08-16 09:37:23 +00:00
|
|
|
double sin_roll = sin(roll * SGD_DEGREES_TO_RADIANS);
|
|
|
|
double cos_roll = cos(roll * SGD_DEGREES_TO_RADIANS);
|
|
|
|
double sin_pitch = sin(pitch * SGD_DEGREES_TO_RADIANS);
|
|
|
|
double cos_pitch = cos(pitch * SGD_DEGREES_TO_RADIANS);
|
|
|
|
double sin_hdg = sin(hdg * SGD_DEGREES_TO_RADIANS);
|
|
|
|
double cos_hdg = cos(hdg * SGD_DEGREES_TO_RADIANS);
|
|
|
|
|
|
|
|
// Transform this back the the horizontal local frame.
|
|
|
|
sgdMat3 trans;
|
|
|
|
|
|
|
|
// set up the transform matrix
|
|
|
|
trans[0][0] = cos_pitch*cos_hdg;
|
|
|
|
trans[0][1] = sin_roll*sin_pitch*cos_hdg - cos_roll*sin_hdg;
|
|
|
|
trans[0][2] = cos_roll*sin_pitch*cos_hdg + sin_roll*sin_hdg;
|
|
|
|
|
|
|
|
trans[1][0] = cos_pitch*sin_hdg;
|
|
|
|
trans[1][1] = sin_roll*sin_pitch*sin_hdg + cos_roll*cos_hdg;
|
|
|
|
trans[1][2] = cos_roll*sin_pitch*sin_hdg - sin_roll*cos_hdg;
|
|
|
|
|
|
|
|
trans[2][0] = -sin_pitch;
|
|
|
|
trans[2][1] = sin_roll*cos_pitch;
|
|
|
|
trans[2][2] = cos_roll*cos_pitch;
|
|
|
|
|
|
|
|
sgdSetVec3( vel_wrt_earth,
|
2004-11-26 10:24:48 +00:00
|
|
|
- cos_lon*sin_lat*v_north - sin_lon*v_east,
|
|
|
|
- sin_lon*sin_lat*v_north + cos_lon*v_east,
|
|
|
|
cos_lat*v_north );
|
2005-08-16 09:37:23 +00:00
|
|
|
sgGeodToCart(pos.lat() * SGD_DEGREES_TO_RADIANS,
|
|
|
|
pos.lon() * SGD_DEGREES_TO_RADIANS,
|
|
|
|
pos.elev(), rot_pivot_wrt_earth);
|
2004-11-26 10:24:48 +00:00
|
|
|
|
2005-08-16 09:37:23 +00:00
|
|
|
// Now update the position and heading. This will compute new hdg and
|
|
|
|
// roll values required for the rotation speed computation.
|
|
|
|
FGAIShip::update(dt);
|
|
|
|
|
|
|
|
|
|
|
|
//automatic turn into wind with a target wind of 25 kts otd
|
|
|
|
if(turn_to_launch_hdg){
|
|
|
|
TurnToLaunch();
|
|
|
|
} else if(OutsideBox() || returning) {// check that the carrier is inside the operating box
|
|
|
|
ReturnToBox();
|
|
|
|
} else { //if(!returning
|
|
|
|
TurnToBase();
|
|
|
|
} //end if
|
|
|
|
|
|
|
|
// Only change these values if we are able to compute them safely
|
|
|
|
if (dt < DBL_MIN)
|
|
|
|
sgdSetVec3( rot_wrt_earth, 0.0, 0.0, 0.0);
|
|
|
|
else {
|
|
|
|
// Compute the change of the euler angles.
|
|
|
|
double hdg_dot = SGD_DEGREES_TO_RADIANS * (hdg-old_hdg)/dt;
|
|
|
|
// Allways assume that the movement was done by the shorter way.
|
|
|
|
if (hdg_dot < - SGD_DEGREES_TO_RADIANS * 180)
|
|
|
|
hdg_dot += SGD_DEGREES_TO_RADIANS * 360;
|
|
|
|
if (hdg_dot > SGD_DEGREES_TO_RADIANS * 180)
|
|
|
|
hdg_dot -= SGD_DEGREES_TO_RADIANS * 360;
|
|
|
|
double pitch_dot = SGD_DEGREES_TO_RADIANS * (pitch-old_pitch)/dt;
|
|
|
|
// Allways assume that the movement was done by the shorter way.
|
|
|
|
if (pitch_dot < - SGD_DEGREES_TO_RADIANS * 180)
|
|
|
|
pitch_dot += SGD_DEGREES_TO_RADIANS * 360;
|
|
|
|
if (pitch_dot > SGD_DEGREES_TO_RADIANS * 180)
|
|
|
|
pitch_dot -= SGD_DEGREES_TO_RADIANS * 360;
|
|
|
|
double roll_dot = SGD_DEGREES_TO_RADIANS * (roll-old_roll)/dt;
|
|
|
|
// Allways assume that the movement was done by the shorter way.
|
|
|
|
if (roll_dot < - SGD_DEGREES_TO_RADIANS * 180)
|
|
|
|
roll_dot += SGD_DEGREES_TO_RADIANS * 360;
|
|
|
|
if (roll_dot > SGD_DEGREES_TO_RADIANS * 180)
|
|
|
|
roll_dot -= SGD_DEGREES_TO_RADIANS * 360;
|
|
|
|
/*cout << "euler derivatives = "
|
|
|
|
<< roll_dot << " " << pitch_dot << " " << hdg_dot << endl;*/
|
|
|
|
|
|
|
|
// Now Compute the rotation vector in the carriers coordinate frame
|
|
|
|
// originating from the euler angle changes.
|
|
|
|
sgdVec3 body;
|
|
|
|
body[0] = roll_dot - hdg_dot*sin_pitch;
|
|
|
|
body[1] = pitch_dot*cos_roll + hdg_dot*sin_roll*cos_pitch;
|
|
|
|
body[2] = -pitch_dot*sin_roll + hdg_dot*cos_roll*cos_pitch;
|
|
|
|
|
|
|
|
// Transform that back to the horizontal local frame.
|
|
|
|
sgdVec3 hl;
|
|
|
|
hl[0] = body[0]*trans[0][0] + body[1]*trans[0][1] + body[2]*trans[0][2];
|
|
|
|
hl[1] = body[0]*trans[1][0] + body[1]*trans[1][1] + body[2]*trans[1][2];
|
|
|
|
hl[2] = body[0]*trans[2][0] + body[1]*trans[2][1] + body[2]*trans[2][2];
|
|
|
|
|
|
|
|
// Now we need to project out rotation components ending in speeds in y
|
|
|
|
// direction in the hoirizontal local frame.
|
|
|
|
hl[1] = 0;
|
|
|
|
|
|
|
|
// Transform that to the earth centered frame.
|
|
|
|
sgdSetVec3(rot_wrt_earth,
|
|
|
|
- cos_lon*sin_lat*hl[0] - sin_lon*hl[1] - cos_lat*cos_lon*hl[2],
|
|
|
|
- sin_lon*sin_lat*hl[0] + cos_lon*hl[1] - cos_lat*sin_lon*hl[2],
|
|
|
|
cos_lat*hl[0] - sin_lat*hl[2]);
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateWind(dt);
|
|
|
|
UpdateTACAN(dt);
|
|
|
|
UpdateFlols(trans);
|
|
|
|
} //end update
|
2004-11-26 10:24:48 +00:00
|
|
|
|
|
|
|
bool FGAICarrier::init() {
|
|
|
|
if (!FGAIShip::init())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// process the 3d model here
|
|
|
|
// mark some objects solid, mark the wires ...
|
|
|
|
|
|
|
|
// The model should be used for altitude computations.
|
|
|
|
// To avoid that every detail in a carrier 3D model will end into
|
|
|
|
// the aircraft local cache, only set the HOT traversal bit on
|
|
|
|
// selected objects.
|
|
|
|
ssgEntity *sel = aip.getSceneGraph();
|
|
|
|
// Clear the HOT traversal flag
|
|
|
|
mark_nohot(sel);
|
|
|
|
// Selectively set that flag again for wires/cats/solid objects.
|
|
|
|
// Attach a pointer to this carrier class to those objects.
|
|
|
|
mark_wires(sel, wire_objects);
|
|
|
|
mark_cat(sel, catapult_objects);
|
2004-12-27 13:21:18 +00:00
|
|
|
mark_solid(sel, solid_objects);
|
2004-11-26 10:24:48 +00:00
|
|
|
|
2005-08-16 09:37:23 +00:00
|
|
|
_longitude_node = fgGetNode("/position/longitude-deg", true);
|
|
|
|
_latitude_node = fgGetNode("/position/latitude-deg", true);
|
|
|
|
_altitude_node = fgGetNode("/position/altitude-ft", true);
|
|
|
|
_dme_freq_node = fgGetNode("/instrumentation/dme/frequencies/selected-mhz", true);
|
|
|
|
_surface_wind_from_deg_node =
|
|
|
|
fgGetNode("/environment/config/boundary/entry[0]/wind-from-heading-deg", true);
|
|
|
|
_surface_wind_speed_node =
|
|
|
|
fgGetNode("/environment/config/boundary/entry[0]/wind-speed-kt", true);
|
|
|
|
|
|
|
|
|
|
|
|
turn_to_launch_hdg = false;
|
|
|
|
returning = false;
|
|
|
|
|
|
|
|
initialpos = pos;
|
|
|
|
base_course = hdg;
|
|
|
|
base_speed = speed;
|
|
|
|
|
2004-11-26 10:24:48 +00:00
|
|
|
return true;
|
2004-10-28 08:33:55 +00:00
|
|
|
}
|
2005-03-19 09:57:18 +00:00
|
|
|
|
2004-11-30 12:34:11 +00:00
|
|
|
void FGAICarrier::bind() {
|
2005-03-19 09:57:18 +00:00
|
|
|
FGAIShip::bind();
|
2004-11-30 12:34:11 +00:00
|
|
|
|
2005-08-16 09:37:23 +00:00
|
|
|
props->untie("velocities/true-airspeed-kt");
|
|
|
|
|
2004-11-30 12:34:11 +00:00
|
|
|
props->tie("controls/flols/source-lights",
|
|
|
|
SGRawValuePointer<int>(&source));
|
|
|
|
props->tie("controls/flols/distance-m",
|
2005-03-19 09:57:18 +00:00
|
|
|
SGRawValuePointer<double>(&dist));
|
2005-02-24 15:05:56 +00:00
|
|
|
props->tie("controls/flols/angle-degs",
|
2005-03-19 09:57:18 +00:00
|
|
|
SGRawValuePointer<double>(&angle));
|
2005-08-16 09:37:23 +00:00
|
|
|
props->tie("controls/turn-to-launch-hdg",
|
|
|
|
SGRawValuePointer<bool>(&turn_to_launch_hdg));
|
|
|
|
props->tie("controls/in-to-wind",
|
|
|
|
SGRawValuePointer<bool>(&turn_to_launch_hdg));
|
|
|
|
props->tie("controls/base-course-deg",
|
|
|
|
SGRawValuePointer<double>(&base_course));
|
|
|
|
props->tie("controls/base-speed-kts",
|
|
|
|
SGRawValuePointer<double>(&base_speed));
|
|
|
|
props->tie("controls/start-pos-lat-deg",
|
|
|
|
SGRawValuePointer<double>(&initialpos[1]));
|
|
|
|
props->tie("controls/start-pos-long-deg",
|
|
|
|
SGRawValuePointer<double>(&initialpos[0]));
|
|
|
|
props->tie("velocities/speed-kts",
|
|
|
|
SGRawValuePointer<double>(&speed));
|
|
|
|
props->tie("environment/surface-wind-speed-true-kts",
|
|
|
|
SGRawValuePointer<double>(&wind_speed_kts));
|
|
|
|
props->tie("environment/surface-wind-from-true-degs",
|
|
|
|
SGRawValuePointer<double>(&wind_from_deg));
|
|
|
|
props->tie("environment/rel-wind-from-degs",
|
|
|
|
SGRawValuePointer<double>(&rel_wind_from_deg));
|
|
|
|
props->tie("environment/rel-wind-from-carrier-hdg-degs",
|
|
|
|
SGRawValuePointer<double>(&rel_wind));
|
|
|
|
props->tie("environment/rel-wind-speed-kts",
|
|
|
|
SGRawValuePointer<double>(&rel_wind_speed_kts));
|
|
|
|
props->tie("controls/flols/wave-off-lights",
|
|
|
|
SGRawValuePointer<bool>(&wave_off_lights));
|
|
|
|
|
2004-11-30 12:34:11 +00:00
|
|
|
props->setBoolValue("controls/flols/cut-lights", false);
|
|
|
|
props->setBoolValue("controls/flols/wave-off-lights", false);
|
2005-03-19 09:57:18 +00:00
|
|
|
props->setBoolValue("controls/flols/cond-datum-lights", true);
|
|
|
|
props->setBoolValue("controls/crew", false);
|
|
|
|
|
2005-10-01 09:56:53 +00:00
|
|
|
props->setStringValue("navaids/tacan/channel-ID", TACAN_channel_id.c_str());
|
2005-03-19 09:57:18 +00:00
|
|
|
props->setStringValue("sign", sign.c_str());
|
|
|
|
}
|
2004-11-30 12:34:11 +00:00
|
|
|
|
|
|
|
void FGAICarrier::unbind() {
|
2005-03-19 09:57:18 +00:00
|
|
|
FGAIShip::unbind();
|
2005-08-16 09:37:23 +00:00
|
|
|
|
|
|
|
props->untie("velocities/true-airspeed-kt");
|
|
|
|
|
2004-11-30 12:34:11 +00:00
|
|
|
props->untie("controls/flols/source-lights");
|
2005-03-19 09:57:18 +00:00
|
|
|
props->untie("controls/flols/distance-m");
|
|
|
|
props->untie("controls/flols/angle-degs");
|
2005-08-16 09:37:23 +00:00
|
|
|
props->untie("controls/turn-to-launch-hdg");
|
|
|
|
props->untie("velocities/speed-kts");
|
|
|
|
props->untie("environment/wind-speed-true-kts");
|
|
|
|
props->untie("environment/wind-from-true-degs");
|
|
|
|
props->untie("environment/rel-wind-from-degs");
|
|
|
|
props->untie("environment/rel-wind-speed-kts");
|
|
|
|
props->untie("controls/flols/wave-off-lights");
|
2005-10-01 09:56:53 +00:00
|
|
|
|
2004-11-30 12:34:11 +00:00
|
|
|
}
|
2005-03-19 09:57:18 +00:00
|
|
|
|
Mathias Fröhlich:
I have introduced the posibility to start directly on the carrier.
With that patch you will have a --carrrier=id argument where id can either be
the pennant number configured in the nimitz scenario or the carriers name
also configured in the carriers scenario.
Additionaly you can use --parkpos=id to select different positions on the
carrier. They are also configured in the scenario file.
That includes the switch of the whole FGInterface class to make use of the
groundcache.
That means that an aircraft no longer uses the current elevation value from
the scenery class. It rather has its own local cache of the aircrafts
environment which is setup in the common_init method of FGInterface and
updated either manually by calling
FGInterface::get_groundlevel_m(lat, lon, alt_m);
or implicitly by calling the above method in the
FGInterface::_updateGeo*Position(lat, lon, alt);
methods.
A call get_groundlevel_m rebuilds the groundcache if the request is outside
the range of the cache.
Note that for the real usage of the groundcache including the correct
information about the movement of objects and the velocity information, you
still need to set up the groundcache in the usual way like YASim and JSBSim
currently does.
If you use the native interface, you will get only static objects correctly.
But for FDM's only using one single ground level for a whole step this is IMO
sufficient.
The AIManager gets a way to return the location of a object which is placed
wrt an AI Object. At the moment it only honours AICarriers for that.
That method is a static one, which loads the scenario file for that reason and
throws it away afterwards. This looked like the aprioriate way, because the
AIManager is initialized much later in flightgears bootstrap, and I did not
find an easy way to reorder that for my needs. Since this additional load is
very small and does only happen if such a relative location is required, I
think that this is ok.
Note that moving on the carrier will only work correctly for JSBSim and YASim,
but you should now be able to start and move on every not itself moving
object with any FDM.
2005-07-03 09:39:14 +00:00
|
|
|
bool FGAICarrier::getParkPosition(const string& id, Point3D& geodPos,
|
|
|
|
double& hdng, sgdVec3 uvw)
|
|
|
|
{
|
2005-08-16 09:37:23 +00:00
|
|
|
|
|
|
|
// FIXME: does not yet cover rotation speeds.
|
Mathias Fröhlich:
I have introduced the posibility to start directly on the carrier.
With that patch you will have a --carrrier=id argument where id can either be
the pennant number configured in the nimitz scenario or the carriers name
also configured in the carriers scenario.
Additionaly you can use --parkpos=id to select different positions on the
carrier. They are also configured in the scenario file.
That includes the switch of the whole FGInterface class to make use of the
groundcache.
That means that an aircraft no longer uses the current elevation value from
the scenery class. It rather has its own local cache of the aircrafts
environment which is setup in the common_init method of FGInterface and
updated either manually by calling
FGInterface::get_groundlevel_m(lat, lon, alt_m);
or implicitly by calling the above method in the
FGInterface::_updateGeo*Position(lat, lon, alt);
methods.
A call get_groundlevel_m rebuilds the groundcache if the request is outside
the range of the cache.
Note that for the real usage of the groundcache including the correct
information about the movement of objects and the velocity information, you
still need to set up the groundcache in the usual way like YASim and JSBSim
currently does.
If you use the native interface, you will get only static objects correctly.
But for FDM's only using one single ground level for a whole step this is IMO
sufficient.
The AIManager gets a way to return the location of a object which is placed
wrt an AI Object. At the moment it only honours AICarriers for that.
That method is a static one, which loads the scenario file for that reason and
throws it away afterwards. This looked like the aprioriate way, because the
AIManager is initialized much later in flightgears bootstrap, and I did not
find an easy way to reorder that for my needs. Since this additional load is
very small and does only happen if such a relative location is required, I
think that this is ok.
Note that moving on the carrier will only work correctly for JSBSim and YASim,
but you should now be able to start and move on every not itself moving
object with any FDM.
2005-07-03 09:39:14 +00:00
|
|
|
list<ParkPosition>::iterator it = ppositions.begin();
|
|
|
|
while (it != ppositions.end()) {
|
|
|
|
// Take either the specified one or the first one ...
|
|
|
|
if ((*it).name == id || id.empty()) {
|
|
|
|
ParkPosition ppos = *it;
|
|
|
|
geodPos = getGeocPosAt(ppos.offset);
|
|
|
|
hdng = hdg + ppos.heading_deg;
|
|
|
|
double shdng = sin(ppos.heading_deg * SGD_DEGREES_TO_RADIANS);
|
|
|
|
double chdng = cos(ppos.heading_deg * SGD_DEGREES_TO_RADIANS);
|
|
|
|
double speed_fps = speed*1.6878099;
|
|
|
|
sgdSetVec3(uvw, chdng*speed_fps, shdng*speed_fps, 0);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2004-11-26 10:24:48 +00:00
|
|
|
void FGAICarrier::mark_nohot(ssgEntity* e) {
|
|
|
|
if (e->isAKindOf(ssgTypeBranch())) {
|
|
|
|
ssgBranch* br = (ssgBranch*)e;
|
|
|
|
ssgEntity* kid;
|
|
|
|
for ( kid = br->getKid(0); kid != NULL ; kid = br->getNextKid() )
|
|
|
|
mark_nohot(kid);
|
2004-10-28 08:33:55 +00:00
|
|
|
|
2004-11-26 10:24:48 +00:00
|
|
|
br->clrTraversalMaskBits(SSGTRAV_HOT);
|
|
|
|
|
|
|
|
} else if (e->isAKindOf(ssgTypeLeaf())) {
|
|
|
|
|
|
|
|
e->clrTraversalMaskBits(SSGTRAV_HOT);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-27 13:21:18 +00:00
|
|
|
bool FGAICarrier::mark_wires(ssgEntity* e, const list<string>& wire_objects, bool mark) {
|
2004-11-26 10:24:48 +00:00
|
|
|
bool found = false;
|
|
|
|
if (e->isAKindOf(ssgTypeBranch())) {
|
|
|
|
ssgBranch* br = (ssgBranch*)e;
|
|
|
|
ssgEntity* kid;
|
2004-12-27 13:21:18 +00:00
|
|
|
|
|
|
|
list<string>::const_iterator it;
|
|
|
|
for (it = wire_objects.begin(); it != wire_objects.end(); ++it)
|
|
|
|
mark = mark || (e->getName() && (*it) == e->getName());
|
|
|
|
|
2004-11-26 10:24:48 +00:00
|
|
|
for ( kid = br->getKid(0); kid != NULL ; kid = br->getNextKid() )
|
2004-12-27 13:21:18 +00:00
|
|
|
found = mark_wires(kid, wire_objects, mark) || found;
|
2004-11-26 10:24:48 +00:00
|
|
|
|
|
|
|
if (found)
|
|
|
|
br->setTraversalMaskBits(SSGTRAV_HOT);
|
|
|
|
|
|
|
|
} else if (e->isAKindOf(ssgTypeLeaf())) {
|
|
|
|
list<string>::const_iterator it;
|
|
|
|
for (it = wire_objects.begin(); it != wire_objects.end(); ++it) {
|
2004-12-27 13:21:18 +00:00
|
|
|
if (mark || (e->getName() && (*it) == e->getName())) {
|
2004-11-26 10:24:48 +00:00
|
|
|
e->setTraversalMaskBits(SSGTRAV_HOT);
|
2004-12-27 13:21:18 +00:00
|
|
|
ssgBase* ud = e->getUserData();
|
|
|
|
if (ud) {
|
|
|
|
FGAICarrierHardware* ch = dynamic_cast<FGAICarrierHardware*>(ud);
|
|
|
|
if (ch) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_WARN,
|
|
|
|
"AICarrier: Carrier hardware gets marked twice!\n"
|
|
|
|
" You have propably a whole branch marked as"
|
|
|
|
" a wire which also includes other carrier hardware."
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT,
|
|
|
|
"AICarrier: Found user data attached to a leaf node which "
|
|
|
|
"should be marked as a wire!\n ****Skipping!****");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
e->setUserData( FGAICarrierHardware::newWire( this ) );
|
|
|
|
ssgLeaf *l = (ssgLeaf*)e;
|
|
|
|
if ( l->getNumLines() != 1 ) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT,
|
|
|
|
"AICarrier: Found wires not modelled with exactly one line!");
|
|
|
|
}
|
|
|
|
found = true;
|
2004-11-26 10:24:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2004-12-18 10:33:36 +00:00
|
|
|
bool FGAICarrier::mark_solid(ssgEntity* e, const list<string>& solid_objects, bool mark) {
|
2004-11-26 10:24:48 +00:00
|
|
|
bool found = false;
|
|
|
|
if (e->isAKindOf(ssgTypeBranch())) {
|
|
|
|
ssgBranch* br = (ssgBranch*)e;
|
|
|
|
ssgEntity* kid;
|
2004-12-18 10:33:36 +00:00
|
|
|
|
|
|
|
list<string>::const_iterator it;
|
|
|
|
for (it = solid_objects.begin(); it != solid_objects.end(); ++it)
|
2004-12-27 13:21:18 +00:00
|
|
|
mark = mark || (e->getName() && (*it) == e->getName());
|
2004-12-18 10:33:36 +00:00
|
|
|
|
2004-11-26 10:24:48 +00:00
|
|
|
for ( kid = br->getKid(0); kid != NULL ; kid = br->getNextKid() )
|
2004-12-18 10:33:36 +00:00
|
|
|
found = mark_solid(kid, solid_objects, mark) || found;
|
2004-11-26 10:24:48 +00:00
|
|
|
|
|
|
|
if (found)
|
|
|
|
br->setTraversalMaskBits(SSGTRAV_HOT);
|
|
|
|
|
|
|
|
} else if (e->isAKindOf(ssgTypeLeaf())) {
|
|
|
|
list<string>::const_iterator it;
|
|
|
|
for (it = solid_objects.begin(); it != solid_objects.end(); ++it) {
|
2004-12-18 10:33:36 +00:00
|
|
|
if (mark || (e->getName() && (*it) == e->getName())) {
|
2004-11-26 10:24:48 +00:00
|
|
|
e->setTraversalMaskBits(SSGTRAV_HOT);
|
2004-12-27 13:21:18 +00:00
|
|
|
ssgBase* ud = e->getUserData();
|
|
|
|
if (ud) {
|
|
|
|
FGAICarrierHardware* ch = dynamic_cast<FGAICarrierHardware*>(ud);
|
|
|
|
if (ch) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_WARN,
|
|
|
|
"AICarrier: Carrier hardware gets marked twice!\n"
|
|
|
|
" You have propably a whole branch marked solid"
|
|
|
|
" which also includes other carrier hardware."
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT,
|
|
|
|
"AICarrier: Found user data attached to a leaf node which "
|
|
|
|
"should be marked solid!\n ****Skipping!****");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
e->setUserData( FGAICarrierHardware::newSolid( this ) );
|
|
|
|
found = true;
|
|
|
|
}
|
2004-11-26 10:24:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2004-12-27 13:21:18 +00:00
|
|
|
bool FGAICarrier::mark_cat(ssgEntity* e, const list<string>& cat_objects, bool mark) {
|
2004-11-26 10:24:48 +00:00
|
|
|
bool found = false;
|
|
|
|
if (e->isAKindOf(ssgTypeBranch())) {
|
|
|
|
ssgBranch* br = (ssgBranch*)e;
|
|
|
|
ssgEntity* kid;
|
2004-12-27 13:21:18 +00:00
|
|
|
|
|
|
|
list<string>::const_iterator it;
|
|
|
|
for (it = cat_objects.begin(); it != cat_objects.end(); ++it)
|
|
|
|
mark = mark || (e->getName() && (*it) == e->getName());
|
|
|
|
|
2004-11-26 10:24:48 +00:00
|
|
|
for ( kid = br->getKid(0); kid != NULL ; kid = br->getNextKid() )
|
2004-12-27 13:21:18 +00:00
|
|
|
found = mark_cat(kid, cat_objects, mark) || found;
|
2004-11-26 10:24:48 +00:00
|
|
|
|
|
|
|
if (found)
|
|
|
|
br->setTraversalMaskBits(SSGTRAV_HOT);
|
|
|
|
|
|
|
|
} else if (e->isAKindOf(ssgTypeLeaf())) {
|
|
|
|
list<string>::const_iterator it;
|
|
|
|
for (it = cat_objects.begin(); it != cat_objects.end(); ++it) {
|
2004-12-27 13:21:18 +00:00
|
|
|
if (mark || (e->getName() && (*it) == e->getName())) {
|
2004-11-26 10:24:48 +00:00
|
|
|
e->setTraversalMaskBits(SSGTRAV_HOT);
|
2004-12-27 13:21:18 +00:00
|
|
|
ssgBase* ud = e->getUserData();
|
|
|
|
if (ud) {
|
|
|
|
FGAICarrierHardware* ch = dynamic_cast<FGAICarrierHardware*>(ud);
|
|
|
|
if (ch) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_WARN,
|
|
|
|
"AICarrier: Carrier hardware gets marked twice!\n"
|
2005-02-24 15:05:56 +00:00
|
|
|
"You have probably a whole branch marked as"
|
|
|
|
"a catapult which also includes other carrier hardware."
|
|
|
|
);
|
2004-12-27 13:21:18 +00:00
|
|
|
} else {
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT,
|
|
|
|
"AICarrier: Found user data attached to a leaf node which "
|
|
|
|
"should be marked as a catapult!\n ****Skipping!****");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
e->setUserData( FGAICarrierHardware::newCatapult( this ) );
|
|
|
|
ssgLeaf *l = (ssgLeaf*)e;
|
|
|
|
if ( l->getNumLines() != 1 ) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT,
|
|
|
|
"AICarrier: Found a cat not modelled with exactly "
|
|
|
|
"one line!");
|
|
|
|
} else {
|
|
|
|
// Now some special code to make sure the cat points in the right
|
|
|
|
// direction. The 0 index must be the backward end, the 1 index
|
|
|
|
// the forward end.
|
|
|
|
// Forward is positive x-direction in our 3D model, also the model
|
|
|
|
// as such is flattened when it is loaded, so we do not need to
|
|
|
|
// care for transforms ...
|
|
|
|
short v[2];
|
|
|
|
l->getLine(0, v, v+1 );
|
|
|
|
sgVec3 ends[2];
|
|
|
|
for (int k=0; k<2; ++k)
|
|
|
|
sgCopyVec3( ends[k], l->getVertex( v[k] ) );
|
|
|
|
|
|
|
|
// When the 1 end is behind the 0 end, swap the coordinates.
|
|
|
|
if (ends[0][0] < ends[1][0]) {
|
|
|
|
sgCopyVec3( l->getVertex( v[0] ), ends[1] );
|
|
|
|
sgCopyVec3( l->getVertex( v[1] ), ends[0] );
|
|
|
|
}
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
}
|
2004-11-26 10:24:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
}
|
2004-10-28 08:33:55 +00:00
|
|
|
|
2005-10-25 13:49:55 +00:00
|
|
|
void FGAICarrier::UpdateFlols(const sgdMat3& trans) {
|
2005-03-19 09:57:18 +00:00
|
|
|
|
|
|
|
float in[3];
|
|
|
|
float out[3];
|
|
|
|
|
|
|
|
double flolsXYZ[3], eyeXYZ[3];
|
|
|
|
double lat, lon, alt;
|
|
|
|
Point3D eyepos;
|
|
|
|
Point3D flolspos;
|
|
|
|
|
2004-11-30 12:34:11 +00:00
|
|
|
/* cout << "x_offset " << flols_x_offset
|
|
|
|
<< " y_offset " << flols_y_offset
|
|
|
|
<< " z_offset " << flols_z_offset << endl;
|
|
|
|
|
|
|
|
cout << "roll " << roll
|
|
|
|
<< " heading " << hdg
|
|
|
|
<< " pitch " << pitch << endl;
|
|
|
|
|
|
|
|
cout << "carrier lon " << pos[0]
|
|
|
|
<< " lat " << pos[1]
|
|
|
|
<< " alt " << pos[2] << endl;*/
|
|
|
|
|
|
|
|
// set the Flols intitial position to the carrier position
|
|
|
|
|
|
|
|
flolspos = pos;
|
|
|
|
|
|
|
|
/* cout << "flols lon " << flolspos[0]
|
|
|
|
<< " lat " << flolspos[1]
|
|
|
|
<< " alt " << flolspos[2] << endl;*/
|
|
|
|
|
|
|
|
// set the offsets in metres
|
|
|
|
|
|
|
|
/* cout << "flols_x_offset " << flols_x_offset << endl
|
|
|
|
<< "flols_y_offset " << flols_y_offset << endl
|
|
|
|
<< "flols_z_offset " << flols_z_offset << endl;*/
|
|
|
|
|
2005-03-19 09:57:18 +00:00
|
|
|
in[0] = flols_off.x();
|
|
|
|
in[1] = flols_off.y();
|
|
|
|
in[2] = flols_off.z();
|
2004-11-30 12:34:11 +00:00
|
|
|
|
|
|
|
// multiply the input and transform matrices
|
|
|
|
|
|
|
|
out[0] = in[0] * trans[0][0] + in[1] * trans[0][1] + in[2] * trans[0][2];
|
|
|
|
out[1] = in[0] * trans[1][0] + in[1] * trans[1][1] + in[2] * trans[1][2];
|
|
|
|
out[2] = in[0] * trans[2][0] + in[1] * trans[2][1] + in[2] * trans[2][2];
|
|
|
|
|
|
|
|
// convert meters to ft to degrees of latitude
|
|
|
|
out[0] = (out[0] * 3.28083989501) /(366468.96 - 3717.12 * cos(flolspos[0] * SG_DEGREES_TO_RADIANS));
|
|
|
|
|
|
|
|
// convert meters to ft to degrees of longitude
|
|
|
|
out[1] = (out[1] * 3.28083989501)/(365228.16 * cos(flolspos[1] * SG_DEGREES_TO_RADIANS));
|
|
|
|
|
|
|
|
//print out the result
|
|
|
|
/* cout << "lat adjust deg" << out[0]
|
|
|
|
<< " lon adjust deg " << out[1]
|
|
|
|
<< " alt adjust m " << out[2] << endl;*/
|
|
|
|
|
|
|
|
// adjust Flols position
|
|
|
|
flolspos[0] += out[0];
|
|
|
|
flolspos[1] += out[1];
|
|
|
|
flolspos[2] += out[2];
|
|
|
|
|
|
|
|
// convert flols position to cartesian co-ordinates
|
|
|
|
|
|
|
|
sgGeodToCart(flolspos[1] * SG_DEGREES_TO_RADIANS,
|
|
|
|
flolspos[0] * SG_DEGREES_TO_RADIANS,
|
|
|
|
flolspos[2] , flolsXYZ );
|
|
|
|
|
|
|
|
|
|
|
|
/* cout << "flols X " << flolsXYZ[0]
|
|
|
|
<< " Y " << flolsXYZ[1]
|
|
|
|
<< " Z " << flolsXYZ[2] << endl;
|
|
|
|
|
|
|
|
// check the conversion
|
|
|
|
|
|
|
|
sgCartToGeod(flolsXYZ, &lat, &lon, &alt);
|
|
|
|
|
|
|
|
cout << "flols check lon " << lon
|
|
|
|
<< " lat " << lat
|
|
|
|
<< " alt " << alt << endl; */
|
|
|
|
|
|
|
|
//get the current position of the pilot's eyepoint (cartesian cordinates)
|
|
|
|
|
|
|
|
sgdCopyVec3( eyeXYZ, globals->get_current_view()->get_absolute_view_pos() );
|
|
|
|
|
|
|
|
/* cout << "Eye_X " << eyeXYZ[0]
|
|
|
|
<< " Eye_Y " << eyeXYZ[1]
|
|
|
|
<< " Eye_Z " << eyeXYZ[2] << endl; */
|
|
|
|
|
|
|
|
sgCartToGeod(eyeXYZ, &lat, &lon, &alt);
|
|
|
|
|
|
|
|
eyepos[0] = lon * SG_RADIANS_TO_DEGREES;
|
|
|
|
eyepos[1] = lat * SG_RADIANS_TO_DEGREES;
|
|
|
|
eyepos[2] = alt;
|
|
|
|
|
|
|
|
/* cout << "eye lon " << eyepos[0]
|
|
|
|
<< " eye lat " << eyepos[1]
|
|
|
|
<< " eye alt " << eyepos[2] << endl; */
|
|
|
|
|
|
|
|
//calculate the ditance from eye to flols
|
|
|
|
|
|
|
|
dist = sgdDistanceVec3( flolsXYZ, eyeXYZ );
|
2005-02-24 15:05:56 +00:00
|
|
|
|
|
|
|
//apply an index error
|
|
|
|
|
|
|
|
dist -= 100;
|
2004-11-30 12:34:11 +00:00
|
|
|
|
|
|
|
//cout << "distance " << dist << endl;
|
|
|
|
|
|
|
|
if ( dist < 5000 ) {
|
|
|
|
// calculate height above FLOLS
|
|
|
|
double y = eyepos[2] - flolspos[2];
|
|
|
|
|
|
|
|
// calculate the angle from the flols to eye
|
|
|
|
// above the horizontal
|
2005-02-24 15:05:56 +00:00
|
|
|
// double angle;
|
|
|
|
|
2004-11-30 12:34:11 +00:00
|
|
|
if ( dist != 0 ) {
|
|
|
|
angle = asin( y / dist );
|
|
|
|
} else {
|
|
|
|
angle = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
angle *= SG_RADIANS_TO_DEGREES;
|
|
|
|
|
|
|
|
|
|
|
|
// cout << " height " << y << " angle " << angle ;
|
|
|
|
|
|
|
|
// set the value of source
|
|
|
|
|
|
|
|
if ( angle <= 4.35 && angle > 4.01 )
|
|
|
|
{ source = 1; }
|
|
|
|
else if ( angle <= 4.01 && angle > 3.670 )
|
|
|
|
{ source = 2; }
|
|
|
|
else if ( angle <= 3.670 && angle > 3.330 )
|
|
|
|
{ source = 3; }
|
|
|
|
else if ( angle <= 3.330 && angle > 2.990 )
|
|
|
|
{ source = 4; }
|
|
|
|
else if ( angle <= 2.990 && angle > 2.650 )
|
|
|
|
{ source = 5; }
|
|
|
|
else if ( angle <= 2.650 )
|
|
|
|
{ source = 6; }
|
|
|
|
else
|
|
|
|
{ source = 0; }
|
|
|
|
|
|
|
|
// cout << " source " << source << endl;
|
|
|
|
|
|
|
|
}
|
|
|
|
} // end updateflols
|
|
|
|
|
2005-08-16 09:37:23 +00:00
|
|
|
// find relative wind
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FGAICarrier::UpdateWind( double dt) {
|
|
|
|
|
|
|
|
double recip;
|
|
|
|
|
|
|
|
//calculate the reciprocal hdg
|
|
|
|
|
|
|
|
if (hdg >= 180){
|
|
|
|
recip = hdg - 180;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
recip = hdg + 180;
|
|
|
|
}
|
|
|
|
|
|
|
|
//cout <<" heading: " << hdg << "recip: " << recip << endl;
|
|
|
|
|
|
|
|
//get the surface wind speed and direction
|
|
|
|
wind_from_deg = _surface_wind_from_deg_node->getDoubleValue();
|
|
|
|
wind_speed_kts = _surface_wind_speed_node->getDoubleValue();
|
|
|
|
|
|
|
|
//calculate the surface wind speed north and east in kts
|
|
|
|
double wind_speed_from_north_kts = cos( wind_from_deg / SGD_RADIANS_TO_DEGREES )* wind_speed_kts ;
|
|
|
|
double wind_speed_from_east_kts = sin( wind_from_deg / SGD_RADIANS_TO_DEGREES )* wind_speed_kts ;
|
|
|
|
|
|
|
|
//calculate the carrier speed north and east in kts
|
|
|
|
double speed_north_kts = cos( hdg / SGD_RADIANS_TO_DEGREES )* speed ;
|
|
|
|
double speed_east_kts = sin( hdg / SGD_RADIANS_TO_DEGREES )* speed ;
|
|
|
|
|
|
|
|
//calculate the relative wind speed north and east in kts
|
|
|
|
double rel_wind_speed_from_east_kts = wind_speed_from_east_kts + speed_east_kts;
|
|
|
|
double rel_wind_speed_from_north_kts = wind_speed_from_north_kts + speed_north_kts;
|
|
|
|
|
|
|
|
//combine relative speeds north and east to get relative windspeed in kts
|
|
|
|
rel_wind_speed_kts = sqrt((rel_wind_speed_from_east_kts * rel_wind_speed_from_east_kts)
|
|
|
|
+ (rel_wind_speed_from_north_kts * rel_wind_speed_from_north_kts));
|
|
|
|
|
|
|
|
//calculate the relative wind direction
|
|
|
|
rel_wind_from_deg = atan(rel_wind_speed_from_east_kts/rel_wind_speed_from_north_kts)
|
|
|
|
* SG_RADIANS_TO_DEGREES;
|
|
|
|
|
|
|
|
// rationalise the output
|
|
|
|
if (rel_wind_speed_from_north_kts <= 0){
|
|
|
|
rel_wind_from_deg = 180 + rel_wind_from_deg;
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
if(rel_wind_speed_from_east_kts <= 0){
|
|
|
|
rel_wind_from_deg = 360 + rel_wind_from_deg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//calculate rel wind
|
|
|
|
rel_wind = rel_wind_from_deg - hdg ;
|
|
|
|
if (rel_wind > 180) rel_wind -= 360;
|
|
|
|
|
|
|
|
//switch the wave-off lights
|
|
|
|
if (InToWind()){
|
|
|
|
wave_off_lights = false;
|
|
|
|
}else{
|
|
|
|
wave_off_lights = true;
|
|
|
|
}
|
|
|
|
|
2005-10-01 09:56:53 +00:00
|
|
|
// cout << "rel wind: " << rel_wind << endl;
|
2005-08-16 09:37:23 +00:00
|
|
|
|
|
|
|
}// end update wind
|
|
|
|
|
|
|
|
void FGAICarrier::TurnToLaunch(){
|
|
|
|
|
|
|
|
//calculate tgt speed
|
|
|
|
double tgt_speed = 25 - wind_speed_kts;
|
|
|
|
if (tgt_speed < 10) tgt_speed = 10;
|
|
|
|
|
|
|
|
//turn the carrier
|
|
|
|
FGAIShip::TurnTo(wind_from_deg);
|
|
|
|
FGAIShip::AccelTo(tgt_speed);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} // end turn to launch
|
|
|
|
|
|
|
|
void FGAICarrier::TurnToBase(){
|
|
|
|
|
|
|
|
//turn the carrier
|
|
|
|
FGAIShip::TurnTo(base_course);
|
|
|
|
FGAIShip::AccelTo(base_speed);
|
|
|
|
|
|
|
|
} // end turn to base
|
|
|
|
|
|
|
|
void FGAICarrier::ReturnToBox(){
|
|
|
|
double course, distance;
|
|
|
|
|
|
|
|
//get the carrier position
|
|
|
|
carrierpos = pos;
|
|
|
|
|
|
|
|
//cout << "lat: " << carrierpos[1] << " lon: " << carrierpos[0] << endl;
|
|
|
|
|
|
|
|
//calculate the bearing and range of the initial position from the carrier
|
|
|
|
geo_inverse_wgs_84(carrierpos[2],
|
|
|
|
carrierpos[1],
|
|
|
|
carrierpos[0],
|
|
|
|
initialpos[1],
|
|
|
|
initialpos[0],
|
|
|
|
&course, &az2, &distance);
|
|
|
|
|
|
|
|
distance *= SG_METER_TO_NM;
|
|
|
|
|
2005-10-01 09:56:53 +00:00
|
|
|
//cout << "return course: " << course << " distance: " << distance << endl;
|
2005-08-16 09:37:23 +00:00
|
|
|
//turn the carrier
|
|
|
|
FGAIShip::TurnTo(course);
|
|
|
|
FGAIShip::AccelTo(base_speed);
|
|
|
|
if (distance >= 1 ){
|
|
|
|
returning = true;
|
|
|
|
}else{
|
|
|
|
returning = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // end turn to base
|
|
|
|
|
|
|
|
|
|
|
|
void FGAICarrier::UpdateTACAN(double dt){ //update the TACAN
|
|
|
|
|
|
|
|
//cout << "TACAN: " << TACAN_channel_id << endl;
|
|
|
|
|
|
|
|
double max_range_nm = 100; //nm
|
|
|
|
|
|
|
|
double dme_freq = _dme_freq_node->getDoubleValue();
|
|
|
|
|
|
|
|
//cout << "dme_freq: " << dme_freq << endl;
|
|
|
|
|
|
|
|
if (TACAN_channel_id == "017X"){
|
|
|
|
|
|
|
|
//get the aircraft position
|
|
|
|
double longitude_deg = _longitude_node->getDoubleValue();
|
|
|
|
double latitude_deg = _latitude_node->getDoubleValue();
|
|
|
|
double altitude_m = _altitude_node->getDoubleValue() * SG_FEET_TO_METER;
|
|
|
|
|
|
|
|
//get the carrier position
|
|
|
|
carrierpos = pos;
|
|
|
|
|
|
|
|
//cout << "lat: " << carrierpos[1] << " lon: " << carrierpos[0] << endl;
|
|
|
|
|
|
|
|
//calculate the bearing and range of the carrier from the aircraft
|
|
|
|
geo_inverse_wgs_84(altitude_m,
|
|
|
|
latitude_deg,
|
|
|
|
longitude_deg,
|
|
|
|
carrierpos[1],
|
|
|
|
carrierpos[0],
|
|
|
|
&bearing, &az2, &range);
|
|
|
|
|
|
|
|
range *= SG_METER_TO_NM;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double aircraft_horizon_nm = Horizon(altitude_m) * SG_METER_TO_NM;
|
|
|
|
double carrier_horizon_nm = Horizon(50) * SG_METER_TO_NM;
|
|
|
|
double horizon_nm = aircraft_horizon_nm + carrier_horizon_nm;
|
|
|
|
|
|
|
|
if (range > horizon_nm || range > max_range_nm) {
|
|
|
|
range = 0;
|
|
|
|
bearing = 0 ;
|
|
|
|
}
|
|
|
|
/*cout << "bearing: " << bearing << " range: " << range << " altitude: " << altitude_m
|
|
|
|
<< " horizon: " << horizon_nm << endl; */
|
|
|
|
} else {
|
|
|
|
range = 0;
|
|
|
|
bearing = 0 ;
|
|
|
|
} // end if
|
|
|
|
|
|
|
|
}// end update TACAN
|
|
|
|
|
|
|
|
bool FGAICarrier::OutsideBox(){ //returns true if the carrier is outside operating box
|
|
|
|
|
|
|
|
if ( max_lat == 0 && min_lat == 0 && max_long == 0 && min_long == 0) {
|
2005-10-22 19:33:32 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_BULK,"AICarrier: No Operating Box defined" );
|
2005-08-16 09:37:23 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (initialpos[1] >= 0){//northern hemisphere
|
|
|
|
if (pos[1] >= initialpos[1] + max_lat) {return true;}
|
|
|
|
else if (pos[1] <= initialpos[1] - min_lat) {return true;}
|
|
|
|
}else{ //southern hemisphere
|
|
|
|
if (pos[1] <= initialpos[1] - max_lat) {return true;}
|
|
|
|
else if (pos[1] >= initialpos[1] + min_lat) {return true;}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (initialpos[0] >=0) {//eastern hemisphere
|
|
|
|
if (pos[0] >= initialpos[0] + max_long) {return true;}
|
|
|
|
else if (pos[0] <= initialpos[0] - min_long) {return true;}
|
|
|
|
}else{ //western hemisphere
|
|
|
|
if (pos[0] <= initialpos[0] - max_long) {return true;}
|
|
|
|
else if (pos[0] >= initialpos[0] + min_long) {return true;}
|
|
|
|
}
|
|
|
|
|
2005-11-28 10:18:39 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_BULK,"AICarrier: Inside Operating Box" );
|
2005-08-16 09:37:23 +00:00
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
} // end OutsideBox
|
|
|
|
|
|
|
|
// return the distance to the horizon, given the altitude and the radius of the earth
|
|
|
|
float FGAICarrier::Horizon(float h) { return RADIUS_M * acos(RADIUS_M / (RADIUS_M + h)); }
|
|
|
|
|
|
|
|
bool FGAICarrier::InToWind(){
|
|
|
|
|
|
|
|
// test
|
|
|
|
if ( fabs(rel_wind) < 5 ) return true;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
} //end InToWind
|
2004-11-26 10:24:48 +00:00
|
|
|
int FGAICarrierHardware::unique_id = 1;
|