analyze_inl_from_sine
Overview
analyze_inl_from_sine computes Integral Nonlinearity (INL) and Differential Nonlinearity (DNL) from sine wave histogram test using the inverse cosine method. This is the Python implementation following IEEE Std 1241-2010.
Syntax
from adctoolbox import analyze_inl_from_sine
# Basic usage with auto-detected parameters
result = analyze_inl_from_sine(data)
# Specify resolution
result = analyze_inl_from_sine(data, num_bits=12)
# Adjust clipping
result = analyze_inl_from_sine(data, clip_percent=0.05, show_plot=True)
# No plotting, just computation
result = analyze_inl_from_sine(data, show_plot=False)
Parameters
data(array_like) — ADC output signalAnalog: Float values (normalized 0-1 or full-scale voltage)
Digital: Integer codes or float representation
num_bits(int, optional) — ADC resolution in bits. If None, inferred from data rangefull_scale(float, optional) — Full-scale voltage for quantizationIf provided: codes = round(data × 2^num_bits / full_scale)
If None: assumes normalized input (0-1 range)
clip_percent(float, default=0.01) — Fraction of code range to exclude from edges (0.01 = 1%)Removes unreliable bins near saturation
show_plot(bool, default=True) — Display INL/DNL plotsshow_title(bool, default=True) — Show auto-generated title with min/max rangescol_title(str, optional) — Column title above DNL plot (e.g., “N = 2^10”)ax(matplotlib axis, optional) — Axis to plot on. If None, uses current axis
Returns
Dictionary containing:
inl— Integral nonlinearity (LSB units)dnl— Differential nonlinearity (LSB units)code— Corresponding code values (x-axis)
Algorithm
1. Histogram Construction
code_range = np.arange(np.floor(data.min()), np.ceil(data.max()) + 1)
clip_bins = int(clip_percent * len(code_range) / 2)
code_valid = code_range[clip_bins : -clip_bins]
hist_counts, _ = np.histogram(data, bins=len(code_valid))
2. Inverse Cosine Transform
Sine wave PDF is p(x) ∝ 1/√(1 - x²), corresponding CDF:
CDF = np.cumsum(hist_counts) / np.sum(hist_counts)
Linearized_CDF = -np.cos(np.pi * CDF)
This maps the nonlinear sine wave distribution to a linear ideal ADC response.
3. DNL Calculation
DNL_raw = np.diff(Linearized_CDF)
DNL_normalized = DNL_raw / np.mean(DNL_raw) * (num_codes - 1) - 1
DNL = DNL_normalized - np.mean(DNL_normalized) # Remove offset
Units: LSB (Least Significant Bit)
DNL = 0: Ideal step size
DNL = -1: Missing code
DNL > 0: Code width > 1 LSB
4. INL Calculation
INL = np.cumsum(DNL)
Units: LSB
INL = 0: Ideal transfer function
INL > 0: Output higher than ideal
INL < 0: Output lower than ideal
Examples
Example 1: Basic Usage
import numpy as np
from adctoolbox import analyze_inl_from_sine
# Analyze 12-bit ADC output
result = analyze_inl_from_sine(adc_data, num_bits=12, show_plot=True)
print(f"Peak INL: {np.max(np.abs(result['inl'])):.3f} LSB")
print(f"Peak DNL: {np.max(np.abs(result['dnl'])):.3f} LSB")
print(f"DNL range: [{result['dnl'].min():.3f}, {result['dnl'].max():.3f}] LSB")
Example 2: Tight Clipping
# Exclude top/bottom 5% to avoid saturation effects
result = analyze_inl_from_sine(data, clip_percent=0.05)
Example 3: Custom Plotting
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10, 8))
result = analyze_inl_from_sine(data, ax=ax, col_title="Test Condition A")
plt.tight_layout()
plt.show()
Example 4: Batch Analysis
# Analyze multiple datasets
datasets = [data1, data2, data3]
results = [analyze_inl_from_sine(d, show_plot=False) for d in datasets]
for i, res in enumerate(results):
print(f"Dataset {i+1}: INL={np.max(np.abs(res['inl'])):.2f} LSB")
Interpretation
DNL Analysis
DNL Value |
Meaning |
|---|---|
|
Ideal uniform code width |
|
Code width < 0.5 LSB → potential missing code |
|
Missing code (zero histogram hits) |
|
Code width > 1.5 LSB → significant nonlinearity |
INL Analysis
INL Value |
ADC Quality |
|---|---|
|
Excellent (< 0.5 LSB error) |
|
Good (< 1 LSB error) |
|
Poor, needs calibration |
INL shape: bow |
Gain/offset error |
INL shape: S-curve |
2nd-order nonlinearity |
INL shape: periodic |
Cyclic error (e.g., flash ADC) |
Limitations
Sine wave input required: Assumes sine wave histogram PDF
∝ 1/√(1 - x²)Ramp or triangle inputs require different methods
Sufficient samples: Requires
>> 2^Nsamples for N-bit ADC (typically 10× minimum)For 12-bit ADC: need > 40,000 samples for reliable results
Clipping sensitive: Edge bins unreliable due to saturation → adjust
clip_percentMissing codes: DNL = -1 causes CDF discontinuities, may need special handling
Common Issues
Low Sample Count
# BAD: Only 1000 samples for 12-bit ADC
result = analyze_inl_from_sine(data[:1000], num_bits=12) # Noisy results
# GOOD: Sufficient samples
result = analyze_inl_from_sine(data[:50000], num_bits=12) # Clean results
Signal Clipping
# If signal clips at rails, increase clip_percent
result = analyze_inl_from_sine(data, clip_percent=0.05) # Exclude 5% from edges
See Also
fit_sine_4param— Sine wave parameter extractioncalibrate_weight_sine— Digital weight calibration to reduce INL/DNLanalyze_spectrum— Frequency-domain linearity (SFDR, THD)compute_inl_from_sine— Core computation without plotting
References
IEEE Std 1241-2010, Section 5.5, “Histogram Test Method”
J. Doernberg et al., “Full-Speed Testing of A/D Converters,” IEEE JSSC, 1984
M. F. Wagdy and W. Ng, “Validity of Uniform Quantization Error Model for Sinusoidal Signals Without and With Dither,” IEEE Trans. IM, 1989