Skip to content

Add Butterworth zeta constructor for uniform damping ratio#57

Merged
MitchellThompkins merged 8 commits into
mainfrom
add-zeta-control
Jun 29, 2026
Merged

Add Butterworth zeta constructor for uniform damping ratio#57
MitchellThompkins merged 8 commits into
mainfrom
add-zeta-control

Conversation

@MitchellThompkins

@MitchellThompkins MitchellThompkins commented Jun 28, 2026

Copy link
Copy Markdown
Owner

Resolves #19.

Related to #14.

Changes

The classical Butterworth constructor places poles at angles
theta_k = pi*(2k+N-1)/(2N), so each conjugate pair has a different damping
ratio determined by its angle. For N=2 this gives zeta = 1/sqrt(2), and for
higher orders the pairs span a range of zeta values, with the pair closest to
the real axis having the highest damping.

This PR adds a second constructor that accepts an explicit uniform damping ratio.
Instead of spreading poles around the left half of the unit circle at prescribed
angles, every pair is placed at the same location
wc*(-zeta +/- j*sqrt(1 - zeta^2)). The magnitude is still wc for the
underdamped case, but the angle is arccos(-zeta) for all pairs rather than
varying by pair index. This trades the maximally flat magnitude property of
classical Butterworth for direct control over transient damping behavior.

The primary use case is as a building block for a biquad cascade
(#20), where N=2 sections
with a specified zeta are composed rather than designing a single high-order
filter.

The ZOH path is restricted to N<=3 for this constructor. Uniform zeta produces
repeated complex eigenvalue pairs for N>=4, which causes the QR-based
eigendecomposition used by the generic ZOH path to return wrong results. MatchedZ
and Tustin are unaffected. See
#56 for details.

Summary by CodeRabbit

  • New Features
    • Added a Butterworth constructor that accepts a uniform damping ratio (zeta), applying consistent damping across all pole pairs.
  • Tests
    • Expanded compile-time coefficient checks and reference generation for uniform-zeta low-pass/high-pass designs across orders and discretization methods, including first-order zeta behavior.
    • Enforced and validated the uniform-zeta ZOH order restriction (N ≤ 3).
  • Documentation
    • Documented the uniform-damping constructor, coefficient/polynomial formation, and the ZOH limitation.
  • Chores
    • Added compile-time detection for the ZOH discretization mode.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a Butterworth constructor overload that accepts zeta, computes zeta-specific continuous-time transfer functions, generates new zeta reference data, and extends runtime and compile-time Butterworth tests.

Changes

Butterworth Uniform-Zeta Constructor

Layer / File(s) Summary
Zeta constructor and continuous-TF computation
include/constfilt/analog_filter.hpp, include/constfilt/butterworth.hpp, include/constfilt/discretize.hpp
Adds the public Butterworth(cutoff_hz, sample_rate_hz, zeta) constructor, the zeta-specific transfer-function helpers, the marker types used by the transfer-function builders, and the ZOH tag trait used to constrain discretization behavior.
Butterworth zeta documentation
docs/butterworth.md
Documents the zeta constructor, the uniform damping pole form, the real-arithmetic denominator construction, the highpass transform, and the ZOH restriction.
Octave reference data generation for zeta variants
octave/generate_butterworth_tests.m
Adds uniform-zeta low-pass and high-pass helpers and generates zeta=0.5 reference cases across multiple orders and discretization methods.
Runtime and compile-time test coverage
tests/butterworth.test.cpp, tests/constexpr_asserts.cpp
Adds runtime FULL_MATRIX coverage for uniform-zeta Butterworth filters and compile-time assertions matching the new reference data and the N=1 no-op case.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • MitchellThompkins/constfilt#47: Adds factored transfer-function plumbing that overlaps with the discretization and filter-shape handling touched by the new zeta constructor path.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding a Butterworth constructor with explicit zeta support.
Linked Issues check ✅ Passed The PR fulfills issue #19 by allowing callers to pass zeta and control Butterworth pole placement.
Out of Scope Changes check ✅ Passed The additional docs, tests, and supporting internals all appear directly related to uniform-zeta Butterworth support.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-zeta-control

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread include/constfilt/butterworth.hpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@include/constfilt/butterworth.hpp`:
- Around line 192-204: Reject negative damping ratios in
butterworth_poly_coeffs_zeta before constructing the transfer function: validate
zeta with the existing constexpr-friendly stability path or CheckStab template
parameter and fail for zeta < 0 while still allowing zeta == 0. Update the
butterworth_poly_coeffs_zeta logic (and any constructor or helper that calls it)
so unstable inputs are filtered out consistently with the library’s Stability
handling, using the existing symbols CheckStab, Stability, and
butterworth_poly_coeffs_zeta.

In `@tests/butterworth.test.cpp`:
- Around line 339-341: The ZOH Butterworth test coverage is documenting an
unsupported N>=4 path instead of fixing it; update the relevant Butterworth/ZOH
implementation so repeated-eigenvalue cases work for arbitrary order, and then
expand the tests in Butterworth<T, N, ZOH> to cover N>=4 rather than limiting
them to N<=3. Use the Butterworth constructor/discretization path and any
eigenvalue/consteig handling it relies on, and remove the implicit order ceiling
so only the required static_assert(N >= 1) remains.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1b625d88-9386-4060-8cba-7f173649f6b3

📥 Commits

Reviewing files that changed from the base of the PR and between 240b5c8 and df1bf05.

📒 Files selected for processing (5)
  • include/constfilt/butterworth.hpp
  • octave/generate_butterworth_tests.m
  • tests/butterworth.test.cpp
  • tests/butterworth_reference.hpp
  • tests/constexpr_asserts.cpp
📜 Review details
⏰ Context from checks skipped due to timeout. (6)
  • GitHub Check: cross.arm-gcc
  • GitHub Check: clang
  • GitHub Check: gcc
  • GitHub Check: cross.arm-clang
  • GitHub Check: cross.arm-clang
  • GitHub Check: clang
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{hpp,h,cpp}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{hpp,h,cpp}: Use C++17 only; do not use C++20 features (no std::is_constant_evaluated, no concepts)
Enforce clang-format code formatting via CI; run make container.make.format before committing

Files:

  • include/constfilt/butterworth.hpp
  • tests/constexpr_asserts.cpp
  • tests/butterworth.test.cpp
include/constfilt/**

📄 CodeRabbit inference engine (AGENTS.md)

Maintain header-only library structure; do not add .cpp translation units to the library itself

Files:

  • include/constfilt/butterworth.hpp
**/*.{hpp,h}

📄 CodeRabbit inference engine (AGENTS.md)

Implement constexpr everywhere possible; coefficient computation must work at compile time; Filter::operator() batch path must remain constexpr

Files:

  • include/constfilt/butterworth.hpp
include/constfilt/**/*.{hpp,h}

📄 CodeRabbit inference engine (AGENTS.md)

include/constfilt/**/*.{hpp,h}: Do not introduce hard-coded filter-order ceiling; Filter classes should only use static_assert(N >= 1)
Use unsigned int for template parameter order N; implicit conversion with consteig::Size (size_t) is acceptable; do not add casts unless compiler error requires it
Follow a[0] = 1 convention for discrete transfer functions (monic-denominator); Filter stores _a with a[0] included for uniformity but DF2T equations assume a[0] = 1
Implement stability check via CheckStab template parameter (default true) to reject Stability::Unstable systems while accepting Stable and MarginallyStable; check not yet implemented (issue #14)
Use DirectForm II Transposed (DF2T) implementation for runtime filter with both sample-by-sample real-time path and constexpr-capable batch path
Support Butterworth filters with lowpass and highpass modes, arbitrary order (N >= 1), and ZOH or Matched-Z discretization options
Support Elliptic filters with lowpass and highpass modes, arbitrary order (N >= 1), and ZOH or Matched-Z discretization options; use Nome-series Cauer design (see docs/elliptic.md)

Files:

  • include/constfilt/butterworth.hpp
**/*.hpp

⚙️ CodeRabbit configuration file

**/*.hpp: This is a constexpr C++17 header-only filter design library. Focus on:

  • Correctness of constexpr computations and static_assert usage
  • Type consistency (float vs double tolerances must match the filter's numeric type)
  • No standard library dependencies in core library code (freestanding constraint)
  • Correct use of vendored consteig (matrix ops) and gcem (math functions) via the wrapper
  • Headers named *_reference.hpp contain precomputed test data — do not suggest algorithmic changes to their values

Files:

  • include/constfilt/butterworth.hpp
**/tests/**

⚙️ CodeRabbit configuration file

**/tests/**: These are compile-time and runtime tests. Check:

  • static_assert failures = build failures = test failures (treat equally)
  • Tests are split into individual .cpp files intentionally to avoid constexpr budget exhaustion
  • Do not suggest merging test files
  • Reference values live in *_reference.hpp headers — do not suggest hardcoding tolerances inline

Files:

  • tests/constexpr_asserts.cpp
  • tests/butterworth.test.cpp
octave/**

⚙️ CodeRabbit configuration file

octave/**: GNU Octave/MATLAB scripts for test generation and algorithm prototyping.
Generated reference output is checked in — do not suggest regenerating unless filter parameters change.

Files:

  • octave/generate_butterworth_tests.m
🔇 Additional comments (3)
octave/generate_butterworth_tests.m (1)

221-369: LGTM!

tests/butterworth.test.cpp (1)

344-350: LGTM!

Also applies to: 352-410, 418-484

tests/constexpr_asserts.cpp (1)

719-737: LGTM!

Comment thread include/constfilt/butterworth.hpp
Comment thread tests/butterworth.test.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/butterworth.md`:
- Around line 129-141: Clarify the supported zeta range in the pole-placement
section: the current formula in butterworth.md only applies to the underdamped
case handled by the pole placement description, so either state that the
constructor assumes 0 <= zeta <= 1 or expand the “Pole placement” explanation to
include the overdamped/repeated-real-pole case when zeta > 1. Update the
surrounding text near the “Pole placement” heading and the p = ωc(-zeta ± j√(1 -
zeta^2)) formula so it matches the behavior of the Butterworth constructor.

In `@include/constfilt/butterworth.hpp`:
- Around line 55-56: The Butterworth compile-time check is using a std type
trait in a std-free area; replace the std::is_same<Method, ZOH> check in
butterworth.hpp with the internal tag-trait pattern used by analog_filter.hpp.
Update the static_assert to rely on the local trait for identifying ZOH instead
of any std:: namespace dependency, keeping include/constfilt self-contained.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 86a2ea79-58c9-460b-9e20-749ff9ca23c0

📥 Commits

Reviewing files that changed from the base of the PR and between df1bf05 and cae6a14.

📒 Files selected for processing (2)
  • docs/butterworth.md
  • include/constfilt/butterworth.hpp
📜 Review details
⏰ Context from checks skipped due to timeout. (8)
  • GitHub Check: gcc
  • GitHub Check: cross.arm-clang
  • GitHub Check: clang
  • GitHub Check: cross.arm-gcc
  • GitHub Check: cross.arm-gcc
  • GitHub Check: gcc
  • GitHub Check: cross.arm-clang
  • GitHub Check: clang
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{hpp,h,cpp}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{hpp,h,cpp}: Use C++17 only; do not use C++20 features (no std::is_constant_evaluated, no concepts)
Enforce clang-format code formatting via CI; run make container.make.format before committing

Files:

  • include/constfilt/butterworth.hpp
include/constfilt/**

📄 CodeRabbit inference engine (AGENTS.md)

Maintain header-only library structure; do not add .cpp translation units to the library itself

Files:

  • include/constfilt/butterworth.hpp
**/*.{hpp,h}

📄 CodeRabbit inference engine (AGENTS.md)

Implement constexpr everywhere possible; coefficient computation must work at compile time; Filter::operator() batch path must remain constexpr

Files:

  • include/constfilt/butterworth.hpp
include/constfilt/**/*.{hpp,h}

📄 CodeRabbit inference engine (AGENTS.md)

include/constfilt/**/*.{hpp,h}: Do not introduce hard-coded filter-order ceiling; Filter classes should only use static_assert(N >= 1)
Use unsigned int for template parameter order N; implicit conversion with consteig::Size (size_t) is acceptable; do not add casts unless compiler error requires it
Follow a[0] = 1 convention for discrete transfer functions (monic-denominator); Filter stores _a with a[0] included for uniformity but DF2T equations assume a[0] = 1
Implement stability check via CheckStab template parameter (default true) to reject Stability::Unstable systems while accepting Stable and MarginallyStable; check not yet implemented (issue #14)
Use DirectForm II Transposed (DF2T) implementation for runtime filter with both sample-by-sample real-time path and constexpr-capable batch path
Support Butterworth filters with lowpass and highpass modes, arbitrary order (N >= 1), and ZOH or Matched-Z discretization options
Support Elliptic filters with lowpass and highpass modes, arbitrary order (N >= 1), and ZOH or Matched-Z discretization options; use Nome-series Cauer design (see docs/elliptic.md)

Files:

  • include/constfilt/butterworth.hpp
**/*.hpp

⚙️ CodeRabbit configuration file

**/*.hpp: This is a constexpr C++17 header-only filter design library. Focus on:

  • Correctness of constexpr computations and static_assert usage
  • Type consistency (float vs double tolerances must match the filter's numeric type)
  • No standard library dependencies in core library code (freestanding constraint)
  • Correct use of vendored consteig (matrix ops) and gcem (math functions) via the wrapper
  • Headers named *_reference.hpp contain precomputed test data — do not suggest algorithmic changes to their values

Files:

  • include/constfilt/butterworth.hpp
🔇 Additional comments (1)
include/constfilt/butterworth.hpp (1)

52-59: 🎯 Functional Correctness

Please reconcile this N <= 3u guard with the public Butterworth contract.

This hard-codes an order ceiling into include/constfilt, but the library rules for this path say Butterworth filters should support arbitrary order N >= 1 and should only static_assert(N >= 1). If issue 56 really needs a temporary ZOH exception, please confirm the repo wants that exception in the public API contract before merging. As per coding guidelines, "Do not introduce hard-coded filter-order ceiling; Filter classes should only use static_assert(N >= 1)".

Source: Coding guidelines

Comment thread docs/butterworth.md
Comment thread include/constfilt/butterworth.hpp Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/butterworth.md`:
- Around line 140-146: The overdamped description in the Butterworth
documentation uses the wrong radical form for zeta > 1. Update the text in the
section describing the poles so it either states the real-pole expression p =
omega_c(-zeta ± sqrt(zeta^2 - 1)) or explains that the complex form j*sqrt(1 -
zeta^2) reduces to that real form in the overdamped case, keeping the
explanation aligned with butterworth_poly_coeffs_zeta and the pole cases above
it.

In `@include/constfilt/butterworth.hpp`:
- Around line 55-59: The Butterworth zeta constructor currently imposes an
unwanted public order cap via the static_assert in Butterworth, blocking valid
Butterworth<N, ZOH> use for N >= 4. Remove the hard-coded N <= 3u ceiling from
this constructor and keep only the general filter-shape validation, then move
any ZOH repeated-pole handling into the discretization/backend path used by the
ZOH method rather than the public API. Use the Butterworth template and its zeta
constructor as the place to locate and adjust this check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3dd00020-3e1d-4f73-8cb5-5d8204cb5e06

📥 Commits

Reviewing files that changed from the base of the PR and between cae6a14 and 229528d.

📒 Files selected for processing (3)
  • docs/butterworth.md
  • include/constfilt/butterworth.hpp
  • include/constfilt/discretize.hpp
📜 Review details
⏰ Context from checks skipped due to timeout. (8)
  • GitHub Check: clang
  • GitHub Check: gcc
  • GitHub Check: cross.arm-clang
  • GitHub Check: cross.arm-gcc
  • GitHub Check: gcc
  • GitHub Check: clang
  • GitHub Check: cross.arm-gcc
  • GitHub Check: cross.arm-clang
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{hpp,h,cpp}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{hpp,h,cpp}: Use C++17 only; do not use C++20 features (no std::is_constant_evaluated, no concepts)
Enforce clang-format code formatting via CI; run make container.make.format before committing

Files:

  • include/constfilt/discretize.hpp
  • include/constfilt/butterworth.hpp
include/constfilt/**

📄 CodeRabbit inference engine (AGENTS.md)

Maintain header-only library structure; do not add .cpp translation units to the library itself

Files:

  • include/constfilt/discretize.hpp
  • include/constfilt/butterworth.hpp
**/*.{hpp,h}

📄 CodeRabbit inference engine (AGENTS.md)

Implement constexpr everywhere possible; coefficient computation must work at compile time; Filter::operator() batch path must remain constexpr

Files:

  • include/constfilt/discretize.hpp
  • include/constfilt/butterworth.hpp
include/constfilt/**/*.{hpp,h}

📄 CodeRabbit inference engine (AGENTS.md)

include/constfilt/**/*.{hpp,h}: Do not introduce hard-coded filter-order ceiling; Filter classes should only use static_assert(N >= 1)
Use unsigned int for template parameter order N; implicit conversion with consteig::Size (size_t) is acceptable; do not add casts unless compiler error requires it
Follow a[0] = 1 convention for discrete transfer functions (monic-denominator); Filter stores _a with a[0] included for uniformity but DF2T equations assume a[0] = 1
Implement stability check via CheckStab template parameter (default true) to reject Stability::Unstable systems while accepting Stable and MarginallyStable; check not yet implemented (issue #14)
Use DirectForm II Transposed (DF2T) implementation for runtime filter with both sample-by-sample real-time path and constexpr-capable batch path
Support Butterworth filters with lowpass and highpass modes, arbitrary order (N >= 1), and ZOH or Matched-Z discretization options
Support Elliptic filters with lowpass and highpass modes, arbitrary order (N >= 1), and ZOH or Matched-Z discretization options; use Nome-series Cauer design (see docs/elliptic.md)

Files:

  • include/constfilt/discretize.hpp
  • include/constfilt/butterworth.hpp
**/*.hpp

⚙️ CodeRabbit configuration file

**/*.hpp: This is a constexpr C++17 header-only filter design library. Focus on:

  • Correctness of constexpr computations and static_assert usage
  • Type consistency (float vs double tolerances must match the filter's numeric type)
  • No standard library dependencies in core library code (freestanding constraint)
  • Correct use of vendored consteig (matrix ops) and gcem (math functions) via the wrapper
  • Headers named *_reference.hpp contain precomputed test data — do not suggest algorithmic changes to their values

Files:

  • include/constfilt/discretize.hpp
  • include/constfilt/butterworth.hpp
🔇 Additional comments (2)
include/constfilt/butterworth.hpp (1)

200-259: LGTM!

include/constfilt/discretize.hpp (1)

64-72: LGTM!

Comment thread docs/butterworth.md Outdated
Comment thread include/constfilt/butterworth.hpp
@MitchellThompkins

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@MitchellThompkins

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
octave/generate_butterworth_tests.m (1)

259-289: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Stop emitting uniform-zeta ZOH references for orders above 3.

include/constfilt/butterworth.hpp statically rejects Butterworth(..., zeta) with constfilt::ZOH for N > 3, but these loops still generate and check in case_zeta_* / case_hp_zeta_* fixtures for orders 4..8. That leaves tests/butterworth_reference.hpp describing a path the library intentionally does not support.

Suggested fix
 for ci = 1:length(zeta_lp_cases)
     ord = zeta_lp_cases{ci}{1};
@@
-    % --- ZOH ---
-    sys_d = c2d(sys_c, 1.0/fs, 'zoh');
-    [b_d, a_d] = tfdata(sys_d, 'v');
-    while length(b_d) < length(a_d); b_d = [0, b_d]; end
-    y_step = filter(b_d, a_d, u);
-    y_imp  = filter(b_d, a_d, [1, zeros(1, STEP_LEN-1)]);
-    y_c    = filter(b_d, a_d, u_c);
-    emit_case(fid, sprintf('case_zeta_%d_%dHz_%dHz_%s', ord, fc, fs, zeta_tag), ...
-              ord, fc, fs, b_d, a_d, y_step, y_imp, u_c, y_c);
+    if ord <= 3
+        % --- ZOH ---
+        sys_d = c2d(sys_c, 1.0/fs, 'zoh');
+        [b_d, a_d] = tfdata(sys_d, 'v');
+        while length(b_d) < length(a_d); b_d = [0, b_d]; end
+        y_step = filter(b_d, a_d, u);
+        y_imp  = filter(b_d, a_d, [1, zeros(1, STEP_LEN-1)]);
+        y_c    = filter(b_d, a_d, u_c);
+        emit_case(fid, sprintf('case_zeta_%d_%dHz_%dHz_%s', ord, fc, fs, zeta_tag), ...
+                  ord, fc, fs, b_d, a_d, y_step, y_imp, u_c, y_c);
+    end
@@
 for ci = 1:length(zeta_hp_cases)
     ord = zeta_hp_cases{ci}{1};
@@
-    % --- ZOH ---
-    sys_d = c2d(sys_c, 1.0/fs, 'zoh');
-    [b_d, a_d] = tfdata(sys_d, 'v');
-    y_step = filter(b_d, a_d, u);
-    y_imp  = filter(b_d, a_d, [1, zeros(1, STEP_LEN-1)]);
-    y_c    = filter(b_d, a_d, u_c);
-    emit_case(fid, sprintf('case_hp_zeta_%d_%dHz_%dHz_%s', ord, fc, fs, zeta_tag), ...
-              ord, fc, fs, b_d, a_d, y_step, y_imp, u_c, y_c);
+    if ord <= 3
+        % --- ZOH ---
+        sys_d = c2d(sys_c, 1.0/fs, 'zoh');
+        [b_d, a_d] = tfdata(sys_d, 'v');
+        y_step = filter(b_d, a_d, u);
+        y_imp  = filter(b_d, a_d, [1, zeros(1, STEP_LEN-1)]);
+        y_c    = filter(b_d, a_d, u_c);
+        emit_case(fid, sprintf('case_hp_zeta_%d_%dHz_%dHz_%s', ord, fc, fs, zeta_tag), ...
+                  ord, fc, fs, b_d, a_d, y_step, y_imp, u_c, y_c);
+    end

As per path instructions, "Generated reference output is checked in — do not suggest regenerating unless filter parameters change."

Also applies to: 322-351

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@octave/generate_butterworth_tests.m` around lines 259 - 289, Stop generating
uniform-zeta ZOH reference cases for Butterworth orders above 3, since the
library intentionally rejects that path in `Butterworth(..., zeta)` with
`constfilt::ZOH`. Update the `zeta_lp_cases` loop in
`generate_butterworth_tests.m` (and the matching `case_hp_zeta_*` loop it also
applies to) so `emit_case` is only produced for supported orders, keeping
`tests/butterworth_reference.hpp` aligned with the supported API. Use the
existing `uniform_zeta_lp`, `sys_d = c2d(..., 'zoh')`, and `emit_case` flow, but
gate out `ord > 3` rather than checking in unsupported fixtures.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@octave/generate_butterworth_tests.m`:
- Around line 259-289: Stop generating uniform-zeta ZOH reference cases for
Butterworth orders above 3, since the library intentionally rejects that path in
`Butterworth(..., zeta)` with `constfilt::ZOH`. Update the `zeta_lp_cases` loop
in `generate_butterworth_tests.m` (and the matching `case_hp_zeta_*` loop it
also applies to) so `emit_case` is only produced for supported orders, keeping
`tests/butterworth_reference.hpp` aligned with the supported API. Use the
existing `uniform_zeta_lp`, `sys_d = c2d(..., 'zoh')`, and `emit_case` flow, but
gate out `ord > 3` rather than checking in unsupported fixtures.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 251c489a-9da0-412b-a5bd-f9ee7ba1ac1e

📥 Commits

Reviewing files that changed from the base of the PR and between 16dcbe6 and d347427.

📒 Files selected for processing (3)
  • octave/generate_butterworth_tests.m
  • tests/butterworth.test.cpp
  • tests/butterworth_reference.hpp
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
octave/**

⚙️ CodeRabbit configuration file

octave/**: GNU Octave/MATLAB scripts for test generation and algorithm prototyping.
Generated reference output is checked in — do not suggest regenerating unless filter parameters change.

Files:

  • octave/generate_butterworth_tests.m
**/*.{hpp,h,cpp}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{hpp,h,cpp}: Use C++17 only; do not use C++20 features (no std::is_constant_evaluated, no concepts)
Enforce clang-format code formatting via CI; run make container.make.format before committing

Files:

  • tests/butterworth.test.cpp
**/tests/**

⚙️ CodeRabbit configuration file

**/tests/**: These are compile-time and runtime tests. Check:

  • static_assert failures = build failures = test failures (treat equally)
  • Tests are split into individual .cpp files intentionally to avoid constexpr budget exhaustion
  • Do not suggest merging test files
  • Reference values live in *_reference.hpp headers — do not suggest hardcoding tolerances inline

Files:

  • tests/butterworth.test.cpp
🔇 Additional comments (1)
tests/butterworth.test.cpp (1)

344-351: LGTM!

Also applies to: 352-383, 384-415, 416-447, 454-461, 466-493, 498-525, 530-556

@MitchellThompkins MitchellThompkins merged commit 1fd3060 into main Jun 29, 2026
13 checks passed
@MitchellThompkins MitchellThompkins deleted the add-zeta-control branch June 29, 2026 05:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Butterworth: allow specifying zeta instead of assuming 0.707

1 participant