Adaptive notch control targets one sinusoidal component with a quadrature pair.
The controller synthesizes cosine and sine references at the tracked frequency, then adapts two weights to cancel the measured component after accounting for secondary-path delay or compensation filtering. It is the narrowband choice for a dominant rotating-order or single-frequency disturbance.
Core Equations
The synthesized references are:
\[x_0(n) = A \cos(k \omega_0 n)\] \[x_1(n) = A \sin(k \omega_0 n)\]The canceling signal is the weighted combination:
\[y(n) = w_0(n) x_0(n) + w_1(n) x_1(n)\]and the update follows the same ANC sign convention used elsewhere:
\[w_i(n+1) = w_i(n) - \mu e(n) x'_i(n)\]where $x’_i(n)$ is either delay-compensated or filtered through the secondary-path estimate.
Benchmark Interpretation
The primary benchmark shows the notch controller converging quickly in the single-tone scenario and delivering a solid narrowband attenuation result. The scenario comparison also shows the limit of the method: once the disturbance drifts or stops being a single stable tone, a fixed-frequency notch section is no longer well matched.
Use the right model class
Adaptive notch is excellent when the disturbance really is one tracked sinusoid. It is not a broadband controller and should not be evaluated as if it were one.
Tuning Notes
- The tracked frequency matters more than controller order because the controller only has two adaptive weights.
- Delay compensation or secondary-path compensation is what keeps the cosine and sine channels aligned with the physical plant.
- When the disturbance contains several harmonics instead of one, move to the multi-tone controller instead of trying to stretch a single notch section too far.
Using It From C++
#include "anc/adaptive_notch_controller.h"
anc::AdaptiveNotchConfig config;
config.frequencyRadians = 2.0 * 3.14159265358979323846 / 64.0;
config.amplitude = 1.0;
config.stepSize = 0.02;
config.delaySamples = 1;
config.secondaryEstimate = {0.0, 0.12, 0.38, 0.21, -0.05};
anc::AdaptiveNotchController controller(config);
for (double error : errorSignal) {
const double control = controller.synthesize();
sendToActuator(control);
controller.adapt(error);
}
CLI Reproduction
build\Debug\anc_cli.exe `
--algorithm notch `
--summary analysis_output\raw\notch_single_tone_summary.csv `
--timeseries analysis_output\raw\notch_single_tone_timeseries.csv `
--period-samples 64 `
--delay-samples 1 `
--step-size 0.02