FERS 1.0.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
simulation Namespace Reference

Classes

struct  PreviewLink
 A calculated link segment for 3D visualization. More...
 
class  RangeError
 Exception thrown when a range calculation fails, typically due to objects being too close. More...
 
struct  ReResults
 Stores the intermediate results of a radar equation calculation for a single time point. More...
 

Enumerations

enum class  LinkType { Monostatic , BistaticTxTgt , BistaticTgtRx , DirectTxRx }
 Categorizes the visual link for rendering. More...
 
enum class  LinkQuality { Strong , Weak }
 Describes the radiometric quality of the link. More...
 

Functions

void solveRe (const radar::Transmitter *trans, const radar::Receiver *recv, const radar::Target *targ, const std::chrono::duration< RealType > &time, const fers_signal::RadarSignal *wave, ReResults &results)
 Solves the bistatic radar equation for a reflected path (Tx -> Tgt -> Rx).
 
void solveReDirect (const radar::Transmitter *trans, const radar::Receiver *recv, const std::chrono::duration< RealType > &time, const fers_signal::RadarSignal *wave, ReResults &results)
 Solves the radar equation for a direct path (Tx -> Rx).
 
ComplexType calculateDirectPathContribution (const radar::Transmitter *trans, const radar::Receiver *recv, RealType timeK)
 Calculates the complex envelope contribution for a direct propagation path (Tx -> Rx) at a specific time.
 
ComplexType calculateReflectedPathContribution (const radar::Transmitter *trans, const radar::Receiver *recv, const radar::Target *targ, RealType timeK)
 Calculates the complex envelope contribution for a reflected path (Tx -> Tgt -> Rx) at a specific time.
 
std::unique_ptr< serial::ResponsecalculateResponse (const radar::Transmitter *trans, const radar::Receiver *recv, const fers_signal::RadarSignal *signal, RealType startTime, const radar::Target *targ=nullptr)
 Creates a Response object by simulating a signal's interaction over its duration.
 
std::vector< PreviewLinkcalculatePreviewLinks (const core::World &world, RealType time)
 Calculates all visual links for the current world state at a specific time.
 

Enumeration Type Documentation

◆ LinkQuality

enum class simulation::LinkQuality
strong

Describes the radiometric quality of the link.

Enumerator
Strong 

SNR > 0 dB.

Weak 

SNR < 0 dB (Geometric line of sight, but below noise floor)

Definition at line 178 of file channel_model.h.

179 {
180 Strong, ///< SNR > 0 dB
181 Weak ///< SNR < 0 dB (Geometric line of sight, but below noise floor)
182 };
@ Weak
SNR < 0 dB (Geometric line of sight, but below noise floor)

◆ LinkType

enum class simulation::LinkType
strong

Categorizes the visual link for rendering.

Enumerator
Monostatic 

Combined Tx/Rx path.

BistaticTxTgt 

Illuminator path.

BistaticTgtRx 

Scattered path.

DirectTxRx 

Interference path.

Definition at line 166 of file channel_model.h.

167 {
168 Monostatic, ///< Combined Tx/Rx path
169 BistaticTxTgt, ///< Illuminator path
170 BistaticTgtRx, ///< Scattered path
171 DirectTxRx ///< Interference path
172 };
@ DirectTxRx
Interference path.
@ Monostatic
Combined Tx/Rx path.
@ BistaticTgtRx
Scattered path.
@ BistaticTxTgt
Illuminator path.

Function Documentation

◆ calculateDirectPathContribution()

ComplexType simulation::calculateDirectPathContribution ( const radar::Transmitter trans,
const radar::Receiver recv,
RealType  timeK 
)

Calculates the complex envelope contribution for a direct propagation path (Tx -> Rx) at a specific time.

This function is used for Continuous Wave (CW) simulations.

Parameters
transThe transmitter.
recvThe receiver.
timeKThe current simulation time.
Returns
The complex I/Q sample contribution for this path.

Definition at line 314 of file channel_model.cpp.

315 {
316 // Check for co-location to prevent singularities.
317 // If they share the same platform, we assume they are isolated (no leakage) or explicit
318 // monostatic handling is required (which is not modeled via the far-field path).
319 if (trans->getPlatform() == recv->getPlatform())
320 {
321 return {0.0, 0.0};
322 }
323
324 const auto p_tx = trans->getPlatform()->getPosition(timeK);
325 const auto p_rx = recv->getPlatform()->getPosition(timeK);
326
327 LinkGeometry link;
328 try
329 {
330 link = computeLink(p_tx, p_rx);
331 }
332 catch (const RangeError&)
333 {
334 return {0.0, 0.0};
335 }
336
337 const RealType tau = link.dist / params::c();
338 const auto signal = trans->getSignal();
339 const RealType carrier_freq = signal->getCarrier();
340 const RealType lambda = params::c() / carrier_freq;
341
342 // Tx Gain: Direction Tx -> Rx
343 const RealType tx_gain = computeAntennaGain(trans, link.u_vec, timeK, lambda);
344 // Rx Gain: Direction Rx -> Tx (-u_vec)
345 const RealType rx_gain = computeAntennaGain(recv, -link.u_vec, timeK + tau, lambda);
346
347 const bool no_loss = recv->checkFlag(Receiver::RecvFlag::FLAG_NOPROPLOSS);
348 const RealType scaling_factor = computeDirectPathPower(tx_gain, rx_gain, lambda, link.dist, no_loss);
349
350 // Include Signal Power
351 const RealType amplitude = std::sqrt(signal->getPower() * scaling_factor);
352
353 // Carrier Phase
354 const RealType phase = -2 * PI * carrier_freq * tau;
355 ComplexType contribution = std::polar(amplitude, phase);
356
357 // Non-coherent Local Oscillator Effects
358 const RealType non_coherent_phase = computeTimingPhase(trans, recv, timeK);
359 contribution *= std::polar(1.0, non_coherent_phase);
360
361 return contribution;
362 }
RealType getCarrier() const noexcept
Gets the carrier frequency of the radar signal.
Platform * getPlatform() const noexcept
Retrieves the associated platform of the object.
Definition object.h:61
math::Vec3 getPosition(const RealType time) const
Gets the position of the platform at a specific time.
Definition platform.h:74
bool checkFlag(RecvFlag flag) const noexcept
Checks if a specific flag is set.
Definition receiver.h:86
fers_signal::RadarSignal * getSignal() const noexcept
Retrieves the radar signal currently being transmitted.
Definition transmitter.h:68
double RealType
Type for real numbers.
Definition config.h:27
std::complex< RealType > ComplexType
Type for complex numbers.
Definition config.h:35
constexpr RealType PI
Mathematical constant π (pi).
Definition config.h:43
RealType c() noexcept
Get the speed of light.
Definition parameters.h:79

References params::c(), radar::Receiver::checkFlag(), fers_signal::RadarSignal::getCarrier(), radar::Object::getPlatform(), radar::Platform::getPosition(), radar::Transmitter::getSignal(), and PI.

Referenced by core::runEventDrivenSim(), and processing::runPulsedFinalizer().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ calculatePreviewLinks()

std::vector< PreviewLink > simulation::calculatePreviewLinks ( const core::World world,
RealType  time 
)

Calculates all visual links for the current world state at a specific time.

This function utilizes the core radar equation helpers to determine visibility, power levels, and SNR for all Tx/Rx/Target combinations. It is lightweight and does not update simulation state.

Parameters
worldThe simulation world containing radar components.
timeThe time at which to calculate geometry.
Returns
A vector of renderable links.

Definition at line 497 of file channel_model.cpp.

498 {
499 std::vector<PreviewLink> links;
500
501 // Default wavelength (1GHz) if no waveform is attached, to allow geometric visualization
502 const RealType lambda_default = 0.3;
503
504 for (const auto& tx : world.getTransmitters())
505 {
506 // 1. Check Transmitter Schedule
507 if (!isComponentActive(tx->getSchedule(), time))
508 {
509 continue;
510 }
511
512 const auto p_tx = tx->getPosition(time);
513 const auto* waveform = tx->getSignal();
514 const RealType pt = waveform ? waveform->getPower() : 0.0;
515 const RealType lambda = waveform ? (params::c() / waveform->getCarrier()) : lambda_default;
516
517 // --- PRE-CALCULATE ILLUMINATOR PATHS (Tx -> Tgt) ---
518 // These depend only on the Transmitter and Targets. We calculate them once per Tx
519 // to avoid duplication when multiple receivers are present.
520 // We do NOT filter out Monostatic transmitters here. Even in a monostatic setup,
521 // the "Illuminator" link provides distinct info (Power Density at Target) compared
522 // to the "Monostatic" link (Received Power at Rx). By being outside the Rx loop,
523 // it is guaranteed to be unique per Target.
524 for (const auto& tgt : world.getTargets())
525 {
526 const auto p_tgt = tgt->getPosition(time);
527 const Vec3 vec_tx_tgt = p_tgt - p_tx;
528 const RealType r1 = vec_tx_tgt.length();
529
530 if (r1 <= EPSILON)
531 continue;
532
533 const Vec3 u_tx_tgt = vec_tx_tgt / r1;
534 // Tx Gain: Tx -> Tgt
535 const RealType gt = computeAntennaGain(tx.get(), u_tx_tgt, time, lambda);
536
537 // Power Density at Target: S = (Pt * Gt) / (4 * pi * R1^2)
538 const RealType p_density = (pt * gt) / (4.0 * PI * r1 * r1);
539
540 links.push_back({.type = LinkType::BistaticTxTgt,
541 .quality = LinkQuality::Strong,
542 .label = std::format("{:.1f} dBW/m\u00B2", wattsToDb(p_density)),
543 .source_name = tx->getName(),
544 .dest_name = tgt->getName(),
545 .origin_name = tx->getName()});
546 }
547
548 for (const auto& rx : world.getReceivers())
549 {
550 // 2. Check Receiver Schedule
551 if (!isComponentActive(rx->getSchedule(), time))
552 {
553 continue;
554 }
555
556 const auto p_rx = rx->getPosition(time);
557 const bool is_monostatic = (tx->getAttached() == rx.get());
558 const bool no_loss = rx->checkFlag(Receiver::RecvFlag::FLAG_NOPROPLOSS);
559
560 if (is_monostatic)
561 {
562 // --- Monostatic Case ---
563 // Calculates the round-trip Received Power (dBm)
564 for (const auto& tgt : world.getTargets())
565 {
566 const auto p_tgt = tgt->getPosition(time);
567
568 // Use computeLink helper logic, but catch exception manually by checking dist
569 // to avoid try/catch overhead in render loop
570 const Vec3 vec_tx_tgt = p_tgt - p_tx;
571 const RealType dist = vec_tx_tgt.length();
572
573 if (dist <= EPSILON)
574 continue;
575
576 const Vec3 u_tx_tgt = vec_tx_tgt / dist; // Unit vec Tx -> Tgt
577
578 // Reusing internal helpers
579 // Tx Gain: Direction Tx -> Tgt
580 const RealType gt = computeAntennaGain(tx.get(), u_tx_tgt, time, lambda);
581 // Rx Gain: Direction Rx -> Tgt (which is -u_tx_tgt because Rx is at Tx)
582 const RealType gr = computeAntennaGain(rx.get(), u_tx_tgt, time, lambda);
583
584 // RCS: In = Tx->Tgt, Out = Tgt->Rx (which is -(Tx->Tgt))
585 SVec3 in_angle(u_tx_tgt);
586 SVec3 out_angle(-u_tx_tgt);
587 const RealType rcs = tgt->getRcs(in_angle, out_angle, time);
588
589 // Reusing Reflected Path Helper
590 // r_tx = dist, r_rx = dist
591 const RealType power_ratio =
592 computeReflectedPathPower(gt, gr, rcs, lambda, dist, dist, no_loss);
593
594 const RealType pr_watts = pt * power_ratio;
595
596 links.push_back(
597 {.type = LinkType::Monostatic,
598 .quality = isSignalStrong(pr_watts, rx->getNoiseTemperature()) ? LinkQuality::Strong
599 : LinkQuality::Weak,
600 .label = std::format("{:.1f} dBm (RCS: {:.1f}m\u00B2)", wattsToDbm(pr_watts), rcs),
601 .source_name = tx->getName(), // Monostatic implies Tx/Rx is same platform/system
602 .dest_name = tgt->getName(),
603 .origin_name = tx->getName()});
604 }
605 }
606 else
607 {
608 // --- Bistatic Case ---
609
610 // 1. Direct Path (Interference)
611 if (!rx->checkFlag(Receiver::RecvFlag::FLAG_NODIRECT))
612 {
613 const Vec3 vec_direct = p_rx - p_tx;
614 const RealType dist = vec_direct.length();
615
616 if (dist > EPSILON)
617 {
618 const Vec3 u_tx_rx = vec_direct / dist;
619
620 // Tx Gain: Tx -> Rx
621 const RealType gt = computeAntennaGain(tx.get(), u_tx_rx, time, lambda);
622 // Rx Gain: Rx -> Tx (which is -u_tx_rx)
623 const RealType gr = computeAntennaGain(rx.get(), -u_tx_rx, time, lambda);
624
625 const RealType power_ratio = computeDirectPathPower(gt, gr, lambda, dist, no_loss);
626 const RealType pr_watts = pt * power_ratio;
627
628 links.push_back({.type = LinkType::DirectTxRx,
629 .quality = LinkQuality::Strong,
630 .label = std::format("Direct: {:.1f} dBm", wattsToDbm(pr_watts)),
631 .source_name = tx->getName(),
632 .dest_name = rx->getName(),
633 .origin_name = tx->getName()});
634 }
635 }
636
637 // 2. Reflected Path (Scattered Leg: Tgt -> Rx)
638 for (const auto& tgt : world.getTargets())
639 {
640 const auto p_tgt = tgt->getPosition(time);
641 const Vec3 vec_tx_tgt = p_tgt - p_tx;
642 const Vec3 vec_tgt_rx = p_rx - p_tgt; // Vector Tgt -> Rx
643
644 const RealType r1 = vec_tx_tgt.length();
645 const RealType r2 = vec_tgt_rx.length();
646
647 if (r1 <= EPSILON || r2 <= EPSILON)
648 continue;
649
650 const Vec3 u_tx_tgt = vec_tx_tgt / r1;
651 const Vec3 u_tgt_rx = vec_tgt_rx / r2;
652
653 // Tx Gain: Tx -> Tgt
654 const RealType gt = computeAntennaGain(tx.get(), u_tx_tgt, time, lambda);
655 // Rx Gain: Rx -> Tgt (Vector is Tgt->Rx, so look vector is -u_tgt_rx)
656 const RealType gr = computeAntennaGain(rx.get(), -u_tgt_rx, time, lambda);
657
658 // RCS
659 SVec3 in_angle(u_tx_tgt);
660 SVec3 out_angle(-u_tgt_rx); // Out angle is usually defined from Target OUTWARDS
661 const RealType rcs = tgt->getRcs(in_angle, out_angle, time);
662
663 const RealType power_ratio = computeReflectedPathPower(gt, gr, rcs, lambda, r1, r2, no_loss);
664 const RealType pr_watts = pt * power_ratio;
665
666 // Note: Illuminator leg (Tx->Tgt) was handled in the outer loop.
667
668 // Leg 2: Scattered
669 links.push_back({.type = LinkType::BistaticTgtRx,
670 .quality = isSignalStrong(pr_watts, rx->getNoiseTemperature())
671 ? LinkQuality::Strong
672 : LinkQuality::Weak,
673 .label = std::format("{:.1f} dBm", wattsToDbm(pr_watts)),
674 .source_name = tgt->getName(),
675 .dest_name = rx->getName(),
676 .origin_name = tx->getName()});
677 }
678 }
679 }
680 }
681 return links;
682 }
A class representing a vector in spherical coordinates.
A class representing a vector in rectangular coordinates.
RealType length() const noexcept
Calculates the length (magnitude) of the vector.
constexpr RealType EPSILON
Machine epsilon for real numbers.
Definition config.h:51

References BistaticTgtRx, BistaticTxTgt, params::c(), radar::Receiver::checkFlag(), DirectTxRx, EPSILON, radar::Radar::getAttached(), radar::Object::getName(), radar::Receiver::getNoiseTemperature(), radar::Object::getPosition(), fers_signal::RadarSignal::getPower(), core::World::getReceivers(), radar::Receiver::getSchedule(), radar::Transmitter::getSchedule(), radar::Transmitter::getSignal(), core::World::getTargets(), core::World::getTransmitters(), math::Vec3::length(), Monostatic, PI, Strong, and Weak.

Referenced by fers_calculate_preview_links().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ calculateReflectedPathContribution()

ComplexType simulation::calculateReflectedPathContribution ( const radar::Transmitter trans,
const radar::Receiver recv,
const radar::Target targ,
RealType  timeK 
)

Calculates the complex envelope contribution for a reflected path (Tx -> Tgt -> Rx) at a specific time.

This function is used for Continuous Wave (CW) simulations.

Parameters
transThe transmitter.
recvThe receiver.
targThe target.
timeKThe current simulation time.
Returns
The complex I/Q sample contribution for this path.

Definition at line 364 of file channel_model.cpp.

366 {
367 // Check for co-location involving the target.
368 // We do not model a platform tracking itself (R=0) or illuminating itself (R=0).
369 if (trans->getPlatform() == targ->getPlatform() || recv->getPlatform() == targ->getPlatform())
370 {
371 return {0.0, 0.0};
372 }
373
374 const auto p_tx = trans->getPlatform()->getPosition(timeK);
375 const auto p_rx = recv->getPlatform()->getPosition(timeK);
376 const auto p_tgt = targ->getPlatform()->getPosition(timeK);
377
378 LinkGeometry link_tx_tgt;
379 LinkGeometry link_tgt_rx;
380
381 try
382 {
383 link_tx_tgt = computeLink(p_tx, p_tgt);
384 link_tgt_rx = computeLink(p_tgt, p_rx);
385 }
386 catch (const RangeError&)
387 {
388 return {0.0, 0.0};
389 }
390
391 const RealType tau = (link_tx_tgt.dist + link_tgt_rx.dist) / params::c();
392 const auto signal = trans->getSignal();
393 const RealType carrier_freq = signal->getCarrier();
394 const RealType lambda = params::c() / carrier_freq;
395
396 // RCS Lookups: In (Tx->Tgt), Out (Rx->Tgt = - (Tgt->Rx))
397 SVec3 in_angle(link_tx_tgt.u_vec);
398 SVec3 out_angle(-link_tgt_rx.u_vec);
399 const RealType rcs = targ->getRcs(in_angle, out_angle, timeK);
400
401 // Tx Gain: Direction Tx -> Tgt
402 const RealType tx_gain = computeAntennaGain(trans, link_tx_tgt.u_vec, timeK, lambda);
403 // Rx Gain: Direction Rx -> Tgt (- (Tgt->Rx)). Time: timeK + tau.
404 const RealType rx_gain = computeAntennaGain(recv, -link_tgt_rx.u_vec, timeK + tau, lambda);
405
406 const bool no_loss = recv->checkFlag(Receiver::RecvFlag::FLAG_NOPROPLOSS);
407 const RealType scaling_factor =
408 computeReflectedPathPower(tx_gain, rx_gain, rcs, lambda, link_tx_tgt.dist, link_tgt_rx.dist, no_loss);
409
410 // Include Signal Power
411 const RealType amplitude = std::sqrt(signal->getPower() * scaling_factor);
412
413 const RealType phase = -2 * PI * carrier_freq * tau;
414 ComplexType contribution = std::polar(amplitude, phase);
415
416 // Non-coherent Local Oscillator Effects
417 const RealType non_coherent_phase = computeTimingPhase(trans, recv, timeK);
418 contribution *= std::polar(1.0, non_coherent_phase);
419
420 return contribution;
421 }
virtual RealType getRcs(math::SVec3 &inAngle, math::SVec3 &outAngle, RealType time) const =0
Gets the RCS value for the target.

References params::c(), radar::Receiver::checkFlag(), fers_signal::RadarSignal::getCarrier(), radar::Object::getPlatform(), radar::Platform::getPosition(), radar::Target::getRcs(), radar::Transmitter::getSignal(), and PI.

Referenced by core::runEventDrivenSim(), and processing::runPulsedFinalizer().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ calculateResponse()

std::unique_ptr< serial::Response > simulation::calculateResponse ( const radar::Transmitter trans,
const radar::Receiver recv,
const fers_signal::RadarSignal signal,
RealType  startTime,
const radar::Target targ = nullptr 
)

Creates a Response object by simulating a signal's interaction over its duration.

This function iterates over the duration of a transmitted pulse, calling the appropriate channel model function (solveRe or solveReDirect) at discrete time steps to generate a series of InterpPoints. These points capture the time-varying properties of the received signal and are collected into a Response object.

Parameters
transPointer to the transmitter.
recvPointer to the receiver.
signalPointer to the transmitted pulse signal.
startTimeThe absolute simulation time when the pulse transmission starts.
targOptional pointer to a target. If null, a direct path is simulated.
Returns
A unique pointer to the generated Response object.
Exceptions
RangeErrorIf the channel model reports an invalid geometry.
std::runtime_errorIf the simulation parameters result in zero time steps.

Definition at line 423 of file channel_model.cpp.

426 {
427 // If calculating direct path (no target) and components are co-located:
428 // 1. If explicitly attached (monostatic), skip (internal leakage handled elsewhere).
429 // 2. If independent but on the same platform, distance is 0. Far-field logic (1/R^2)
430 // diverges. We skip calculation to avoid RangeError crashes, assuming
431 // no direct coupling/interference for co-located far-field antennas.
432 if (targ == nullptr && (trans->getAttached() == recv || trans->getPlatform() == recv->getPlatform()))
433 {
434 LOG(Level::TRACE, "Skipping direct path calculation for co-located Transmitter {} and Receiver {}",
435 trans->getName(), recv->getName());
436 return nullptr;
437 }
438
439 // If calculating reflected path and target is co-located with either Tx or Rx:
440 // Skip to avoid singularity. Simulating a radar tracking its own platform
441 // requires near-field clutter models, not point-target RCS models.
442 if (targ != nullptr &&
443 (targ->getPlatform() == trans->getPlatform() || targ->getPlatform() == recv->getPlatform()))
444 {
445 LOG(Level::TRACE,
446 "Skipping reflected path calculation for Target {} co-located with Transmitter {} or Receiver {}",
447 targ->getName(), trans->getName(), recv->getName());
448 return nullptr;
449 }
450
451 const auto start_time_chrono = std::chrono::duration<RealType>(startTime);
452 const auto end_time_chrono = start_time_chrono + std::chrono::duration<RealType>(signal->getLength());
453 const auto sample_time_chrono = std::chrono::duration<RealType>(1.0 / params::simSamplingRate());
454 const int point_count = static_cast<int>(std::ceil(signal->getLength() / sample_time_chrono.count()));
455
456 if (targ && point_count == 0)
457 {
458 LOG(Level::FATAL, "No time points are available for execution!");
459 throw std::runtime_error("No time points are available for execution!");
460 }
461
462 auto response = std::make_unique<serial::Response>(signal, trans);
463
464 try
465 {
466 for (int i = 0; i <= point_count; ++i)
467 {
468 const auto current_time =
469 i < point_count ? start_time_chrono + i * sample_time_chrono : end_time_chrono;
470
471 ReResults results{};
472 if (targ)
473 {
474 solveRe(trans, recv, targ, current_time, signal, results);
475 }
476 else
477 {
478 solveReDirect(trans, recv, current_time, signal, results);
479 }
480
481 interp::InterpPoint point{.power = results.power,
482 .time = current_time.count() + results.delay,
483 .delay = results.delay,
484 .phase = results.phase};
485 response->addInterpPoint(point);
486 }
487 }
488 catch (const RangeError&)
489 {
490 LOG(Level::FATAL, "Receiver or Transmitter too close for accurate simulation");
491 throw; // Re-throw to be caught by the runner
492 }
493
494 return response;
495 }
RealType getLength() const noexcept
Gets the length of the radar signal.
const std::string & getName() const noexcept
Retrieves the name of the object.
Definition object.h:68
const Radar * getAttached() const noexcept
Retrieves the attached radar object.
Definition radar_obj.h:71
#define LOG(level,...)
Definition logging.h:19
RealType simSamplingRate() noexcept
Get the simulation sampling rate.
Definition parameters.h:103
void solveReDirect(const Transmitter *trans, const Receiver *recv, const std::chrono::duration< RealType > &time, const RadarSignal *wave, ReResults &results)
Solves the radar equation for a direct path (Tx -> Rx).
void solveRe(const Transmitter *trans, const Receiver *recv, const Target *targ, const std::chrono::duration< RealType > &time, const RadarSignal *wave, ReResults &results)
Solves the bistatic radar equation for a reflected path (Tx -> Tgt -> Rx).
Stores data for an interpolation point.
RealType power
Power level of the signal at the interpolation point.

References radar::Radar::getAttached(), fers_signal::RadarSignal::getLength(), radar::Object::getName(), radar::Object::getPlatform(), LOG, interp::InterpPoint::power, params::simSamplingRate(), solveRe(), and solveReDirect().

Referenced by core::runEventDrivenSim().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ solveRe()

void simulation::solveRe ( const radar::Transmitter trans,
const radar::Receiver recv,
const radar::Target targ,
const std::chrono::duration< RealType > &  time,
const fers_signal::RadarSignal wave,
ReResults results 
)

Solves the bistatic radar equation for a reflected path (Tx -> Tgt -> Rx).

This function calculates the signal properties (power, delay, phase) for a signal traveling from a transmitter, reflecting off a target, and arriving at a receiver. It accounts for antenna gains, target RCS, and propagation loss.

Parameters
transPointer to the transmitter.
recvPointer to the receiver.
targPointer to the target.
timeThe time at which the pulse is transmitted.
wavePointer to the transmitted radar signal.
resultsOutput struct to store the calculation results.
Exceptions
RangeErrorIf the target is too close to the transmitter or receiver.

Definition at line 224 of file channel_model.cpp.

226 {
227 // Note: RangeError log messages are handled by the original catch block in calculateResponse
228 // or explicitly here if strict adherence to original logging is required.
229 // Using the helper logic which throws RangeError on epsilon check.
230
231 const RealType t_val = time.count();
232 const auto p_tx = trans->getPosition(t_val);
233 const auto p_rx = recv->getPosition(t_val);
234 const auto p_tgt = targ->getPosition(t_val);
235
236 // Link 1: Tx -> Target
237 LinkGeometry link_tx_tgt;
238 // Link 2: Target -> Rx (Note: Vector for calculation is Tgt->Rx)
239 LinkGeometry link_tgt_rx;
240
241 try
242 {
243 link_tx_tgt = computeLink(p_tx, p_tgt);
244 link_tgt_rx = computeLink(p_tgt, p_rx); // Vector Tgt -> Rx
245 }
246 catch (const RangeError&)
247 {
248 LOG(Level::FATAL, "Transmitter or Receiver too close to Target for accurate simulation");
249 throw;
250 }
251
252 results.delay = (link_tx_tgt.dist + link_tgt_rx.dist) / params::c();
253
254 // Calculate RCS
255 // Note: getRcs expects (InAngle, OutAngle).
256 // InAngle: Tx -> Tgt (link_tx_tgt.u_vec)
257 // OutAngle: Rx -> Tgt (Opposite of Tgt->Rx, so -link_tgt_rx.u_vec)
258 // This matches existing logic.
259 SVec3 in_angle(link_tx_tgt.u_vec);
260 SVec3 out_angle(-link_tgt_rx.u_vec);
261 const auto rcs = targ->getRcs(in_angle, out_angle, t_val);
262
263 const auto wavelength = params::c() / wave->getCarrier();
264
265 // Tx Gain: Direction Tx -> Tgt
266 const auto tx_gain = computeAntennaGain(trans, link_tx_tgt.u_vec, t_val, wavelength);
267 // Rx Gain: Direction Rx -> Tgt (Opposite of Tgt->Rx).
268 // Time is time + delay.
269 const auto rx_gain = computeAntennaGain(recv, -link_tgt_rx.u_vec, results.delay + t_val, wavelength);
270
271 const bool no_loss = recv->checkFlag(Receiver::RecvFlag::FLAG_NOPROPLOSS);
272 results.power =
273 computeReflectedPathPower(tx_gain, rx_gain, rcs, wavelength, link_tx_tgt.dist, link_tgt_rx.dist, no_loss);
274
275 results.phase = -results.delay * 2 * PI * wave->getCarrier();
276 }
math::Vec3 getPosition(const RealType time) const
Retrieves the position of the object.
Definition object.h:46
RealType delay
Signal propagation delay in seconds.
RealType power
Power scaling factor (dimensionless, relative to transmitted power).
RealType phase
Phase shift in radians due to propagation delay.

References params::c(), radar::Receiver::checkFlag(), simulation::ReResults::delay, fers_signal::RadarSignal::getCarrier(), radar::Object::getPosition(), radar::Target::getRcs(), LOG, simulation::ReResults::phase, PI, and simulation::ReResults::power.

Referenced by calculateResponse().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ solveReDirect()

void simulation::solveReDirect ( const radar::Transmitter trans,
const radar::Receiver recv,
const std::chrono::duration< RealType > &  time,
const fers_signal::RadarSignal wave,
ReResults results 
)

Solves the radar equation for a direct path (Tx -> Rx).

This function calculates the signal properties for a direct line-of-sight signal traveling from a transmitter to a receiver.

Parameters
transPointer to the transmitter.
recvPointer to the receiver.
timeThe time at which the pulse is transmitted.
wavePointer to the transmitted radar signal.
resultsOutput struct to store the calculation results.
Exceptions
RangeErrorIf the transmitter and receiver are too close.

Definition at line 278 of file channel_model.cpp.

280 {
281 const RealType t_val = time.count();
282 const auto p_tx = trans->getPosition(t_val);
283 const auto p_rx = recv->getPosition(t_val);
284
285 LinkGeometry link;
286 try
287 {
288 link = computeLink(p_tx, p_rx); // Vector Tx -> Rx
289 }
290 catch (const RangeError&)
291 {
292 LOG(Level::FATAL, "Transmitter or Receiver too close for accurate simulation");
293 throw;
294 }
295
296 results.delay = link.dist / params::c();
297 const RealType wavelength = params::c() / wave->getCarrier();
298
299 // Discrepancy Fix: Original code used (Rx - Tx) for Receiver Gain but (Tx - Rx) logic for Transmitter gain
300 // was ambiguous/incorrect (using `tpos - rpos` which is Rx->Tx).
301 // Per `calculateDirectPathContribution` preference:
302 // Tx Gain uses Vector Tx -> Rx.
303 // Rx Gain uses Vector Rx -> Tx.
304
305 const auto tx_gain = computeAntennaGain(trans, link.u_vec, t_val, wavelength);
306 const auto rx_gain = computeAntennaGain(recv, -link.u_vec, t_val + results.delay, wavelength);
307
308 const bool no_loss = recv->checkFlag(Receiver::RecvFlag::FLAG_NOPROPLOSS);
309 results.power = computeDirectPathPower(tx_gain, rx_gain, wavelength, link.dist, no_loss);
310
311 results.phase = -results.delay * 2 * PI * wave->getCarrier();
312 }

References params::c(), radar::Receiver::checkFlag(), simulation::ReResults::delay, fers_signal::RadarSignal::getCarrier(), radar::Object::getPosition(), LOG, simulation::ReResults::phase, PI, and simulation::ReResults::power.

Referenced by calculateResponse().

+ Here is the call graph for this function:
+ Here is the caller graph for this function: