FERS 1.0.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
processing::pipeline Namespace Reference

Functions

void advanceTimingModel (timing::Timing *timing_model, const radar::Receiver *receiver, RealType rate)
 Advances the receiver's timing model to the start of the next processing window.
 
std::tuple< RealType, RealTypecalculateJitteredStart (RealType ideal_start, RealType first_phase_noise, RealType carrier_freq, RealType rate)
 Calculates the jittered start time and fractional delay from a phase noise sample.
 
void applyCwInterference (std::span< ComplexType > window, RealType actual_start, 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 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 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 exportCwToHdf5 (const std::string &filename, const std::vector< ComplexType > &iq_buffer, RealType fullscale, RealType ref_freq, const core::OutputFileMetadata *metadata=nullptr)
 Exports a finalized continuous-wave IQ buffer to an HDF5 file.
 

Function Documentation

◆ addPhaseNoiseToWindow()

void processing::pipeline::addPhaseNoiseToWindow ( std::span< const RealType noise,
std::span< ComplexType window 
)

Applies a pre-generated sequence of phase noise samples to an I/Q buffer.

This function performs the complex multiplication IQ_out = IQ_in * e^(j*phase_noise) for each sample in the window, effectively modulating the phase of the signal.

Parameters
noiseA span of phase noise samples in radians.
windowThe I/Q buffer to be modified.

Definition at line 124 of file finalizer_pipeline.cpp.

125 {
126 for (auto [n, w] : std::views::zip(noise, window))
127 {
128 w *= std::polar(1.0, n);
129 }
130 }

Referenced by processing::finalizeCwReceiver(), and processing::runPulsedFinalizer().

+ Here is the caller graph for this function:

◆ advanceTimingModel()

void processing::pipeline::advanceTimingModel ( timing::Timing timing_model,
const radar::Receiver receiver,
RealType  rate 
)

Advances the receiver's timing model to the start of the next processing window.

This function handles the "dead time" between receive windows. For sync-on-pulse models, it resets the phase and skips to the window's start offset. For free-running models, it skips the number of samples corresponding to the inter-pulse period.

Parameters
timing_modelThe stateful timing model instance to advance.
receiverThe receiver whose properties (sync mode, PRF, etc.) determine how to advance the model.
rateThe oversampled simulation rate, used to calculate samples to skip.

Definition at line 29 of file finalizer_pipeline.cpp.

30 {
31
32 if ((timing_model == nullptr) || !timing_model->isEnabled())
33 {
34 return;
35 }
36
37 // Convert the duration-derived sample advance to a signed temporary first.
38 // The physical calculation can legitimately produce a negative or zero result
39 // (for example due to floor() or an inter-pulse gap that is not positive),
40 // but skipSamples() models advancing by a non-negative sample count only.
41 // After validating that the computed value is > 0, cast to std::size_t for
42 // the skipSamples() call chain:
43 // Timing::skipSamples -> ClockModelGenerator::skipSamples ->
44 // MultirateGenerator::skipSamples.
45 // This preserves the sign until validation and avoids passing invalid negative
46 // counts into the timing/noise generators.
47 if (timing_model->getSyncOnPulse())
48 {
49 timing_model->reset();
50 const auto skip_samples = static_cast<long long>(std::floor(rate * receiver->getWindowSkip()));
51 if (skip_samples > 0)
52 {
53 timing_model->skipSamples(static_cast<std::size_t>(skip_samples));
54 }
55 }
56 else
57 {
58 const RealType inter_pulse_skip_duration = 1.0 / receiver->getWindowPrf() - receiver->getWindowLength();
59 const auto samples_to_skip = static_cast<long long>(std::floor(rate * inter_pulse_skip_duration));
60 if (samples_to_skip > 0)
61 {
62 timing_model->skipSamples(static_cast<std::size_t>(samples_to_skip));
63 }
64 }
65 }
RealType getWindowPrf() const noexcept
Retrieves the pulse repetition frequency (PRF) of the radar window.
Definition receiver.h:116
RealType getWindowSkip() const noexcept
Retrieves the window skip time.
Definition receiver.h:123
RealType getWindowLength() const noexcept
Retrieves the radar window length.
Definition receiver.h:109
void reset() noexcept
Resets the timing model.
Definition timing.h:136
void skipSamples(std::size_t samples) noexcept
Skips a number of samples in the timing model.
Definition timing.cpp:31
bool getSyncOnPulse() const noexcept
Checks if the timing source synchronizes on pulse.
Definition timing.h:90
bool isEnabled() const noexcept
Checks if the timing source is enabled.
Definition timing.h:116
double RealType
Type for real numbers.
Definition config.h:27

References timing::Timing::getSyncOnPulse(), radar::Receiver::getWindowLength(), radar::Receiver::getWindowPrf(), radar::Receiver::getWindowSkip(), timing::Timing::isEnabled(), timing::Timing::reset(), and timing::Timing::skipSamples().

Referenced by processing::runPulsedFinalizer().

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

◆ applyCwInterference()

void processing::pipeline::applyCwInterference ( std::span< ComplexType window,
RealType  actual_start,
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.

Iterates through each sample of a processing window, calculating the combined direct and reflected path contributions from all active CW sources at that precise moment in time. The resulting complex sample is added to the window.

Parameters
windowThe I/Q buffer for the receive window to which interference will be added.
actual_startThe jittered, sample-aligned start time of the window.
dtThe time step between samples (1.0 / rate).
receiverThe receiver being interfered with.
cw_sourcesA list of currently active CW transmitters.
targetsThe list of all targets for calculating reflected paths.

Definition at line 76 of file finalizer_pipeline.cpp.

79 {
80 RealType t_sample = actual_start;
81 for (auto& window_sample : window)
82 {
83 ComplexType cw_interference_sample{0.0, 0.0};
84 for (const auto* cw_source : cw_sources)
85 {
87 {
88 cw_interference_sample +=
89 simulation::calculateDirectPathContribution(cw_source, receiver, t_sample);
90 }
91 for (const auto& target_ptr : *targets)
92 {
93 cw_interference_sample +=
94 simulation::calculateReflectedPathContribution(cw_source, receiver, target_ptr.get(), t_sample);
95 }
96 }
97 window_sample += cw_interference_sample;
98 t_sample += dt;
99 }
100 }
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 radar::Receiver::FLAG_NODIRECT.

Referenced by processing::runPulsedFinalizer().

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

◆ applyDownsamplingAndQuantization()

RealType processing::pipeline::applyDownsamplingAndQuantization ( std::vector< ComplexType > &  buffer)

Downsamples and quantizes an IQ buffer.

This function performs the final processing steps. If oversampling is enabled, it first downsamples the buffer to the final output rate. It then simulates an ADC by quantizing and scaling the data.

Parameters
bufferThe I/Q buffer to be processed. This is an in-out parameter; it will be replaced by the downsampled buffer if applicable.
Returns
The full-scale value (RealType) calculated during quantization, which is needed for HDF5 metadata.

Definition at line 132 of file finalizer_pipeline.cpp.

133 {
134 if (params::oversampleRatio() > 1)
135 {
136 buffer = std::move(fers_signal::downsample(buffer));
137 }
138 return quantizeAndScaleWindow(buffer);
139 }
std::vector< ComplexType > downsample(std::span< const ComplexType > in)
Downsamples a signal by a given ratio.
unsigned oversampleRatio() noexcept
Get the oversampling ratio.
Definition parameters.h:151
RealType quantizeAndScaleWindow(std::span< ComplexType > window)
Simulates ADC quantization and scales a window of complex I/Q samples.

References fers_signal::downsample(), params::oversampleRatio(), and processing::quantizeAndScaleWindow().

Referenced by processing::finalizeCwReceiver(), and processing::runPulsedFinalizer().

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

◆ applyPulsedInterference()

void processing::pipeline::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.

Processes a log of Response objects that represent pulsed signals received during a CW receiver's operation. Each response is rendered into a temporary buffer and then added to the main CW IQ buffer at the correct time offset.

Parameters
iq_bufferThe main, simulation-long IQ buffer for the CW receiver.
interference_logA list of Response objects representing the pulsed interference.

Definition at line 102 of file finalizer_pipeline.cpp.

104 {
105 for (const auto& response : interference_log)
106 {
107 unsigned psize;
108 RealType prate;
109 const auto rendered_pulse = response->renderBinary(prate, psize, 0.0);
110
111 const RealType dt_sim = 1.0 / prate;
112 const auto start_index = static_cast<size_t>((response->startTime() - params::startTime()) / dt_sim);
113
114 for (size_t i = 0; i < psize; ++i)
115 {
116 if (start_index + i < iq_buffer.size())
117 {
118 iq_buffer[start_index + i] += rendered_pulse[i];
119 }
120 }
121 }
122 }
RealType startTime() noexcept
Get the start time for the simulation.
Definition parameters.h:103

References params::startTime().

Referenced by processing::finalizeCwReceiver().

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

◆ calculateJitteredStart()

std::tuple< RealType, RealType > processing::pipeline::calculateJitteredStart ( RealType  ideal_start,
RealType  first_phase_noise,
RealType  carrier_freq,
RealType  rate 
)

Calculates the jittered start time and fractional delay from a phase noise sample.

Converts the first phase noise sample (in radians) of a window into a time jitter offset. It then decomposes the resulting "actual" start time into a component aligned with the sample clock and a fractional delay to be handled by the rendering engine's interpolation filter.

Parameters
ideal_startThe perfect, jitter-free start time of the window.
first_phase_noiseThe first phase noise sample (radians) for this window.
carrier_freqThe carrier frequency, needed to convert phase to time.
rateThe sampling rate, used for sample clock alignment.
Returns
A tuple containing:
  1. The sample-aligned start time (RealType).
  2. The fractional sample delay (RealType).

Definition at line 67 of file finalizer_pipeline.cpp.

69 {
70 const RealType actual_start = ideal_start + first_phase_noise / (2.0 * PI * carrier_freq);
71 const RealType rounded_start = std::round(actual_start * rate) / rate;
72 const RealType fractional_delay = actual_start * rate - std::round(actual_start * rate);
73 return {rounded_start, fractional_delay};
74 }
constexpr RealType PI
Mathematical constant π (pi).
Definition config.h:43

References PI.

Referenced by processing::runPulsedFinalizer().

+ Here is the caller graph for this function:

◆ exportCwToHdf5()

void processing::pipeline::exportCwToHdf5 ( const std::string &  filename,
const std::vector< ComplexType > &  iq_buffer,
RealType  fullscale,
RealType  ref_freq,
const core::OutputFileMetadata metadata = nullptr 
)

Exports a finalized continuous-wave IQ buffer to an HDF5 file.

Creates an HDF5 file, splits the complex buffer into real (I) and imaginary (Q) components, writes them as separate datasets, and adds relevant simulation parameters (sample rate, start time, etc.) as file attributes.

Parameters
filenameThe path to the output HDF5 file.
iq_bufferThe final, processed I/Q data to write.
fullscaleThe full-scale value from quantization, saved as metadata.
ref_freqThe reference carrier frequency, saved as metadata.

Definition at line 141 of file finalizer_pipeline.cpp.

143 {
144 std::scoped_lock lock(serial::hdf5_global_mutex);
145 try
146 {
147 HighFive::File file(filename, HighFive::File::Truncate);
148
149 std::vector<RealType> i_data(iq_buffer.size());
150 std::vector<RealType> q_data(iq_buffer.size());
151 std::ranges::transform(iq_buffer, i_data.begin(), [](const auto& c) { return c.real(); });
152 std::ranges::transform(iq_buffer, q_data.begin(), [](const auto& c) { return c.imag(); });
153
154 HighFive::DataSet i_dataset = file.createDataSet<RealType>("I_data", HighFive::DataSpace::From(i_data));
155 i_dataset.write(i_data);
156 HighFive::DataSet q_dataset = file.createDataSet<RealType>("Q_data", HighFive::DataSpace::From(q_data));
157 q_dataset.write(q_data);
158
159 file.createAttribute("sampling_rate", params::rate());
160 file.createAttribute("start_time", params::startTime());
161 file.createAttribute("fullscale", fullscale);
162 file.createAttribute("reference_carrier_frequency", ref_freq);
163 if (metadata != nullptr)
164 {
166 }
167
168 LOG(logging::Level::INFO, "Successfully exported CW data to '{}'", filename);
169 }
170 catch (const HighFive::Exception& err)
171 {
172 LOG(logging::Level::FATAL, "Error writing CW data to HDF5 file '{}': {}", filename, err.what());
173 }
174 }
#define LOG(level,...)
Definition logging.h:19
@ FATAL
Fatal level for severe error events.
@ INFO
Info level for informational messages.
RealType rate() noexcept
Get the rendering sample rate.
Definition parameters.h:121
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.

References logging::FATAL, serial::hdf5_global_mutex, logging::INFO, LOG, params::rate(), params::startTime(), and serial::writeOutputFileMetadataAttributes().

Referenced by processing::finalizeCwReceiver().

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