14#include <highfive/highfive.hpp>
42 for (
const auto& chunk : metadata.
chunks)
49 if (!metadata.
chunks.empty())
51 const auto expected = metadata.
chunks.front().sample_count;
53 { return chunk.sample_count == expected; });
61 const std::size_t total_samples)
64 .receiver_name = receiver->
getName(),
66 .path = hdf5_filename,
67 .total_samples =
static_cast<std::uint64_t
>(total_samples),
69 .sample_end_exclusive =
static_cast<std::uint64_t
>(total_samples)};
71 const auto append_segment = [&](
const RealType start_time,
const RealType end_time)
73 const auto start_sample =
static_cast<std::uint64_t
>(std::min<RealType>(
74 static_cast<RealType>(total_samples),
76 const auto end_sample =
static_cast<std::uint64_t
>(std::min<RealType>(
77 static_cast<RealType>(total_samples),
79 if (start_sample < end_sample)
81 metadata.
cw_segments.push_back({.start_time = start_time,
83 .sample_count = end_sample - start_sample,
84 .sample_start = start_sample,
85 .sample_end_exclusive = end_sample});
96 for (
const auto& period : schedule)
102 append_segment(start, end);
112 std::shared_ptr<core::ProgressReporter> reporter,
const std::string& output_dir,
113 std::shared_ptr<core::OutputMetadataCollector> metadata_collector)
115 const auto timing_model = receiver->
getTiming()->clone();
122 std::filesystem::path out_path(output_dir);
123 if (!std::filesystem::exists(out_path))
125 std::filesystem::create_directories(out_path);
127 const auto hdf5_filename = (out_path / std::format(
"{}_results.h5", receiver->
getName())).
string();
129 .receiver_name = receiver->
getName(),
131 .path = hdf5_filename};
133 std::unique_ptr<HighFive::File> h5_file;
136 h5_file = std::make_unique<HighFive::File>(hdf5_filename, HighFive::File::Truncate);
139 unsigned chunk_index = 0;
142 receiver->
getName(), hdf5_filename);
144 auto last_report_time = std::chrono::steady_clock::now();
145 const auto report_interval = std::chrono::milliseconds(100);
157 const auto window_samples =
static_cast<unsigned>(std::ceil(job.
duration * rate));
158 std::vector pnoise(window_samples, 0.0);
163 if (timing_model->isEnabled())
166 std::ranges::generate(pnoise, [&] {
return timing_model->getNextSample(); });
171 std::vector<ComplexType> window_buffer(window_samples);
180 if (timing_model->isEnabled())
187 const auto current_chunk_index = chunk_index++;
188 const auto sample_start = file_metadata.total_samples;
190 .i_dataset = std::format(
"chunk_{:06}_I", current_chunk_index),
191 .q_dataset = std::format(
"chunk_{:06}_Q", current_chunk_index),
192 .start_time = actual_start,
193 .sample_count =
static_cast<std::uint64_t
>(window_buffer.size()),
194 .sample_start = sample_start,
195 .sample_end_exclusive = sample_start +
196 static_cast<std::uint64_t
>(window_buffer.size())};
200 file_metadata.chunks.push_back(std::move(chunk_metadata));
201 file_metadata.total_samples = file_metadata.chunks.back().sample_end_exclusive;
205 const auto now = std::chrono::steady_clock::now();
206 if ((now - last_report_time) >= report_interval)
208 reporter->report(std::format(
"Exporting {}: Chunk {}", receiver->
getName(), chunk_index),
209 static_cast<int>(chunk_index), 0);
210 last_report_time = now;
215 finalizePulsedMetadata(file_metadata);
227 if (metadata_collector)
229 metadata_collector->addFile(std::move(file_metadata));
234 reporter->report(std::format(
"Finished Exporting {}", receiver->
getName()), 100, 100);
240 std::shared_ptr<core::ProgressReporter> reporter,
const std::string& output_dir,
241 std::shared_ptr<core::OutputMetadataCollector> metadata_collector)
246 reporter->report(std::format(
"Finalizing CW Receiver {}", receiver->
getName()), 0, 100);
250 if (iq_buffer.empty())
258 reporter->report(std::format(
"Rendering Interference for {}", receiver->
getName()), 25, 100);
262 const auto timing_model = receiver->
getTiming()->clone();
271 reporter->report(std::format(
"Applying Noise for {}", receiver->
getName()), 50, 100);
275 if (timing_model->isEnabled())
277 std::vector pnoise(iq_buffer.size(), 0.0);
278 std::ranges::generate(pnoise, [&] {
return timing_model->getNextSample(); });
286 reporter->report(std::format(
"Writing HDF5 for {}", receiver->
getName()), 75, 100);
289 std::filesystem::path out_path(output_dir);
290 if (!std::filesystem::exists(out_path))
292 std::filesystem::create_directories(out_path);
294 const auto hdf5_filename = (out_path / std::format(
"{}_results.h5", receiver->
getName())).
string();
295 auto file_metadata = buildCwMetadata(receiver, hdf5_filename, iq_buffer.size());
297 if (metadata_collector)
299 metadata_collector->addFile(std::move(file_metadata));
304 reporter->report(std::format(
"Finalized {}", receiver->
getName()), 100, 100);
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.
const std::vector< SchedulePeriod > & getSchedule() const noexcept
Retrieves the list of active reception periods.
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.
SimId getId() const noexcept
Retrieves the unique ID of the receiver.
RealType getNoiseTemperature() const noexcept
Retrieves the noise temperature of the receiver.
bool waitAndDequeueFinalizerJob(core::RenderingJob &job)
Waits for and dequeues a RenderingJob from the finalizer queue.
double RealType
Type for real numbers.
Declares the functions for the asynchronous receiver finalization pipelines.
Declares focused, testable pipeline steps for receiver finalization.
Header file for HDF5 data export and import functions.
Header file for the logging system.
@ FATAL
Fatal level for severe error events.
@ INFO
Info level for informational messages.
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.
void addPhaseNoiseToWindow(std::span< const RealType > noise, std::span< ComplexType > window)
Applies a pre-generated sequence of phase noise samples to an I/Q buffer.
RealType applyDownsamplingAndQuantization(std::vector< ComplexType > &buffer)
Downsamples and quantizes an IQ buffer.
void advanceTimingModel(timing::Timing *timing_model, const radar::Receiver *receiver, const RealType rate)
Advances the receiver's timing model to the start of the next processing window.
void exportCwToHdf5(const std::string &filename, const std::vector< ComplexType > &iq_buffer, const RealType fullscale, const RealType ref_freq, const core::OutputFileMetadata *metadata)
Exports a finalized continuous-wave IQ buffer to an HDF5 file.
std::tuple< RealType, RealType > calculateJitteredStart(const RealType ideal_start, const RealType first_phase_noise, const RealType carrier_freq, const RealType rate)
Calculates the jittered start time and fractional delay from a phase noise sample.
void applyPulsedInterference(std::vector< ComplexType > &iq_buffer, const std::vector< std::unique_ptr< serial::Response > > &interference_log)
Renders and applies pulsed interference to a continuous-wave IQ buffer.
void applyCwInterference(std::span< ComplexType > window, const RealType actual_start, const RealType dt, const radar::Receiver *receiver, const std::vector< radar::Transmitter * > &cw_sources, const std::vector< std::unique_ptr< radar::Target > > *targets)
Applies continuous-wave interference to a time window.
void finalizeCwReceiver(radar::Receiver *receiver, pool::ThreadPool *, std::shared_ptr< core::ProgressReporter > reporter, const std::string &output_dir, std::shared_ptr< core::OutputMetadataCollector > metadata_collector)
The finalization task for a continuous-wave (CW) mode receiver.
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 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 runPulsedFinalizer(radar::Receiver *receiver, const std::vector< std::unique_ptr< radar::Target > > *targets, std::shared_ptr< core::ProgressReporter > reporter, const std::string &output_dir, std::shared_ptr< core::OutputMetadataCollector > metadata_collector)
The main function for a dedicated pulsed-mode receiver finalizer thread.
std::mutex hdf5_global_mutex
Global mutex to protect all HDF5 C-library calls, which are not thread-safe.
void writeOutputFileMetadataAttributes(HighFive::File &file, const core::OutputFileMetadata &metadata)
Writes additive FERS output metadata attributes to an open HDF5 file.
void addChunkToFile(HighFive::File &file, const std::vector< ComplexType > &data, const RealType time, const RealType fullscale, const unsigned count, const core::PulseChunkMetadata *metadata)
Adds a chunk of data to an HDF5 file.
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.
Timing source for simulation objects.