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; });
344 const auto oversampled_samples =
static_cast<std::size_t
>(samples) *
static_cast<std::size_t
>(ratio);
347 throw std::overflow_error(
"Oversampled signal sample count exceeds unsigned range");
351 _rate = sampleRate *
static_cast<RealType>(ratio);
355 std::ranges::copy(
inData, _data.begin());
363 std::vector<ComplexType>
Signal::render(
const std::vector<interp::InterpPoint>& points,
unsigned&
size,
369 return std::vector<ComplexType>(_size);
378 auto out = std::vector<ComplexType>(sampleCount);
379 if (_size == 0 || _rate <= 0.0 ||
outputSampleRate <= 0.0 || points.empty())
388 auto iter = points.begin();
389 auto next = points.size() > 1 ? std::next(
iter) :
iter;
393 for (std::size_t i = 0; i < sampleCount; ++i)
398 if (std::next(next) != points.end())
434 std::tuple<RealType, RealType, RealType, int>
435 Signal::calculateWeightsAndDelays(
const std::vector<interp::InterpPoint>::const_iterator
iter,
436 const std::vector<interp::InterpPoint>::const_iterator next,
442 const RealType amplitude = std::lerp(std::sqrt(
iter->power), std::sqrt(next->power),
bw);
455 const int start = std::max(-
filtLength / 2, -i);
456 const int end = std::min(
filtLength / 2,
static_cast<int>(_size) - i);
460 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.
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 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.
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.
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.