//------------------------------------------------------------------------------ // 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; }
// 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; }