"""
ADC spectrum analysis with ENOB, SNDR, SFDR, SNR, THD, Noise Floor, NSD calculations.
MATLAB counterpart: specPlot.m, plotspec.m
This is a wrapper function that combines core FFT calculations and plotting
for backward compatibility with existing code.
"""
import numpy as np
from adctoolbox.spectrum.compute_spectrum import compute_spectrum
from adctoolbox.spectrum.plot_spectrum import plot_spectrum
[docs]
def analyze_spectrum(data, fs=1.0, osr=1, max_scale_range=None, win_type='hann', side_bin=None,
max_harmonic=5, nf_method=2, assumed_sig_pwr_dbfs=np.nan, coherent_averaging=False,
create_plot: bool = True, show_title=True, show_label=True, plot_harmonics_up_to=3, ax=None):
"""
Spectral analysis and plotting. (Wrapper function for modular core and plotting)
This function first calculates all metrics and then conditionally plots the spectrum.
For the Virtuoso/ADE-Explorer dark-theme stem plot variant, see
`analyze_spectrum_virtuoso` (separate function with its own defaults β
rectangular window, dark-theme plotter).
Parameters:
data: Input data (N,) or (M, N)
fs: Sampling frequency
max_scale_range: Full scale range for normalization.
Can be: scalar (direct range), tuple/list [min, max], or None (auto-detect)
win_type: Window function type ('hann', 'hamming', 'boxcar')
side_bin: Number of side bins around fundamental (None for automatic selection)
osr: Oversampling ratio
max_harmonic: Number of harmonics for THD calculation
nf_method: Noise floor calculation method (0=median, 1=trimmed mean, 2=exclude harmonics)
assumed_sig_pwr_dbfs: Pre-defined signal level in dBFS
create_plot: Plot the spectrum (True) or not (False)
show_title: Display auto-generated title (True) or not (False)
show_label: Add labels and annotations (True) or not (False)
plot_harmonics_up_to: Number of harmonics to mark on the plot
ax: Optional matplotlib axes object. If None and create_plot=True, a new figure is created.
Returns:
dict: Dictionary with performance metrics:
- enob: Effective Number of Bits
- sndr_dbc: Signal-to-Noise and Distortion Ratio (dBc)
- sfdr_dbc: Spurious-Free Dynamic Range (dBc)
- snr_dbc: Signal-to-Noise Ratio (dBc)
- thd_dbc: Total Harmonic Distortion (dBc)
- sig_pwr_dbfs: Signal power (dBFS)
- noise_floor_dbfs: Noise floor (dBFS)
- nsd_dbfs_hz: Noise Spectral Density (dBFS/Hz)
"""
# 1. --- Core Calculation ---
# Pass all relevant parameters to the pure calculation kernel.
results = compute_spectrum(
data=data,
fs=fs,
max_scale_range=max_scale_range,
win_type=win_type,
side_bin=side_bin,
osr=osr,
max_harmonic=max_harmonic,
nf_method=nf_method,
coherent_averaging=coherent_averaging,
assumed_sig_pwr_dbfs=assumed_sig_pwr_dbfs
)
# Print warning if harmonics collide with fundamental
collided = results['plot_data'].get('collided_harmonics', [])
if collided and show_label:
print(f"[Warning from analyze_spectrum]: Harmonics {collided} alias to fundamental (excluded from THD)")
# 2. --- Optional Plotting ---
if create_plot:
# Pass the analysis results to the pure plotting function.
plot_spectrum(
compute_results=results,
show_title=show_title,
show_label=show_label,
plot_harmonics_up_to=plot_harmonics_up_to,
ax=ax
)
return results['metrics']
def analyze_spectrum_virtuoso(data, fs=1.0, osr=1, max_scale_range=None, win_type='rectangular',
side_bin=None, max_harmonic=5, nf_method=2,
assumed_sig_pwr_dbfs=np.nan, coherent_averaging=False,
create_plot: bool = True, show_title=True, show_label=True,
plot_harmonics_up_to=3, ax=None):
"""
Same as `analyze_spectrum`, but defaults are tuned for Cadence Virtuoso /
ADE-Explorer aesthetics:
- `win_type` defaults to 'rectangular' (one stem = one bin, no main-lobe
smearing β matches what ADE Explorer renders by default).
- Plot rendered by `plot_spectrum_virtuoso` (black canvas, red stems,
yellow/cyan annotation markers, fine dotted grid).
All other parameters and the returned metric dict are identical to
`analyze_spectrum`. Pass `win_type='hann'` explicitly if you want
Hann metrics with the Virtuoso plot style.
"""
# Local import to avoid circular at module load for the typical (analyzer) path
from adctoolbox.spectrum.plot_spectrum_virtuoso import plot_spectrum_virtuoso
results = compute_spectrum(
data=data,
fs=fs,
max_scale_range=max_scale_range,
win_type=win_type,
side_bin=side_bin,
osr=osr,
max_harmonic=max_harmonic,
nf_method=nf_method,
coherent_averaging=coherent_averaging,
assumed_sig_pwr_dbfs=assumed_sig_pwr_dbfs,
)
collided = results['plot_data'].get('collided_harmonics', [])
if collided and show_label:
print(f"[Warning from analyze_spectrum_virtuoso]: Harmonics {collided} alias to fundamental (excluded from THD)")
if create_plot:
plot_spectrum_virtuoso(
compute_results=results,
show_title=show_title,
show_label=show_label,
plot_harmonics_up_to=plot_harmonics_up_to,
ax=ax,
)
return results['metrics']