//------------------------------------------------------------------------------
// getHeatSignature() - Get the heat signature
//------------------------------------------------------------------------------
double* AircraftIrSignature::getHeatSignature(IrQueryMsg* msg)
{
    Player* target = msg->getTarget();
    if (target != nullptr) {
        IrAtmosphere* atmos = nullptr;
        WorldModel* sim = target->getWorldModel();
        if (sim != nullptr) {
            atmos = dynamic_cast<IrAtmosphere*>( sim->getAtmosphere() );
        }

        unsigned int numBins = getNumWaveBands();
        if (airframeSig == nullptr)  airframeSig = new double [numBins * 3];
        if (plumeSigs == nullptr)    plumeSigs = new double [numBins * 3];
        if (hotPartsSigs == nullptr) hotPartsSigs = new double [numBins * 3];

        //double reflectivity = 1.0f - getEmissivity();
        double lowerBound = msg->getLowerWavelength();
        double upperBound = msg->getUpperWavelength();

        if (atmos != nullptr) {
            if (atmos->getNumWaveBands() != getNumWaveBands()) {
                // warning message
            }
        }

        // apparently no emissivity factor in these airframe signatures
        getAirframeSignatures(msg, lowerBound, upperBound);
        getPlumeSignatures(msg, lowerBound, upperBound);
        getHotPartsSignatures(msg, lowerBound, upperBound);

        const double* centerWavelengths = getWaveBandCenters();
        const double* widths = getWaveBandWidths();
        //double totalWavelengthRange = ((centerWavelengths[getNumWaveBands() - 1] + (widths[getNumWaveBands() - 1] / 2.0f))-(centerWavelengths[0] - (widths[0] / 2.0f)));

        for (unsigned int i=0; i<getNumWaveBands(); i++) {
            // determine if our sensor band overlap this signature band
            double lowerBandBound = centerWavelengths[i] - (widths[i] / 2.0f);
            double upperBandBound = lowerBandBound + widths[i];
            if (upperBound > lowerBandBound && lowerBound < upperBandBound) {

                // calculate how much of this wave band overlaps the sensor limits
                double lowerOverlap = getLowerEndOfWavelengthOverlap(lowerBandBound, lowerBound);
                double upperOverlap = getUpperEndOfWavelengthOverlap(upperBandBound, upperBound);
                if (upperOverlap < lowerOverlap) upperOverlap = lowerOverlap;
                double overlapRatio = (upperOverlap - lowerOverlap) / (upperBandBound - lowerBandBound);

                // get our main signature piece - airframe
                double baseHeatSignatureInBand = airframeSig[i*3 + 2];

                //if (isMessageEnabled(MSG_INFO)) {
                //std::cout << "For wavelength " << currentWavelength << " Airframe Heat Signature: " << baseHeatSignatureInBand << std::endl;
                //std::cout << "For wavelength " << currentWavelength << " Plume Signature: " << plumeSigs[i *3 +2] << std::endl;
                //std::cout << "For wavelength " << currentWavelength << " Hot Parts: " << hotPartsSigs[i * 3 + 2] << std::endl;
                //}

                // assume plume bins & hot parts bins are same as airframe bins, so we
                // can re-use the same overlap ratios and fractions. If not,
                // they will have to be separately calculated.
                baseHeatSignatureInBand += plumeSigs[i*3 + 2];
                baseHeatSignatureInBand += hotPartsSigs[i*3 + 2];

                // if we have an atmosphere model, get the reflected solar radiation contribution
                // Solar signature is solar value * reflectivity i.e. (1 - emissivity)
                // use of reflectivity here suggests that this is solar radiation reflected from the target
                // this now done in the atmosphere model, during query return processing
                //if (atmos != nullptr)
                //   baseHeatSignatureInBand += (reflectivity * atmos->getSolarRadiation(centerWavelengths[i], (double) target->getAltitudeM()));

                airframeSig[i*3 + 2] = baseHeatSignatureInBand * overlapRatio;
            }
        } // for loop over waveBand
    } // if (target != nullptr)
    return airframeSig;
}
Beispiel #2
0
// this is called by the IrSeeker in the transmit frame, once for each target that returns a query
bool IrSensor::calculateIrQueryReturn(IrQueryMsg* const msg)
{
   Player* ownship = getOwnship();
   IrAtmosphere* atmos = nullptr;
   double totalSignal = 0.0;
   double totalBackground = 0.0;

   if (msg->getSendingSensor() != this) {
      // this should not happen
   }

   if (ownship != nullptr) {
      WorldModel* sim = ownship->getWorldModel();
      if (sim)
         atmos = dynamic_cast<IrAtmosphere*>(sim->getAtmosphere());
   }

   if (atmos == nullptr) {
      // assume simple signature
      totalSignal = msg->getSignatureAtRange();
   }
   else {
      atmos->calculateAtmosphereContribution(msg, &totalSignal, &totalBackground);
   }

   if (totalSignal > 0.0) {

      const double targetRange = msg->getRange();
      const double rangeSquared =  targetRange * targetRange;
      const double reflectorArea = msg->getProjectedArea();
      const double ifov = msg->getInstantaneousFieldOfView();

      // The relevant amount of background area is the field of view multiplied by the range squared

      const double backgroundArea =  ifov * rangeSquared;

      // The seeker detects by comparing the amount of signal present
      // with the target to the amount of signal there would be without the target.
      // The amount of signal with the target represents the signal power plus
      // that part of the background radiation that is not blocked by the target.
      // If the target is larger than the field of view than it is all of background
      // power in the field of view. Use this as the default value.

     // noiseBlockedByTarget is irradiance, in units of watts/m^2

      double noiseBlockedByTarget = totalBackground * ifov;

      // If the target is smaller than the field of view, it is the background power
      // in the effective field of view represented by the target, i.e. the
      // target area divided by the range squared.
      if (reflectorArea < backgroundArea) {
         // these two are equivalent
         // noiseBlockedByTarget *=  (reflectorArea/backgroundArea)
          noiseBlockedByTarget = (totalBackground * reflectorArea) / rangeSquared;
      }

      // attenuatedPower is irradiance, in watts/m^2
      const double attenuatedPower = totalSignal / rangeSquared;

      // signalAboveNoise is the signal that the detector sees minus what it would see with
      // only the background radiation, and is just the amount of power subtracted by how much
      // background power is being blocked.
      // = (attenuatedPower + totalBackground*ifov - noiseBlockedByTarget) - totalBackground*ifov
      // signalAboveNoise is irradiance, in watts/m^2

      double signalAboveNoise = attenuatedPower - noiseBlockedByTarget;

      // only Contrast seekers take absolute value in this equation.
      // Hotspot does not.

      if (signalAboveNoise < 0.0 &&
               (msg->getSendingSensor()->getSensorType() == IrSensor::CONTRAST)) {
         signalAboveNoise = -signalAboveNoise;
      }

      const double nei = msg->getNEI();

      // Determine the ratio between the signal above the noise as compared to the level of
      // radiation that would create a response at the same level as the sensor's internal noise.
      // if NEI is in watts/m^2, then SNR will be dimensionless.
      // if NEI is in watts/cm^2, then need to correct by 10^4.

      const double signalToNoiseRatio = signalAboveNoise / nei;
      const double backgroundNoiseRatio = noiseBlockedByTarget / nei;
      //double signalToNoiseThreshold = msg->getSendingSensor()->getThreshold();

      // allow all signals to be returned; threshold test will be applied in process()
      {
         const auto outMsg = new IrQueryMsg();
         outMsg->setTarget(msg->getTarget());
         outMsg->setGimbalAzimuth( static_cast<double>(msg->getGimbal()->getAzimuth()) );
         outMsg->setGimbalElevation( static_cast<double>(msg->getGimbal()->getElevation()) );
         outMsg->setAzimuthAoi(msg->getAzimuthAoi());
         outMsg->setElevationAoi(msg->getElevationAoi());

         base::Vec3d los = msg->getLosVec();

         {
            // This is for non-ownHdgOnly-stabilized gimbal angles
            base::Vec4d los0( los.x(), los.y(), los.z(), 0.0 );
            base::Vec4d los_vec = ownship->getRotMat() * los0;
            double ra = std::sqrt(los_vec.x() * los_vec.x() + los_vec.y()*los_vec.y());
            double az = std::atan2(los_vec.y(), los_vec.x());
            double el = std::atan2(-los_vec.z(), ra);
            outMsg->setRelativeAzimuth(az);
            outMsg->setRelativeElevation(el);
         }

         outMsg->setLosVec(msg->getLosVec());
         outMsg->setTgtLosVec( -msg->getLosVec() );
         outMsg->setPosVec(msg->getTarget()->getPosition());
         outMsg->setVelocityVec(msg->getTarget()->getVelocity());
         outMsg->setAccelVec(msg->getTarget()->getAcceleration());

         double angleAspect1 = outMsg->getPosVec().y() *
                                 outMsg->getVelocityVec().x() -
                                 outMsg->getPosVec().x() *
                                 outMsg->getVelocityVec().y();

         double angleAspect2 = outMsg->getPosVec().x() *
                                 outMsg->getVelocityVec().x() +
                                 outMsg->getPosVec().y() *
                                 outMsg->getVelocityVec().y();

         outMsg->setAngleAspect(std::atan2(-angleAspect1,angleAspect2));

         outMsg->setSignalToNoiseRatio(signalToNoiseRatio);
         outMsg->setBackgroundNoiseRatio(backgroundNoiseRatio);
         outMsg->setSendingSensor(msg->getSendingSensor());

         // probably unnecessary - should be default val
         outMsg->setQueryMergeStatus(IrQueryMsg::NOT_MERGED);   // FAB

         msg->getSendingSensor()->addStoredMessage(outMsg);
      }
      //else   // FAB - debug
      //{
      // int x = 0;
      // x = x +1;
      // }
   } //totalSignal > 0
 //  else   // FAB - debug
 //  {
    //   int x = 0;
    //   x = x +1;
    //}

   return true;
}