Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions adc_eval/adcs/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ class ADC:
"""
Generic ADC Class.
...
Parameters
----------
nbits : int, default=8
Expand Down
103 changes: 96 additions & 7 deletions adc_eval/eval/calc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,90 @@


def db_to_pow(value, places=3):
"""Convert dBW to W."""
"""
Convert dBW to W.

Parameters
----------
value : float or ndarray
Value to convert to power, in dBW.
places : int, optional
Number of places to round output value to. Default is 3.

Returns
-------
float or ndarray
Returns either the rounded and converted value, or the ndarray
"""
if isinstance(value, np.ndarray):
return np.round(10 ** (0.1 * value), places)
return round(10 ** (0.1 * value), places)


def dBW(value, places=1):
"""Convert to dBW."""
"""
Convert to dBW.

Parameters
----------
value : float or ndarray
Value to convert to dBW, in W.
places : int, optional
Number of places to round output value to. Default is 1.

Returns
-------
float or ndarray
Returns either the rounded and converted value, or the ndarray
"""
if isinstance(value, np.ndarray):
return np.round(10 * np.log10(value), places)
return round(10 * np.log10(value), places)


def enob(sndr, places=1):
"""Return ENOB for given SNDR."""
"""
Return ENOB for given SNDR.

Parameters
----------
sndr : float
SNDR value in dBW to convert to ENOB.
places : int, optional
Number of places to round output value to. Default is 1.

Returns
-------
float or ndarray
Returns either the rounded and converted value, or the ndarray
"""
return round((sndr - 1.76) / 6.02, places)


def sndr_sfdr(spectrum, freq, fs, nfft, leak, full_scale=0):
"""Get SNDR and SFDR."""

def sndr_sfdr(spectrum, freq, fs, nfft, leak=0, full_scale=0):
"""
Get SNDR and SFDR.

Parameters
----------
spectrum : ndarray
Power spectrum as ndarray in units of Watts.
freq : ndarray
Array of frequencies for the input power spectrum.
fs : float
Sample frequency of power spectrum in Hz.
nfft : int
Number of samples in the FFT.
leak : int, optional
Number of leakage bins to consider when looking for peaks. Default is 0.
full_scale : float, optional
Full scale reference value for spectrum in Watts.

Returns
-------
dict
Returns a dictionary of computed stats.
"""
# Zero the DC bin
for i in range(0, leak + 1):
spectrum[i] = 0
Expand Down Expand Up @@ -81,7 +144,33 @@ def sndr_sfdr(spectrum, freq, fs, nfft, leak, full_scale=0):


def find_harmonics(spectrum, freq, nfft, bin_sig, psig, harms=5, leak=20, fscale=1e6):
"""Get the harmonic contents of the data."""
"""
Get the harmonic contents of the data.

Parameters
----------
spectrum : ndarray
Power spectrum as ndarray in units of Watts.
freq : ndarray
Array of frequencies for the input power spectrum.
nfft : int
Number of samples in the FFT.
bin_sig : int
Frequency bin of the dominant signal.
psig : float
Power of dominant signal in spectrum.
harms : int, optional
Number of input harmonics to calculate. Default is 5.
leak : int, optional
Number of leakage bins to look at when finding harmonics. Default is 20.
fscale : float, optional
Value to scale frequencies by in Hz. Default is 1MHz.

Returns
-------
dict
Returns a dictionary of computed stats.
"""
harm_stats = {"harm": {}}
harm_index = 2
for harm in bin_sig * np.arange(2, harms + 1):
Expand Down
25 changes: 23 additions & 2 deletions adc_eval/eval/simulate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,28 @@


class Simulator:
"""Class for handling simulation functions."""
"""
Class for handling simulation functions.
Parameters
----------
adc_obj : ADC.__class__
An ADC object from the adc_eval.eval.adc class list.
xarray : ndarray
Input signal array to simulate the adc_obj with.
Attributes
----------
out : ndarray of ADC output values.
adc : Reference to the input adc_obj.
vin : xarray with global signal errors included as set by adj_obj.
Methods
-------
run
"""

def __init__(self, adc_obj, xarray):
"""Initialize the simulator class."""
Expand All @@ -19,7 +40,7 @@ def out(self):
return np.array(self.dval)

def calc_error(self, vin):
"""Using the adc obj, calculates global signal error."""
"""Using the adc_obj, calculates global signal error before simulation."""
vinx = vin

# First calculate gain error
Expand Down
131 changes: 125 additions & 6 deletions adc_eval/eval/spectrum.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,25 @@
from adc_eval.eval import calc


def calc_psd(data, fs, nfft=2**12):
"""Calculate the PSD using the Bartlett method."""
def calc_psd(data, fs=1, nfft=2**12):
"""
Calculate the PSD using the Bartlett method.

Parameters
----------
data : ndarray
Time-series input data.
fs : float, optional
Sample frequency of the input time series data in Hz. Default is 1Hz.
nfft : int, optional
Number of FFT samples to use for PSD calculation. Default is 2^12.

Returns
-------
list
[freq_ss, psd_ss, freq_ds, psd_ds]
List containing single and double-sided PSDs along with frequncy array.
"""
nwindows = max(1, int(np.floor(len(data) / nfft)))
nfft = int(nfft)
xs = data[0 : int(nwindows * nfft)]
Expand All @@ -29,15 +46,49 @@ def calc_psd(data, fs, nfft=2**12):


def get_spectrum(data, fs=1, nfft=2**12, single_sided=True):
"""Get the power spectrum for an input signal."""
"""
Get the power spectrum for an input signal.

Parameters
----------
data : ndarray
Time-series input data.
fs : float, optional
Sample frequency of the input time series data in Hz. Default is 1Hz.
nfft : int, optional
Number of FFT samples to use for PSD calculation. Default is 2^12.
single_sided : bool, optional
Set to `True` for single-sided spectrum or `False` for double-sided.
Default is `True`.

Returns
-------
tuple
(freq, psd)
Tuple containing frequency array and PSD of input data.
"""
(freq_ss, psd_ss, freq_ds, psd_ds) = calc_psd(np.array(data), fs=fs, nfft=nfft)
if single_sided:
return (freq_ss, psd_ss * fs / nfft)
return (freq_ds, psd_ds * fs / nfft)


def window_data(data, window="rectangular"):
"""Applies a window to the time-domain data."""
"""
Applies a window to the time-domain data.

Parameters
----------
data : ndarray
Time-series input data.
window : str, optional
Window to use for input data. Default is rectangular.

Returns
-------
ndarray
Windowed version of input data.
"""
try:
wsize = data.size
except AttributeError:
Expand Down Expand Up @@ -71,7 +122,41 @@ def plot_spectrum(
single_sided=True,
fscale=("MHz", 1e6),
):
"""Plot Power Spectrum for input signal."""
"""
Plot Power Spectrum for input signal.

Parameters
----------
data : ndarray
Time-series input data.
fs : float, optional
Sample frequency of the input time series data in Hz. Default is 1Hz.
nfft : int, optional
Number of FFT samples to use for PSD calculation. Default is 2^12.
dr : float, optional
Dynamic range for input data to be referenced to. Default is 1.
harmonics : int, optional
Number of harmonics to calculate and annotate on plot. Default is 7.
leak : int, optional
Number of leakage bins to use in signal and harmonic calculation. Default is 1.
window : str, optional
Type of input window to use for input data. Default is rectangular.
no_plot : bool, optional
Selects whether to plot (`False`) or not (`True`). Default is `False`.
yaxis : str, optional
Selects y-axis reference units. Example: `power`, `fullscale`, etc. Default is `power`.
single_sided : bool, optional
Set to `True` for single-sided spectrum or `False` for double-sided.
Default is `True`.
fscale : tuple, optional
Selects x-axis scaling and units. Default is ('MHz', 1e6).

Returns
-------
tuple
(freq, psd, stats)
Tuple containing frequency array, PSD of input data, and calculated statstics dictionary.
"""
(freq, pwr) = get_spectrum(data, fs=fs, nfft=nfft, single_sided=single_sided)

# Calculate the fullscale range of the spectrum in Watts
Expand Down Expand Up @@ -210,7 +295,41 @@ def analyze(
single_sided=True,
fscale="MHz",
):
"""Perform spectral analysis on input waveform."""
"""
Perform spectral analysis on input waveform.

Parameters
----------
data : ndarray
Time-series input data.
nfft : int
Number of FFT samples to use for PSD calculation.
fs : float, optional
Sample frequency of the input time series data in Hz. Default is 1Hz.
dr : float, optional
Dynamic range for input data to be referenced to. Default is 1.
harmonics : int, optional
Number of harmonics to calculate and annotate on plot. Default is 7.
leak : int, optional
Number of leakage bins to use in signal and harmonic calculation. Default is 1.
window : str, optional
Type of input window to use for input data. Default is rectangular.
no_plot : bool, optional
Selects whether to plot (`False`) or not (`True`). Default is `False`.
yaxis : str, optional
Selects y-axis reference units. Example: `power`, `fullscale`, etc. Default is `power`.
single_sided : bool, optional
Set to `True` for single-sided spectrum or `False` for double-sided.
Default is `True`.
fscale : str, optional
Selects x-axis units. Default is 'MHz'.

Returns
-------
tuple
(freq, psd, stats)
Tuple containing frequency array, PSD of input data, and calculated statstics dictionary.
"""
fscalar = {
"uHz": 1e-6,
"mHz": 1e-3,
Expand Down
4 changes: 0 additions & 4 deletions adc_eval/filt.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ class CICDecimate:
"""
Generic CIC Decimator Object.
...
Parameters
----------
dec : int, default=2
Expand Down Expand Up @@ -138,8 +136,6 @@ class FIRLowPass:
"""
Generic FIR Low Pass Filter.
...
Parameters
----------
dec : int, optional
Expand Down
Loading