// 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; }
//------------------------------------------------------------------------------ // onRfEmissionEventAntenna() -- process events for RF Emission not sent by us. // // 1) Build a list of emission packets from the queue and compute the // Line-Of-Sight (LOS) vectors back to the transmitter. // // 2) Transform LOS vectors to antenna coordinates // // 3) Compute antenna gains in the direction of the transmitter // // 4) Compute Antenna Effective Gains // // 5) Compute Antenna Effective Area and Polarization Gains // // 6) Compute total receiving antenaa gain and send the emission to our sensor //------------------------------------------------------------------------------ bool Antenna::onRfEmissionEvent(Emission* const em) { // Is this emission from a player of interest? if (fromPlayerOfInterest(em)) { Player* ownship = getOwnship(); RfSystem* sys1 = getSystem(); if (ownship != 0 && sys1 != 0) { sys1->ref(); // Line-Of-Sight (LOS) vectors back to the transmitter. osg::Vec3d xlos = em->getTgtLosVec(); osg::Vec4d los0( xlos.x(), xlos.y(), xlos.z(), 0.0); // 2) Transform local NED LOS vectors to antenna coordinates osg::Matrixd mm = getRotMat(); mm *= ownship->getRotMat(); osg::Vec4d losA = mm * los0; // --- // Compute antenna gains in the direction of the transmitter // --- double rGainDb = 0.0f; if (gainPattern != 0) { Basic::Func1* gainFunc1 = dynamic_cast<Basic::Func1*>(gainPattern); Basic::Func2* gainFunc2 = dynamic_cast<Basic::Func2*>(gainPattern); if (gainFunc2 != 0) { // --- // 3-a) Antenna pattern: 2D table (az & el off antenna boresight) // --- // Get component arrays and ground range squared double xa = losA.x(); double ya = losA.y(); double za = -losA.z(); double ra2 = xa*xa + ya*ya; // Compute range along antenna x-y plane double ra = sqrt(ra2); // Compute azimuth off boresight double aazr = atan2(ya,xa); // Compute elevation off boresight double aelr = atan2(za,ra); // Lookup gain in 2D table and convert from dB if (gainPatternDeg) rGainDb = gainFunc2->f( aazr * Basic::Angle::R2DCC, aelr * Basic::Angle::R2DCC ); else rGainDb = gainFunc2->f( aazr, aelr ); } else if (gainFunc1 != 0) { // --- // 3-b) Antenna Pattern: 1D table (off antenna boresight only // --- // Compute angle off antenna boresight double aar = acos(losA.x()); // Lookup gain in 1D table and convert from dB if (gainPatternDeg) rGainDb = gainFunc1->f( aar * Basic::Angle::R2DCC ); else rGainDb = gainFunc1->f(aar); } } // Compute off-boresight gain double rGain = pow(10.0,rGainDb/10.0); // Compute Antenna Effective Gain double aeGain = rGain * getGain(); double lambda = em->getWavelength(); double aea = getEffectiveArea(aeGain, lambda); double pGain = getPolarizationGain(em->getPolarization()); double raGain = aea * pGain; sys1->rfReceivedEmission(em, this, LCreal(raGain)); sys1->unref(); } } return BaseClass::onRfEmissionEvent(em); }