45 throw std::runtime_error(
"Unsupported FMCW chirp direction '" + std::string(
direction) +
"'.");
56 const std::size_t step_count,
const RealType dwell_time,
57 const RealType step_period, std::optional<std::size_t> sweep_count) :
58 _start_frequency_offset(start_frequency_offset), _step_size(step_size), _step_count(step_count),
59 _dwell_time(dwell_time), _step_period(step_period), _sweep_count(sweep_count)
65 return carrier_frequency + _start_frequency_offset;
72 return firstFrequency(carrier_frequency);
74 return firstFrequency(carrier_frequency) +
static_cast<RealType>(_step_count - 1U) * _step_size;
83 return std::abs(
static_cast<RealType>(_step_count - 1U) * _step_size);
88 return static_cast<RealType>(_step_count) * std::abs(_step_size);
93 if (!_sweep_count.has_value())
100 std::optional<SteppedFrequencySignal::StepState>
102 const RealType carrier_frequency)
const noexcept
109 const RealType sweep_period = getSweepPeriod();
111 if (_sweep_count.has_value() && sweep_index >= *_sweep_count)
117 const auto step_index =
static_cast<std::size_t
>(std::floor(
local_sweep_time / _step_period));
118 if (step_index >= _step_count)
124 static_cast<RealType>(sweep_index) * sweep_period +
static_cast<RealType>(step_index) * _step_period;
132 .sweep_index = sweep_index,
133 .step_start_time = step_start_time,
134 .dwell_end_time = step_start_time + _dwell_time,
135 .step_end_time = step_start_time + _step_period,
137 firstFrequency(carrier_frequency) +
static_cast<RealType>(step_index) * _step_size};
150 _chirp_bandwidth(chirp_bandwidth), _chirp_duration(chirp_duration), _chirp_period(chirp_period),
151 _start_frequency_offset(start_frequency_offset), _chirp_count(chirp_count),
152 _chirp_rate(chirp_bandwidth / chirp_duration), _direction(
direction)
156 std::optional<std::size_t>
165 if (_chirp_count.has_value() &&
chirp_index >= *_chirp_count)
180 std::optional<RealType>
206 const RealType start_frequency_offset,
207 std::optional<std::size_t> triangle_count) :
208 _chirp_bandwidth(chirp_bandwidth), _chirp_duration(chirp_duration),
209 _start_frequency_offset(start_frequency_offset), _triangle_count(triangle_count),
210 _chirp_rate(chirp_bandwidth / chirp_duration), _triangle_period(2.0 * chirp_duration),
211 _delta_phi_up(2.0 *
PI * start_frequency_offset * chirp_duration +
212 PI * _chirp_rate * chirp_duration * chirp_duration)
223 const auto triangle_index =
static_cast<std::size_t
>(std::floor(
triangle_time / _triangle_period));
228 static_cast<RealType>(triangle_index) * 2.0 * _delta_phi_up + (
down_leg ? _delta_phi_up : 0.0);
231 return phi_base + 2.0 *
PI * _start_frequency_offset *
u +
PI * _chirp_rate *
u *
u;
233 return phi_base + 2.0 *
PI * (_start_frequency_offset + _chirp_bandwidth) *
u -
PI * _chirp_rate *
u *
u;
236 std::optional<RealType>
245 if (_triangle_count.has_value() && triangle_index >= *_triangle_count)
273 throw std::runtime_error(
"Signal is empty");
283 std::ranges::for_each(
data, [
scale](
auto& value) { value *=
scale; });
295 std::ranges::for_each(
data, [
scale](
auto& value) { value *=
scale; });
302 if (
dynamic_cast<const CwSignal*
>(_signal.get()) !=
nullptr)
344 return dynamic_cast<const FileSignal*
>(_signal.get());
357 const auto oversampled_samples =
static_cast<std::size_t
>(samples) *
static_cast<std::size_t
>(ratio);
360 throw std::overflow_error(
"Oversampled signal sample count exceeds unsigned range");
364 _rate = sampleRate *
static_cast<RealType>(ratio);
368 std::ranges::copy(
inData, _data.begin());
388 const auto filter_length =
static_cast<long long>(filter.size());
389 const auto sample_count =
static_cast<long long>(_data.size());
391 for (
long long tap = 0;
tap < filter_length; ++
tap)
396 value += _data[
static_cast<std::size_t
>(
sample_index)] * filter[
static_cast<std::size_t
>(
tap)];
402 std::vector<ComplexType>
Signal::render(
const std::vector<interp::InterpPoint>& points,
unsigned&
size,
408 return std::vector<ComplexType>(_size);
417 auto out = std::vector<ComplexType>(sampleCount);
418 if (_size == 0 || _rate <= 0.0 ||
outputSampleRate <= 0.0 || points.empty())
427 auto iter = points.begin();
428 auto next = points.size() > 1 ? std::next(
iter) :
iter;
432 for (std::size_t i = 0; i < sampleCount; ++i)
437 if (std::next(next) != points.end())
473 std::tuple<RealType, RealType, RealType, int>
474 Signal::calculateWeightsAndDelays(
const std::vector<interp::InterpPoint>::const_iterator
iter,
475 const std::vector<interp::InterpPoint>::const_iterator next,
481 const RealType amplitude = std::lerp(std::sqrt(
iter->power), std::sqrt(next->power),
bw);
494 const int start = std::max(-
filtLength / 2, -i);
495 const int end = std::min(
filtLength / 2,
static_cast<int>(_size) - i);
499 for (
int j = start;
j < end; ++
j)
Thread-safe Meyers singleton for generating unique object IDs.
Continuous-wave signal implementation.
std::vector< ComplexType > render(const std::vector< interp::InterpPoint > &points, unsigned &size, RealType fracWinDelay) const override
Renders the signal data.
File-backed sampled waveform with explicit radar-mode identity.
FMCW linear chirp signal implementation.
std::optional< RealType > instantaneousBasebandPhase(RealType time_since_segment_start) const noexcept
Computes instantaneous baseband phase at a time since segment start.
RealType basebandPhaseForChirpTime(RealType chirp_time) const noexcept
Computes baseband phase for a time inside a chirp.
std::vector< ComplexType > render(const std::vector< interp::InterpPoint > &points, unsigned &size, RealType fracWinDelay) const override
Renders an FMCW waveform from interpolation points.
FmcwChirpSignal(RealType chirp_bandwidth, RealType chirp_duration, RealType chirp_period, RealType start_frequency_offset=0.0, std::optional< std::size_t > chirp_count=std::nullopt, FmcwChirpDirection direction=FmcwChirpDirection::Up)
Constructs an FMCW chirp signal with timing and sweep parameters.
std::optional< std::size_t > activeChirpIndexAt(RealType time_since_segment_start) const noexcept
Returns the active chirp index for a time since the segment start.
FMCW symmetric triangular modulation signal implementation.
RealType basebandPhaseForTriangleTime(RealType triangle_time) const noexcept
Computes baseband phase at a time since the triangle train start.
std::optional< RealType > instantaneousBasebandPhase(RealType time_since_segment_start) const noexcept
Computes instantaneous baseband phase at a time since segment start.
std::vector< ComplexType > render(const std::vector< interp::InterpPoint > &points, unsigned &size, RealType fracWinDelay) const override
Renders an FMCW waveform from interpolation points.
FmcwTriangleSignal(RealType chirp_bandwidth, RealType chirp_duration, RealType start_frequency_offset=0.0, std::optional< std::size_t > triangle_count=std::nullopt)
Constructs an FMCW triangular modulation signal.
const class SteppedFrequencySignal * getSteppedFrequencySignal() const noexcept
Gets the stepped-frequency implementation, if this signal owns one.
RadarSignal(std::string name, RealType power, RealType carrierfreq, RealType length, std::unique_ptr< Signal > signal, const SimId id=0)
Constructs a RadarSignal object.
std::vector< ComplexType > renderSlice(const std::vector< interp::InterpPoint > &points, RealType outputStartTime, RealType outputSampleRate, std::size_t sampleCount, RealType fracWinDelay) const
Renders a bounded absolute-time slice on the requested output grid.
const class FileSignal * getFileSignal() const noexcept
Gets the file-backed signal implementation, if this signal owns one.
const class FmcwTriangleSignal * getFmcwTriangleSignal() const noexcept
Gets the FMCW triangle implementation, if this signal owns one.
std::vector< ComplexType > render(const std::vector< interp::InterpPoint > &points, unsigned &size, RealType fracWinDelay) const
Renders the radar signal.
bool isFmcwTriangle() const noexcept
Returns true when this signal is an FMCW triangular modulation signal.
bool isFmcwFamily() const noexcept
Returns true when this signal belongs to the FMCW waveform family.
bool isSteppedFrequency() const noexcept
Returns true when this signal is a stepped-frequency CW waveform.
bool isFmcwChirp() const noexcept
Returns true when this signal is an FMCW linear chirp signal.
bool isCw() const noexcept
Returns true when this signal is a continuous-wave signal.
const class FmcwChirpSignal * getFmcwChirpSignal() const noexcept
Gets the FMCW chirp implementation, if this signal owns one.
virtual std::vector< ComplexType > renderSlice(const std::vector< interp::InterpPoint > &points, RealType outputStartTime, RealType outputSampleRate, std::size_t sampleCount, RealType fracWinDelay) const
Renders a bounded absolute-time slice on the requested output grid.
void clear() noexcept
Clears the internal signal data.
void load(std::span< const ComplexType > inData, unsigned samples, RealType sampleRate)
Loads complex radar waveform data.
ComplexType sampleAt(RealType time_since_start) const noexcept
Samples the finite stored complex envelope using the render interpolation filter.
virtual std::vector< ComplexType > render(const std::vector< interp::InterpPoint > &points, unsigned &size, double fracWinDelay) const
Renders the signal data based on interpolation points.
Stepped-frequency continuous-wave signal implementation.
RealType effectiveBandwidth() const noexcept
Gets DFT-convention effective bandwidth in hertz.
std::optional< StepState > activeStepAt(RealType time_since_segment_start, RealType carrier_frequency) const noexcept
Returns the active step for a time since the segment start.
std::optional< RealType > totalDuration() const noexcept
Gets finite waveform duration, if sweep_count is configured.
RealType getSweepPeriod() const noexcept
Gets full sweep period in seconds.
RealType lastFrequency(RealType carrier_frequency) const noexcept
Gets final-step RF frequency in hertz.
RealType frequencySpan() const noexcept
Gets first-to-last absolute span in hertz.
RealType firstFrequency(RealType carrier_frequency) const noexcept
Gets first-step RF frequency in hertz.
SteppedFrequencySignal(RealType start_frequency_offset, RealType step_size, std::size_t step_count, RealType dwell_time, RealType step_period, std::optional< std::size_t > sweep_count=std::nullopt)
Constructs a uniform stepped-frequency CW signal.
std::vector< ComplexType > render(const std::vector< interp::InterpPoint > &points, unsigned &size, RealType fracWinDelay) const override
Renders the signal data. For SFCW signals, this is a no-op.
static InterpFilter & getInstance() noexcept
Retrieves the singleton instance of the InterpFilter class.
std::span< const RealType > getFilter(RealType delay) const
Retrieves a span of precomputed filter values for a given delay.
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.
Interpolation filter implementation using Kaiser windowing.
Defines a structure to store interpolation point data for signal processing.
FmcwChirpDirection parseFmcwChirpDirection(const std::string_view direction)
Parses a schema chirp direction token.
void upsample(const std::span< const ComplexType > in, const unsigned size, std::span< ComplexType > out)
Upsamples a complex waveform with zero-stuffing followed by Blackman FIR filtering.
std::string_view fmcwChirpDirectionToken(const FmcwChirpDirection direction) noexcept
Converts a chirp direction to the schema token.
FmcwChirpDirection
Sweep direction for a linear FMCW chirp.
@ Down
Instantaneous baseband frequency decreases over the chirp.
@ Up
Instantaneous baseband frequency increases over the chirp.
unsigned oversampleRatio() noexcept
Get the oversampling ratio.
unsigned renderFilterLength() noexcept
Get the render filter length.
Defines the Parameters struct and provides methods for managing simulation parameters.
Classes for handling radar waveforms and signals.
uint64_t SimId
64-bit Unique Simulation ID.
ObjectType
Categorizes objects for ID generation.
Active SFCW dwell selected for one local waveform time.
std::size_t step_index
Zero-based step index inside a sweep.