FERS 0.1.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, ReceiverOutputSink *output_sink=nullptr, std::function< bool()> cancel_callback=nullptr, bool eager_context_stream_open=false)
 Constructs the simulation engine.
 
void run ()
 Starts and runs the main simulation loop until completion.
 
bool cancelled () const noexcept
 Returns true after cooperative cancellation has been requested.
 
void processStreamingPhysics (RealType t_event)
 Advances the time-stepped inner loop for active streaming 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 handleTxStreamingStart (const ActiveStreamingSource &source)
 Handles a streaming transmitter turning on.
 
void handleTxStreamingEnd (radar::Transmitter *tx)
 Handles a streaming transmitter turning off.
 
void handleRxStreamingStart (radar::Receiver *rx)
 Handles a streaming receiver starting to record.
 
void handleRxStreamingEnd (radar::Receiver *rx)
 Handles a streaming receiver stopping recording.
 

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 109 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,
ReceiverOutputSink output_sink = nullptr,
std::function< bool()>  cancel_callback = nullptr,
bool  eager_context_stream_open = false 
)

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 816 of file sim_threading.cpp.

820 :
821 _world(world), _pool(pool), _reporter(std::move(reporter)), _metadata_collector(std::move(metadata_collector)),
822 _output_sink(output_sink), _cancel_callback(std::move(cancel_callback)),
823 _eager_context_stream_open(eager_context_stream_open), _last_report_time(std::chrono::steady_clock::now()),
824 _next_context_heartbeat_time(params::startTime() + 1.0), _output_dir(std::move(output_dir)),
825 _internal_stop_time(params::endTime())
826 {
827 _streaming_tracker_caches.resize(_world->getReceivers().size());
828 _if_pulse_tracker_caches.resize(_world->getReceivers().size());
829 _fmcw_if_block_buffers.resize(_world->getReceivers().size());
830 _fmcw_if_block_start_times.resize(_world->getReceivers().size(), params::startTime());
831 _streaming_output_block_buffers.resize(_world->getReceivers().size());
832 _streaming_output_processed_buffers.resize(_world->getReceivers().size());
833 _streaming_output_block_start_times.resize(_world->getReceivers().size(), params::startTime());
834 _streaming_output_block_start_indices.resize(_world->getReceivers().size(), 0);
835 _streaming_downsamplers.resize(_world->getReceivers().size());
836 _streaming_downsample_base_indices.resize(_world->getReceivers().size(), 0);
837 _streaming_downsample_segment_start_times.resize(_world->getReceivers().size(), params::startTime());
838 _streaming_output_sample_cursors.resize(_world->getReceivers().size(), 0);
839 _streaming_output_stream_ids.resize(_world->getReceivers().size(), 0);
840 _streaming_output_stream_open.resize(_world->getReceivers().size(), false);
841 _streaming_output_file_metadata.resize(_world->getReceivers().size());
842 for (auto& block : _streaming_output_block_buffers)
843 {
845 }
846 for (auto& block : _streaming_output_processed_buffers)
847 {
849 }
850 }
const std::vector< std::unique_ptr< radar::Receiver > > & getReceivers() const noexcept
Retrieves the list of radar receivers.
Definition world.h:236
RealType endTime() noexcept
Get the end time for the simulation.
Definition parameters.h:109
RealType startTime() noexcept
Get the start time for the simulation.
Definition parameters.h:103
math::Vec3 max

References core::World::getReceivers(), max, and params::startTime().

+ Here is the call graph for this function:

Member Function Documentation

◆ cancelled()

bool core::SimulationEngine::cancelled ( ) const
noexcept

Returns true after cooperative cancellation has been requested.

Definition at line 130 of file sim_threading.h.

130{ return _cancelled; }

◆ 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 1881 of file sim_threading.cpp.

1882 {
1883 rx->setActive(false);
1884 const auto active_streaming_sources =
1885 collectStreamingSourcesForWindow(t_event - rx->getWindowLength(), t_event);
1886
1887 RenderingJob job{.ideal_start_time = t_event - rx->getWindowLength(),
1888 .duration = rx->getWindowLength(),
1889 .responses = rx->drainInbox(),
1890 .active_streaming_sources = active_streaming_sources};
1891
1892 rx->enqueueFinalizerJob(std::move(job));
1893
1894 const RealType next_theoretical = t_event - rx->getWindowLength() + 1.0 / rx->getWindowPrf();
1895 if (const auto next_start = rx->getNextWindowTime(next_theoretical);
1897 {
1899 }
1900 }
std::priority_queue< Event, std::vector< Event >, EventComparator > & getEventQueue() noexcept
Gets a mutable reference to the global event queue.
Definition world.h:313
double RealType
Type for real numbers.
Definition config.h:27
@ RX_PULSED_WINDOW_START
A pulsed receiver opens its listening window.

References params::endTime(), core::World::getEventQueue(), max, and core::RX_PULSED_WINDOW_START.

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 1875 of file sim_threading.cpp.

1876 {
1877 rx->setActive(true);
1878 _world->getEventQueue().push({t_event + rx->getWindowLength(), EventType::RX_PULSED_WINDOW_END, rx});
1879 }
@ RX_PULSED_WINDOW_END
A pulsed receiver closes its listening window.

References core::World::getEventQueue(), max, and core::RX_PULSED_WINDOW_END.

Referenced by processEvent().

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

◆ handleRxStreamingEnd()

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

Handles a streaming receiver stopping recording.

Parameters
rxPointer to the receiving radar object.

Definition at line 1937 of file sim_threading.cpp.

1938 {
1939 const auto receiver_it = std::ranges::find_if(_world->getReceivers(), [rx](const auto& receiver_ptr)
1940 { return receiver_ptr.get() == rx; });
1941 if (receiver_it != _world->getReceivers().end())
1942 {
1943 const auto receiver_index = static_cast<std::size_t>(receiver_it - _world->getReceivers().begin());
1944 flushFmcwIfBlock(receiver_index);
1945 flushStreamingOutputBlock(receiver_index, true);
1946 }
1947 if (rx->hasFmcwIfResamplingSink() && _world->getSimulationState().t_current >= params::endTime() &&
1948 _world->getSimulationState().t_current < _internal_stop_time && activePastUserEnd(rx))
1949 {
1950 return;
1951 }
1952 if (rx->hasFmcwIfResamplingSink())
1953 {
1954 rx->endFmcwIfResamplingSegment();
1955 }
1956 if (_output_sink != nullptr && receiver_it != _world->getReceivers().end())
1957 {
1958 const auto receiver_index = static_cast<std::size_t>(receiver_it - _world->getReceivers().begin());
1959 if (_streaming_output_stream_open[receiver_index])
1960 {
1961 _output_sink->closeStream(_streaming_output_stream_ids[receiver_index]);
1962 _streaming_output_stream_open[receiver_index] = false;
1963 }
1964 }
1965 rx->setActive(false);
1966 }
virtual void closeStream(std::uint32_t stream_id)=0
SimulationState & getSimulationState() noexcept
Gets a mutable reference to the global simulation state.
Definition world.h:322
RealType t_current
The master simulation clock, advanced by the event loop.

References core::ReceiverOutputSink::closeStream(), params::endTime(), core::World::getReceivers(), core::World::getSimulationState(), max, and core::SimulationState::t_current.

Referenced by processEvent().

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

◆ handleRxStreamingStart()

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

Handles a streaming receiver starting to record.

Parameters
rxPointer to the receiving radar object.

Definition at line 1916 of file sim_threading.cpp.

1917 {
1918 rx->setActive(true);
1919 const auto receiver_it = std::ranges::find_if(_world->getReceivers(), [rx](const auto& receiver_ptr)
1920 { return receiver_ptr.get() == rx; });
1921 if (receiver_it != _world->getReceivers().end())
1922 {
1923 const auto receiver_index = static_cast<std::size_t>(receiver_it - _world->getReceivers().begin());
1924 _streaming_downsamplers[receiver_index].reset();
1925 if (_eager_context_stream_open)
1926 {
1927 ensureStreamingOutputStreamOpen(receiver_index, _world->getSimulationState().t_current,
1928 streamingOutputSampleRate(receiver_index));
1929 }
1930 }
1931 if (rx->hasFmcwIfResamplingSink())
1932 {
1933 rx->beginFmcwIfResamplingSegment(_world->getSimulationState().t_current);
1934 }
1935 }

References core::World::getReceivers(), core::World::getSimulationState(), max, and core::SimulationState::t_current.

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 1851 of file sim_threading.cpp.

1852 {
1853 for (const auto& rx_ptr : _world->getReceivers())
1854 {
1855 if (!rx_ptr->checkFlag(Receiver::RecvFlag::FLAG_NODIRECT))
1856 {
1857 routeResponse(rx_ptr.get(), simulation::calculateResponse(tx, rx_ptr.get(), tx->getSignal(), t_event));
1858 }
1859 for (const auto& target_ptr : _world->getTargets())
1860 {
1861 routeResponse(
1862 rx_ptr.get(),
1863 simulation::calculateResponse(tx, rx_ptr.get(), tx->getSignal(), t_event, target_ptr.get()));
1864 }
1865 }
1866
1867 const RealType next_theoretical_time = t_event + 1.0 / tx->getPrf();
1868 if (const auto next_pulse_opt = tx->getNextPulseTime(next_theoretical_time);
1870 {
1872 }
1873 }
@ 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(), core::World::getReceivers(), core::World::getTargets(), max, and core::TX_PULSED_START.

Referenced by processEvent().

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

◆ handleTxStreamingEnd()

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

Handles a streaming transmitter turning off.

Parameters
txPointer to the transmitting radar object.

Definition at line 1908 of file sim_threading.cpp.

1909 {
1910 (void)tx;
1911 // A transmitter stop is a transmit-time boundary, not an instantaneous receive-time cutoff.
1912 // Ended sources are removed only after all future receive-time samples fail the retarded-time gate.
1913 cleanupInactiveStreamingSources(_world->getSimulationState().t_current);
1914 }

References core::World::getSimulationState(), max, and core::SimulationState::t_current.

Referenced by processEvent().

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

◆ handleTxStreamingStart()

void core::SimulationEngine::handleTxStreamingStart ( const ActiveStreamingSource source)

Handles a streaming transmitter turning on.

Parameters
txPointer to the transmitting radar object.

Definition at line 1902 of file sim_threading.cpp.

1903 {
1904 _world->getSimulationState().active_streaming_transmitters.push_back(source);
1905 appendStreamingTrackerSource();
1906 }
std::vector< ActiveStreamingSource > active_streaming_transmitters
A global list of all currently active streaming transmitters.

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

Referenced by processEvent().

+ 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 1800 of file sim_threading.cpp.

1801 {
1802 // NOLINTBEGIN(cppcoreguidelines-pro-type-static-cast-downcast)
1803 switch (event.type)
1804 {
1806 handleTxPulsedStart(static_cast<Transmitter*>(event.source_object), event.timestamp);
1807 break;
1809 handleRxPulsedWindowStart(static_cast<Receiver*>(event.source_object), event.timestamp);
1810 break;
1812 handleRxPulsedWindowEnd(static_cast<Receiver*>(event.source_object), event.timestamp);
1813 break;
1815 if (const auto source = streamingSourceAtEvent(static_cast<Transmitter*>(event.source_object),
1816 event.timestamp, _internal_stop_time);
1817 source.has_value())
1818 {
1819 handleTxStreamingStart(*source);
1820 }
1821 break;
1823 handleTxStreamingEnd(static_cast<Transmitter*>(event.source_object));
1824 break;
1826 handleRxStreamingStart(static_cast<Receiver*>(event.source_object));
1827 break;
1829 handleRxStreamingEnd(static_cast<Receiver*>(event.source_object));
1830 break;
1831 }
1832 // NOLINTEND(cppcoreguidelines-pro-type-static-cast-downcast)
1833 }
void handleRxStreamingStart(radar::Receiver *rx)
Handles a streaming receiver starting to record.
void handleTxStreamingEnd(radar::Transmitter *tx)
Handles a streaming transmitter turning off.
void handleRxPulsedWindowEnd(radar::Receiver *rx, RealType t_event)
Handles the closing of a pulsed receiver's listening window, triggering finalization.
void handleRxPulsedWindowStart(radar::Receiver *rx, RealType t_event)
Handles the opening of a pulsed receiver's listening window.
void handleTxStreamingStart(const ActiveStreamingSource &source)
Handles a streaming transmitter turning on.
void handleRxStreamingEnd(radar::Receiver *rx)
Handles a streaming 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:47
Represents a radar transmitter system.
Definition transmitter.h:34
@ TX_STREAMING_END
A streaming transmitter stops transmitting.
@ RX_STREAMING_END
A streaming receiver stops listening.
@ TX_STREAMING_START
A streaming transmitter starts transmitting.
@ RX_STREAMING_START
A streaming receiver starts listening.

References handleRxPulsedWindowEnd(), handleRxPulsedWindowStart(), handleRxStreamingEnd(), handleRxStreamingStart(), handleTxPulsedStart(), handleTxStreamingEnd(), handleTxStreamingStart(), max, core::RX_PULSED_WINDOW_END, core::RX_PULSED_WINDOW_START, core::RX_STREAMING_END, core::RX_STREAMING_START, core::Event::source_object, core::Event::timestamp, core::TX_PULSED_START, core::TX_STREAMING_END, core::TX_STREAMING_START, and core::Event::type.

Referenced by run().

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

◆ processStreamingPhysics()

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

Advances the time-stepped inner loop for active streaming systems.

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

Definition at line 1064 of file sim_threading.cpp.

1065 {
1066 auto& state = _world->getSimulationState();
1067 auto& t_current = state.t_current;
1068
1069 if (t_event <= t_current)
1070 {
1071 return;
1072 }
1073
1075 const auto first_index = streamingSampleIndexAtOrAfter(t_current, dt_sim);
1077 const auto sample_count = final_index - first_index;
1078 const auto progress_report_stride = std::max<std::size_t>(1, sample_count / 1000);
1079
1080 ensureCwPhaseNoiseLookup();
1081
1082 while (t_current < t_event && !isCancellationRequested())
1083 {
1084 cleanupInactiveStreamingSources(t_current);
1085
1086 const RealType chunk_end = streamingChunkEnd(t_current, t_event);
1087 if (chunk_end <= t_current)
1088 {
1089 break;
1090 }
1091
1092 const auto start_index = streamingSampleIndexAtOrAfter(t_current, dt_sim);
1095 {
1096 if (shouldStopStreamingChunk(sample_index, start_index))
1097 {
1098 break;
1099 }
1101 }
1102
1103 t_current = chunk_end;
1104 emitContextHeartbeatsThrough(t_current);
1105 }
1106 cleanupInactiveStreamingSources(t_current);
1107 }
RealType rate() noexcept
Get the rendering sample rate.
Definition parameters.h:121
unsigned oversampleRatio() noexcept
Get the oversampling ratio.
Definition parameters.h:151

References core::World::getSimulationState(), max, params::oversampleRatio(), and params::rate().

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 852 of file sim_threading.cpp.

853 {
854 if (_reporter)
855 {
856 _reporter->report("Initializing event-driven simulation...", 0, 100);
857 }
858
859 initializeFmcwIfResamplers();
860
862
863 initializeFinalizers();
864
865 LOG(Level::INFO, "Starting unified event-driven simulation loop.");
866 logStreamingSummaries();
867
868 auto& event_queue = _world->getEventQueue();
869 auto& state = _world->getSimulationState();
870 const RealType end_time = _internal_stop_time;
871
872 while (!event_queue.empty() && state.t_current <= end_time)
873 {
874 if (isCancellationRequested())
875 {
876 break;
877 }
878 const Event event = event_queue.top();
879 event_queue.pop();
880
881 processStreamingPhysics(event.timestamp);
882 if (isCancellationRequested())
883 {
884 break;
885 }
886 flushFmcwIfBlocks();
887 flushStreamingOutputBlocks();
888
889 state.t_current = event.timestamp;
890
891 processEvent(event);
892 updateProgress();
893 }
894
895 const bool has_active_if_overrender =
896 std::ranges::any_of(_world->getReceivers(), [](const auto& receiver)
897 { return receiver->isActive() && receiver->hasFmcwIfResamplingSink(); });
898 if (!isCancellationRequested() && has_active_if_overrender)
899 {
900 processStreamingPhysics(end_time);
901 }
902 flushFmcwIfBlocks();
903 flushStreamingOutputBlocks();
904
905 shutdown();
906 }
const Receiver & receiver
void processEvent(const Event &event)
Dispatches a discrete simulation event to its specific handler.
void processStreamingPhysics(RealType t_event)
Advances the time-stepped inner loop for active streaming systems.
#define LOG(level,...)
Definition logging.h:19
void logSimulationMemoryProjection(const World &world)
Logs the projected simulation memory footprint for the provided world.

References core::World::getEventQueue(), core::World::getReceivers(), core::World::getSimulationState(), LOG, core::logSimulationMemoryProjection(), max, processEvent(), processStreamingPhysics(), and receiver.

+ Here is the call graph for this function:

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