33 throw std::invalid_argument(
"Kaiser Bessel approximation requires x >= 0");
41 t * (3.5156229 + t * (3.0899424 + t * (1.2067492 + t * (0.2659732 + t * (0.0360768 + t * 0.0045813)))));
54 inv * (-0.02057706 +
inv * (0.02635537 +
inv * (-0.01647633 +
inv * 0.00392377)))))));
55 return poly * std::exp(x) / std::sqrt(x);
79 const RealType arg =
beta * std::sqrt(std::max<RealType>(0.0, 1.0 - ratio * ratio));
85 taps = std::max<std::size_t>(3,
taps);
93 if (!std::isfinite(transition_width_hz) || transition_width_hz <= 0.0 ||
96 throw std::invalid_argument(
"IF resampler transition width and design sample rate must be positive");
102 throw std::invalid_argument(
"IF resampler normalized transition width is outside (0, 0.5)");
114 throw std::invalid_argument(
message);
122 if (
request.filter_transition_width_hz.has_value())
125 "IF resampler filter transition width must be positive");
127 "IF resampler filter transition width must fit below output Nyquist");
128 return *
request.filter_transition_width_hz;
136 const auto up = std::max<std::uint64_t>(1, up_factor);
137 const auto branch =
static_cast<std::size_t
>((
taps +
up - 1) /
up);
143 const std::uint64_t up_factor,
const std::uint64_t down_factor,
const RealType passband_hz,
153 throw std::runtime_error(
"IF resampler passband does not fit stage Nyquist limit");
159 throw std::runtime_error(
"IF resampler transition width is too narrow for stage");
168 throw std::runtime_error(
169 "IF resampler FIR tap count " + std::to_string(
taps) +
" exceeds maximum " +
171 "; increase if_sample_rate, reduce if_filter_bandwidth, or increase transition width");
178 stage.up_factor = up_factor;
179 stage.down_factor = down_factor;
181 stage.phase_refinement =
183 stage.phase_count =
static_cast<std::size_t
>(up_factor) *
stage.phase_refinement;
187 stage.stopband_attenuation_db = stopband_attenuation_db;
196 stage.estimated_macs_per_stage_output =
204 validateLimit(std::isfinite(input_sample_rate_hz) && input_sample_rate_hz > 0.0,
205 "IF resampler input sample rate must be positive");
206 validateLimit(std::isfinite(output_sample_rate_hz) && output_sample_rate_hz > 0.0,
207 "IF resampler output sample rate must be positive");
209 "IF resampler output sample rate must be <= input sample rate");
210 return output_sample_rate_hz / input_sample_rate_hz;
214 const std::uint64_t down_factor)
noexcept
216 const long double scaled =
static_cast<long double>(
input_count) *
static_cast<long double>(up_factor) /
217 static_cast<long double>(down_factor);
218 return static_cast<std::size_t
>(std::ceil(
scaled - 1.0e-12L));
231 for (
const auto&
stage : plan.stages)
270 stage.initial_input_advance =
static_cast<std::int64_t
>(std::floor(
u0 /
modulus));
272 stage.initial_phase_accumulator =
static_cast<std::size_t
>(std::floor(
residual));
274 stage.applies_fractional_delay =
true;
286 throw std::runtime_error(
"IF resampler fractional-delay MAC cost " +
301 return {.numerator = 1,
303 .requested_ratio =
target,
305 .relative_error = std::abs(
target - 1.0)};
309 std::uint64_t
num = 1;
311 std::uint64_t
den = 0;
317 const auto a =
static_cast<std::uint64_t
>(std::floor(x));
329 .requested_ratio =
target,
331 .relative_error = relative_error};
338 if (std::abs(
remainder) <= std::numeric_limits<RealType>::epsilon())
349 throw std::runtime_error(
"IF resampler cannot approximate output/input sample-rate ratio within tolerance; "
350 "increase max denominator or choose a representable IF sample rate");
358 "IF resampler filter bandwidth must be positive");
360 "IF resampler filter bandwidth must be below output Nyquist");
361 validateLimit(
request.limits.max_taps_per_stage > 0,
"IF resampler max taps per stage must be positive");
362 validateLimit(
request.limits.max_macs_per_output_sample > 0.0,
"IF resampler max MAC budget must be positive");
363 validateLimit(
request.limits.max_phase_refinement > 0,
"IF resampler phase refinement limit must be positive");
418 throw std::runtime_error(
420 " exceeds maximum " + std::to_string(
request.limits.max_macs_per_output_sample) +
421 "; increase if_sample_rate, reduce if_filter_bandwidth, or increase transition width");
456 throw std::logic_error(
"Cannot consume IF resampler input after finish");
458 _input.insert(_input.end(),
block.begin(),
block.end());
459 _input_total +=
block.size();
467 throw std::logic_error(
"Cannot consume IF resampler input after finish");
498 keepTrailingZeroHistory();
504 std::vector<ComplexType>
out;
525 _input_start_index = 0;
526 _next_output_index = 0;
535 void buildPhaseTable()
538 const std::size_t phase_count = std::max<std::size_t>(1, _plan.
phase_count);
539 _phase_table.assign(phase_count *
taps, 0.0);
548 for (std::size_t phase = 0; phase < phase_count; ++phase)
552 for (std::size_t i = 0; i <
taps; ++i)
560 if (std::abs(
sum) > std::numeric_limits<RealType>::epsilon())
562 for (std::size_t i = 0; i <
taps; ++i)
564 _phase_table[phase *
taps + i] /=
sum;
571 const bool final)
const noexcept
573 const auto half =
static_cast<std::int64_t
>((_plan.
tap_count - 1) / 2);
587 throw std::logic_error(
"IF resampler discarded input still needed by FIR branch");
589 return _input[
static_cast<std::size_t
>(
local)];
592 [[
nodiscard]]
ComplexType evaluateBranch(
const std::size_t phase,
const std::int64_t base_index)
const
594 const auto half =
static_cast<std::int64_t
>((_plan.
tap_count - 1) / 2);
595 const auto* coeffs = _phase_table.data() + phase * _plan.
tap_count;
597 for (std::size_t i = 0; i < _plan.
tap_count; ++i)
607 const std::size_t phase_count = std::max<std::size_t>(1, _plan.
phase_count);
617 static_cast<long double>(phase_count);
621 struct BranchPosition
623 std::int64_t base_index = 0;
624 std::size_t lower_phase = 0;
625 std::size_t upper_phase = 0;
626 std::int64_t upper_offset = 0;
632 const std::size_t phase_count = std::max<std::size_t>(1, _plan.
phase_count);
634 const auto base_index =
static_cast<std::int64_t
>(std::floor(
position + 1.0e-12L));
635 long double frac =
position -
static_cast<long double>(base_index);
642 auto lower_phase =
static_cast<std::size_t
>(std::floor(
phase_position + 1.0e-12L));
644 if (lower_phase >= phase_count)
650 std::size_t upper_phase = lower_phase + 1;
651 std::int64_t upper_offset = 0;
652 if (upper_phase == phase_count)
658 return {.base_index = base_index,
659 .lower_phase = lower_phase,
660 .upper_phase = upper_phase,
661 .upper_offset = upper_offset,
669 const auto half =
static_cast<std::int64_t
>((_plan.
tap_count - 1) / 2);
693 void process(
const bool final)
700 const auto branch = branchPosition(_next_output_index);
701 if (!hasSamplesFor(
branch.base_index, 0,
final) ||
702 !hasSamplesFor(
branch.base_index,
branch.upper_offset,
final))
710 ++_next_output_index;
713 discardUnneededInput();
716 void discardUnneededInput()
723 const long double next_position = stagePosition(_next_output_index);
724 const auto half =
static_cast<std::int64_t
>((_plan.
tap_count - 1) / 2);
733 const auto count = std::min<std::size_t>(
static_cast<std::size_t
>(
discard), _input.size());
734 _input.erase(_input.begin(), _input.begin() +
static_cast<std::ptrdiff_t
>(count));
735 _input_start_index +=
static_cast<std::int64_t
>(count);
738 void keepTrailingZeroHistory()
740 const auto keep = std::min<std::size_t>(zeroHistoryKeepCount(), _input_total);
742 _input_start_index =
static_cast<std::int64_t
>(_input_total -
keep);
745 FmcwIfResamplerStagePlan _plan;
746 std::vector<RealType> _phase_table;
747 std::vector<ComplexType> _input;
748 std::vector<ComplexType> _pending;
749 std::size_t _input_total = 0;
750 std::int64_t _input_start_index = 0;
751 std::size_t _next_output_index = 0;
752 bool _finished =
false;
759 _stages.push_back(std::make_unique<Stage>(
stage));
771 throw std::logic_error(
"Cannot consume IF resampler input after finish");
776 _output.insert(_output.end(),
block.begin(),
block.end());
781 for (
auto&
stage : _stages)
793 throw std::logic_error(
"Cannot consume IF resampler input after finish");
809 std::vector<ComplexType>
current;
811 for (
auto&
stage : _stages)
836 std::vector<ComplexType>
out;
854 std::vector<ComplexType>
current;
855 for (std::size_t i = 0; i < _stages.size(); ++i)
861 current = _stages[i]->finish();
869 for (
auto&
stage : _stages)
void consume(std::span< const ComplexType > block)
Stage(FmcwIfResamplerStagePlan plan)
std::vector< ComplexType > finish()
ZeroInputResult consumeZeroInput(std::size_t input_count)
std::vector< ComplexType > take()
std::vector< ComplexType > takeOutput()
FmcwIfResamplingSink(FmcwIfResamplerPlan plan)
FmcwIfZeroInputResult consumeZeroInput(std::size_t input_count)
std::vector< ComplexType > finish()
const FmcwIfResamplerPlan & plan() const noexcept
double RealType
Type for real numbers.
std::complex< RealType > ComplexType
Type for complex numbers.
constexpr RealType PI
Mathematical constant π (pi).
Internal FMCW IF rational resampler planning and streaming sink.
FmcwIfResamplerPlan planFmcwIfResampler(const FmcwIfResamplerRequest &request)
FmcwIfRateRatio approximateFmcwIfRateRatio(const RealType output_sample_rate_hz, const RealType input_sample_rate_hz, const FmcwIfResamplerLimits &limits)
std::size_t max_taps_per_stage
std::uint64_t max_ratio_denominator
RealType ratio_relative_tolerance
std::size_t max_phase_refinement
RealType max_macs_per_output_sample
RealType fractional_output_delay_samples
std::uint64_t warmup_discard_samples
RealType actual_output_sample_rate_hz
FmcwIfRateRatio overall_ratio
std::size_t phase_refinement
RealType stopband_attenuation_db
RealType estimated_macs_per_output_sample
RealType group_delay_output_samples
RealType estimated_timing_error_seconds
std::vector< FmcwIfResamplerStagePlan > stages
FmcwIfResamplerLimits limits
RealType requested_output_sample_rate_hz
RealType estimated_phase_error_radians
RealType branch_interpolation_fraction
RealType input_sample_rate_hz
RealType group_delay_seconds
RealType filter_transition_width_hz
RealType fractional_phase_offset
RealType filter_bandwidth_hz
std::int64_t initial_input_advance
RealType stopband_attenuation_db
RealType initial_branch_interpolation_fraction
bool applies_fractional_delay
RealType input_sample_rate_hz
RealType output_sample_rate_hz
std::uint64_t down_factor
std::size_t initial_phase_accumulator