35#include <highfive/highfive.hpp>
59 void addPhaseNoiseToWindow(std::span<const RealType>
noise, std::span<ComplexType> window)
61 for (
auto [n, w] : std::views::zip(
noise, window))
63 w *= std::polar(1.0, n);
71 std::shared_ptr<core::ProgressReporter> reporter)
75 const auto timing_model = receiver->
getTiming()->clone();
82 const auto hdf5_filename = std::format(
"{}_results.h5", receiver->
getName());
83 HighFive::File h5_file(hdf5_filename, HighFive::File::Truncate);
84 unsigned chunk_index = 0;
86 receiver->
getName(), hdf5_filename);
89 auto last_report_time = std::chrono::steady_clock::now();
90 const auto report_interval = std::chrono::milliseconds(100);
104 const auto window_samples =
static_cast<unsigned>(std::ceil(job.
duration * rate));
105 std::vector pnoise(window_samples, 0.0);
109 if (timing_model->isEnabled())
112 if (timing_model->getSyncOnPulse())
115 timing_model->reset();
116 timing_model->skipSamples(
static_cast<int>(std::floor(rate * receiver->
getWindowSkip())));
121 const RealType inter_pulse_skip_duration =
123 const auto samples_to_skip =
static_cast<long>(std::floor(rate * inter_pulse_skip_duration));
124 timing_model->skipSamples(samples_to_skip);
128 std::ranges::generate(pnoise, [&] {
return timing_model->getNextSample(); });
131 const RealType carrier = timing_model->getFrequency();
132 actual_start += pnoise[0] / (2.0 *
PI * carrier);
138 std::tie(actual_start, frac_delay) = [&actual_start, rate]
140 RealType rounded_start = std::round(actual_start * rate) / rate;
141 RealType fractional_delay = actual_start * rate - std::round(actual_start * rate);
142 return std::tuple{rounded_start, fractional_delay};
146 std::vector<ComplexType> window_buffer(window_samples);
154 for (
auto& window_sample : window_buffer)
162 cw_interference_sample +=
165 for (
const auto& target_ptr : *targets)
168 cw_source, receiver, target_ptr.get(), t_sample);
171 window_sample += cw_interference_sample;
179 if (timing_model->isEnabled())
181 addPhaseNoiseToWindow(pnoise, window_buffer);
200 const auto now = std::chrono::steady_clock::now();
201 if ((now - last_report_time) >= report_interval)
203 reporter->report(std::format(
"Exporting {}: Chunk {}", receiver->
getName(), chunk_index),
204 static_cast<int>(chunk_index), 0);
205 last_report_time = now;
213 reporter->report(std::format(
"Finished Exporting {}", receiver->
getName()), 100, 100);
219 std::shared_ptr<core::ProgressReporter> reporter)
226 reporter->report(std::format(
"Finalizing CW Receiver {}", receiver->
getName()), 0, 100);
233 if (iq_buffer.empty())
241 reporter->report(std::format(
"Rendering Interference for {}", receiver->
getName()), 25, 100);
246 for (
const auto& response : interference_log)
250 const auto rendered_pulse = response->renderBinary(prate, psize, 0.0);
252 const RealType dt_sim = 1.0 / prate;
253 const auto start_index =
static_cast<size_t>((response->startTime() -
params::startTime()) / dt_sim);
255 for (
size_t i = 0; i < psize; ++i)
257 if (start_index + i < iq_buffer.size())
259 iq_buffer[start_index + i] += rendered_pulse[i];
265 const auto timing_model = receiver->
getTiming()->clone();
274 reporter->report(std::format(
"Applying Noise for {}", receiver->
getName()), 50, 100);
280 std::vector pnoise(iq_buffer.size(), 0.0);
281 if (timing_model->isEnabled())
283 std::ranges::generate(pnoise, [&] {
return timing_model->getNextSample(); });
284 addPhaseNoiseToWindow(pnoise, iq_buffer);
300 reporter->report(std::format(
"Writing HDF5 for {}", receiver->
getName()), 75, 100);
304 const auto hdf5_filename = std::format(
"{}_results.h5", receiver->
getName());
307 HighFive::File file(hdf5_filename, HighFive::File::Truncate);
309 std::vector<RealType> i_data(iq_buffer.size());
310 std::vector<RealType> q_data(iq_buffer.size());
311 std::ranges::transform(iq_buffer, i_data.begin(), [](
const auto& c) { return c.real(); });
312 std::ranges::transform(iq_buffer, q_data.begin(), [](
const auto& c) { return c.imag(); });
314 HighFive::DataSet i_dataset = file.createDataSet<
RealType>(
"I_data", HighFive::DataSpace::From(i_data));
315 i_dataset.write(i_data);
316 HighFive::DataSet q_dataset = file.createDataSet<
RealType>(
"Q_data", HighFive::DataSpace::From(q_data));
317 q_dataset.write(q_data);
321 file.createAttribute(
"fullscale", fullscale);
322 file.createAttribute(
"reference_carrier_frequency", timing_model->getFrequency());
327 catch (
const HighFive::Exception& err)
334 reporter->report(std::format(
"Finalized {}", receiver->
getName()), 100, 100);
Header for radar channel propagation and interaction models.
A simple thread pool implementation.
const std::string & getName() const noexcept
Retrieves the name of the object.
math::SVec3 getRotation(const RealType time) const
Retrieves the rotation of the object.
std::shared_ptr< timing::Timing > getTiming() const
Retrieves the timing source for the radar.
Manages radar signal reception and response processing.
std::mt19937 & getRngEngine() noexcept
Gets the receiver's internal random number generator engine.
bool checkFlag(RecvFlag flag) const noexcept
Checks if a specific flag is set.
std::vector< ComplexType > & getMutableCwData()
Retrieves the collected CW IQ data for modification.
const std::vector< std::unique_ptr< serial::Response > > & getPulsedInterferenceLog() const
Retrieves the log of pulsed interferences for CW mode.
RealType getNoiseTemperature() const noexcept
Retrieves the noise temperature of the receiver.
RealType getWindowPrf() const noexcept
Retrieves the pulse repetition frequency (PRF) of the radar window.
bool waitAndDequeueFinalizerJob(core::RenderingJob &job)
Waits for and dequeues a RenderingJob from the finalizer queue.
RealType getWindowSkip() const noexcept
Retrieves the window skip time.
RealType getWindowLength() const noexcept
Retrieves the radar window length.
double RealType
Type for real numbers.
std::complex< RealType > ComplexType
Type for complex numbers.
constexpr RealType PI
Mathematical constant π (pi).
Header file for Digital Signal Processing (DSP) filters and upsampling/downsampling functionality.
Declares the functions for the asynchronous receiver finalization pipelines.
Header file for HDF5 data export and import functions.
Header file for the logging system.
std::vector< ComplexType > downsample(std::span< const ComplexType > in)
Downsamples a signal by a given ratio.
@ FATAL
Fatal level for severe error events.
@ INFO
Info level for informational messages.
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.
void runPulsedFinalizer(radar::Receiver *receiver, const std::vector< std::unique_ptr< radar::Target > > *targets, std::shared_ptr< core::ProgressReporter > reporter)
The main function for a dedicated pulsed-mode receiver finalizer thread.
void applyThermalNoise(std::span< ComplexType > window, const RealType noiseTemperature, std::mt19937 &rngEngine)
Applies thermal (Johnson-Nyquist) noise to a window of I/Q samples.
void finalizeCwReceiver(radar::Receiver *receiver, pool::ThreadPool *pool, std::shared_ptr< core::ProgressReporter > reporter)
The finalization task for a continuous-wave (CW) mode receiver.
RealType quantizeAndScaleWindow(std::span< ComplexType > window)
Simulates ADC quantization and scales a window of complex I/Q samples.
void renderWindow(std::vector< ComplexType > &window, const RealType length, const RealType start, const RealType fracDelay, const std::span< const std::unique_ptr< serial::Response > > responses)
Renders a time-window of I/Q data from a collection of raw radar responses.
void addChunkToFile(HighFive::File &file, const std::vector< ComplexType > &data, const RealType time, const RealType fullscale, const unsigned count)
Adds a chunk of data to an HDF5 file.
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...
Defines the Parameters struct and provides methods for managing simulation parameters.
Radar Receiver class for managing signal reception and response handling.
Defines the data packet for asynchronous receiver finalization.
Header for receiver-side signal processing and rendering.
Header file for the main simulation runner.
A data packet containing all information needed to process one receive window.
RealType duration
The duration of the receive window in seconds.
std::vector< std::unique_ptr< serial::Response > > responses
A list of all Response objects that overlap with this window.
RealType ideal_start_time
The ideal, jitter-free start time of the receive window.
std::vector< radar::Transmitter * > active_cw_sources
A list of all CW transmitters that were active during this window.
Defines classes for radar targets and their Radar Cross-Section (RCS) models.
Timing source for simulation objects.
Header file for the Transmitter class in the radar namespace.