FERS 0.1.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
fmcw_validation.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-only
2//
3// Copyright (c) 2026-present FERS Contributors (see AUTHORS.md).
4//
5// See the GNU GPLv2 LICENSE file in the FERS project root for more information.
6
7#include "fmcw_validation.h"
8
9#include <algorithm>
10#include <cmath>
11#include <cstdint>
12#include <format>
13
14#include "core/logging.h"
15#include "core/parameters.h"
16#include "signal/radar_signal.h"
17
19{
20 namespace
21 {
22 /// Emits a warning when a streaming scenario may allocate a large I/Q buffer.
23 void warnStreamingMemory(const std::string& owner)
24 {
25 const RealType duration = std::max<RealType>(0.0, params::endTime() - params::startTime());
27 const auto estimated_samples = static_cast<std::uint64_t>(std::ceil(duration * effective_rate));
28 const std::uint64_t estimated_bytes = estimated_samples * sizeof(ComplexType);
29 constexpr std::uint64_t warning_threshold_bytes = 2ULL * 1024ULL * 1024ULL * 1024ULL;
31 {
32 return;
33 }
34
35 const double estimated_gib = static_cast<double>(estimated_bytes) / (1024.0 * 1024.0 * 1024.0);
37 "{} will buffer about {:.2f} GiB of FMCW streaming output for simulation_duration={} s at "
38 "effective_rate={} Hz. Large simulation_time * bandwidth/rate products need chunking.",
40 }
41
42 /// Returns schedule periods, or the full simulation as one implicit active period.
43 std::vector<radar::SchedulePeriod> effectiveSchedule(const std::vector<radar::SchedulePeriod>& schedule)
44 {
45 if (!schedule.empty())
46 {
47 return schedule;
48 }
50 }
51
52 /// Validates common FMCW sweep constraints.
53 void validateCommonSweep(const fers_signal::RadarSignal& wave, const std::string& owner,
54 const RealType chirp_duration, const RealType chirp_bandwidth,
56 {
57 if (chirp_duration <= 0.0)
58 {
59 throw_error(owner + " has FMCW chirp_duration <= 0.");
60 }
61 if (chirp_bandwidth <= 0.0)
62 {
63 throw_error(owner + " has FMCW chirp_bandwidth <= 0.");
64 }
65
66 const RealType f_low = std::min(sweep_start, sweep_end);
67 const RealType f_high = std::max(sweep_start, sweep_end);
69 const RealType max_baseband = std::max(std::abs(f_low), std::abs(f_high));
71 {
72 throw_error(owner + " violates FMCW baseband aliasing constraint.");
73 }
74 if (max_baseband > 0.0 && effective_rate < 1.1 * max_baseband)
75 {
77 "{} is within 10% of the FMCW aliasing limit: effective_rate={} Hz, max_baseband={} Hz.", owner,
79 }
80
81 const RealType min_rf = wave.getCarrier() + f_low;
82 if (min_rf <= 0.0)
83 {
84 throw_error(owner + " yields a non-positive RF-equivalent frequency.");
85 }
86 }
87 }
88
89 void validateWaveform(const fers_signal::RadarSignal& wave, const std::string& owner, const Thrower& throw_error)
90 {
91 if (const auto* fmcw = wave.getFmcwChirpSignal(); fmcw != nullptr)
92 {
93 if (fmcw->getChirpPeriod() < fmcw->getChirpDuration())
94 {
95 throw_error(owner + " has chirp_period shorter than chirp_duration; FMCW requires T_rep >= T_c.");
96 }
97
98 const RealType sweep_start = fmcw->getStartFrequencyOffset();
99 const RealType sweep_end =
100 sweep_start + (fmcw->isDownChirp() ? -fmcw->getChirpBandwidth() : fmcw->getChirpBandwidth());
101 validateCommonSweep(wave, owner, fmcw->getChirpDuration(), fmcw->getChirpBandwidth(), sweep_start,
104 return;
105 }
106
107 if (const auto* triangle = wave.getFmcwTriangleSignal(); triangle != nullptr)
108 {
109 const RealType sweep_start = triangle->getStartFrequencyOffset();
110 const RealType sweep_end = sweep_start + triangle->getChirpBandwidth();
111 validateCommonSweep(wave, owner, triangle->getChirpDuration(), triangle->getChirpBandwidth(), sweep_start,
114 }
115 }
116
118 const std::string& owner, const Thrower& throw_error)
119 {
120 const bool is_pulsed = !wave.isCw() && !wave.isFmcwFamily();
121 const bool matches = (mode == radar::OperationMode::PULSED_MODE && is_pulsed) ||
122 (mode == radar::OperationMode::CW_MODE && wave.isCw()) ||
123 (mode == radar::OperationMode::FMCW_MODE && wave.isFmcwFamily());
124 if (!matches)
125 {
126 throw_error(owner + " mode does not match waveform '" + wave.getName() + "'.");
127 }
128 }
129
130 void validateSchedule(const std::vector<radar::SchedulePeriod>& schedule, const fers_signal::FmcwChirpSignal& fmcw,
131 const std::string& owner, const Thrower& throw_error)
132 {
133 for (const auto& period : effectiveSchedule(schedule))
134 {
135 const RealType duration = period.end - period.start;
136 if (duration < fmcw.getChirpDuration())
137 {
138 throw_error(std::format(
139 "{} has schedule period [{}, {}] duration {} s shorter than FMCW chirp_duration T_c={} s.", owner,
140 period.start, period.end, duration, fmcw.getChirpDuration()));
141 }
142 if (duration < fmcw.getChirpPeriod())
143 {
144 LOG(logging::Level::WARNING, "{} has a schedule period [{}, {}] shorter than FMCW chirp_period ({}s).",
145 owner, period.start, period.end, fmcw.getChirpPeriod());
146 }
147 }
148 }
149
150 void validateSchedule(const std::vector<radar::SchedulePeriod>& schedule, const fers_signal::RadarSignal& wave,
151 const std::string& owner, const Thrower& throw_error)
152 {
153 if (const auto* fmcw = wave.getFmcwChirpSignal(); fmcw != nullptr)
154 {
156 return;
157 }
158
159 const auto* triangle = wave.getFmcwTriangleSignal();
160 if (triangle == nullptr)
161 {
162 return;
163 }
164
165 const RealType T_tri = triangle->getTrianglePeriod();
166 for (const auto& period : effectiveSchedule(schedule))
167 {
168 const RealType duration = period.end - period.start;
169 if (duration < T_tri)
170 {
171 throw_error(std::format(
172 "{} has schedule period [{}, {}] duration {} s shorter than FMCW triangle_period T_tri={} s.",
173 owner, period.start, period.end, duration, T_tri));
174 }
175 const RealType full_triangles = std::floor(duration / T_tri);
177 const RealType leftover = duration - used;
178 if (leftover > 1.0e-12)
179 {
181 "{} has schedule period [{}, {}] that is not an integer multiple of FMCW triangle_period ({}s); "
182 "{}s will be silent after the last complete triangle.",
183 owner, period.start, period.end, T_tri, leftover);
184 }
185 }
186 }
187}
FMCW linear chirp signal implementation.
RealType getChirpDuration() const noexcept
Gets the chirp duration in seconds.
RealType getChirpPeriod() const noexcept
Gets the chirp period in seconds.
Class representing a radar signal with associated properties.
double RealType
Type for real numbers.
Definition config.h:27
std::complex< RealType > ComplexType
Type for complex numbers.
Definition config.h:35
Header file for the logging system.
#define LOG(level,...)
Definition logging.h:19
@ WARNING
Warning level for potentially harmful situations.
RealType endTime() noexcept
Get the end time for the simulation.
Definition parameters.h:109
RealType rate() noexcept
Get the rendering sample rate.
Definition parameters.h:121
RealType startTime() noexcept
Get the start time for the simulation.
Definition parameters.h:103
unsigned oversampleRatio() noexcept
Get the oversampling ratio.
Definition parameters.h:151
OperationMode
Defines the operational mode of a radar component.
Definition radar_obj.h:39
@ PULSED_MODE
The component operates in a pulsed mode.
@ CW_MODE
The component operates in a continuous-wave mode.
@ FMCW_MODE
The component operates in an FMCW streaming mode.
void validateWaveform(const fers_signal::RadarSignal &wave, const std::string &owner, const Thrower &throw_error)
Validates that a waveform is compatible with FMCW streaming constraints.
std::function< void(const std::string &)> Thrower
Callback used by validators to report an error through the caller's mechanism.
void validateSchedule(const std::vector< radar::SchedulePeriod > &schedule, const fers_signal::FmcwChirpSignal &fmcw, const std::string &owner, const Thrower &throw_error)
Validates that an FMCW waveform schedule can emit complete chirps.
void validateWaveformModeMatch(const fers_signal::RadarSignal &wave, const radar::OperationMode mode, const std::string &owner, const Thrower &throw_error)
Validates that a waveform and radar operation mode are compatible.
Defines the Parameters struct and provides methods for managing simulation parameters.
Classes for handling radar waveforms and signals.
math::Vec3 max
Represents a time period during which the transmitter is active.
RealType start
Period start time in seconds.