39 return static_cast<std::size_t
>(
nearest);
41 return static_cast<std::size_t
>(std::ceil(
raw_index));
65 if (
token ==
"physical")
73 throw std::runtime_error(
"Unsupported FMCW dechirp_mode '" + std::string(
token) +
"'.");
94 if (
token ==
"attached")
98 if (
token ==
"transmitter")
102 if (
token ==
"custom")
106 throw std::runtime_error(
"Unsupported dechirp_reference source '" + std::string(
token) +
"'.");
112 _mode(mode), _rng(
seed)
118 std::scoped_lock
const lock(_inbox_mutex);
119 _inbox.push_back(std::move(
response));
124 std::scoped_lock
const lock(_interference_log_mutex);
125 _pulsed_interference_log.push_back(std::move(
response));
130 std::scoped_lock
const lock(_interference_log_mutex);
131 std::erase_if(_pulsed_interference_log,
137 std::scoped_lock
const lock(_inbox_mutex);
146 std::scoped_lock
const lock(_finalizer_queue_mutex);
147 _finalizer_queue.push(std::move(
job));
149 _finalizer_queue_cv.notify_one();
154 std::unique_lock
lock(_finalizer_queue_mutex);
155 _finalizer_queue_cv.wait(
lock, [
this] {
return !_finalizer_queue.empty(); });
157 job = std::move(_finalizer_queue.front());
158 _finalizer_queue.pop();
174 throw std::runtime_error(
"Noise temperature must be positive");
176 _noise_temperature =
temp;
184 setDechirpMode(DechirpMode::None);
191 _dechirp_mode = mode;
192 if (_dechirp_mode == DechirpMode::None)
194 _dechirp_reference = {};
195 _dechirp_sources.clear();
197 _fmcw_if_plan.reset();
198 _fmcw_if_sink.reset();
199 _fmcw_if_samples_to_discard = 0;
200 _fmcw_if_input_cursor = 0;
201 _fmcw_if_output_cursor = 0;
202 _fmcw_if_segment_active =
false;
208 _dechirp_reference = std::move(
reference);
209 _dechirp_sources.clear();
215 _fmcw_if_plan.reset();
216 _fmcw_if_sink.reset();
217 _fmcw_if_samples_to_discard = 0;
218 _fmcw_if_input_cursor = 0;
219 _fmcw_if_output_cursor = 0;
220 _fmcw_if_segment_active =
false;
225 _fmcw_if_plan = std::move(plan);
226 _fmcw_if_samples_to_discard = _fmcw_if_plan->warmup_discard_samples;
227 _fmcw_if_sink = std::make_unique<fers_signal::FmcwIfResamplingSink>(*_fmcw_if_plan);
228 _fmcw_if_input_cursor = 0;
229 _fmcw_if_output_cursor = 0;
230 _fmcw_if_segment_active =
false;
235 if (_fmcw_if_sink ==
nullptr || !_fmcw_if_plan.has_value())
240 _fmcw_if_segment_active =
true;
245 if (_fmcw_if_sink ==
nullptr || !_fmcw_if_plan.has_value())
247 throw std::logic_error(
"FMCW IF resampling sink has not been initialized.");
249 const auto input_sample_rate_hz = _fmcw_if_plan->input_sample_rate_hz;
250 if (!_fmcw_if_segment_active)
257 throw std::logic_error(
"FMCW IF resampling input blocks must be supplied in chronological order.");
260 _fmcw_if_sink->consume(
block);
261 _fmcw_if_input_cursor +=
block.size();
262 auto emitted = _fmcw_if_sink->takeOutput();
263 appendFmcwIfOutput(std::move(emitted));
268 _fmcw_if_output_callback = std::move(
callback);
271 void Receiver::appendFmcwIfOutput(std::vector<ComplexType> emitted)
273 if (_fmcw_if_samples_to_discard > 0)
275 const auto discard = std::min<std::uint64_t>(_fmcw_if_samples_to_discard, emitted.size());
276 emitted.erase(emitted.begin(), emitted.begin() +
static_cast<std::ptrdiff_t
>(
discard));
277 _fmcw_if_samples_to_discard -=
discard;
283 const auto output_start =
static_cast<std::uint64_t
>(_fmcw_if_output_cursor);
284 if (_fmcw_if_output_callback)
286 _fmcw_if_output_callback(std::span<const ComplexType>(emitted.data(), emitted.size()),
output_start);
288 _fmcw_if_output_cursor += emitted.size();
291 void Receiver::advanceFmcwIfOutputZeros(std::size_t sample_count)
293 if (_fmcw_if_samples_to_discard > 0)
295 const auto discard = std::min<std::uint64_t>(_fmcw_if_samples_to_discard, sample_count);
296 sample_count -=
static_cast<std::size_t
>(
discard);
297 _fmcw_if_samples_to_discard -=
discard;
299 if (sample_count == 0)
303 _fmcw_if_output_cursor += sample_count;
308 if (_fmcw_if_sink ==
nullptr)
312 _fmcw_if_segment_active =
false;
317 if (_fmcw_if_sink ==
nullptr)
322 if (_fmcw_if_plan.has_value())
325 1.0 / _fmcw_if_plan->actual_output_sample_rate_hz;
327 auto emitted = _fmcw_if_sink->finish();
328 appendFmcwIfOutput(std::move(emitted));
330 _fmcw_if_sink.reset();
335 if (_fmcw_if_sink ==
nullptr)
337 throw std::logic_error(
"FMCW IF resampling sink has not been initialized.");
345 auto result = _fmcw_if_sink->consumeZeroInput(count);
347 appendFmcwIfOutput(std::move(
result.emitted));
348 advanceFmcwIfOutputZeros(
result.skipped_output_samples);
353 _dechirp_sources = std::move(
sources);
359 _window_length = length;
360 _window_prf = 1 / (std::floor(rate /
prf) / rate);
361 _window_skip = std::floor(rate *
skip) / rate;
368 return static_cast<unsigned>(std::ceil(
pulses));
377 throw std::logic_error(
"Receiver must be associated with timing source");
387 if (_schedule.empty())
391 for (
const auto&
period : _schedule)
static SimIdGenerator & instance()
Get the singleton instance of SimIdGenerator.
A class representing a vector in spherical coordinates.
const std::string & getName() const noexcept
Retrieves the name of the object.
Represents a radar system on a platform.
std::shared_ptr< timing::Timing > _timing
Timing source for the radar.
virtual RealType getNoiseTemperature(const math::SVec3 &angle) const noexcept
Gets the noise temperature of the radar.
void addInterferenceToLog(std::unique_ptr< serial::Response > response) noexcept
Adds a pulsed interference response to the receiver's streaming-mode log.
void setMode(OperationMode mode) noexcept
Sets the operational mode of the receiver.
void prunePulsedInterferenceEndingBefore(RealType cutoff_time) noexcept
Removes logged pulsed interference responses that ended before a receive time.
std::vector< std::unique_ptr< serial::Response > > drainInbox() noexcept
Moves all responses from the inbox into a RenderingJob.
void initializeFmcwIfResampling(fers_signal::FmcwIfResamplerPlan plan)
Creates the online FMCW IF resampling sink and clears the output buffer.
DechirpReferenceSource
Source used to construct the receiver LO reference.
@ Transmitter
Use a named transmitter.
@ Attached
Use the attached transmitter.
@ None
No reference configured.
@ Custom
Use a named top-level waveform with the receiver schedule.
unsigned getWindowCount() const noexcept
Gets the number of radar windows.
void setDechirpReference(DechirpReference reference)
Stores the unresolved dechirp reference parsed from scenario input.
void setResolvedDechirpSources(std::vector< core::ActiveStreamingSource > sources)
Replaces resolved receive-time LO source segments.
void consumeFmcwIfBlock(std::span< const ComplexType > block, RealType block_start_time)
Feeds one completed high-rate dechirped block into the online IF sink.
RealType getWindowStart(unsigned window) const
Retrieves the start time of a specific radar window.
std::function< void(std::span< const ComplexType >, std::uint64_t)> FmcwIfOutputCallback
void beginFmcwIfResamplingSegment(RealType segment_start_time)
Advances the continuous IF resampling timeline to the next active segment start.
std::optional< RealType > getNextWindowTime(RealType time) const
Determines the next valid window start time at or after the given time.
void setDechirpMode(DechirpMode mode) noexcept
Sets the receiver-side dechirp mode.
void endFmcwIfResamplingSegment()
Marks the current scheduled IF segment inactive.
void setSchedule(std::vector< SchedulePeriod > schedule)
Sets the active schedule for the receiver.
void enqueueFinalizerJob(core::RenderingJob &&job)
Adds a completed RenderingJob to the finalizer queue.
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.
void setWindowProperties(RealType length, RealType prf, RealType skip) noexcept
Sets the properties for radar windows.
void setFmcwIfOutputCallback(FmcwIfOutputCallback callback)
Routes emitted IF samples to a live consumer instead of the HDF5 accumulation buffer.
void addResponseToInbox(std::unique_ptr< serial::Response > response) noexcept
Adds a response to the receiver's pulsed-mode inbox.
DechirpMode
Receiver-side FMCW dechirping mode.
@ Ideal
Ignore timing phase noise during the dechirp mix.
@ None
Output raw pre-mix streaming IQ.
@ Physical
Preserve timing phase-noise decorrelation in the IF output.
void setFmcwIfChainRequest(FmcwIfChainRequest request) noexcept
Stores the receiver-local FMCW IF-chain request.
void setNoiseTemperature(RealType temp)
Sets the noise temperature of the receiver.
void flushFmcwIfResampling()
Flushes remaining samples from the online IF sink into the output buffer.
Receiver(Platform *platform, std::string name, unsigned seed, OperationMode mode, const SimId id=0) noexcept
Constructs a Receiver object.
double RealType
Type for real numbers.
constexpr RealType EPSILON
Machine epsilon for real numbers.
@ FATAL
Fatal level for severe error events.
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.
std::string_view dechirpReferenceSourceToken(const Receiver::DechirpReferenceSource source) noexcept
Converts a dechirp reference source to its scenario token.
OperationMode
Defines the operational mode of a radar component.
@ FMCW_MODE
The component operates in an FMCW streaming mode.
Receiver::DechirpReferenceSource parseDechirpReferenceSourceToken(const std::string_view token)
Parses a dechirp reference source scenario token.
std::string_view dechirpModeToken(const Receiver::DechirpMode mode) noexcept
Converts a dechirp mode to its scenario token.
Receiver::DechirpMode parseDechirpModeToken(const std::string_view token)
Parses a dechirp mode scenario token.
Defines the Parameters struct and provides methods for managing simulation parameters.
Radar Receiver class for managing signal reception and response handling.
Classes for managing radar signal responses.
uint64_t SimId
64-bit Unique Simulation ID.
A data packet containing all information needed to process one receive window.
Parsed and resolved dechirp reference details.
Receiver-local FMCW IF-chain request parsed from scenario input.