Skip to content

Commit 61b5ae3

Browse files
committed
Minor bugs and doc fixes
1 parent 6f4ff66 commit 61b5ae3

File tree

7 files changed

+56
-25
lines changed

7 files changed

+56
-25
lines changed

docs/dataproc.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ There are several functions present for filtering the data to smooth it or reduc
100100

101101
- Next are convolution filters which operate by convolving the trace with a given kernel function. These involve :func:`.filters.gaussian_filter` (and :func:`.filters.gaussian_filter_nd`, which is simply a wrapper around :func:`scipy.ndimage.gaussian_filter`), and a more generic :func:`.filters.convolution_filter`. Related are infinite impulse response (IIR) filter :func:`.filters.low_pass_filter` and :func:`.filters.high_pass_filter`, which mimic standard single-pole low-pass and high-pass filters. In principle, they can be modelled as a convolution with an exponential decay, but the implementation using the recursive filters is more efficient for large widths.
102102
- Finally, there are Fourier filters, which Fourier-transform the trace, scale the transform values, and transform it back to the real domain. These involve the main function :func:`.filters.fourier_filter`, which takes a generic frequency response function, as well as two specific response function generators :func:`.filters.fourier_filter_bandpass` and :func:`.filters.fourier_filter_bandstop`, both generating hard frequency cutoff filters.
103+
- In addition to "post-processing" filters described above, there are also "real-time" filters which serve to fitler data as it is acquired, e.g., to filter out temporary noise or spikes. There are two filters of this kind: :class:`.filters.RunningDecimationFilter` and :class:`.filters.RunningDebounceFilter`. They are implemented as classes, and both have methods to add a new datapoint, to get the current filter value, and to reset the filter.
103104

104105

105106
.. _dataproc_fourier:

pylablib/core/dataproc/filters.py

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,8 @@ def _sliding_func(trace, filtering_function, width, mode="reflect", cval=0.):
196196
if width is None or width<=1:
197197
return trace
198198
l=len(trace)
199-
width=(int(width)//2)*2+1
200-
trace=utils.pad_trace(np.asarray(trace),pad=width//2,mode=mode,cval=cval)
201-
return np.array([filtering_function(trace[i-width//2:i+width//2+1]) for i in range(width//2,l+width//2)])
199+
trace=utils.pad_trace(np.asarray(trace),pad=(width-1)//2,mode=mode,cval=cval)
200+
return np.array([filtering_function(trace[i-width//2:i+(width+1)//2]) for i in range(width//2,l+width//2)])
202201

203202
def sliding_filter(trace, n, dec="bin", mode="reflect", cval=0.):
204203
"""
@@ -604,24 +603,32 @@ def __init__(self, n, precision=None, initial=None):
604603
self.initial=initial
605604
self.value=self.initial
606605
self._candidate=None
607-
self._cnt=0
606+
self._cnt=None
608607
def get(self):
609608
"""Get the filtered result"""
610609
return self.value
610+
def _newval(self, v, x):
611+
return abs(x-v)>self.precision if self.precision is not None else x!=v
611612
def add(self, x):
612613
"""Add a new sample"""
613614
if self.value is None:
614-
self.value=self._candidate=x
615+
self.value=x
615616
else:
616-
cont=abs(x-self._candidate)<self.precision if self.precision is not None else x==self._candidate
617-
if cont:
618-
self._cnt+=1
619-
else:
620-
self._cnt=0
621-
self._candidate=x
622-
if self._cnt+1>=self.n:
623-
self.value=self._candidate=x
617+
newc=self._newval(x,self._candidate) if self._candidate is not None else True
618+
if newc:
624619
self._cnt=0
620+
self._candidate=None
621+
if self._candidate is None:
622+
newv=self._newval(x,self.value)
623+
if newv:
624+
self._cnt=0
625+
self._candidate=x
626+
else:
627+
self._cnt+=1
628+
if self._cnt>=self.n:
629+
self.value=x
630+
self._candidate=None
631+
self._cnt=0
625632
return self.get()
626633
def reset(self):
627634
"""Reset the filter"""

pylablib/core/utils/general.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,20 @@ def get_props(obj, prop_names):
3333
"""
3434
return [getattr(obj,p) for p in prop_names]
3535

36+
def getattr_multivar(obj, attrs, **kwargs):
37+
"""
38+
Try to get an attribute of `obj` given a list `attrs` of its potential names.
39+
40+
If no attributes are found and ``default`` keyword argument is supplied, return this default value; otherwise, raise :exc:`AttributeError`.
41+
"""
42+
for a in attrs:
43+
try:
44+
return getattr(obj,a)
45+
except AttributeError:
46+
pass
47+
if "default" in kwargs:
48+
return kwargs["default"]
49+
raise AttributeError("{} object has no attributes {}".format(type(obj).__name__,attrs))
3650

3751

3852
### Defaulting to method call in a function ###

pylablib/devices/NI/daq.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -224,15 +224,11 @@ def get_export_clock_terminal(self):
224224
return self._strip_channel_name(term) if term else None
225225

226226
if nidaqmx is not None:
227-
try:
228-
_diff_cfg=nidaqmx.constants.TerminalConfiguration.DIFFERENTIAL # pylint: disable=no-member
229-
except AttributeError:
230-
_diff_cfg=nidaqmx.constants.TerminalConfiguration.BAL_DIFF # pylint: disable=no-member
231227
_voltage_input_terminal_cfgs={ "default":nidaqmx.constants.TerminalConfiguration.DEFAULT,
232228
"rse":nidaqmx.constants.TerminalConfiguration.RSE,
233229
"nrse":nidaqmx.constants.TerminalConfiguration.NRSE,
234-
"diff":_diff_cfg,
235-
"pseudodiff":nidaqmx.constants.TerminalConfiguration.PSEUDODIFFERENTIAL}
230+
"diff":general.getattr_multivar(nidaqmx.constants.TerminalConfiguration,["DIFFERENTIAL","BAL_DIFF","DIFF"],default=10106),
231+
"pseudodiff":general.getattr_multivar(nidaqmx.constants.TerminalConfiguration,["PSEUDODIFFERENTIAL","PSEUDO_DIFF"],default=12529)}
236232
else:
237233
_voltage_input_terminal_cfgs={}
238234
_p_voltage_input_terminal_cfg=interface.EnumParameterClass("voltage_input_terminal_cfg",_voltage_input_terminal_cfgs)

pylablib/devices/Thorlabs/elliptec.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,12 @@ def get_connected_addrs(self):
8686
"""Get a list of all connected device addresses"""
8787
return list(self._addrs)
8888
def _change_addr(self, addr, newaddr):
89-
self._addrs[self._addrs.index(addr)]=newaddr
90-
if self._default_addr==addr:
91-
self._default_addr=newaddr
92-
for d in [self._model_no,self._stage_scale,self._scale]:
93-
d[newaddr]=d.pop(addr)
89+
if addr in self._addrs:
90+
self._addrs[self._addrs.index(addr)]=newaddr
91+
if self._default_addr==addr:
92+
self._default_addr=newaddr
93+
for d in [self._model_no,self._stage_scale,self._scale]:
94+
d[newaddr]=d.pop(addr)
9495
def get_default_addr(self):
9596
"""Get the current default address"""
9697
return self._default_addr

tests/core/dataproc/test_dataproc.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,15 @@ def asarr(data, check_index=True, l=None):
140140
compare_tables(filters.fourier_filter(data,resp),ffldata,decimal=6)
141141
resp=filters.fourier_filter_bandstop(0.1-1E-5,0.2-1E-5)
142142
compare_tables(filters.fourier_filter(data,resp),data-ffldata,decimal=6)
143+
# Running
144+
for dec in ["mean","min"]:
145+
flt=filters.RunningDecimationFilter(10,mode=dec)
146+
pfdata=filters.sliding_filter(adata[:,0],10,dec=dec)[5:-5]
147+
rfdata=np.array([flt.add(x) for x in adata[:,0]][10:])
148+
compare_tables(rfdata,pfdata,decimal=6)
149+
flt.reset()
150+
rfdata=np.array([flt.add(x) for x in adata[:,0]][10:])
151+
compare_tables(rfdata,pfdata,decimal=6)
143152

144153
def test_fourier(table_loader):
145154
builder=table_loader.builder

tox.ini

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
# and then run "tox" from this directory.
55

66
[tox]
7-
envlist = lpy{36,37,38,39}, lpy{36,37,38,39}_32
7+
envlist = lpy{36,37,38,39,310}, lpy{36,37,38,39}_32
88

99
[testenv]
1010
basepython =
1111
lpy36: ../Distribs/Unpacked/python-3.6.8.amd64/python.exe
1212
lpy37: ../Distribs/Unpacked/python-3.7.7.amd64/python.exe
1313
lpy38: ../Distribs/Unpacked/python-3.8.9.amd64/python.exe
1414
lpy39: ../Distribs/Unpacked/python-3.9.4.amd64/python.exe
15+
lpy310: ../Distribs/Unpacked/python-3.10.5.amd64/python.exe
1516
lpy36_32: ../Distribs/Unpacked/python-3.6.8/python.exe
1617
lpy37_32: ../Distribs/Unpacked/python-3.7.7/python.exe
1718
lpy38_32: ../Distribs/Unpacked/python-3.8.9/python.exe
@@ -34,6 +35,7 @@ deps =
3435
ft232_7: pyft232==0.7
3536
ft232_6: pyft232==0.6
3637
ft232_5: pyft232==0.5
38+
visa_1_11: pyvisa<1.12
3739
visa_1_10: pyvisa<1.11
3840
visa_1_9: pyvisa<1.10
3941
visa_1_8: pyvisa<1.09
@@ -50,6 +52,7 @@ deps =
5052
np_1_17: numpy==1.17.0
5153
np_1_16: numpy==1.16.0
5254
np_1_15: numpy==1.15.0
55+
scp_1_8: scipy==1.8.0
5356
scp_1_7: scipy==1.7.0
5457
scp_1_6: scipy==1.6.0
5558
scp_1_5: scipy==1.5.0

0 commit comments

Comments
 (0)