+void FGAIBallistic::setTime(double s) {
+ _life_timer = s;
+}
+
+void FGAIBallistic::handleEndOfLife(double elevation) {
+ report_impact(elevation);
+
+ // Make the submodel invisible if the submodel is immortal, otherwise kill it if it has no subsubmodels
+ if (life == -1) {
+ invisible = true;
+ }
+ else if (_subID == 0) {
+ // Kill the AIObject if there is no subsubmodel
+ setDie(true);
+ }
+}
+
+void FGAIBallistic::handle_impact() {
+ // Try terrain intersection
+ double start = pos.getElevationM() + 100;
+
+ if (!getHtAGL(start))
+ return;
+
+ if (_ht_agl_ft <= 0) {
+ SG_LOG(SG_AI, SG_DEBUG, "AIBallistic: terrain impact material" << _mat_name);
+ _impact_reported = true;
+ handleEndOfLife(_elevation_m);
+ }
+}
+
+void FGAIBallistic::handle_expiry() {
+ _expiry_reported = true;
+ handleEndOfLife(pos.getElevationM());
+}
+
+void FGAIBallistic::handle_collision()
+{
+ const FGAIBase *object = manager->calcCollision(pos.getElevationFt(),
+ pos.getLatitudeDeg(),pos.getLongitudeDeg(), _fuse_range);
+
+ if (object) {
+ report_impact(pos.getElevationM(), object);
+ _collision_reported = true;
+ }
+}
+
+void FGAIBallistic::report_impact(double elevation, const FGAIBase *object)
+{
+ _impact_lat = pos.getLatitudeDeg();
+ _impact_lon = pos.getLongitudeDeg();
+ _impact_elev = elevation;
+ _impact_speed = speed * SG_KT_TO_MPS;
+ _impact_hdg = hdg;
+ _impact_pitch = pitch;
+ _impact_roll = roll;
+
+ SGPropertyNode *n = props->getNode("impact", true);
+
+ if (object)
+ n->setStringValue("type", object->getTypeString());
+ else
+ n->setStringValue("type", "terrain");
+
+ SG_LOG(SG_AI, SG_DEBUG, "AIBallistic: object impact " << _name
+ << " lon " <<_impact_lon << " lat " <<_impact_lat << " sec " << _life_timer);
+
+ n->setDoubleValue("longitude-deg", _impact_lon);
+ n->setDoubleValue("latitude-deg", _impact_lat);
+ n->setDoubleValue("elevation-m", _impact_elev);
+ n->setDoubleValue("heading-deg", _impact_hdg);
+ n->setDoubleValue("pitch-deg", _impact_pitch);
+ n->setDoubleValue("roll-deg", _impact_roll);
+ n->setDoubleValue("speed-mps", _impact_speed);
+
+ _impact_report_node->setStringValue(props->getPath());
+}
+
+SGVec3d FGAIBallistic::getCartHitchPos() const {
+ // convert geodetic positions to geocentered
+ SGVec3d cartuserPos = globals->get_aircraft_position_cart();
+
+ //SGVec3d cartPos = getCartPos();
+
+ // Transform to the right coordinate frame, configuration is done in
+ // the x-forward, y-right, z-up coordinates (feet), computation
+ // in the simulation usual body x-forward, y-right, z-down coordinates
+ // (meters) )
+ SGVec3d _off(_x_offset * SG_FEET_TO_METER,
+ _y_offset * SG_FEET_TO_METER,
+ -_z_offset * SG_FEET_TO_METER);
+
+ // Transform the user position to the horizontal local coordinate system.
+ SGQuatd hlTrans = SGQuatd::fromLonLat(globals->get_aircraft_position());
+
+ // and postrotate the orientation of the user model wrt the horizontal
+ // local frame
+ hlTrans *= SGQuatd::fromYawPitchRollDeg(
+ manager->get_user_heading(),
+ manager->get_user_pitch(),
+ manager->get_user_roll());
+
+ // The offset converted to the usual body fixed coordinate system
+ // rotated to the earth-fixed coordinates axis
+ SGVec3d off = hlTrans.backTransform(_off);
+
+ // Add the position offset of the user model to get the geocentered position
+ SGVec3d offsetPos = cartuserPos + off;
+ return offsetPos;
+}
+
+void FGAIBallistic::setOffsetPos(SGGeod inpos, double heading, double pitch, double roll) {
+ // Convert the hitch geocentered position to geodetic
+ SGVec3d cartoffsetPos = getCartOffsetPos(inpos, heading, pitch, roll);
+ SGGeodesy::SGCartToGeod(cartoffsetPos, _offsetpos);
+}
+
+double FGAIBallistic::getDistanceToHitch() const {
+ //calculate the distance load to hitch
+ SGVec3d carthitchPos = getCartHitchPos();
+ SGVec3d cartPos = getCartPos();
+
+ SGVec3d diff = carthitchPos - cartPos;
+ double distance = norm(diff);
+ return distance * SG_METER_TO_FEET;
+}
+
+double FGAIBallistic::getElevToHitch() const {
+ // now the angle, positive angles are upwards
+ double distance = getDistanceToHitch() * SG_FEET_TO_METER;
+ double angle = 0;
+ double daltM = _offsetpos.getElevationM() - pos.getElevationM();
+
+ if (fabs(distance) < SGLimits<float>::min()) {
+ angle = 0;
+ } else {
+ double sAngle = daltM/distance;
+ sAngle = SGMiscd::min(1, SGMiscd::max(-1, sAngle));
+ angle = SGMiscd::rad2deg(asin(sAngle));
+ }
+
+ return angle;
+}
+
+double FGAIBallistic::getBearingToHitch() const {
+ //calculate the bearing and range of the second pos from the first
+ double distance = getDistanceToHitch() * SG_FEET_TO_METER;
+ double az1, az2;
+
+ geo_inverse_wgs_84(pos, _offsetpos, &az1, &az2, &distance);
+
+ return az1;
+}
+
+double FGAIBallistic::getRelBrgHitchToUser() const {
+ //calculate the relative bearing
+ double az1, az2, distance;
+
+ geo_inverse_wgs_84(_offsetpos, globals->get_aircraft_position(), &az1, &az2, &distance);
+
+ double rel_brg = az1 - hdg;
+
+ SG_NORMALIZE_RANGE(rel_brg, -180.0, 180.0);
+
+ return rel_brg;
+}
+
+double FGAIBallistic::getElevHitchToUser() const {
+ // Calculate the distance from the user position
+ SGVec3d carthitchPos = getCartHitchPos();
+ SGVec3d cartuserPos = globals->get_aircraft_position_cart();
+
+ SGVec3d diff = cartuserPos - carthitchPos;
+
+ double distance = norm(diff);
+ double angle = 0;
+
+ double daltM = globals->get_aircraft_position().getElevationM() - _offsetpos.getElevationM();
+
+ // Now the angle, positive angles are upwards
+ if (fabs(distance) < SGLimits<float>::min()) {
+ angle = 0;
+ }
+ else {
+ double sAngle = daltM/distance;
+ sAngle = SGMiscd::min(1, SGMiscd::max(-1, sAngle));
+ angle = SGMiscd::rad2deg(asin(sAngle));
+ }
+
+ return angle;
+}
+
+void FGAIBallistic::setTgtOffsets(double dt, double coeff) {
+ double c = dt / (coeff + dt);
+
+ _x_offset = (_tgt_x_offset * c) + (_x_offset * (1 - c));
+ _y_offset = (_tgt_y_offset * c) + (_y_offset * (1 - c));
+ _z_offset = (_tgt_z_offset * c) + (_z_offset * (1 - c));
+}
+
+void FGAIBallistic::calcVSHS() {
+ // Calculate vertical and horizontal speed components
+ double speed_fps = speed * SG_KT_TO_FPS;
+
+ if (speed == 0.0) {
+ hs = vs = 0.0;
+ }
+ else {
+ vs = sin( _elevation * SG_DEGREES_TO_RADIANS ) * speed_fps;
+ hs = cos( _elevation * SG_DEGREES_TO_RADIANS ) * speed_fps;
+ }
+}
+
+void FGAIBallistic::calcNE() {
+ // Resolve horizontal speed into north and east components:
+ _speed_north_fps = cos(_azimuth / SG_RADIANS_TO_DEGREES) * hs;
+ _speed_east_fps = sin(_azimuth / SG_RADIANS_TO_DEGREES) * hs;
+
+ // Convert horizontal speed (fps) to degrees per second
+ speed_north_deg_sec = _speed_north_fps / ft_per_deg_lat;
+ speed_east_deg_sec = _speed_east_fps / ft_per_deg_lon;
+}
+
+SGVec3d FGAIBallistic::getCartOffsetPos(SGGeod inpos, double user_heading,
+ double user_pitch, double user_roll
+ ) const {
+ // Convert geodetic positions to geocentered
+ SGVec3d cartuserPos = SGVec3d::fromGeod(inpos);
+
+ // Transform to the right coordinate frame, configuration is done in
+ // the x-forward, y-right, z-up coordinates (feet), computation
+ // in the simulation usual body x-forward, y-right, z-down coordinates
+ // (meters) )
+ SGVec3d _off(_x_offset * SG_FEET_TO_METER,
+ _y_offset * SG_FEET_TO_METER,
+ -_z_offset * SG_FEET_TO_METER);
+
+ // Transform the user position to the horizontal local coordinate system.
+ SGQuatd hlTrans = SGQuatd::fromLonLat(inpos);
+
+ // And postrotate the orientation of the user model wrt the horizontal
+ // local frame
+ hlTrans *= SGQuatd::fromYawPitchRollDeg(
+ user_heading,
+ user_pitch,
+ user_roll);
+
+ // The offset converted to the usual body fixed coordinate system
+ // rotated to the earth-fixed coordinates axis
+ SGVec3d off = hlTrans.backTransform(_off);
+
+ // Add the position offset of the user model to get the geocentered position
+ SGVec3d offsetPos = cartuserPos + off;
+
+ return offsetPos;
+}
+
+void FGAIBallistic::setOffsetVelocity(double dt, SGGeod offsetpos) {
+ // Calculate the distance from the previous offset position
+ SGVec3d cartoffsetPos = SGVec3d::fromGeod(offsetpos);
+ SGVec3d diff = cartoffsetPos - _oldcartoffsetPos;
+
+ double distance = norm(diff);
+ // Calculate speed knots
+ speed = (distance / dt) * SG_MPS_TO_KT;
+
+ // Now calulate the angle between the old and current postion positions (degrees)
+ double angle = 0;
+ double daltM = offsetpos.getElevationM() - _oldoffsetpos.getElevationM();
+
+ if (fabs(distance) < SGLimits<float>::min()) {
+ angle = 0;
+ }
+ else {
+ double sAngle = daltM / distance;
+ sAngle = SGMiscd::min(1, SGMiscd::max(-1, sAngle));
+ angle = SGMiscd::rad2deg(asin(sAngle));
+ }
+
+ _elevation = angle;
+
+ // Calculate vertical and horizontal speed components
+ calcVSHS();
+
+ // Calculate the bearing of the new offset position from the old
+ // Don't do this if speed is low
+ //cout << "speed " << speed << endl;
+ if (speed > 0.1) {
+ double az1, az2, dist;
+ geo_inverse_wgs_84(_oldoffsetpos, offsetpos, &az1, &az2, &dist);
+ _azimuth = az1;
+ //cout << "offset az " << _azimuth << endl;
+ }
+ else {
+ _azimuth = hdg;
+ //cout << " slow offset az " << _azimuth << endl;
+ }
+
+ // Resolve horizontal speed into north and east components
+ calcNE();
+
+ // And finally store the new values
+ _oldcartoffsetPos = cartoffsetPos;
+ _oldoffsetpos = offsetpos;
+}