FERS 1.0.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
core::SimulationEngine Class Reference

Encapsulates the state and logic of the event-driven simulation loop. More...

#include "sim_threading.h"

Public Member Functions

 SimulationEngine (World *world, pool::ThreadPool &pool, std::shared_ptr< ProgressReporter > reporter, std::string output_dir, std::shared_ptr< OutputMetadataCollector > metadata_collector=nullptr)
 Constructs the simulation engine.
 
void run ()
 Starts and runs the main simulation loop until completion.
 
void processCwPhysics (RealType t_event)
 Advances the time-stepped inner loop for active continuous-wave (CW) systems.
 
void processEvent (const Event &event)
 Dispatches a discrete simulation event to its specific handler.
 
void handleTxPulsedStart (radar::Transmitter *tx, RealType t_event)
 Handles the start of a pulsed transmission.
 
void handleRxPulsedWindowStart (radar::Receiver *rx, RealType t_event)
 Handles the opening of a pulsed receiver's listening window.
 
void handleRxPulsedWindowEnd (radar::Receiver *rx, RealType t_event)
 Handles the closing of a pulsed receiver's listening window, triggering finalization.
 
void handleTxCwStart (radar::Transmitter *tx)
 Handles a continuous-wave transmitter turning on.
 
void handleTxCwEnd (radar::Transmitter *tx)
 Handles a continuous-wave transmitter turning off.
 
void handleRxCwStart (radar::Receiver *rx)
 Handles a continuous-wave receiver starting to record.
 
void handleRxCwEnd (radar::Receiver *rx)
 Handles a continuous-wave receiver stopping recording.
 
ComplexType calculateCwSample (radar::Receiver *rx, RealType t_step, const std::vector< radar::Transmitter * > &cw_sources) const
 Calculates the total complex I/Q sample for a receiver at a specific time step.
 

Detailed Description

Encapsulates the state and logic of the event-driven simulation loop.

Breaking the simulation loop into this class allows for easily testable, focused functions with low cyclomatic complexity.

Definition at line 98 of file sim_threading.h.

Constructor & Destructor Documentation

◆ SimulationEngine()

core::SimulationEngine::SimulationEngine ( World world,
pool::ThreadPool pool,
std::shared_ptr< ProgressReporter reporter,
std::string  output_dir,
std::shared_ptr< OutputMetadataCollector metadata_collector = nullptr 
)

Constructs the simulation engine.

Parameters
worldPointer to the simulation world containing all entities.
poolReference to the thread pool for asynchronous tasks.
reporterShared pointer to the thread-safe progress reporter.
output_dirOutput directory for the simulation files.

Definition at line 44 of file sim_threading.cpp.

46 :
47 _world(world), _pool(pool), _reporter(std::move(reporter)), _metadata_collector(std::move(metadata_collector)),
48 _last_report_time(std::chrono::steady_clock::now()), _output_dir(std::move(output_dir))
49 {
50 }

Member Function Documentation

◆ calculateCwSample()

ComplexType core::SimulationEngine::calculateCwSample ( radar::Receiver rx,
RealType  t_step,
const std::vector< radar::Transmitter * > &  cw_sources 
) const

Calculates the total complex I/Q sample for a receiver at a specific time step.

Parameters
rxPointer to the receiving radar object.
t_stepThe exact simulation time for the sample.
cw_sourcesA list of currently active continuous-wave transmitters.
Returns
The calculated complex I/Q sample combining direct and reflected paths.

Definition at line 123 of file sim_threading.cpp.

125 {
126 ComplexType total_sample{0.0, 0.0};
127 for (const auto& cw_source : cw_sources)
128 {
129 if (!rx->checkFlag(Receiver::RecvFlag::FLAG_NODIRECT))
130 {
131 total_sample += simulation::calculateDirectPathContribution(cw_source, rx, t_step);
132 }
133 for (const auto& target_ptr : _world->getTargets())
134 {
135 total_sample += simulation::calculateReflectedPathContribution(cw_source, rx, target_ptr.get(), t_step);
136 }
137 }
138 return total_sample;
139 }
bool checkFlag(RecvFlag flag) const noexcept
Checks if a specific flag is set.
Definition receiver.h:88
std::complex< RealType > ComplexType
Type for complex numbers.
Definition config.h:35
ComplexType calculateDirectPathContribution(const Transmitter *trans, const Receiver *recv, const RealType timeK)
Calculates the complex envelope contribution for a direct propagation path (Tx -> Rx) at a specific t...
ComplexType calculateReflectedPathContribution(const Transmitter *trans, const Receiver *recv, const Target *targ, const RealType timeK)
Calculates the complex envelope contribution for a reflected path (Tx -> Tgt -> Rx) at a specific tim...

References simulation::calculateDirectPathContribution(), simulation::calculateReflectedPathContribution(), radar::Receiver::checkFlag(), and core::World::getTargets().

Referenced by processCwPhysics().

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

◆ handleRxCwEnd()

void core::SimulationEngine::handleRxCwEnd ( radar::Receiver rx)

Handles a continuous-wave receiver stopping recording.

Parameters
rxPointer to the receiving radar object.

Definition at line 250 of file sim_threading.cpp.

250{ rx->setActive(false); }
void setActive(const bool active) noexcept
Sets the active state of the receiver.
Definition receiver.h:172

References radar::Receiver::setActive().

Referenced by processEvent().

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

◆ handleRxCwStart()

void core::SimulationEngine::handleRxCwStart ( radar::Receiver rx)

Handles a continuous-wave receiver starting to record.

Parameters
rxPointer to the receiving radar object.

Definition at line 248 of file sim_threading.cpp.

248{ rx->setActive(true); }

References radar::Receiver::setActive().

Referenced by processEvent().

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

◆ handleRxPulsedWindowEnd()

void core::SimulationEngine::handleRxPulsedWindowEnd ( radar::Receiver rx,
RealType  t_event 
)

Handles the closing of a pulsed receiver's listening window, triggering finalization.

Parameters
rxPointer to the receiving radar object.
t_eventThe timestamp of the window closing event.

Definition at line 217 of file sim_threading.cpp.

218 {
219 rx->setActive(false);
220 const auto& active_cw_transmitters = _world->getSimulationState().active_cw_transmitters;
221
222 RenderingJob job{.ideal_start_time = t_event - rx->getWindowLength(),
223 .duration = rx->getWindowLength(),
224 .responses = rx->drainInbox(),
225 .active_cw_sources = active_cw_transmitters};
226
227 rx->enqueueFinalizerJob(std::move(job));
228
229 const RealType next_theoretical = t_event - rx->getWindowLength() + 1.0 / rx->getWindowPrf();
230 if (const auto next_start = rx->getNextWindowTime(next_theoretical);
231 next_start && *next_start <= params::endTime())
232 {
233 _world->getEventQueue().push({*next_start, EventType::RX_PULSED_WINDOW_START, rx});
234 }
235 }
SimulationState & getSimulationState() noexcept
Gets a mutable reference to the global simulation state.
Definition world.h:284
std::priority_queue< Event, std::vector< Event >, EventComparator > & getEventQueue() noexcept
Gets a mutable reference to the global event queue.
Definition world.h:275
std::vector< std::unique_ptr< serial::Response > > drainInbox() noexcept
Moves all responses from the inbox into a RenderingJob.
Definition receiver.cpp:42
std::optional< RealType > getNextWindowTime(RealType time) const
Determines the next valid window start time at or after the given time.
Definition receiver.cpp:132
void enqueueFinalizerJob(core::RenderingJob &&job)
Adds a completed RenderingJob to the finalizer queue.
Definition receiver.cpp:50
RealType getWindowPrf() const noexcept
Retrieves the pulse repetition frequency (PRF) of the radar window.
Definition receiver.h:116
RealType getWindowLength() const noexcept
Retrieves the radar window length.
Definition receiver.h:109
double RealType
Type for real numbers.
Definition config.h:27
@ RX_PULSED_WINDOW_START
A pulsed receiver opens its listening window.
RealType endTime() noexcept
Get the end time for the simulation.
Definition parameters.h:109
std::vector< radar::Transmitter * > active_cw_transmitters
A global list of all currently active continuous-wave transmitters.

References core::SimulationState::active_cw_transmitters, radar::Receiver::drainInbox(), params::endTime(), radar::Receiver::enqueueFinalizerJob(), core::World::getEventQueue(), radar::Receiver::getNextWindowTime(), core::World::getSimulationState(), radar::Receiver::getWindowLength(), radar::Receiver::getWindowPrf(), core::RenderingJob::ideal_start_time, core::RX_PULSED_WINDOW_START, and radar::Receiver::setActive().

Referenced by processEvent().

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

◆ handleRxPulsedWindowStart()

void core::SimulationEngine::handleRxPulsedWindowStart ( radar::Receiver rx,
RealType  t_event 
)

Handles the opening of a pulsed receiver's listening window.

Parameters
rxPointer to the receiving radar object.
t_eventThe timestamp of the window opening event.

Definition at line 211 of file sim_threading.cpp.

212 {
213 rx->setActive(true);
214 _world->getEventQueue().push({t_event + rx->getWindowLength(), EventType::RX_PULSED_WINDOW_END, rx});
215 }
@ RX_PULSED_WINDOW_END
A pulsed receiver closes its listening window.

References core::World::getEventQueue(), radar::Receiver::getWindowLength(), core::RX_PULSED_WINDOW_END, and radar::Receiver::setActive().

Referenced by processEvent().

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

◆ handleTxCwEnd()

void core::SimulationEngine::handleTxCwEnd ( radar::Transmitter tx)

Handles a continuous-wave transmitter turning off.

Parameters
txPointer to the transmitting radar object.

Definition at line 242 of file sim_threading.cpp.

243 {
244 auto& cw_txs = _world->getSimulationState().active_cw_transmitters;
245 std::erase(cw_txs, tx);
246 }

References core::SimulationState::active_cw_transmitters, and core::World::getSimulationState().

Referenced by processEvent().

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

◆ handleTxCwStart()

void core::SimulationEngine::handleTxCwStart ( radar::Transmitter tx)

Handles a continuous-wave transmitter turning on.

Parameters
txPointer to the transmitting radar object.

Definition at line 237 of file sim_threading.cpp.

238 {
239 _world->getSimulationState().active_cw_transmitters.push_back(tx);
240 }

References core::SimulationState::active_cw_transmitters, and core::World::getSimulationState().

Referenced by processEvent().

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

◆ handleTxPulsedStart()

void core::SimulationEngine::handleTxPulsedStart ( radar::Transmitter tx,
RealType  t_event 
)

Handles the start of a pulsed transmission.

Parameters
txPointer to the transmitting radar object.
t_eventThe timestamp of the transmission event.

Definition at line 187 of file sim_threading.cpp.

188 {
189 for (const auto& rx_ptr : _world->getReceivers())
190 {
191 if (!rx_ptr->checkFlag(Receiver::RecvFlag::FLAG_NODIRECT))
192 {
193 routeResponse(rx_ptr.get(), simulation::calculateResponse(tx, rx_ptr.get(), tx->getSignal(), t_event));
194 }
195 for (const auto& target_ptr : _world->getTargets())
196 {
197 routeResponse(
198 rx_ptr.get(),
199 simulation::calculateResponse(tx, rx_ptr.get(), tx->getSignal(), t_event, target_ptr.get()));
200 }
201 }
202
203 const RealType next_theoretical_time = t_event + 1.0 / tx->getPrf();
204 if (const auto next_pulse_opt = tx->getNextPulseTime(next_theoretical_time);
205 next_pulse_opt && *next_pulse_opt <= params::endTime())
206 {
207 _world->getEventQueue().push({*next_pulse_opt, EventType::TX_PULSED_START, tx});
208 }
209 }
RealType getPrf() const noexcept
Retrieves the pulse repetition frequency (PRF).
Definition transmitter.h:64
fers_signal::RadarSignal * getSignal() const noexcept
Retrieves the radar signal currently being transmitted.
Definition transmitter.h:71
std::optional< RealType > getNextPulseTime(RealType time) const
Determines the valid simulation time for a pulse at or after the given time.
@ TX_PULSED_START
A pulsed transmitter begins emitting a pulse.
std::unique_ptr< serial::Response > calculateResponse(const Transmitter *trans, const Receiver *recv, const RadarSignal *signal, const RealType startTime, const Target *targ)
Creates a Response object by simulating a signal's interaction over its duration.

References simulation::calculateResponse(), params::endTime(), core::World::getEventQueue(), radar::Transmitter::getNextPulseTime(), radar::Transmitter::getPrf(), core::World::getReceivers(), radar::Transmitter::getSignal(), core::World::getTargets(), and core::TX_PULSED_START.

Referenced by processEvent().

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

◆ processCwPhysics()

void core::SimulationEngine::processCwPhysics ( RealType  t_event)

Advances the time-stepped inner loop for active continuous-wave (CW) systems.

Parameters
t_eventThe timestamp of the next discrete event to process up to.

Definition at line 95 of file sim_threading.cpp.

96 {
97 auto& [t_current, active_cw_transmitters] = _world->getSimulationState();
98
99 if (t_event <= t_current)
100 {
101 return;
102 }
103
104 const RealType dt_sim = 1.0 / (params::rate() * params::oversampleRatio());
105 const auto start_index = static_cast<size_t>(std::ceil((t_current - params::startTime()) / dt_sim));
106 const auto end_index = static_cast<size_t>(std::ceil((t_event - params::startTime()) / dt_sim));
107
108 for (size_t sample_index = start_index; sample_index < end_index; ++sample_index)
109 {
110 const RealType t_step = params::startTime() + static_cast<RealType>(sample_index) * dt_sim;
111
112 for (const auto& receiver_ptr : _world->getReceivers())
113 {
114 if (receiver_ptr->getMode() == OperationMode::CW_MODE && receiver_ptr->isActive())
115 {
116 ComplexType sample = calculateCwSample(receiver_ptr.get(), t_step, active_cw_transmitters);
117 receiver_ptr->setCwSample(sample_index, sample);
118 }
119 }
120 }
121 }
ComplexType calculateCwSample(radar::Receiver *rx, RealType t_step, const std::vector< radar::Transmitter * > &cw_sources) const
Calculates the total complex I/Q sample for a receiver at a specific time step.
RealType rate() noexcept
Get the rendering sample rate.
Definition parameters.h:121
RealType startTime() noexcept
Get the start time for the simulation.
Definition parameters.h:103
unsigned oversampleRatio() noexcept
Get the oversampling ratio.
Definition parameters.h:151

References calculateCwSample(), core::World::getReceivers(), core::World::getSimulationState(), params::oversampleRatio(), params::rate(), and params::startTime().

Referenced by run().

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

◆ processEvent()

void core::SimulationEngine::processEvent ( const Event event)

Dispatches a discrete simulation event to its specific handler.

Parameters
eventThe event to process.

Definition at line 141 of file sim_threading.cpp.

142 {
143 // NOLINTBEGIN(cppcoreguidelines-pro-type-static-cast-downcast)
144 switch (event.type)
145 {
147 handleTxPulsedStart(static_cast<Transmitter*>(event.source_object), event.timestamp);
148 break;
150 handleRxPulsedWindowStart(static_cast<Receiver*>(event.source_object), event.timestamp);
151 break;
153 handleRxPulsedWindowEnd(static_cast<Receiver*>(event.source_object), event.timestamp);
154 break;
156 handleTxCwStart(static_cast<Transmitter*>(event.source_object));
157 break;
159 handleTxCwEnd(static_cast<Transmitter*>(event.source_object));
160 break;
162 handleRxCwStart(static_cast<Receiver*>(event.source_object));
163 break;
165 handleRxCwEnd(static_cast<Receiver*>(event.source_object));
166 break;
167 }
168 // NOLINTEND(cppcoreguidelines-pro-type-static-cast-downcast)
169 }
void handleRxCwStart(radar::Receiver *rx)
Handles a continuous-wave receiver starting to record.
void handleRxPulsedWindowEnd(radar::Receiver *rx, RealType t_event)
Handles the closing of a pulsed receiver's listening window, triggering finalization.
void handleTxCwEnd(radar::Transmitter *tx)
Handles a continuous-wave transmitter turning off.
void handleTxCwStart(radar::Transmitter *tx)
Handles a continuous-wave transmitter turning on.
void handleRxPulsedWindowStart(radar::Receiver *rx, RealType t_event)
Handles the opening of a pulsed receiver's listening window.
void handleRxCwEnd(radar::Receiver *rx)
Handles a continuous-wave receiver stopping recording.
void handleTxPulsedStart(radar::Transmitter *tx, RealType t_event)
Handles the start of a pulsed transmission.
Manages radar signal reception and response processing.
Definition receiver.h:37
Represents a radar transmitter system.
Definition transmitter.h:33
@ TX_CW_START
A continuous-wave transmitter starts transmitting.
@ TX_CW_END
A continuous-wave transmitter stops transmitting.
@ RX_CW_END
A continuous-wave receiver stops listening.
@ RX_CW_START
A continuous-wave receiver starts listening.

References handleRxCwEnd(), handleRxCwStart(), handleRxPulsedWindowEnd(), handleRxPulsedWindowStart(), handleTxCwEnd(), handleTxCwStart(), handleTxPulsedStart(), core::RX_CW_END, core::RX_CW_START, core::RX_PULSED_WINDOW_END, core::RX_PULSED_WINDOW_START, core::Event::source_object, core::Event::timestamp, core::TX_CW_END, core::TX_CW_START, core::TX_PULSED_START, and core::Event::type.

Referenced by run().

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

◆ run()

void core::SimulationEngine::run ( )

Starts and runs the main simulation loop until completion.

Definition at line 52 of file sim_threading.cpp.

53 {
54 if (_reporter)
55 {
56 _reporter->report("Initializing event-driven simulation...", 0, 100);
57 }
58
59 initializeFinalizers();
60
61 LOG(Level::INFO, "Starting unified event-driven simulation loop.");
62
63 auto& event_queue = _world->getEventQueue();
64 auto& state = _world->getSimulationState();
65 const RealType end_time = params::endTime();
66
67 while (!event_queue.empty() && state.t_current <= end_time)
68 {
69 const Event event = event_queue.top();
70 event_queue.pop();
71
72 processCwPhysics(event.timestamp);
73
74 state.t_current = event.timestamp;
75
76 processEvent(event);
77 updateProgress();
78 }
79
80 shutdown();
81 }
void processEvent(const Event &event)
Dispatches a discrete simulation event to its specific handler.
void processCwPhysics(RealType t_event)
Advances the time-stepped inner loop for active continuous-wave (CW) systems.
#define LOG(level,...)
Definition logging.h:19

References params::endTime(), core::World::getEventQueue(), core::World::getSimulationState(), LOG, processCwPhysics(), and processEvent().

Referenced by core::runEventDrivenSim().

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

The documentation for this class was generated from the following files: