diff --git a/src/AIModel/AIBase.cxx b/src/AIModel/AIBase.cxx index 5cf5d01cf..3675bf695 100644 --- a/src/AIModel/AIBase.cxx +++ b/src/AIModel/AIBase.cxx @@ -56,6 +56,7 @@ FGAIBase::FGAIBase() manager( NULL ) { _type_str = "model"; + tgt_heading = tgt_altitude = tgt_speed = 0.0; tgt_roll = roll = tgt_pitch = tgt_yaw = tgt_vs = vs = pitch = 0.0; bearing = elevation = range = rdot = 0.0; x_shift = y_shift = rotation = 0.0; @@ -343,7 +344,7 @@ FGAIBase::getCartPosAt(const Point3D& off) const // Now transform to the wgs84 earth centeres system. Point3D pos2(pos.lon()* SGD_DEGREES_TO_RADIANS, pos.lat() * SGD_DEGREES_TO_RADIANS, - pos.elev() * SG_FEET_TO_METER); + pos.elev()); Point3D cartPos3D = sgGeodToCart(pos2); sgdMat4 ecTrans; sgdMakeCoordMat4(ecTrans, cartPos3D.x(), cartPos3D.y(), cartPos3D.z(), diff --git a/src/AIModel/AIBase.hxx b/src/AIModel/AIBase.hxx index ac859e1ff..5def20200 100644 --- a/src/AIModel/AIBase.hxx +++ b/src/AIModel/AIBase.hxx @@ -91,6 +91,12 @@ typedef struct { string pennant_number; // used by carrier objects string acType; // used by aircraft objects string company; // used by aircraft objects + string TACAN_channel_ID; // used by carrier objects + double max_lat; // used by carrier objects + double min_lat; // used by carrier objects + double max_long; // used by carrier objects + double min_long; // used by carrier objects + } FGAIModelEntity; diff --git a/src/AIModel/AICarrier.cxx b/src/AIModel/AICarrier.cxx index 161dbe536..474dc300e 100644 --- a/src/AIModel/AICarrier.cxx +++ b/src/AIModel/AICarrier.cxx @@ -32,18 +32,47 @@ #include "AICarrier.hxx" - #include "AIScenario.hxx" +/** Value of earth radius (meters) */ +#define RADIUS_M SG_EQUATORIAL_RADIUS_M + + FGAICarrier::FGAICarrier(FGAIManager* mgr) : FGAIShip(mgr) { _type_str = "carrier"; _otype = otCarrier; + + } FGAICarrier::~FGAICarrier() { } +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); +} + void FGAICarrier::setSolidObjects(const list& so) { solid_objects = so; } @@ -64,17 +93,28 @@ void FGAICarrier::setSign(const string& s) { sign = s; } +void FGAICarrier::setTACANChannelID(const string& id) { + TACAN_channel_id = id; +} + void FGAICarrier::setFlolsOffset(const Point3D& off) { flols_off = off; } -void FGAICarrier::getVelocityWrtEarth(sgVec3 v) { - sgCopyVec3(v, vel_wrt_earth ); +void FGAICarrier::getVelocityWrtEarth(sgdVec3 v, sgdVec3 omega, sgdVec3 pivot) { + sgdCopyVec3(v, vel_wrt_earth ); + sgdCopyVec3(omega, rot_wrt_earth ); + sgdCopyVec3(pivot, rot_pivot_wrt_earth ); } void FGAICarrier::update(double dt) { - UpdateFlols(dt); - FGAIShip::update(dt); + + // 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; // Update the velocity information stored in those nodes. double v_north = 0.51444444*speed*cos(hdg * SGD_DEGREES_TO_RADIANS); @@ -84,12 +124,105 @@ void FGAICarrier::update(double dt) { 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); - sgSetVec3( vel_wrt_earth, + 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, - cos_lon*sin_lat*v_north - sin_lon*v_east, - sin_lon*sin_lat*v_north + cos_lon*v_east, cos_lat*v_north ); + sgGeodToCart(pos.lat() * SGD_DEGREES_TO_RADIANS, + pos.lon() * SGD_DEGREES_TO_RADIANS, + pos.elev(), rot_pivot_wrt_earth); -} + // 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 bool FGAICarrier::init() { if (!FGAIShip::init()) @@ -111,36 +244,103 @@ bool FGAICarrier::init() { mark_cat(sel, catapult_objects); mark_solid(sel, solid_objects); + _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; + return true; } void FGAICarrier::bind() { FGAIShip::bind(); + props->untie("velocities/true-airspeed-kt"); + props->tie("controls/flols/source-lights", SGRawValuePointer(&source)); props->tie("controls/flols/distance-m", SGRawValuePointer(&dist)); props->tie("controls/flols/angle-degs", SGRawValuePointer(&angle)); + props->tie("controls/turn-to-launch-hdg", + SGRawValuePointer(&turn_to_launch_hdg)); + props->tie("controls/in-to-wind", + SGRawValuePointer(&turn_to_launch_hdg)); + props->tie("controls/base-course-deg", + SGRawValuePointer(&base_course)); + props->tie("controls/base-speed-kts", + SGRawValuePointer(&base_speed)); + props->tie("controls/start-pos-lat-deg", + SGRawValuePointer(&initialpos[1])); + props->tie("controls/start-pos-long-deg", + SGRawValuePointer(&initialpos[0])); + props->tie("velocities/speed-kts", + SGRawValuePointer(&speed)); + props->tie("environment/surface-wind-speed-true-kts", + SGRawValuePointer(&wind_speed_kts)); + props->tie("environment/surface-wind-from-true-degs", + SGRawValuePointer(&wind_from_deg)); + props->tie("environment/rel-wind-from-degs", + SGRawValuePointer(&rel_wind_from_deg)); + props->tie("environment/rel-wind-from-carrier-hdg-degs", + SGRawValuePointer(&rel_wind)); + props->tie("environment/rel-wind-speed-kts", + SGRawValuePointer(&rel_wind_speed_kts)); + props->tie("controls/flols/wave-off-lights", + SGRawValuePointer(&wave_off_lights)); + props->tie("instrumentation/TACAN/bearing-true-deg", + SGRawValuePointer(&bearing)); + props->tie("instrumentation/TACAN/range-nm", + SGRawValuePointer(&range)); + props->setBoolValue("controls/flols/cut-lights", false); props->setBoolValue("controls/flols/wave-off-lights", false); props->setBoolValue("controls/flols/cond-datum-lights", true); props->setBoolValue("controls/crew", false); + props->setStringValue("instrumentation/TACAN/channel-ID", TACAN_channel_id.c_str()); + props->setStringValue("sign", sign.c_str()); } void FGAICarrier::unbind() { FGAIShip::unbind(); + + props->untie("velocities/true-airspeed-kt"); + props->untie("controls/flols/source-lights"); props->untie("controls/flols/distance-m"); props->untie("controls/flols/angle-degs"); + 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"); + props->untie("instrumentation/TACAN/bearing-true-deg"); + props->untie("instrumentation/TACAN/range-nm"); + props->untie("instrumentation/TACAN/channel-ID"); } bool FGAICarrier::getParkPosition(const string& id, Point3D& geodPos, double& hdng, sgdVec3 uvw) { + + // FIXME: does not yet cover rotation speeds. list::iterator it = ppositions.begin(); while (it != ppositions.end()) { // Take either the specified one or the first one ... @@ -341,16 +541,11 @@ bool FGAICarrier::mark_cat(ssgEntity* e, const list& cat_objects, bool m return found; } -void FGAICarrier::UpdateFlols( double dt) { +void FGAICarrier::UpdateFlols(sgdMat3 trans) { - float trans[3][3]; float in[3]; float out[3]; - float cosRx, sinRx; - float cosRy, sinRy; - float cosRz, sinRz; - double flolsXYZ[3], eyeXYZ[3]; double lat, lon, alt; Point3D eyepos; @@ -386,29 +581,6 @@ void FGAICarrier::UpdateFlols( double dt) { in[1] = flols_off.y(); in[2] = flols_off.z(); -// pre-process the trig functions - - cosRx = cos(roll * SG_DEGREES_TO_RADIANS); - sinRx = sin(roll * SG_DEGREES_TO_RADIANS); - cosRy = cos(pitch * SG_DEGREES_TO_RADIANS); - sinRy = sin(pitch * SG_DEGREES_TO_RADIANS); - cosRz = cos(hdg * SG_DEGREES_TO_RADIANS); - sinRz = sin(hdg * SG_DEGREES_TO_RADIANS); - -// set up the transform matrix - - trans[0][0] = cosRy * cosRz; - trans[0][1] = -1 * cosRx * sinRz + sinRx * sinRy * cosRz ; - trans[0][2] = sinRx * sinRz + cosRx * sinRy * cosRz; - - trans[1][0] = cosRy * sinRz; - trans[1][1] = cosRx * cosRz + sinRx * sinRy * sinRz; - trans[1][2] = -1 * sinRx * cosRx + cosRx * sinRy * sinRz; - - trans[2][0] = -1 * sinRy; - trans[2][1] = sinRx * cosRy; - trans[2][2] = cosRx * cosRy; - // multiply the input and transform matrices out[0] = in[0] * trans[0][0] + in[1] * trans[0][1] + in[2] * trans[0][2]; @@ -519,4 +691,216 @@ void FGAICarrier::UpdateFlols( double dt) { } } // end updateflols +// 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; + } + + cout << "rel wind: " << rel_wind << endl; + +}// 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; + + cout << "return course: " << course << " distance: " << distance << endl; + //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) { + SG_LOG(SG_GENERAL, SG_INFO,"AICarrier: No Operating Box defined" ); + 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;} + } + + SG_LOG(SG_GENERAL, SG_INFO,"AICarrier: Inside Operating Box" ); + + 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 int FGAICarrierHardware::unique_id = 1; diff --git a/src/AIModel/AICarrier.hxx b/src/AIModel/AICarrier.hxx index fd03644a3..24802c55d 100644 --- a/src/AIModel/AICarrier.hxx +++ b/src/AIModel/AICarrier.hxx @@ -30,6 +30,10 @@ SG_USING_STD(string); SG_USING_STD(list); #include "AIShip.hxx" + +#include "AIManager.hxx" +#include "AIBase.hxx" + class FGAIManager; class FGAICarrier; @@ -81,11 +85,27 @@ public: void setParkingPositions(const list& p); void setSign(const string& ); void setFlolsOffset(const Point3D& off); + void setTACANChannelID(const string &); - void getVelocityWrtEarth(sgVec3 v); + void getVelocityWrtEarth(sgdVec3 v, sgdVec3 omega, sgdVec3 pivot); virtual void bind(); virtual void unbind(); - void UpdateFlols ( double dt ); + void UpdateFlols ( sgdMat3 trans ); + void UpdateWind ( double dt ); + void UpdateTACAN( double dt ); + void setWind_from_east( double fps ); + void setWind_from_north( double fps ); + void setMaxLat( double deg ); + void setMinLat( double deg ); + void setMaxLong( double deg ); + void setMinLong( double deg ); + void TurnToLaunch(); + void TurnToBase(); + void ReturnToBox(); + float Horizon(float h); + double TACAN_freq; + bool OutsideBox(); + bool init(); @@ -96,9 +116,15 @@ private: void update(double dt); void mark_nohot(ssgEntity*); + bool mark_wires(ssgEntity*, const list&, bool = false); bool mark_cat(ssgEntity*, const list&, bool = false); bool mark_solid(ssgEntity*, const list&, bool = false); + double wind_from_east; // fps + double wind_from_north; // fps + double rel_wind_speed_kts; + double rel_wind_from_deg; + list solid_objects; // List of solid object names list wire_objects; // List of wire object names @@ -107,7 +133,10 @@ private: string sign; // The sign of this carrier. // Velocity wrt earth. - sgVec3 vel_wrt_earth; + sgdVec3 vel_wrt_earth; + sgdVec3 rot_wrt_earth; + sgdVec3 rot_pivot_wrt_earth; + // these describe the flols Point3D flols_off; @@ -115,6 +144,36 @@ private: double dist; // the distance of the eyepoint from the flols double angle; int source; // the flols light which is visible at the moment + bool wave_off_lights; + + // these are for manoeuvring the carrier + Point3D carrierpos; + Point3D initialpos; + + double wind_speed_from_north_kts ; + double wind_speed_from_east_kts ; + double wind_speed_kts; //true wind speed + double wind_from_deg; //true wind direction + double rel_wind; + double max_lat, min_lat, max_long, min_long; + double base_course, base_speed; + + bool turn_to_launch_hdg; + bool returning; // set if the carrier is returning to an operating box + bool InToWind(); // set if the carrier is in to wind + SGPropertyNode_ptr _longitude_node; + SGPropertyNode_ptr _latitude_node; + SGPropertyNode_ptr _altitude_node; + SGPropertyNode_ptr _surface_wind_from_deg_node; + SGPropertyNode_ptr _surface_wind_speed_node; + + // these are for TACAN + SGPropertyNode_ptr _dme_freq_node; + + double bearing, az2, range; + string TACAN_channel_id; + + }; #endif // _FG_AICARRIER_HXX diff --git a/src/AIModel/AIManager.cxx b/src/AIModel/AIManager.cxx index e0160cd59..be872c4a1 100644 --- a/src/AIModel/AIManager.cxx +++ b/src/AIModel/AIManager.cxx @@ -75,6 +75,9 @@ void FGAIManager::init() { return; wind_from_down_node = fgGetNode("/environment/wind-from-down-fps", true); + wind_from_east_node = fgGetNode("/environment/wind-from-east-fps",true); + wind_from_north_node = fgGetNode("/environment/wind-from-north-fps",true); + user_latitude_node = fgGetNode("/position/latitude-deg", true); user_longitude_node = fgGetNode("/position/longitude-deg", true); user_altitude_node = fgGetNode("/position/altitude-ft", true); @@ -209,7 +212,7 @@ FGAIManager::createShip( FGAIModelEntity *entity ) { ai_ship->setAltitude(entity->altitude); ai_ship->setLongitude(entity->longitude); ai_ship->setLatitude(entity->latitude); - ai_ship->setBank(entity->rudder); + ai_ship->setRudder(entity->rudder); ai_ship->setName(entity->name); if ( entity->fp ) { @@ -236,7 +239,7 @@ FGAIManager::createCarrier( FGAIModelEntity *entity ) { ai_carrier->setAltitude(entity->altitude); ai_carrier->setLongitude(entity->longitude); ai_carrier->setLatitude(entity->latitude); - ai_carrier->setBank(entity->rudder); + ai_carrier->setRudder(entity->rudder); ai_carrier->setSolidObjects(entity->solid_objects); ai_carrier->setWireObjects(entity->wire_objects); ai_carrier->setCatapultObjects(entity->catapult_objects); @@ -245,6 +248,14 @@ FGAIManager::createCarrier( FGAIModelEntity *entity ) { ai_carrier->setSign(entity->pennant_number); ai_carrier->setName(entity->name); ai_carrier->setFlolsOffset(entity->flols_offset); + ai_carrier->setWind_from_east(entity->wind_from_east); + ai_carrier->setWind_from_north(entity->wind_from_north); + ai_carrier->setTACANChannelID(entity->TACAN_channel_ID); + ai_carrier->setMaxLat(entity->max_lat); + ai_carrier->setMinLat(entity->min_lat); + ai_carrier->setMaxLong(entity->max_long); + ai_carrier->setMinLong(entity->min_long); + if ( entity->fp ) { ai_carrier->setFlightPlan(entity->fp); @@ -366,6 +377,10 @@ void FGAIManager::fetchUserState( void ) { user_pitch = user_pitch_node->getDoubleValue(); user_yaw = user_yaw_node->getDoubleValue(); user_speed = user_speed_node->getDoubleValue() * 0.592484; + wind_from_east = wind_from_east_node->getDoubleValue(); + wind_from_north = wind_from_north_node->getDoubleValue(); + + } @@ -458,6 +473,9 @@ bool FGAIManager::getStartPosition(const string& id, const string& pid, ai_carrier->setLatitude(en->latitude); ai_carrier->setBank(en->rudder); ai_carrier->setParkingPositions(en->ppositions); + ai_carrier->setWind_from_east(en->wind_from_east); + ai_carrier->setWind_from_north(en->wind_from_north); + //ai_carrier->setTACANFreq(en->TACAN_freq); if (ai_carrier->getParkPosition(pid, geodPos, heading, uvw)) { delete ai_carrier; diff --git a/src/AIModel/AIManager.hxx b/src/AIModel/AIManager.hxx index 8031edee9..2ecdbef1c 100644 --- a/src/AIModel/AIManager.hxx +++ b/src/AIModel/AIManager.hxx @@ -99,6 +99,8 @@ public: inline double get_user_pitch() { return user_pitch; } inline double get_user_yaw() { return user_yaw; } inline double get_user_speed() {return user_speed; } + inline double get_wind_from_east() {return wind_from_east; } + inline double get_wind_from_north() {return wind_from_north; } inline int getNum( FGAIBase::object_type ot ) { return (0 < ot && ot < FGAIBase::MAX_OBJECTS) ? numObjects[ot] : numObjects[0]; @@ -126,6 +128,8 @@ private: SGPropertyNode* user_pitch_node; SGPropertyNode* user_yaw_node; SGPropertyNode* user_speed_node; + SGPropertyNode* wind_from_east_node ; + SGPropertyNode* wind_from_north_node ; string scenario_filename; @@ -136,6 +140,8 @@ private: double user_pitch; double user_yaw; double user_speed; + double wind_from_east; + double wind_from_north; double _dt; int dt_count; void fetchUserState( void ); diff --git a/src/AIModel/AIScenario.cxx b/src/AIModel/AIScenario.cxx index 8f4e22b8c..a1a77b529 100644 --- a/src/AIModel/AIScenario.cxx +++ b/src/AIModel/AIScenario.cxx @@ -47,6 +47,7 @@ FGAIScenario::FGAIScenario(const string &filename) path.append( ("/Data/AI/" + filename + ".xml").c_str() ); SGPropertyNode root; + readProperties(path.str(), &root); // cout <<"path " << path.str() << endl; @@ -84,7 +85,7 @@ FGAIScenario::FGAIScenario(const string &filename) en->roll = entry_node->getDoubleValue("roll", 0.0); en->azimuth = entry_node->getDoubleValue("azimuth", 0.0); en->elevation = entry_node->getDoubleValue("elevation", 0.0); - en->rudder = entry_node->getDoubleValue("rudder", 0.0); + en->rudder = entry_node->getFloatValue("rudder", 0.0); en->strength = entry_node->getDoubleValue("strength-fps", 8.0); en->strength = entry_node->getDoubleValue("strength-norm", 1.0); en->diameter = entry_node->getDoubleValue("diameter-ft", 0.0); @@ -98,12 +99,17 @@ FGAIScenario::FGAIScenario(const string &filename) en->cd = entry_node->getDoubleValue("cd", 0.029); en->mass = entry_node->getDoubleValue("mass", 0.007); en->radius = entry_node->getDoubleValue("turn-radius-ft", 2000); + en->TACAN_channel_ID= entry_node->getStringValue("TACAN-channel-ID", "017X"); en->name = entry_node->getStringValue("name", ""); en->pennant_number = entry_node->getStringValue("pennant-number", ""); en->wire_objects = getAllStringNodeVals("wire", entry_node); en->catapult_objects = getAllStringNodeVals("catapult", entry_node); en->solid_objects = getAllStringNodeVals("solid", entry_node); en->ppositions = getAllOffsetNodeVals("parking-pos", entry_node); + en->max_lat = entry_node->getDoubleValue("max-lat", 0); + en->min_lat = entry_node->getDoubleValue("min-lat",0); + en->max_long = entry_node->getDoubleValue("max-long", 0); + en->min_long = entry_node->getDoubleValue("min-long", 0); list flolspos = getAllOffsetNodeVals("flols-pos", entry_node); en->flols_offset = flolspos.front().offset; diff --git a/src/AIModel/AIShip.cxx b/src/AIModel/AIShip.cxx index 2d8fc31a8..eecbd0e8f 100644 --- a/src/AIModel/AIShip.cxx +++ b/src/AIModel/AIShip.cxx @@ -32,8 +32,6 @@ FGAIShip::FGAIShip(FGAIManager* mgr) { _type_str = "ship"; _otype = otShip; - hdg_lock = false; - rudder = 0.0; } FGAIShip::~FGAIShip() { @@ -41,6 +39,16 @@ FGAIShip::~FGAIShip() { bool FGAIShip::init() { + + hdg_lock = false; + rudder = 0.0; + no_roll = false; + + rudder_constant = 0.5; + roll_constant = 0.001; + speed_constant = 0.05; + hdg_constant = 0.01; + return FGAIBase::init(); } @@ -48,7 +56,21 @@ void FGAIShip::bind() { FGAIBase::bind(); props->tie("surface-positions/rudder-pos-deg", - SGRawValuePointer(&rudder)); + SGRawValuePointer(&rudder)); + props->tie("controls/heading-lock", + SGRawValuePointer(&hdg_lock)); + props->tie("controls/tgt-speed-kts", + SGRawValuePointer(&tgt_speed)); + props->tie("controls/tgt-heading-degs", + SGRawValuePointer(&tgt_heading)); + props->tie("controls/constants/rudder", + SGRawValuePointer(&rudder_constant)); + props->tie("controls/constants/roll", + SGRawValuePointer(&roll_constant)); + props->tie("controls/constants/rudder", + SGRawValuePointer(&rudder_constant)); + props->tie("controls/constants/speed", + SGRawValuePointer(&speed_constant)); props->setStringValue("name", name.c_str()); } @@ -56,6 +78,13 @@ void FGAIShip::bind() { void FGAIShip::unbind() { FGAIBase::unbind(); props->untie("surface-positions/rudder-pos-deg"); + props->untie("controls/heading-lock"); + props->untie("controls/tgt-speed-kts"); + props->untie("controls/tgt-heading-degs"); + props->untie("controls/constants/roll"); + props->untie("controls/constants/rudder"); + props->untie("controls/constants/speed"); + } void FGAIShip::update(double dt) { @@ -77,12 +106,14 @@ void FGAIShip::Run(double dt) { double speed_east_deg_sec; double dist_covered_ft; double alpha; + double rudder_limit; + double raw_roll; // adjust speed double speed_diff = tgt_speed - speed; if (fabs(speed_diff) > 0.1) { - if (speed_diff > 0.0) speed += 0.1 * dt; - if (speed_diff < 0.0) speed -= 0.1 * dt; + if (speed_diff > 0.0) speed += speed_constant * dt; + if (speed_diff < 0.0) speed -= speed_constant * dt; } // convert speed to degrees per second @@ -97,7 +128,7 @@ void FGAIShip::Run(double dt) { // adjust heading based on current rudder angle - if (rudder != 0.0) { + if (rudder <= -0.25 or rudder >= 0.25) { /* turn_radius_ft = 0.088362 * speed * speed / tan( fabs(rudder) / SG_RADIANS_TO_DEGREES ); turn_circum_ft = SGD_2PI * turn_radius_ft; @@ -105,32 +136,46 @@ void FGAIShip::Run(double dt) { alpha = dist_covered_ft / turn_circum_ft * 360.0;*/ if (turn_radius_ft <= 0) turn_radius_ft = 0; // don't allow nonsense values - -// cout << "speed " << speed << " turn radius " << turn_radius_ft << endl; + if (rudder > 45) rudder = 45; + if (rudder < -45) rudder = -45; // adjust turn radius for speed. The equation is very approximate. sp_turn_radius_ft = 10 * pow ((speed - 15),2) + turn_radius_ft; -// cout << "speed " << speed << " speed turn radius " << sp_turn_radius_ft << endl; +// cout << " speed turn radius " << sp_turn_radius_ft ; // adjust turn radius for rudder angle. The equation is even more approximate. - rd_turn_radius_ft = -130 * (rudder - 15) + sp_turn_radius_ft; -// cout << "rudder " << rudder << " rudder turn radius " << rd_turn_radius_ft << endl; + float a = 19; + float b = -0.2485; + float c = 0.543; + + rd_turn_radius_ft = (a * exp(b * fabs(rudder)) + c) * sp_turn_radius_ft; + +// cout <<" rudder turn radius " << rd_turn_radius_ft << endl; // calculate the angle, alpha, subtended by the arc traversed in time dt alpha = ((speed * 1.686 * dt)/rd_turn_radius_ft) * SG_RADIANS_TO_DEGREES; -// make sure that alpha is applied in the right direction +// make sure that alpha is applied in the right direction hdg += alpha * sign( rudder ); - if ( hdg > 360.0 ) hdg -= 360.0; if ( hdg < 0.0) hdg += 360.0; -//adjust roll for rudder angle and speed - roll = - ( speed / 2 - rudder / 6 ); - -// cout << " hdg " << hdg << "roll "<< roll << endl; +//adjust roll for rudder angle and speed. Another bit of voodoo + raw_roll = -0.0166667 * speed * rudder; } + else + { +// rudder angle is 0 + raw_roll = 0; +// cout << " roll "<< roll << endl; + } + + //low pass filter + roll = (raw_roll * roll_constant) + (roll * (1 - roll_constant)); + + cout << " rudder: " << rudder << " raw roll: "<< raw_roll<<" roll: " << roll ; + cout << " hdg: " << hdg << endl ; // adjust target rudder angle if heading lock engaged if (hdg_lock) { @@ -144,24 +189,43 @@ void FGAIShip::Run(double dt) { } else { rudder_sense = -1.0; } - if (diff < 30) tgt_roll = diff * rudder_sense; + if (diff < 15){ + tgt_rudder = diff * rudder_sense; + } + else + { + tgt_rudder = 45 * rudder_sense; + } } // adjust rudder angle - double rudder_diff = tgt_roll - rudder; - if (fabs(rudder_diff) > 0.1) { - if (rudder_diff > 0.0) rudder += 5.0 * dt; - if (rudder_diff < 0.0) rudder -= 5.0 * dt; + double rudder_diff = tgt_rudder - rudder; + // set the rudder limit by speed + if (speed <= 40 ){ + rudder_limit = (-0.825 * speed) + 35; + }else{ + rudder_limit = 2; } + if (fabs(rudder_diff) > 0.1) { + if (rudder_diff > 0.0){ + rudder += rudder_constant * dt; + if (rudder > rudder_limit) rudder = rudder_limit;// apply the rudder limit + } else if (rudder_diff < 0.0){ + rudder -= rudder_constant * dt; + if (rudder < -rudder_limit) rudder = -rudder_limit; + } } + +}//end function + + void FGAIShip::AccelTo(double speed) { tgt_speed = speed; } - void FGAIShip::PitchTo(double angle) { tgt_pitch = angle; } @@ -204,3 +268,10 @@ void FGAIShip::ProcessFlightPlan(double dt) { // not implemented yet } +void FGAIShip::setRudder(float r) { + rudder = r; +} + +void FGAIShip::setRoll(double rl) { + roll = rl; +} diff --git a/src/AIModel/AIShip.hxx b/src/AIModel/AIShip.hxx index 97412a922..69e446df0 100644 --- a/src/AIModel/AIShip.hxx +++ b/src/AIModel/AIShip.hxx @@ -37,6 +37,9 @@ public: void update(double dt); void setFlightPlan(FGAIFlightPlan* f); void setName(const string&); + void setRudder(float r); + void setRoll(double rl); + void ProcessFlightPlan( double dt ); void AccelTo(double speed); @@ -45,6 +48,7 @@ public: void YawTo(double angle); void ClimbTo(double altitude); void TurnTo(double heading); + bool hdg_lock; protected: @@ -52,8 +56,8 @@ protected: private: - bool hdg_lock; - double rudder; + float rudder, tgt_rudder; + double rudder_constant, roll_constant, speed_constant, hdg_constant; void Run(double dt); double sign(double x); diff --git a/src/FDM/groundcache.cxx b/src/FDM/groundcache.cxx index 871bb56ea..b241a0f1c 100644 --- a/src/FDM/groundcache.cxx +++ b/src/FDM/groundcache.cxx @@ -226,13 +226,15 @@ FGGroundCache::extractGroundProperty( ssgLeaf* l ) } // Copy the velocity from the carrier class. - ud->carrier->getVelocityWrtEarth( gp.vel ); + ud->carrier->getVelocityWrtEarth( gp.vel, gp.rot, gp.pivot ); } else { // Initialize velocity field. - sgSetVec3( gp.vel, 0.0, 0.0, 0.0 ); + sgdSetVec3( gp.vel, 0.0, 0.0, 0.0 ); + sgdSetVec3( gp.rot, 0.0, 0.0, 0.0 ); + sgdSetVec3( gp.pivot, 0.0, 0.0, 0.0 ); } // Get the texture name and decide what ground type we have. @@ -285,7 +287,9 @@ FGGroundCache::putLineLeafIntoCache(const sgdSphere *wsp, const sgdMat4 xform, Wire wire; sgdCopyVec3(wire.ends[0], ends[0]); sgdCopyVec3(wire.ends[1], ends[1]); - sgdSetVec3(wire.velocity, gp.vel); + sgdCopyVec3(wire.velocity, gp.vel); + sgdCopyVec3(wire.rotation, gp.rot); + sgdSubVec3(wire.rotation_pivot, gp.pivot, cache_center); wire.wire_id = gp.wire_id; wires.push_back(wire); @@ -294,7 +298,9 @@ FGGroundCache::putLineLeafIntoCache(const sgdSphere *wsp, const sgdMat4 xform, Catapult cat; sgdCopyVec3(cat.start, ends[0]); sgdCopyVec3(cat.end, ends[1]); - sgdSetVec3(cat.velocity, gp.vel); + sgdCopyVec3(cat.velocity, gp.vel); + sgdCopyVec3(cat.rotation, gp.rot); + sgdSubVec3(cat.rotation_pivot, gp.pivot, cache_center); catapults.push_back(cat); } @@ -334,7 +340,9 @@ FGGroundCache::putSurfaceLeafIntoCache(const sgdSphere *sp, // Check if the sphere around the vehicle intersects the sphere // around that triangle. If so, put that triangle into the cache. if (sphIsec && sp->intersects(&t.sphere)) { - sgdSetVec3(t.velocity, gp.vel); + sgdCopyVec3(t.velocity, gp.vel); + sgdCopyVec3(t.rotation, gp.rot); + sgdSubVec3(t.rotation_pivot, gp.pivot, cache_center); t.type = gp.type; triangles.push_back(t); } @@ -373,15 +381,25 @@ FGGroundCache::velocityTransformTriangle(double dt, dst.sphere.radius = src.sphere.radius; sgdCopyVec3(dst.velocity, src.velocity); + sgdCopyVec3(dst.rotation, src.rotation); + sgdCopyVec3(dst.rotation_pivot, src.rotation_pivot); dst.type = src.type; if (dt*sgdLengthSquaredVec3(src.velocity) != 0) { - sgdAddScaledVec3(dst.vertices[0], src.velocity, dt); - sgdAddScaledVec3(dst.vertices[1], src.velocity, dt); - sgdAddScaledVec3(dst.vertices[2], src.velocity, dt); + sgdVec3 pivotoff, vel; + for (int i = 0; i < 3; ++i) { + sgdSubVec3(pivotoff, src.vertices[i], src.rotation_pivot); + sgdVectorProductVec3(vel, src.rotation, pivotoff); + sgdAddVec3(vel, src.velocity); + sgdAddScaledVec3(dst.vertices[i], vel, dt); + } - dst.plane[3] += dt*sgdScalarProductVec3(dst.plane, src.velocity); + // Transform the plane equation + sgdSubVec3(pivotoff, dst.plane, src.rotation_pivot); + sgdVectorProductVec3(vel, src.rotation, pivotoff); + sgdAddVec3(vel, src.velocity); + dst.plane[3] += dt*sgdScalarProductVec3(dst.plane, vel); sgdAddScaledVec3(dst.sphere.center, src.velocity, dt); } @@ -554,15 +572,23 @@ FGGroundCache::get_cat(double t, const double dpt[3], size_t sz = catapults.size(); for (size_t i = 0; i < sz; ++i) { + sgdVec3 pivotoff, rvel[2]; sgdLineSegment3 ls; sgdCopyVec3(ls.a, catapults[i].start); sgdCopyVec3(ls.b, catapults[i].end); + sgdSubVec3(pivotoff, ls.a, catapults[i].rotation_pivot); + sgdVectorProductVec3(rvel[0], catapults[i].rotation, pivotoff); + sgdAddVec3(rvel[0], catapults[i].velocity); + sgdSubVec3(pivotoff, ls.b, catapults[i].rotation_pivot); + sgdVectorProductVec3(rvel[1], catapults[i].rotation, pivotoff); + sgdAddVec3(rvel[1], catapults[i].velocity); + sgdAddVec3(ls.a, cache_center); sgdAddVec3(ls.b, cache_center); - sgdAddScaledVec3(ls.a, catapults[i].velocity, t); - sgdAddScaledVec3(ls.b, catapults[i].velocity, t); + sgdAddScaledVec3(ls.a, rvel[0], t); + sgdAddScaledVec3(ls.b, rvel[1], t); double this_dist = sgdDistSquaredToLineSegmentVec3( ls, dpt ); if (this_dist < dist) { @@ -573,8 +599,8 @@ FGGroundCache::get_cat(double t, const double dpt[3], // The carrier code takes care of that ordering. sgdCopyVec3( end[0], ls.a ); sgdCopyVec3( end[1], ls.b ); - sgdCopyVec3( vel[0], catapults[i].velocity ); - sgdCopyVec3( vel[1], catapults[i].velocity ); + sgdCopyVec3( vel[0], rvel[0] ); + sgdCopyVec3( vel[1], rvel[1] ); } } @@ -641,8 +667,10 @@ FGGroundCache::get_agl(double t, const double dpt[3], double max_altoff, // The first three values in the vector are the plane normal. sgdCopyVec3( normal, triangle.plane ); // The velocity wrt earth. - /// FIXME: only true for non rotating objects!!!! - sgdCopyVec3( vel, triangle.velocity ); + sgdVec3 pivotoff; + sgdSubVec3(pivotoff, pt, triangle.rotation_pivot); + sgdVectorProductVec3(vel, triangle.rotation, pivotoff); + sgdAddVec3(vel, triangle.velocity); // Save the ground type. *type = triangle.type; // FIXME: figure out how to get that sign ... @@ -704,14 +732,15 @@ bool FGGroundCache::caught_wire(double t, const double pt[4][3]) // You have cautght a wire if they intersect. for (size_t i = 0; i < sz; ++i) { sgdVec3 le[2]; - sgdCopyVec3(le[0], wires[i].ends[0]); - sgdCopyVec3(le[1], wires[i].ends[1]); - - sgdAddVec3(le[0], cache_center); - sgdAddVec3(le[1], cache_center); - - sgdAddScaledVec3(le[0], wires[i].velocity, t); - sgdAddScaledVec3(le[1], wires[i].velocity, t); + for (int k = 0; k < 2; ++k) { + sgdVec3 pivotoff, vel; + sgdCopyVec3(le[k], wires[i].ends[k]); + sgdSubVec3(pivotoff, le[k], wires[i].rotation_pivot); + sgdVectorProductVec3(vel, wires[i].rotation, pivotoff); + sgdAddVec3(vel, wires[i].velocity); + sgdAddScaledVec3(le[k], vel, t); + sgdAddVec3(le[k], cache_center); + } for (int k=0; k<2; ++k) { sgdVec3 isecpoint; @@ -742,17 +771,15 @@ bool FGGroundCache::get_wire_ends(double t, double end[2][3], double vel[2][3]) size_t sz = wires.size(); for (size_t i = 0; i < sz; ++i) { if (wires[i].wire_id == wire_id) { - sgdCopyVec3(end[0], wires[i].ends[0]); - sgdCopyVec3(end[1], wires[i].ends[1]); - - sgdAddVec3(end[0], cache_center); - sgdAddVec3(end[1], cache_center); - - sgdAddScaledVec3(end[0], wires[i].velocity, t); - sgdAddScaledVec3(end[1], wires[i].velocity, t); - - sgdCopyVec3(vel[0], wires[i].velocity); - sgdCopyVec3(vel[1], wires[i].velocity); + for (size_t k = 0; k < 2; ++k) { + sgdVec3 pivotoff; + sgdCopyVec3(end[k], wires[i].ends[k]); + sgdSubVec3(pivotoff, end[k], wires[i].rotation_pivot); + sgdVectorProductVec3(vel[k], wires[i].rotation, pivotoff); + sgdAddVec3(vel[k], wires[i].velocity); + sgdAddScaledVec3(end[k], vel[k], t); + sgdAddVec3(end[k], cache_center); + } return true; } } diff --git a/src/FDM/groundcache.hxx b/src/FDM/groundcache.hxx index e881463bb..f9311c91f 100644 --- a/src/FDM/groundcache.hxx +++ b/src/FDM/groundcache.hxx @@ -88,8 +88,10 @@ private: sgdVec4 plane; // The bounding shpere. sgdSphere sphere; - // The linear velocity. + // The linear and angular velocity. sgdVec3 velocity; + sgdVec3 rotation; + sgdVec3 rotation_pivot; // Ground type int type; }; @@ -97,10 +99,14 @@ private: sgdVec3 start; sgdVec3 end; sgdVec3 velocity; + sgdVec3 rotation; + sgdVec3 rotation_pivot; }; struct Wire { sgdVec3 ends[2]; sgdVec3 velocity; + sgdVec3 rotation; + sgdVec3 rotation_pivot; int wire_id; }; @@ -144,7 +150,9 @@ private: GroundProperty() : type(0) {} int type; int wire_id; - sgVec3 vel; + sgdVec3 vel; + sgdVec3 rot; + sgdVec3 pivot; // not yet implemented ... // double loadCapacity; };