fit_sine_4param

Overview

fit_sine_4param fits a pure sine wave to ADC output using iterative least-squares with frequency refinement. This implements the IEEE Std 1057/1241 algorithm for precise sinusoidal parameter estimation.

Syntax

from adctoolbox import fit_sine_4param

# Auto-detect frequency
result = fit_sine_4param(data)

# With initial frequency estimate
result = fit_sine_4param(data, frequency_estimate=0.123)

# With custom convergence parameters
result = fit_sine_4param(data, frequency_estimate=0.123,
                         max_iterations=10, tolerance=1e-9)

Parameters

  • data (array_like) — ADC output signal (1D or 2D array)

  • frequency_estimate (float, optional) — Initial normalized frequency (0 to 0.5)

    • If None: auto-detect using FFT peak with parabolic interpolation

  • max_iterations (int, default=1) — Number of iterations for frequency refinement

  • tolerance (float, default=1e-9) — Convergence threshold for frequency updates

Returns

Dictionary containing:

  • fitted_signal — Reconstructed sine wave

  • residuals — data - fitted_signal

  • frequency — Refined normalized frequency (0 to 0.5)

  • amplitude — Signal amplitude: sqrt(A² + B²)

  • phase — Phase in radians: atan2(-B, A)

  • dc_offset — DC component

  • rmse — Root mean square error of the fit

For 2D input, all scalar values become 1D arrays (one per column).

Algorithm

1. Frequency Initialization (if not provided)

FFT-based coarse estimate with 3-point parabolic interpolation:

spec = np.abs(np.fft.fft(data))
k0 = np.argmax(spec[1:N//2]) + 1  # Exclude DC
# Parabolic interpolation for sub-bin accuracy
direction = np.sign(spec[k0+1] - spec[k0-1])
freq_estimate = (k0 + direction * spec[k0 + direction] /
                (spec[k0] + spec[k0 + direction])) / N

2. Iterative Least-Squares Refinement

Model: y = A·cos(ωt) + B·sin(ωt) + C

For each iteration:

Step 1: Solve augmented least-squares system:

[cos(θ), sin(θ), ones, ∂/∂freq] × [A; B; C; δf] = data

where:

  • θ = × freq × (0:N-1)

  • ∂/∂freq = derivative of signal w.r.t. frequency

Step 2: Update frequency:

freq += δf / N

Step 3: Check convergence:

if abs(δf) < tolerance: break

3. Parameter Extraction

amplitude = np.sqrt(A**2 + B**2)
phase = np.arctan2(-B, A)
fitted_signal = A * np.cos(θ) + B * np.sin(θ) + C
residuals = data - fitted_signal
rmse = np.sqrt(np.mean(residuals**2))

Examples

Example 1: Auto-Detect Frequency

import numpy as np
from adctoolbox import fit_sine_4param

# Generate test signal
N = 1000
t = np.arange(N)
signal = 0.5 * np.sin(2*np.pi*0.123*t) + 0.1

# Fit sine wave
result = fit_sine_4param(signal)

print(f"Frequency: {result['frequency']:.6f}")
print(f"Amplitude: {result['amplitude']:.4f}")
print(f"Phase: {np.degrees(result['phase']):.2f}°")
print(f"DC Offset: {result['dc_offset']:.4f}")
print(f"RMSE: {result['rmse']:.6f}")

Example 2: Known Frequency

f0 = 0.1234  # Known normalized frequency
result = fit_sine_4param(data, frequency_estimate=f0)

error = result['residuals']
print(f"Fit RMS error: {result['rmse']:.6f}")

Example 3: Tight Convergence

result = fit_sine_4param(data, max_iterations=10, tolerance=1e-12)

Example 4: Visualize Fit Quality

import matplotlib.pyplot as plt

result = fit_sine_4param(signal)

# Plot one period
period = int(1/result['frequency'])
n_samples = min(period * 2, len(signal))

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))

# Original vs fitted
ax1.plot(signal[:n_samples], 'o-', label='Original', alpha=0.7)
ax1.plot(result['fitted_signal'][:n_samples], '--', label='Fitted')
ax1.set_ylabel('Amplitude')
ax1.legend()
ax1.grid(True)

# Residuals
ax2.plot(result['residuals'][:n_samples])
ax2.set_ylabel('Residuals')
ax2.set_xlabel('Sample')
ax2.grid(True)

plt.tight_layout()
plt.show()

Interpretation

Output

Interpretation

amplitude / max(data) ≈ 0.5

Full-scale sine wave input (peak-to-peak = 1)

abs(dc_offset) << amplitude

Well-centered signal

abs(dc_offset) amplitude

Large DC offset, check ADC biasing

High rmse

Distortion, harmonics, or noise present

frequency stable after 1 iteration

Good initial estimate

Use Cases

  • Error Analysis: Remove fitted sine to analyze distortion and noise

  • Signal Characterization: Extract amplitude, frequency, phase for testing

  • Jitter Analysis: Phase variations indicate timing jitter

  • Pre-processing: For INL/DNL, harmonic decomposition, etc.

Limitations

  • Single-tone only: Assumes pure sine wave; fails with multi-tone inputs

  • No distortion modeling: Harmonics treated as noise in residuals

  • Convergence: May fail if initial estimate is very far from true frequency

  • Wraparound: Frequency must be < 0.5 (Nyquist limit)

See Also

References

  1. IEEE Std 1057-2017, "IEEE Standard for Digitizing Waveform Recorders"

  2. IEEE Std 1241-2010, "IEEE Standard for Terminology and Test Methods for ADCs"