Multi-tone cancellation runs a parallel bank of notch-style sections.
Instead of targeting one sinusoid, the multi-tone controller instantiates one adaptive two-weight section per tracked component and sums their outputs. In the benchmark suite this controller produces the most dramatic attenuation because the example disturbance is intentionally a best-case stationary harmonic bank.
Core Idea
For a disturbance modeled as:
\[x(n) = \sum_{m=1}^{M} A_m \cos(\omega_m n)\]The total canceling output is the sum of the individual channel outputs:
\[y(n) = \sum_{m=1}^{M} y_m(n)\]Each $y_m(n)$ comes from its own adaptive notch-style section with the same error signal shared across the bank.
Benchmark Interpretation
The multi-tone controller reaches 269.136 dB attenuation in the headline benchmark because the example is intentionally a perfect match: three stable harmonics in the disturbance and the same three harmonics in the controller bank. The result is real for that case, but it should not be read as proof that multi-tone control is universally superior.
Running RMS
In the matched harmonic case the residual collapses quickly because the controller is solving exactly the disturbance model it assumes.
Spectrum view
The spectral view highlights what multi-tone control does best: it removes energy precisely at the tracked harmonics.
Tuning Notes
- Spend effort defining the tone bank correctly; that matters more than pushing the step size.
- Add one section per meaningful harmonic rather than a large speculative bank.
- If the disturbance drifts in frequency or contains wideband content between harmonics, performance degrades quickly.
Using It From C++
#include "anc/adaptive_notch_controller.h"
std::vector<anc::AdaptiveNotchConfig> bank = {
{2.0 * 3.14159265358979323846 / 64.0, 1.0, 0.02, 1, {0.0, 0.12, 0.38, 0.21, -0.05}},
{2.0 * 2.0 * 3.14159265358979323846 / 64.0, 1.0, 0.02, 1, {0.0, 0.12, 0.38, 0.21, -0.05}},
{3.0 * 2.0 * 3.14159265358979323846 / 64.0, 1.0, 0.02, 1, {0.0, 0.12, 0.38, 0.21, -0.05}}
};
anc::MultiToneController controller(bank);
for (double error : errorSignal) {
const double control = controller.synthesize();
sendToActuator(control);
controller.adapt(error);
}
CLI Reproduction
build\Debug\anc_cli.exe `
--algorithm multitone `
--summary analysis_output\raw\multitone_harmonic_bank_summary.csv `
--timeseries analysis_output\raw\multitone_harmonic_bank_timeseries.csv `
--period-samples 64 `
--delay-samples 1 `
--step-size 0.02