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 180 of file channel_model.h.

181 {
182 Strong, ///< SNR > 0 dB
183 Weak ///< SNR < 0 dB (Geometric line of sight, but below noise floor)
184 };
@ 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 168 of file channel_model.h.

169 {
170 Monostatic, ///< Combined Tx/Rx path
171 BistaticTxTgt, ///< Illuminator path
172 BistaticTgtRx, ///< Scattered path
173 DirectTxRx ///< Interference path
174 };
@ 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 315 of file channel_model.cpp.

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

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

Referenced by processing::pipeline::applyCwInterference(), and core::SimulationEngine::calculateCwSample().

+ 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 496 of file channel_model.cpp.

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

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

+ 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 424 of file channel_model.cpp.

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

+ 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 225 of file channel_model.cpp.

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

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

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: