Skip to content

Expose sine-fit controls and diagnostics in AOUT analyses#56

Merged
Arcadia-1 merged 3 commits into
Arcadia-1:mainfrom
chenzc24:codex/issue-54-harmonic-frequency-options
Jun 30, 2026
Merged

Expose sine-fit controls and diagnostics in AOUT analyses#56
Arcadia-1 merged 3 commits into
Arcadia-1:mainfrom
chenzc24:codex/issue-54-harmonic-frequency-options

Conversation

@chenzc24

Copy link
Copy Markdown
Contributor

Closes #54.

Summary

This PR exposes the sine-fit controls and diagnostics needed by the AOUT residual-analysis APIs while preserving the existing defaults.

The change follows the implementation order proposed in #54:

  1. fit_sine_4param now returns convergence diagnostics:
    • converged
    • n_iterations
    • initial_frequency
    • last_delta_freq
  2. Residual-analysis APIs that call fit_sine_4param now expose max_iterations and tolerance without changing their default values.
  3. Those APIs can optionally return sine-fit metadata via return_fit=True under result[fit].
  4. decompose_harmonic_error and decomposition wrappers now accept frequency, max_iterations, and tolerance, so known fundamentals can bypass auto-detection.
  5. Regression tests cover both issue corner cases: near-DC false residuals and strong-HD2 fundamental mis-detection.

Modification Logic

The core issue is not that the sine fit is mathematically wrong. The problem is that higher-level analysis functions previously hid the fit controls and fit quality from users. When the initial FFT estimate is poor, or when a harmonic is stronger than the actual fundamental, the residual-analysis layer can produce misleading residuals while giving the caller no way to inspect or correct the fit.

This PR keeps the existing fit behavior as the default:

  • default max_iterations remains 1
  • default tolerance remains 1e-9
  • default result dictionaries do not include a new fit key

The new controls are opt-in. Users who already know the input frequency can pass it directly, or increase max_iterations for difficult captures. Users who want fit-quality visibility can pass return_fit=True and inspect convergence diagnostics.

For residual APIs, result[fit] intentionally includes scalar metadata only, not the full fitted waveform arrays, to avoid duplicating large payloads that are already represented by each analyzer's existing residual/error outputs.

For harmonic decomposition, frequency=None preserves the old auto-detection path. Passing frequency=known_freq, max_iterations=0 gives a fixed-frequency decomposition, which is important when a strong HD2 component would otherwise be detected as the fundamental.

Experimental Evidence

Near-DC false residual

Synthetic signal:

N = 8192
freq = 0.70 / N
signal = 0.5 * sin(2*pi*freq*n) + 0.1

For analyze_error_spectrum:

max_iterations=1 warnings=1 converged=False final_bin=0.768744003 error_std=1.277e-01
max_iterations=5 warnings=0 converged=True  final_bin=0.700000000 error_std=4.690e-12

For decompose_harmonic_error:

max_iterations=1 warnings=1 final_bin=0.768744003373 residual_rms=1.057e-03
max_iterations=5 warnings=0 final_bin=0.700000000001 residual_rms=4.514e-15

This shows that the higher-level APIs can now expose and correct the false residual structure by allowing additional fit iterations.

Strong-HD2 fundamental mis-detection

Synthetic signal:

N = 4096
freq = 37 / N
signal = 0.05*sin(2*pi*freq*n + 0.2) + 0.4*sin(2*pi*2*freq*n - 0.4) + 0.1

Auto-detection locks onto the larger HD2 tone:

auto:  final_bin=74.0005, residual_rms=9.9997e-02
fixed: final_bin=37.0000, residual_rms=1.0807e-14

Passing the known fundamental frequency restores the intended harmonic decomposition.

Tests

Targeted tests:

uv run --with pytest pytest tests/unit/aout/test_error_fit_options.py -q
3 passed

uv run --with pytest pytest tests/unit/aout/test_decompose_harmonics.py tests/unit/aout/test_decompose_harmonics_polar.py -q
10 passed

uv run --with pytest pytest tests/unit/aout -q
69 passed

Full unit suite on Windows:

uv run --with pytest pytest tests/unit -q
787 passed, 1 failed, 1 skipped, 11 deselected, 1 xfailed

The single failure is the pre-existing Windows test_matlab_test_runner.py::test_explicit_non_executable_matlab_path_is_rejected issue, unrelated to this PR. It is caused by Windows executable permission semantics for a temporary file named matlab.

Compatibility

  • Existing defaults are preserved.
  • New public API parameters are appended with defaults, so existing positional and keyword calls keep working.
  • Existing result dictionaries are unchanged unless return_fit=True is requested.
  • The legacy decompose_harmonics(freq=...) wrapper now actually honors its existing freq parameter; when provided, it uses fixed-frequency decomposition.

@Arcadia-1 Arcadia-1 merged commit e916c72 into Arcadia-1:main Jun 30, 2026
1 check passed
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.

Expose sine-fit diagnostics and fit options in residual error-analysis APIs

2 participants