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 refinementtolerance(float, default=1e-9) — Convergence threshold for frequency updates
Returns
Dictionary containing:
fitted_signal— Reconstructed sine waveresiduals— data - fitted_signalfrequency— Refined normalized frequency (0 to 0.5)amplitude— Signal amplitude: sqrt(A² + B²)phase— Phase in radians: atan2(-B, A)dc_offset— DC componentrmse— 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:
θ = 2π × 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 |
|---|---|
|
Full-scale sine wave input (peak-to-peak = 1) |
|
Well-centered signal |
|
Large DC offset, check ADC biasing |
High |
Distortion, harmonics, or noise present |
|
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
analyze_decomposition_time— Time-domain harmonic decomposition using fitted sineanalyze_inl_from_sine— INL/DNL extraction from sine waveanalyze_error_pdf— Error distribution analysis
References
IEEE Std 1057-2017, “IEEE Standard for Digitizing Waveform Recorders”
IEEE Std 1241-2010, “IEEE Standard for Terminology and Test Methods for ADCs”