FERS 1.0.0
The Flexible Extensible Radar Simulator
Loading...
Searching...
No Matches
signal_processor.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-only
2//
3// Copyright (c) 2006-2008 Marc Brooker and Michael Inggs
4// Copyright (c) 2008-present FERS Contributors (see AUTHORS.md).
5//
6// See the GNU GPLv2 LICENSE file in the FERS project root for more information.
7
8/**
9 * @file signal_processor.cpp
10 * @brief Implementation for receiver-side signal processing and rendering.
11 *
12 * This file contains the implementation of functions that perform digital signal
13 * processing on received radar signals, as declared in signal_processor.h.
14 */
15
16#include "signal_processor.h"
17
18#include <algorithm>
19#include <cmath>
20#include <future>
21#include <queue>
22#include <tuple>
23#include <vector>
24
25#include "core/parameters.h"
26#include "core/thread_pool.h"
28#include "serial/response.h"
29
30namespace
31{
32 /**
33 * @brief Simulate an ADC quantization process on a window of complex samples.
34 * @param data A span of ComplexType objects representing the window to quantize.
35 * @param bits The number of bits used for quantization.
36 * @param fullscale The full-scale value used for quantization.
37 */
38 void adcSimulate(std::span<ComplexType> data, const unsigned bits, const RealType fullscale) noexcept
39 {
40 const RealType levels = std::pow(2, bits - 1);
41
42 for (auto& sample : data)
43 {
44 auto [i, q] = std::tuple{std::clamp(std::floor(levels * sample.real() / fullscale) / levels, -1.0, 1.0),
45 std::clamp(std::floor(levels * sample.imag() / fullscale) / levels, -1.0, 1.0)};
46 sample = ComplexType(i, q);
47 }
48 }
49
50 /**
51 * @brief Process a single response and add its rendered data to a local window buffer.
52 * @param resp A pointer to a Response object to process.
53 * @param localWindow A reference to a vector of ComplexType representing the local window.
54 * @param rate The sample rate in Hz.
55 * @param start The start time of the window in seconds.
56 * @param fracDelay The fractional delay of the window in seconds.
57 * @param localWindowSize The size of the local window in samples.
58 */
59 void processResponse(const serial::Response* resp, std::vector<ComplexType>& localWindow, const RealType rate,
60 const RealType start, const RealType fracDelay, const unsigned localWindowSize)
61 {
62 unsigned psize;
63 RealType prate;
64 const auto array = resp->renderBinary(prate, psize, fracDelay);
65 int start_sample = static_cast<int>(std::round(rate * (resp->startTime() - start)));
66 const unsigned roffset = start_sample < 0 ? -start_sample : 0;
67 if (start_sample < 0)
68 {
69 start_sample = 0;
70 }
71
72 for (unsigned i = roffset; i < psize && i + start_sample < localWindowSize; ++i)
73 {
74 localWindow[i + start_sample] += array[i];
75 }
76 }
77}
78
79namespace processing
80{
81 void renderWindow(std::vector<ComplexType>& window, const RealType length, const RealType start,
82 const RealType fracDelay, const std::span<const std::unique_ptr<serial::Response>> responses)
83 {
84 const RealType end = start + length;
85 std::queue<serial::Response*> work_list;
86
87 for (const auto& response : responses)
88 {
89 if (response->startTime() <= end && response->endTime() >= start)
90 {
91 work_list.push(response.get());
92 }
93 }
94
96 const auto local_window_size = static_cast<unsigned>(std::ceil(length * rate));
97
98 std::vector local_window(local_window_size, ComplexType{});
99
100 while (!work_list.empty())
101 {
102 const auto* resp = work_list.front();
103 work_list.pop();
104 processResponse(resp, local_window, rate, start, fracDelay, local_window_size);
105 }
106
107 for (unsigned i = 0; i < local_window_size; ++i)
108 {
109 window[i] += local_window[i];
110 }
111 }
112
113 void applyThermalNoise(std::span<ComplexType> window, const RealType noiseTemperature, std::mt19937& rngEngine)
114 {
115 if (noiseTemperature == 0)
116 {
117 return;
118 }
119
120 const RealType b = params::rate() / (2.0 * params::oversampleRatio());
121 const RealType total_power = params::boltzmannK() * noiseTemperature * b;
122
123 // Split total power equally between the I and Q channels
124 const RealType per_channel_power = total_power / 2.0;
125 const RealType stddev = std::sqrt(per_channel_power);
126
127 noise::WgnGenerator generator(rngEngine, stddev);
128 for (auto& sample : window)
129 {
130 sample += ComplexType(generator.getSample(), generator.getSample());
131 }
132 }
133
134 RealType quantizeAndScaleWindow(std::span<ComplexType> window)
135 {
136 RealType max_value = 0;
137 for (const auto& sample : window)
138 {
139 const RealType real_abs = std::fabs(sample.real());
140 const RealType imag_abs = std::fabs(sample.imag());
141
142 max_value = std::max({max_value, real_abs, imag_abs});
143 }
144
145 if (const auto adc_bits = params::adcBits(); adc_bits > 0)
146 {
147 adcSimulate(window, adc_bits, max_value);
148 }
149 else if (max_value != 0)
150 {
151 for (auto& sample : window)
152 {
153 sample /= max_value;
154 }
155 }
156 return max_value;
157 }
158}
Generates white Gaussian noise.
RealType getSample() noexcept override
Generates a sample of white Gaussian noise.
Manages radar signal responses from a transmitter.
Definition response.h:40
RealType startTime() const noexcept
Retrieves the start time of the response.
Definition response.h:64
std::vector< ComplexType > renderBinary(RealType &rate, unsigned &size, RealType fracWinDelay) const
Renders the response in binary format.
Definition response.cpp:28
double RealType
Type for real numbers.
Definition config.h:27
std::complex< RealType > ComplexType
Type for complex numbers.
Definition config.h:35
RealType rate() noexcept
Get the rendering sample rate.
Definition parameters.h:109
RealType boltzmannK() noexcept
Get the Boltzmann constant.
Definition parameters.h:85
unsigned oversampleRatio() noexcept
Get the oversampling ratio.
Definition parameters.h:139
unsigned adcBits() noexcept
Get the ADC quantization bits.
Definition parameters.h:121
void applyThermalNoise(std::span< ComplexType > window, const RealType noiseTemperature, std::mt19937 &rngEngine)
Applies thermal (Johnson-Nyquist) noise to a window of I/Q samples.
RealType quantizeAndScaleWindow(std::span< ComplexType > window)
Simulates ADC quantization and scales a window of complex I/Q samples.
void renderWindow(std::vector< ComplexType > &window, const RealType length, const RealType start, const RealType fracDelay, const std::span< const std::unique_ptr< serial::Response > > responses)
Renders a time-window of I/Q data from a collection of raw radar responses.
Header file for noise generator classes.
Defines the Parameters struct and provides methods for managing simulation parameters.
Classes for managing radar signal responses.
Header for receiver-side signal processing and rendering.
A simple thread pool implementation.