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 signal

    • Analog: 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 range

  • full_scale (float, optional) — Full-scale voltage for quantization

    • If 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 plots

  • show_title (bool, default=True) — Show auto-generated title with min/max ranges

  • col_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

DNL 0

Ideal uniform code width

DNL < -0.5

Code width < 0.5 LSB → potential missing code

DNL = -1

Missing code (zero histogram hits)

DNL > 0.5

Code width > 1.5 LSB → significant nonlinearity

INL Analysis

INL Value

ADC Quality

max(|INL|) < 0.5

Excellent (< 0.5 LSB error)

max(|INL|) < 1.0

Good (< 1 LSB error)

max(|INL|) > 2.0

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^N samples 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_percent

  • Missing 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

References

  1. IEEE Std 1241-2010, Section 5.5, "Histogram Test Method"

  2. J. Doernberg et al., "Full-Speed Testing of A/D Converters," IEEE JSSC, 1984

  3. M. F. Wagdy and W. Ng, "Validity of Uniform Quantization Error Model for Sinusoidal Signals Without and With Dither," IEEE Trans. IM, 1989