67 return static_cast<std::size_t
>(
73 return Vita49StreamMetadata{.receiver_id = stats.receiver_id,
74 .receiver_name = stats.receiver_name,
75 .stream_id = stats.stream_id,
77 .sample_rate = stats.sample_rate,
78 .reference_frequency = stats.reference_frequency,
79 .packets_emitted = stats.packets_emitted,
80 .samples_emitted = stats.samples_emitted,
81 .packets_dropped = stats.packets_dropped,
82 .samples_dropped = stats.samples_dropped,
83 .over_range_count = stats.over_range_count,
84 .late_data_packet_count = stats.late_data_packet_count,
85 .late_context_packet_count = stats.late_context_packet_count,
86 .context_packet_count = stats.context_packets,
87 .first_sample_time = stats.first_sample_time,
88 .end_sample_time = stats.end_sample_time,
89 .first_timestamp = stats.first_timestamp,
90 .end_timestamp = stats.end_timestamp};
95 return count.has_value() ? std::format(
"{}", *count) : std::
string(
"unbounded");
106 "FMCW transmitter '{}' shape=linear {} B={} Hz T_c={} s T_rep={} s f_0={} Hz alpha={} Hz/s "
107 "duty_cycle={} chirp_count={} total_chirp_count={} average_power={} W",
126 "FMCW transmitter '{}' segment [{}, {}] shape=linear {} B={} Hz T_c={} s T_rep={} s f_0={} "
127 "Hz alpha={} Hz/s duty_cycle={} chirp_count={} segment_chirp_count={} total_chirp_count={} "
128 "average_power={} W",
159 "FMCW transmitter '{}' shape=triangle B={} Hz T_c={} s T_tri={} s f_0={} Hz alpha={} Hz/s "
160 "duty_cycle=1 triangle_count={} total_triangle_count={} average_power={} W",
179 "FMCW transmitter '{}' segment [{}, {}] shape=triangle B={} Hz T_c={} s T_tri={} s f_0={} "
180 "Hz alpha={} Hz/s duty_cycle=1 triangle_count={} segment_triangle_count={} "
181 "total_triangle_count={} average_power={} W",
211 "SFCW transmitter '{}' steps={} df={} Hz dwell={} s step_period={} s sweep_period={} s "
212 "f_first={} Hz f_last={} Hz B_eff={} Hz range_resolution={} m unambiguous_range={} m duty_cycle={} "
213 "sweep_count={} total_step_count={} average_power={} W",
235 "SFCW transmitter '{}' segment [{}, {}] steps={} df={} Hz dwell={} s step_period={} s "
236 "sweep_period={} s f_first={} Hz f_last={} Hz B_eff={} Hz range_resolution={} m "
237 "unambiguous_range={} m duty_cycle={} sweep_count={} segment_step_count={} total_step_count={} "
238 "average_power={} W",
280 [](
const auto&
period) { return period.end > params::endTime(); });
292 struct PositionBounds
367 const std::size_t
axis)
noexcept
378 if (
lhs.unbounded ||
rhs.unbounded || !
lhs.valid || !
rhs.valid)
380 return std::numeric_limits<RealType>::infinity();
405 const std::vector<math::Coord>&
coords,
502 for (std::size_t index = 0; index + 1 <
coords.size(); ++index)
513 std::clamp((std::max(start, segment_start) - segment_start) /
segment_length, 0.0, 1.0);
515 std::clamp((std::min(end, segment_end) - segment_start) /
segment_length, 0.0, 1.0);
524 struct QuadraticVelocityExtremum
536 const QuadraticVelocityExtremum&
extremum)
noexcept
558 const std::vector<math::Coord>&
coords,
582 QuadraticVelocityExtremum{.a =
a,
590 QuadraticVelocityExtremum{.a =
a,
601 QuadraticVelocityExtremum{.a =
a,
605 .root_u = -
b / (2.0 *
a),
628 for (std::size_t index = 0; index + 1 <
coords.size(); ++index)
650 return std::numeric_limits<RealType>::infinity();
654 for (std::size_t index = 0; index + 1 <
coords.size(); ++index)
664 std::clamp((std::max(start, segment_start) - segment_start) /
segment_length, 0.0, 1.0);
666 std::clamp((std::min(end, segment_end) - segment_start) /
segment_length, 0.0, 1.0);
721 const auto*
const tx = source.transmitter;
722 if (
tx ==
nullptr ||
rx ==
nullptr ||
tx->getPlatform() ==
rx->getPlatform() ||
params::c() <= 0.0)
727 const auto*
const tx_path =
tx->getPlatform()->getMotionPath();
728 const auto*
const rx_path =
rx->getPlatform()->getMotionPath();
747 const auto*
const tx = source.transmitter;
749 tx->getPlatform() ==
target->getPlatform() ||
rx->getPlatform() ==
target->getPlatform())
754 const auto*
const tx_path =
tx->getPlatform()->getMotionPath();
755 const auto*
const rx_path =
rx->getPlatform()->getMotionPath();
793 if (timestamp >= segment_start && timestamp < source.segment_end)
807 if (timestamp >=
active_start && timestamp < source.segment_end)
817 std::string output_dir,
818 std::shared_ptr<OutputMetadataCollector> metadata_collector,
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)),
824 _next_context_heartbeat_time(
params::startTime() + 1.0), _output_dir(std::
move(output_dir)),
825 _internal_stop_time(
params::endTime())
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());
831 _streaming_output_block_buffers.resize(_world->
getReceivers().size());
832 _streaming_output_processed_buffers.resize(_world->
getReceivers().size());
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);
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)
846 for (
auto&
block : _streaming_output_processed_buffers)
856 _reporter->report(
"Initializing event-driven simulation...", 0, 100);
859 initializeFmcwIfResamplers();
863 initializeFinalizers();
865 LOG(Level::INFO,
"Starting unified event-driven simulation loop.");
866 logStreamingSummaries();
870 const RealType end_time = _internal_stop_time;
874 if (isCancellationRequested())
882 if (isCancellationRequested())
887 flushStreamingOutputBlocks();
889 state.t_current =
event.timestamp;
897 { return receiver->isActive() && receiver->hasFmcwIfResamplingSink(); });
903 flushStreamingOutputBlocks();
908 void SimulationEngine::logStreamingSummaries()
const
913 if (waveform ==
nullptr)
933 void SimulationEngine::initializeFinalizers()
935 if (_output_sink ==
nullptr)
941 if (
receiver_ptr->getMode() == OperationMode::PULSED_MODE)
944 &_world->
getTargets(), _reporter, _output_dir, _metadata_collector,
950 void SimulationEngine::initializeFmcwIfResamplers()
960 extendDechirpSourcesForIfOverrender();
964 void SimulationEngine::initializeFmcwIfResampler(
const std::size_t
receiver_index)
978 .filter_bandwidth_hz = bandwidth,
979 .filter_transition_width_hz =
request.filter_transition_width_hz};
984 if (_output_sink !=
nullptr)
987 [
this,
receiver_index](
const std::span<const ComplexType> samples,
const std::uint64_t sample_start)
996 static_cast<RealType>(sample_start) /
if_plan->actual_output_sample_rate_hz;
998 samples, sample_start);
1001 const RealType actual_output_sample_rate_hz = plan.actual_output_sample_rate_hz;
1002 const auto overall_ratio = plan.overall_ratio;
1003 const RealType filter_bandwidth_hz = plan.filter_bandwidth_hz;
1004 const RealType filter_transition_width_hz = plan.filter_transition_width_hz;
1005 receiver_ptr->initializeFmcwIfResampling(std::move(plan));
1007 "Receiver '{}' enabled FMCW IF resampling: input_rate={} Hz requested_output_rate={} Hz "
1008 "actual_output_rate={} Hz ratio={}/{} passband={} Hz transition={} Hz.",
1010 overall_ratio.numerator, overall_ratio.denominator, filter_bandwidth_hz, filter_transition_width_hz);
1013 void SimulationEngine::extendDechirpSourcesForIfOverrender()
1015 for (
const auto&
receiver_ptr : _world->getReceivers())
1029 if (source.transmitter ==
nullptr || source.transmitter->getSchedule().empty())
1031 source.segment_end = _internal_stop_time;
1038 source.segment_end = std::min(_internal_stop_time,
period.end);
1047 void SimulationEngine::ensureCwPhaseNoiseLookup()
1049 if (_cw_phase_noise_lookup)
1056 for (
const auto& source : _world->getSimulationState().active_streaming_transmitters)
1060 _cw_phase_noise_lookup = std::make_unique<simulation::CwPhaseNoiseLookup>(
1067 auto& t_current =
state.t_current;
1080 ensureCwPhaseNoiseLookup();
1082 while (t_current <
t_event && !isCancellationRequested())
1084 cleanupInactiveStreamingSources(t_current);
1104 emitContextHeartbeatsThrough(t_current);
1106 cleanupInactiveStreamingSources(t_current);
1109 std::optional<RealType> SimulationEngine::nextStreamingCleanupDeadline(
const RealType from_time)
1113 for (
const auto& source : active_streaming_transmitters)
1151 if (_output_sink !=
nullptr &&
t_step >= _next_context_heartbeat_time)
1153 emitContextHeartbeatsThrough(
t_step);
1157 reportSimulationProgress(
t_step);
1167 if (std::ranges::any_of(_streaming_output_block_buffers,
1170 flushStreamingOutputBlocks(
true);
1174 void SimulationEngine::appendReceiverStreamingSample(
const std::size_t
receiver_index,
1190 else if (_output_sink !=
nullptr)
1204 block.push_back(sample);
1214 if (_eager_context_stream_open)
1224 block.push_back(sample);
1227 void SimulationEngine::flushStreamingOutputBlocks(
const bool full_blocks_only)
1229 std::vector<ReceiverSampleBlock>
batch;
1230 batch.reserve(_streaming_output_block_buffers.size());
1239 if (_output_sink !=
nullptr && !
batch.empty())
1246 std::vector<ReceiverSampleBlock>*
batch)
1303 else if (!dechirped)
1331 _streaming_downsamplers[
receiver_index] = std::make_unique<fers_signal::DownsamplingSink>();
1350 return if_plan.has_value() ?
if_plan->actual_output_sample_rate_hz : 0.0;
1359 void SimulationEngine::ensureStreamingOutputStreamOpen(
const std::size_t
receiver_index,
1392 void SimulationEngine::emitStreamingOutputBlock(
const std::size_t
receiver_index,
const RealType first_sample_time,
1394 const std::span<const ComplexType> samples,
1395 const std::uint64_t sample_start,
1396 std::vector<ReceiverSampleBlock>*
batch)
1405 processed.assign(samples.begin(), samples.end());
1410 ensureStreamingOutputStreamOpen(
receiver_index, first_sample_time, sample_rate);
1415 if (
batch !=
nullptr)
1423 _streaming_output_sample_cursors[
receiver_index] = sample_start +
static_cast<std::uint64_t
>(
processed.size());
1426 void SimulationEngine::emitContextHeartbeatsThrough(
const RealType simulation_time)
1428 if (_output_sink ==
nullptr)
1432 if (_next_context_heartbeat_time > simulation_time)
1437 if (simulation_time - _next_context_heartbeat_time < 1.0)
1440 _next_context_heartbeat_time += 1.0;
1445 _next_context_heartbeat_time = simulation_time + 1.0;
1448 void SimulationEngine::flushFmcwIfBlocks()
1456 void SimulationEngine::flushFmcwIfBlock(
const std::size_t
receiver_index)
1479 void SimulationEngine::applyPulsedInterferenceToFmcwIfBlock(
const std::size_t
receiver_index,
1480 std::span<ComplexType>
block,
1488 void SimulationEngine::addPulsedInterferenceSamples(std::span<ComplexType>
block,
1506 if (!
mixer.has_value())
1519 void SimulationEngine::applyPulsedInterferenceToStreamingBlock(
const std::size_t
receiver_index,
1520 std::span<ComplexType>
block,
1530 if (!std::isfinite(sample_rate) || sample_rate <= 0.0)
1556 const auto dest_begin =
static_cast<long long>(
1558 const auto dest_end =
static_cast<long long>(std::min<RealType>(
1590 std::ranges::fill(
tracker_cache.dechirp_reference, FmcwChirpBoundaryTracker{});
1613 return std::nullopt;
1617 if (
rx->getDechirpMode() == Receiver::DechirpMode::Physical && _cw_phase_noise_lookup)
1640 : (
rx->getDechirpMode() == Receiver::DechirpMode::Ideal
1648 if (!
rx->checkFlag(Receiver::RecvFlag::FLAG_NODIRECT))
1678 void SimulationEngine::appendStreamingTrackerSource()
1682 for (
auto&
cache : _streaming_tracker_caches)
1684 cache.direct.emplace_back();
1689 void SimulationEngine::eraseStreamingTrackerSource(
const std::size_t
source_index)
1691 for (
auto&
cache : _streaming_tracker_caches)
1704 void SimulationEngine::cleanupInactiveStreamingSources(
const RealType from_time)
1720 sources.erase(
sources.begin() +
static_cast<std::ptrdiff_t
>(index));
1721 eraseStreamingTrackerSource(index);
1725 std::optional<RealType> SimulationEngine::streamingSourceCleanupDeadline(
const ActiveStreamingSource& source,
1728 if (source.transmitter ==
nullptr || source.carrier_freq <= 0.0)
1730 return std::nullopt;
1734 for (
const auto&
receiver_ptr : _world->getReceivers())
1746 std::optional<RealType> SimulationEngine::receiverCleanupDeadline(
const ActiveStreamingSource& source,
1752 return std::nullopt;
1770 return std::nullopt;
1773 std::optional<RealType>
latest;
1774 if (!
rx->checkFlag(Receiver::RecvFlag::FLAG_NODIRECT))
1778 for (
const auto&
target_ptr : _world->getTargets())
1835 void SimulationEngine::routeResponse(
Receiver*
rx, std::unique_ptr<serial::Response>
response)
const
1841 if (
rx->getMode() == OperationMode::PULSED_MODE)
1847 rx->addInterferenceToLog(std::move(
response));
1855 if (!
rx_ptr->checkFlag(Receiver::RecvFlag::FLAG_NODIRECT))
1877 rx->setActive(
true);
1883 rx->setActive(
false);
1884 const auto active_streaming_sources =
1885 collectStreamingSourcesForWindow(
t_event -
rx->getWindowLength(),
t_event);
1888 .duration =
rx->getWindowLength(),
1889 .responses =
rx->drainInbox(),
1890 .active_streaming_sources = active_streaming_sources};
1892 rx->enqueueFinalizerJob(std::move(
job));
1905 appendStreamingTrackerSource();
1918 rx->setActive(
true);
1920 { return receiver_ptr.get() == rx; });
1925 if (_eager_context_stream_open)
1931 if (
rx->hasFmcwIfResamplingSink())
1940 { return receiver_ptr.get() == rx; });
1952 if (
rx->hasFmcwIfResamplingSink())
1954 rx->endFmcwIfResamplingSegment();
1965 rx->setActive(
false);
1970 bool SimulationEngine::isCancellationRequested()
1976 if (_cancel_callback && _cancel_callback())
1979 LOG(Level::INFO,
"Simulation cancellation requested.");
1982 _reporter->report(
"Simulation cancelled", 100, 100);
1989 void SimulationEngine::reportSimulationProgress(
const RealType t_current)
1998 const RealType duration = end_time - start_time;
2000 const int progress =
static_cast<int>(
2003 if (
const auto now = std::chrono::steady_clock::now();
2004 progress != _last_reported_percent ||
now - _last_report_time >= std::chrono::milliseconds(100))
2006 _reporter->report(std::format(
"Simulating... {:.2f}s / {:.2f}s", t_current, end_time),
progress, 100);
2008 _last_report_time =
now;
2012 std::vector<ActiveStreamingSource> SimulationEngine::collectStreamingSourcesForWindow(
const RealType start_time,
2017 std::vector<ActiveStreamingSource>
sources;
2028 if (source.segment_start < source.segment_end && source.segment_start < end_time)
2048 void SimulationEngine::shutdown()
2050 LOG(Level::INFO,
"Simulation compute loop finished. Waiting for receiver finalization tasks...");
2053 _reporter->report(
"Simulation compute finished. Waiting for receiver finalization...", 100, 100);
2061 if (_output_sink !=
nullptr)
2073 else if (
receiver_ptr->getMode() == OperationMode::PULSED_MODE)
2090 LOG(Level::INFO,
"All finalization tasks complete.");
2094 const std::function<
void(
const std::string&,
int,
int)>& progress_callback,
2096 std::function<
bool()> cancel_callback,
bool* cancelled,
2099 if (cancelled !=
nullptr)
2103 auto reporter = std::make_shared<ProgressReporter>(progress_callback);
2104 auto metadata_collector = std::make_shared<OutputMetadataCollector>(output_dir);
2120 if (cancelled !=
nullptr)
2122 *cancelled =
engine.cancelled();
2126 LOG(Level::INFO,
"Waiting for VITA output stream drain...");
2127 reporter->report(
"Waiting for VITA output stream drain...", 100, 100);
2130 reporter->report(
engine.cancelled() ?
"Simulation cancelled" :
"Simulation complete", 100, 100);
2131 LOG(Level::INFO,
"Event-driven simulation loop finished.");
2132 auto metadata = metadata_collector->snapshot();
2138 if (stats.epoch_unix_nanoseconds.has_value())
2140 vita49_metadata.epoch_unix_nanoseconds = stats.epoch_unix_nanoseconds;
2142 for (
const auto& stream : stats.streams)
const Transmitter & transmitter
const Receiver & receiver
Header for radar channel propagation and interaction models.
virtual void emitContextHeartbeat(RealType simulation_time)=0
virtual void submitBlock(const ReceiverSampleBlock &block)=0
virtual void submitBlocks(const std::span< const ReceiverSampleBlock > blocks)
virtual std::uint32_t registerStream(const ReceiverStreamDescriptor &stream)=0
virtual void closeStream(std::uint32_t stream_id)=0
virtual void openStream(std::uint32_t stream_id, RealType first_sample_time)=0
Encapsulates the state and logic of the event-driven simulation loop.
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.
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 handleRxPulsedWindowStart(radar::Receiver *rx, RealType t_event)
Handles the opening of a pulsed receiver's listening window.
void run()
Starts and runs the main simulation loop until completion.
void processEvent(const Event &event)
Dispatches a discrete simulation event to its specific handler.
void handleTxStreamingStart(const ActiveStreamingSource &source)
Handles a streaming transmitter turning on.
void handleRxStreamingEnd(radar::Receiver *rx)
Handles a streaming receiver stopping recording.
void processStreamingPhysics(RealType t_event)
Advances the time-stepped inner loop for active streaming systems.
void handleTxPulsedStart(radar::Transmitter *tx, RealType t_event)
Handles the start of a pulsed transmission.
The World class manages the simulator environment.
const std::vector< std::unique_ptr< radar::Target > > & getTargets() const noexcept
Retrieves the list of radar targets.
SimulationState & getSimulationState() noexcept
Gets a mutable reference to the global simulation state.
std::priority_queue< Event, std::vector< Event >, EventComparator > & getEventQueue() noexcept
Gets a mutable reference to the global event queue.
const std::vector< std::unique_ptr< radar::Receiver > > & getReceivers() const noexcept
Retrieves the list of radar receivers.
RealType earliestPhaseNoiseLookupStart() const
Finds the earliest simulation time that can require CW phase-noise samples.
Stateful FIR decimator for chunked streaming output.
FMCW linear chirp signal implementation.
RealType getChirpDuration() const noexcept
Gets the chirp duration in seconds.
RealType getChirpBandwidth() const noexcept
Gets the chirp bandwidth in hertz.
RealType getChirpPeriod() const noexcept
Gets the chirp period in seconds.
FmcwChirpDirection getDirection() const noexcept
Gets the FMCW sweep direction.
const std::optional< std::size_t > & getChirpCount() const noexcept
Gets the optional finite chirp count.
RealType getChirpRate() const noexcept
Gets the chirp rate in hertz per second.
RealType getStartFrequencyOffset() const noexcept
Gets the start frequency offset relative to carrier in hertz.
FMCW symmetric triangular modulation signal implementation.
RealType getStartFrequencyOffset() const noexcept
Gets the start frequency offset relative to carrier in hertz.
RealType getChirpBandwidth() const noexcept
Gets the chirp bandwidth in hertz.
RealType getChirpRate() const noexcept
Gets the chirp rate magnitude in hertz per second.
const std::optional< std::size_t > & getTriangleCount() const noexcept
Gets the optional finite triangle count.
RealType getChirpDuration() const noexcept
Gets the per-leg chirp duration in seconds.
RealType getTrianglePeriod() const noexcept
Gets the full up/down triangle period in seconds.
Class representing a radar signal with associated properties.
const class SteppedFrequencySignal * getSteppedFrequencySignal() const noexcept
Gets the stepped-frequency implementation, if this signal owns one.
const class FmcwTriangleSignal * getFmcwTriangleSignal() const noexcept
Gets the FMCW triangle implementation, if this signal owns one.
RealType getCarrier() const noexcept
Gets the carrier frequency of the radar signal.
const class FmcwChirpSignal * getFmcwChirpSignal() const noexcept
Gets the FMCW chirp implementation, if this signal owns one.
RealType getPower() const noexcept
Gets the power of the radar signal.
Stepped-frequency continuous-wave signal implementation.
RealType effectiveBandwidth() const noexcept
Gets DFT-convention effective bandwidth in hertz.
RealType getSweepPeriod() const noexcept
Gets full sweep period in seconds.
const std::optional< std::size_t > & getSweepCount() const noexcept
Gets optional finite sweep count.
RealType getStepSize() const noexcept
Gets the uniform frequency step in hertz.
RealType lastFrequency(RealType carrier_frequency) const noexcept
Gets final-step RF frequency in hertz.
RealType firstFrequency(RealType carrier_frequency) const noexcept
Gets first-step RF frequency in hertz.
std::size_t getStepCount() const noexcept
Gets the number of steps per sweep.
RealType getStepPeriod() const noexcept
Gets step repetition period in seconds.
RealType getDwellTime() const noexcept
Gets active dwell time per step in seconds.
Exception class for handling path-related errors.
Represents a path with coordinates and allows for various interpolation methods.
Vec3 getPosition(RealType t) const
Retrieves the position at a given time along the path.
const std::vector< Coord > & getCoords() const noexcept
Gets the list of coordinates in the path.
@ INTERP_STATIC
Hold the first coordinate for all query times.
@ INTERP_LINEAR
Linearly interpolate between neighboring coordinates.
@ INTERP_CUBIC
Cubically interpolate between neighboring coordinates.
InterpType getType() const noexcept
Retrieves the current interpolation type of the path.
A class representing a vector in rectangular coordinates.
RealType x
The x component of the vector.
RealType z
The z component of the vector.
RealType y
The y component of the vector.
A simple thread pool implementation.
void wait()
Waits for all tasks in the thread pool to finish.
const std::string & getName() const noexcept
Retrieves the name of the object.
Manages radar signal reception and response processing.
std::mt19937 & getRngEngine() noexcept
Gets the receiver's internal random number generator engine.
bool hasFmcwIfResamplingSink() const noexcept
Returns true when this receiver is using the online FMCW IF resampling sink.
void prunePulsedInterferenceEndingBefore(RealType cutoff_time) noexcept
Removes logged pulsed interference responses that ended before a receive time.
const std::vector< SchedulePeriod > & getSchedule() const noexcept
Retrieves the list of active reception periods.
bool isDechirpEnabled() const noexcept
Returns true when the receiver emits dechirped IF data.
void consumeFmcwIfBlock(std::span< const ComplexType > block, RealType block_start_time)
Feeds one completed high-rate dechirped block into the online IF sink.
const std::optional< fers_signal::FmcwIfResamplerPlan > & getFmcwIfResamplerPlan() const noexcept
Gets the active or most recently used IF resampling plan, if any.
RealType getNoiseTemperature() const noexcept
Retrieves the noise temperature of the receiver.
OperationMode getMode() const noexcept
Gets the operational mode of the receiver.
Base class for radar targets.
Represents a radar transmitter system.
bool isStreamingMode() const noexcept
Returns true when the transmitter uses a continuous streaming mode.
const std::vector< SchedulePeriod > & getSchedule() const noexcept
Retrieves the list of active transmission periods.
double RealType
Type for real numbers.
constexpr RealType EPSILON
Machine epsilon for real numbers.
std::complex< RealType > ComplexType
Type for complex numbers.
Declares the functions for the asynchronous receiver finalization pipelines.
Declares focused, testable pipeline steps for receiver finalization.
Internal FMCW IF rational resampler planning and streaming sink.
Header file for the logging system.
Startup memory and output-size projection helpers for simulations.
std::uint64_t countFmcwTriangleStarts(const ActiveStreamingSource &source, const RealType active_start, const RealType active_end)
Counts FMCW triangles that start inside the absolute interval.
std::uint64_t countSfcwStepStarts(const ActiveStreamingSource &source, const RealType active_start, const RealType active_end)
Counts SFCW active dwells that start inside the absolute interval.
void logSimulationMemoryProjection(const World &world)
Logs the projected simulation memory footprint for the provided world.
ActiveStreamingSource makeActiveSource(const radar::Transmitter *const tx, const RealType segment_start, const RealType segment_end)
Builds an active-source cache from a streaming transmitter and segment bounds.
OutputMetadata runEventDrivenSim(World *world, pool::ThreadPool &pool, const std::function< void(const std::string &, int, int)> &progress_callback, const std::string &output_dir, const OutputConfig &output_config, std::function< bool()> cancel_callback, bool *cancelled, ReceiverOutputTelemetryCallback telemetry_callback)
Runs the unified, event-driven radar simulation.
std::function< void(const std::optional< OutputStats > &, std::span< const ReceiverOutputPacketTrace >)> ReceiverOutputTelemetryCallback
bool isVita49Enabled(const OutputConfig &config) noexcept
@ RX_PULSED_WINDOW_START
A pulsed receiver opens its listening window.
@ RX_PULSED_WINDOW_END
A pulsed receiver closes its listening window.
@ TX_STREAMING_END
A streaming transmitter stops transmitting.
@ RX_STREAMING_END
A streaming receiver stops listening.
@ TX_STREAMING_START
A streaming transmitter starts transmitting.
@ TX_PULSED_START
A pulsed transmitter begins emitting a pulse.
@ RX_STREAMING_START
A streaming receiver starts listening.
Vita49OutputMetadata vita49MetadataFromConfig(const Vita49OutputConfig &config)
Builds the static VITA metadata section from runtime output configuration.
std::vector< std::shared_ptr< timing::Timing > > collectCwPhaseNoiseTimings(const World &world)
Collects unique timing sources used by CW/FMCW transmitters and receivers.
std::uint64_t countFmcwChirpStarts(const ActiveStreamingSource &source, const RealType active_start, const RealType active_end)
Counts FMCW chirps that start inside the absolute interval.
FmcwIfResamplerPlan planFmcwIfResampler(const FmcwIfResamplerRequest &request)
std::string_view fmcwChirpDirectionToken(const FmcwChirpDirection direction) noexcept
Converts a chirp direction to the schema token.
RealType endTime() noexcept
Get the end time for the simulation.
RealType rate() noexcept
Get the rendering sample rate.
RealType startTime() noexcept
Get the start time for the simulation.
unsigned oversampleRatio() noexcept
Get the oversampling ratio.
unsigned renderFilterLength() noexcept
Get the render filter length.
Parameters params
Global simulation parameter state.
RealType c() noexcept
Get the speed of light.
core::ReceiverSampleBlock buildReceiverSampleBlock(const radar::Receiver *receiver, const RealType first_sample_time, const RealType sample_rate, const std::span< const ComplexType > samples, const std::uint64_t sample_start, std::shared_ptr< const core::OutputFileMetadata > file_metadata)
Builds a non-owning output sample block over contiguous processed complex samples.
void runPulsedFinalizer(radar::Receiver *receiver, const std::vector< std::unique_ptr< radar::Target > > *targets, const std::shared_ptr< core::ProgressReporter > &reporter, const std::string &output_dir, const std::shared_ptr< core::OutputMetadataCollector > &metadata_collector, core::ReceiverOutputSink *output_sink)
The main function for a dedicated pulsed-mode receiver finalizer thread.
core::OutputFileMetadata buildStreamingOutputMetadata(const radar::Receiver *receiver, const std::string &output_path, const std::size_t total_samples, const std::vector< core::ActiveStreamingSource > &streaming_sources, const RealType output_sample_rate)
Builds HDF5 file metadata for a streaming receiver result emitted through the output sink.
void applyThermalNoiseAtSampleRate(std::span< ComplexType > window, const RealType noiseTemperature, std::mt19937 &rngEngine, const RealType sampleRateHz)
Applies circular complex thermal noise using a caller-specified complex-baseband sample rate.
core::ReceiverStreamDescriptor buildReceiverStreamDescriptor(const radar::Receiver *receiver, const RealType sample_rate, const std::span< const core::ActiveStreamingSource > streaming_sources)
Builds the receiver stream descriptor used by output sinks.
OperationMode
Defines the operational mode of a radar component.
std::unique_ptr< core::ReceiverOutputSink > makeVita49OutputSink(core::ReceiverOutputTelemetryCallback telemetry_callback)
std::unique_ptr< core::ReceiverOutputSink > makeHdf5OutputSink(std::string output_dir, std::shared_ptr< core::OutputMetadataCollector > metadata_collector)
ComplexType calculateStreamingDirectPathContribution(const core::ActiveStreamingSource &source, const Receiver *recv, const RealType timeK, const CwPhaseNoiseLookup *const phase_noise_lookup, core::FmcwChirpBoundaryTracker *const chirp_tracker, const StreamingTimingPhaseMode timing_phase_mode)
Calculates a direct-path contribution from a cached streaming source.
bool calculateStreamingReferenceSample(const core::ActiveStreamingSource &source, const RealType timeK, core::FmcwChirpBoundaryTracker *const chirp_tracker, ComplexType &sample_out)
Evaluates the complete complex reference envelope, including file-backed amplitude modulation.
@ TransmitterOnly
Incoming RF/baseband signal before receiver LO subtraction.
@ None
Ignore timing phase noise entirely.
@ ReceiverRelative
Existing raw streaming convention: transmitter phase minus receiver LO phase.
ComplexType calculateStreamingReflectedPathContribution(const core::ActiveStreamingSource &source, const Receiver *recv, const Target *targ, const RealType timeK, const CwPhaseNoiseLookup *const phase_noise_lookup, core::FmcwChirpBoundaryTracker *const chirp_tracker, const StreamingTimingPhaseMode timing_phase_mode)
Calculates a reflected-path contribution from a cached streaming source.
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.
Defines the Parameters struct and provides methods for managing simulation parameters.
Utility functions for path interpolation and exception handling.
Classes for handling radar waveforms and signals.
Radar Receiver class for managing signal reception and response handling.
Classes for managing radar signal responses.
Header for receiver-side signal processing and rendering.
Defines the core structures for the event-driven simulation engine.
Header file for the main simulation runner.
Cached description of an active streaming transmitter segment.
Represents a single event in the simulation's time-ordered queue.
RealType timestamp
The simulation time at which the event occurs.
EventType type
The type of the event.
radar::Radar * source_object
Pointer to the object that generated the event.
A data packet containing all information needed to process one receive window.
std::vector< ActiveStreamingSource > active_streaming_transmitters
A global list of all currently active streaming transmitters.
RealType t_current
The master simulation clock, advanced by the event loop.
Represents a position in 3D space with an associated time.
static CwPhaseNoiseLookup build(std::span< const std::shared_ptr< timing::Timing > > timings, RealType start_time, RealType end_time)
Builds a phase-noise lookup for the requested timing sources and time range.
Defines classes for radar targets and their Radar Cross-Section (RCS) models.
A simple thread pool implementation.
Timing source for simulation objects.
Header file for the Transmitter class in the radar namespace.
Header file for the World class in the simulator.