Skip to content

Commit b78f80d

Browse files
authored
Merge pull request #129 from fronzbot/dev
v0.4.0rc1
2 parents 25891ce + fcf6256 commit b78f80d

30 files changed

+2314
-737
lines changed

.DS_Store

-6 KB
Binary file not shown.

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
matrix:
1515
platform:
1616
- ubuntu-latest
17-
python-version: ['3.9', '3.10', '3.11', '3.12']
17+
python-version: ['3.10', '3.11', '3.12']
1818

1919
steps:
2020
- uses: actions/checkout@v3

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ python_adc_eval.egg-info
55
dist
66
.ruff_cache
77
.coverage
8+
.secret
89
build

README.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ Given an array of values representing the output of an ADC, the spectrum can be
4242
leak=<adjacent bins to filter>,
4343
window=<window type (rectangular/hanning)>,
4444
no_plot=<True/False>,
45-
yaxis=<"power"/"fullscale">
45+
yaxis=<"power"/"fullscale"/"magnitude">,
46+
single_sided=<True/False>
4647
)
4748
4849

__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Init file for tests."""

adc_eval/__init__.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
11
"""Initialization file for module."""
2-
3-
from . import spectrum
4-
from . import converters
5-
from . import signals

adc_eval/adcs/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""Initialization file for module."""

adc_eval/adcs/basic.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
"""Basic ADC models."""
2+
3+
import numpy as np
4+
5+
6+
def dac(samples, nbits=8, vref=1):
7+
"""Digital to analog converter."""
8+
quants = 2**nbits
9+
dv = vref / quants
10+
return samples * dv
11+
12+
13+
class ADC:
14+
"""
15+
Generic ADC Class.
16+
17+
Parameters
18+
----------
19+
nbits : int, default=8
20+
Number of bits for the ADC.
21+
fs : int or float, default=1
22+
Sample rate for the ADC in Hz.
23+
vref : int or float, default=1
24+
Reference level of the ADC in Volts ([0, +vref] conversion range).
25+
seed : int, default=1
26+
Seed for random variable generation.
27+
**kwargs
28+
Extra arguments.
29+
30+
Attributes
31+
-------
32+
vin : float
33+
Sets or returns the current input voltage level. Assumed +/-vref/2 input
34+
vlsb : float
35+
LSB voltage of the converter. vref/2^nbits
36+
noise : float, default=0
37+
Sets or returns the stdev of the noise generated by the converter.
38+
mismatch : float, default=0
39+
Sets or returns the stdev of the mismatch of the converter.
40+
offset : tuple of float, default=(0, 0)
41+
Sets the (mean, stdev) of the offset of the converter.
42+
gain_error : tuple of float, default=(0, 0)
43+
Sets the (mean, stdev) of the gain error of the converter.
44+
distortion : list of float, default=[1]
45+
Sets the harmonic distortion values with index=0 corresponding to HD1.
46+
Example: For unity gain and only -30dB of HD3, input is [1, 0, 0.032]
47+
dout : int
48+
Digital output code for current vin value.
49+
50+
Methods
51+
-------
52+
run_step
53+
54+
"""
55+
56+
def __init__(self, nbits=8, fs=1, vref=1, seed=1, **kwargs):
57+
"""Initialization function for Generic ADC."""
58+
np.random.seed(seed)
59+
self.nbits = nbits
60+
self.fs = fs
61+
self.vref = vref
62+
self.seed = seed
63+
self.err = {"noise": 0, "gain": 0, "dist": [1], "offset": 0, "mismatch": 0}
64+
self.dbits = np.zeros(nbits)
65+
self.dval = 0
66+
67+
@property
68+
def vin(self):
69+
"""Return input value."""
70+
return self._vin
71+
72+
@vin.setter
73+
def vin(self, x):
74+
"""Set the input value."""
75+
x += self.vref / 2
76+
x = max(0, min(x, self.vref))
77+
self._vin = x
78+
79+
@property
80+
def vlsb(self):
81+
"""Return the LSB voltage."""
82+
return self.vref / 2**self.nbits
83+
84+
@property
85+
def noise(self):
86+
"""Return noise status."""
87+
return self.err["noise"]
88+
89+
@noise.setter
90+
def noise(self, stdev):
91+
"""Set noise stdev in Vrms."""
92+
self.err["noise"] = stdev
93+
94+
@property
95+
def mismatch(self):
96+
"""Return noise stdev."""
97+
print("WARNING: 'mismatch' feature not implemented for this class.")
98+
return False
99+
100+
@mismatch.setter
101+
def mismatch(self, stdev):
102+
"""Set mismatch stdev."""
103+
print("WARNING: 'mismatch' feature not implemented for this class.")
104+
pass
105+
106+
@property
107+
def offset(self):
108+
"""Return offset value."""
109+
return self.err["offset"]
110+
111+
@offset.setter
112+
def offset(self, values):
113+
"""Set offset mean and stdev."""
114+
self.err["offset"] = np.random.normal(values[0], values[1])
115+
116+
@property
117+
def gain_error(self):
118+
"""Return gain error status."""
119+
return self.err["gain"]
120+
121+
@gain_error.setter
122+
def gain_error(self, values):
123+
"""Set gain error mean and stdev."""
124+
self.err["gain"] = np.random.normal(values[0], values[1])
125+
126+
@property
127+
def distortion(self):
128+
"""Return distortion gains (1st-order indexed)."""
129+
return self.err["dist"]
130+
131+
@distortion.setter
132+
def distortion(self, gains):
133+
"""Set distortion gains (1st-order indexed)."""
134+
self.err["dist"] = gains
135+
136+
@property
137+
def dout(self):
138+
"""Return digital output code."""
139+
return int(self.dval)
140+
141+
def run_step(self):
142+
"""Run a single ADC step."""
143+
vinx = self.vin
144+
dval = int(
145+
min(max(int((2**self.nbits) * vinx / self.vref), 0), 2**self.nbits - 1)
146+
)
147+
bits = [int(x) for x in bin(dval)[2:]]
148+
149+
while len(bits) < self.nbits:
150+
bits.insert(0, 0)
151+
self.dbits = bits
152+
self.dval = dval

adc_eval/adcs/sar.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
"""SAR ADC models"""
2+
3+
import numpy as np
4+
from adc_eval.adcs.basic import ADC
5+
6+
7+
class SAR(ADC):
8+
"""
9+
SAR ADC Class.
10+
11+
Parameters
12+
----------
13+
nbits : int, optional
14+
Number of bits for the ADC. The default is 8.
15+
fs : float, optional
16+
Sample rate for the ADC in Hz. The default is 1Hz.
17+
vref : float, optional
18+
Reference level of the ADC in Volts ([0, +vref] conversion range). The default is 1.
19+
seed : int, optional
20+
Seed for random variable generation. The default is 1.
21+
**kwargs
22+
Extra arguments.
23+
weights : list, optional
24+
List of weights for SAR capacitors. Must be >= nbits. Defaults to binary weights.
25+
MSB weight should be in index 0.
26+
27+
Attributes
28+
-------
29+
vin : float
30+
Sets or returns the current input voltage level. Assumed +/-vref/2 input
31+
vlsb : float
32+
LSB voltage of the converter. vref/2^nbits
33+
noise : float, default=0
34+
Sets or returns the stdev of the noise generated by the converter.
35+
weights : list
36+
Sets or returns the capacitor weighting of the array. Default is binary weighting.
37+
mismatch : float
38+
Sets or returns the stdev of the mismatch of the converter. Default is no mismatch.
39+
comp_noise : float
40+
Sets or returns the stdev of the comparator noise. Default is no noise.
41+
offset : tuple of float
42+
Sets the (mean, stdev) of the offset of the converter. Default is no offset.
43+
gain_error : tuple of float
44+
Sets the (mean, stdev) of the gain error of the converter. Default is no gain error.
45+
distortion : list of float
46+
Sets the harmonic distortion values with index=0 corresponding to HD1.
47+
Example: For unity gain and only -30dB of HD3, input is [1, 0, 0.032]
48+
dout : int
49+
Digital output code for current vin value.
50+
51+
Methods
52+
-------
53+
run_step
54+
55+
"""
56+
57+
def __init__(self, nbits=8, fs=1, vref=1, seed=1, **kwargs):
58+
"""Initialization function for Generic ADC."""
59+
super().__init__(nbits, fs, vref, seed)
60+
61+
self._mismatch = None
62+
self._comp_noise = 0
63+
64+
# Get keyword arguments
65+
self._weights = None
66+
self.weights = kwargs.get("weights", self.weights)
67+
68+
@property
69+
def weights(self):
70+
"""Returns capacitor unit weights."""
71+
if self._weights is None:
72+
self._weights = np.flip(2 ** np.linspace(0, self.nbits - 1, self.nbits))
73+
return np.array(self._weights)
74+
75+
@weights.setter
76+
def weights(self, values):
77+
"""Sets the capacitor unit weights."""
78+
self._weights = np.array(values)
79+
self.dbits = np.zeros(len(values))
80+
if self._weights.size < self.nbits:
81+
print(
82+
f"WARNING: Capacitor weight array size is {self._weights.size} for {self.nbits}-bit ADC."
83+
)
84+
self.mismatch = self.err["mismatch"]
85+
86+
@property
87+
def mismatch(self):
88+
"""Return noise stdev."""
89+
if self._mismatch is None:
90+
self._mismatch = np.zeros(self.weights.size)
91+
return self._mismatch
92+
93+
@mismatch.setter
94+
def mismatch(self, stdev):
95+
"""Sets mismatch stdev."""
96+
self.err["mismatch"] = stdev
97+
self._mismatch = np.random.normal(0, stdev, self.weights.size)
98+
self._mismatch /= np.sqrt(self.weights)
99+
100+
@property
101+
def comp_noise(self):
102+
"""Returns the noise of the comparator."""
103+
return self._comp_noise
104+
105+
@comp_noise.setter
106+
def comp_noise(self, value):
107+
"""Sets the noise of the comparator."""
108+
self._comp_noise = value
109+
110+
def run_step(self):
111+
"""Run a single ADC step."""
112+
vinx = self.vin
113+
114+
cweights = self.weights * (1 + self.mismatch)
115+
cdenom = sum(cweights) + 1
116+
117+
comp_noise = np.random.normal(0, self.comp_noise, cweights.size)
118+
119+
# Bit cycling
120+
vdac = vinx
121+
for n, _ in enumerate(cweights):
122+
vcomp = vdac - self.vref / 2 + comp_noise[n]
123+
compout = vcomp * 1e6
124+
compout = -1 if compout <= 0 else 1
125+
self.dbits[n] = max(0, compout)
126+
vdac -= compout * self.vref / 2 * cweights[n] / cdenom
127+
128+
# Re-scale the data
129+
scalar = 2**self.nbits / cdenom
130+
self.dval = min(2**self.nbits - 1, scalar * sum(self.weights * self.dbits))

adc_eval/converters.py

Lines changed: 0 additions & 29 deletions
This file was deleted.

0 commit comments

Comments
 (0)