40 constexpr RealType sinc(
const RealType x)
noexcept {
return x == 0 ? 1.0 : std::sin(x *
PI) / (x *
PI); }
48 struct BlackmanFirDesign
50 std::vector<RealType> coeffs;
77 std::abs(coeff_sum -
static_cast<RealType>(ratio)) /
static_cast<RealType>(ratio);
78 if (relative_error <= 0.01)
84 static std::set<std::pair<unsigned, unsigned>>
warned_pairs;
94 ", filter_length=", filter_length,
", fir_sum=", coeff_sum,
", estimated_roundtrip_gain=",
roundtrip_gain,
95 ". Practical envelope roughly filter_length >= 4 * ratio.");
115 std::ranges::for_each(coeffs,
125 BlackmanFirDesign
design{std::move(coeffs)};
145 void upsample(
const std::span<const ComplexType>
in,
const unsigned size, std::span<ComplexType>
out)
159 for (
unsigned i = 0; i <
size; ++i)
161 tmp[
static_cast<std::size_t
>(i) *
static_cast<std::size_t
>(ratio)] =
in[i];
167 const auto delay =
static_cast<std::size_t
>(
filt_length / 2);
187 throw std::invalid_argument(
"Input span is empty in Downsample");
199 std::ranges::copy(
in,
tmp.begin());
227 _line.assign(_coeffs.size(),
ComplexType{0.0, 0.0});
228 _delay = _coeffs.size() / 2u;
235 throw std::logic_error(
"Cannot consume downsampler input after finish");
244 _pending.insert(_pending.end(),
block.begin(),
block.end());
245 _input_sample_count +=
block.size();
246 _processed_sample_count +=
block.size();
247 _next_output_index +=
block.size();
251 for (
const auto& sample :
block)
253 const auto filtered = processOne(sample);
254 ++_input_sample_count;
256 ++_processed_sample_count;
272 _target_output_count = _input_sample_count / _ratio;
273 while (_next_output_index < _target_output_count)
277 ++_processed_sample_count;
283 std::vector<ComplexType>
out;
292 _input_sample_count = 0;
293 _processed_sample_count = 0;
294 _next_output_index = 0;
295 _target_output_count = 0;
302 const auto filtered = std::transform_reduce(
303 _line.begin(), _line.end(), _coeffs.begin(),
ComplexType{0.0, 0.0}, std::plus<ComplexType>{},
305 if (_line.size() > 1u)
307 std::move_backward(_line.begin(), _line.end() - 1, _line.end());
314 const auto required_input_index = _next_output_index * _ratio +
static_cast<std::uint64_t
>(_delay);
318 ++_next_output_index;
329 std::ranges::rotate(_w, _w.end() - 1);
333 for (
unsigned j = 1;
j < _order; ++
j)
335 _w[0] -= _a[
j] * _w[
j];
338 return std::inner_product(_b.begin(), _b.end(), _w.begin(), 0.0);
343 for (
auto& sample : samples)
345 std::ranges::rotate(_w, _w.end() - 1);
349 for (
unsigned j = 1;
j < _order; ++
j)
351 _w[0] -= _a[
j] * _w[
j];
354 sample = std::inner_product(_b.begin(), _b.end(), _w.begin(), 0.0);
362 for (
auto& sample : samples)
368 std::plus<ComplexType>{},
373 std::ranges::rotate(std::views::reverse(
line),
line.rbegin() + 1);
393 constexpr std::array
num_coeffs = {2.7301694322809e-06, -1.8508123430239e-05, 5.75739466753894e-05,
394 -0.000104348734423658, 0.000111949190289715, -4.9384188225528e-05,
395 -4.9384188225522e-05, 0.00011194919028971, -0.000104348734423656,
396 5.75739466753884e-05, -1.85081234302388e-05, 2.73016943228086e-06};
403 if (
out.size() != 10)
405 throw std::invalid_argument(
"Output span must have a size of 10.");
408 std::fill(
out.begin() + 1,
out.end(), 0);
409 _filter->filter(
out);
void upsample(RealType sample, std::span< RealType > out) const
Upsamples a single sample.
std::vector< ComplexType > takeOutput()
void consume(std::span< const ComplexType > block)
Implements a Finite Impulse Response (FIR) filter.
RealType filter(RealType) override
Filters a single real-valued sample; FIR scalar filtering is unsupported.
IirFilter(const RealType *denCoeffs, const RealType *numCoeffs, unsigned order) noexcept
Constructs an IIR filter with given numerator and denominator coefficients and order.
RealType filter(RealType sample) noexcept override
Filters a single sample.
double RealType
Type for real numbers.
std::complex< RealType > ComplexType
Type for complex numbers.
constexpr RealType PI
Mathematical constant π (pi).
constexpr RealType BLACKMAN_A2
constexpr RealType BLACKMAN_A1
constexpr RealType BLACKMAN_A0
Header file for Digital Signal Processing (DSP) filters and upsampling/downsampling functionality.
Header file for the logging system.
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::vector< ComplexType > downsample(std::span< const ComplexType > in)
Low-pass filters and decimates an oversampled complex waveform back to base rate.
@ WARNING
Warning level for potentially harmful situations.
unsigned oversampleRatio() noexcept
Get the oversampling ratio.
unsigned renderFilterLength() noexcept
Get the render filter length.
void validateOversampleRatio(const unsigned ratio)
Validates that an oversampling ratio is supported.
Defines the Parameters struct and provides methods for managing simulation parameters.