Implemented a high-pass filter for input data
This commit is contained in:
parent
c016636add
commit
3a6ffd130c
@ -2,6 +2,7 @@ __all__ = ['DaqChannel']
|
|||||||
import json, logging
|
import json, logging
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import List
|
from typing import List
|
||||||
|
import numpy as np
|
||||||
from dataclasses_json import dataclass_json
|
from dataclasses_json import dataclass_json
|
||||||
from ..lasp_common import Qty, SIQtys
|
from ..lasp_common import Qty, SIQtys
|
||||||
|
|
||||||
@ -20,10 +21,21 @@ class DaqChannel:
|
|||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
# logging.debug(f'__post_init__({self.channel_name})')
|
# logging.debug(f'__post_init__({self.channel_name})')
|
||||||
self._qty = SIQtys.default()
|
self._qty = SIQtys.default()
|
||||||
if len(self.channel_metadata) > 0:
|
|
||||||
|
# Whether a digital high-pass filter should be used on input data.
|
||||||
|
# Negative means disabled. A positive number corresponds to the cut-on
|
||||||
|
# frequency of the installed highpass filter.
|
||||||
|
self._highpass = -1.0
|
||||||
|
try:
|
||||||
meta = json.loads(self.channel_metadata)
|
meta = json.loads(self.channel_metadata)
|
||||||
if 'qty' in meta:
|
if 'qty' in meta:
|
||||||
|
# The quantity itself is stored as a JSON string, in the JSON
|
||||||
|
# object called channel_metadata.
|
||||||
self._qty = Qty.from_json(meta['qty'])
|
self._qty = Qty.from_json(meta['qty'])
|
||||||
|
if 'highpass' in meta:
|
||||||
|
self._highpass = meta['highpass']
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
logging.debug('No JSON data found in DaqChannel {self.channel_name}')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def qty(self):
|
def qty(self):
|
||||||
@ -32,15 +44,25 @@ class DaqChannel:
|
|||||||
@qty.setter
|
@qty.setter
|
||||||
def qty(self, newqty):
|
def qty(self, newqty):
|
||||||
self._qty = newqty
|
self._qty = newqty
|
||||||
self._store('qty', newqty)
|
self._store('qty', newqty.to_json())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def highpass(self):
|
||||||
|
return self._highpass
|
||||||
|
|
||||||
|
@highpass.setter
|
||||||
|
def highpass(self, newvalue: float):
|
||||||
|
newvalue = float(newvalue)
|
||||||
|
self._highpass = newvalue
|
||||||
|
self._store('highpass', newvalue)
|
||||||
|
|
||||||
def _store(self, name, val):
|
def _store(self, name, val):
|
||||||
if len(self.channel_metadata) > 0:
|
try:
|
||||||
meta = json.loads(self.channel_metadata)
|
meta = json.loads(self.channel_metadata)
|
||||||
else:
|
except json.JSONDecodeError:
|
||||||
meta = {}
|
meta = {}
|
||||||
|
|
||||||
meta[name] = val.to_json()
|
meta[name] = val
|
||||||
self.channel_metadata = json.dumps(meta)
|
self.channel_metadata = json.dumps(meta)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
@ -53,5 +75,6 @@ class DaqChannel:
|
|||||||
self.range_index == other.range_index and
|
self.range_index == other.range_index and
|
||||||
self.ACCoupling_enabled == other.ACCoupling_enabled and
|
self.ACCoupling_enabled == other.ACCoupling_enabled and
|
||||||
self.IEPE_enabled == other.IEPE_enabled and
|
self.IEPE_enabled == other.IEPE_enabled and
|
||||||
self.channel_metadata == other.channel_metadata)
|
self.channel_metadata == other.channel_metadata and
|
||||||
|
np.isclose(self.highpass,other.highpass))
|
||||||
|
|
||||||
|
@ -19,6 +19,8 @@ from .device import Daq, DaqChannel, DaqConfiguration, DeviceInfo
|
|||||||
from .lasp_atomic import Atomic
|
from .lasp_atomic import Atomic
|
||||||
from .lasp_common import AvType
|
from .lasp_common import AvType
|
||||||
from .lasp_multiprocessingpatch import apply_patch
|
from .lasp_multiprocessingpatch import apply_patch
|
||||||
|
from .filter import highpass
|
||||||
|
from .wrappers import SosFilterBank
|
||||||
|
|
||||||
apply_patch()
|
apply_patch()
|
||||||
|
|
||||||
@ -133,21 +135,33 @@ class AudioStream:
|
|||||||
self.daq = Daq(device, daqconfig)
|
self.daq = Daq(device, daqconfig)
|
||||||
en_in_ch = daqconfig.getEnabledInChannels(include_monitor=True)
|
en_in_ch = daqconfig.getEnabledInChannels(include_monitor=True)
|
||||||
en_out_ch = daqconfig.getEnabledOutChannels()
|
en_out_ch = daqconfig.getEnabledOutChannels()
|
||||||
|
|
||||||
if en_in_ch == 0 and en_out_ch == 0:
|
if en_in_ch == 0 and en_out_ch == 0:
|
||||||
raise RuntimeError('No enabled input / output channels')
|
raise RuntimeError('No enabled input / output channels')
|
||||||
|
|
||||||
logging.debug('Ready to start device...')
|
logging.debug('Ready to start device...')
|
||||||
|
|
||||||
samplerate = self.daq.start(self.streamCallback)
|
samplerate = self.daq.start(self.streamCallback)
|
||||||
|
|
||||||
|
# Create required Highpass filters for incoming data
|
||||||
|
self.hpfs = [None]*len(en_in_ch)
|
||||||
|
for i, ch in enumerate(en_in_ch):
|
||||||
|
# Simple filter with a single bank and one section
|
||||||
|
if ch.highpass > 0:
|
||||||
|
fb = SosFilterBank(1, 1)
|
||||||
|
hpf = highpass(samplerate, ch.highpass, Q=np.sqrt(2))
|
||||||
|
fb.setFilter(0, hpf)
|
||||||
|
self.hpfs[i] = fb
|
||||||
|
|
||||||
self.streammetadata = StreamMetaData(
|
self.streammetadata = StreamMetaData(
|
||||||
fs=samplerate,
|
fs=samplerate,
|
||||||
in_ch=daqconfig.getEnabledInChannels(),
|
in_ch=en_in_ch,
|
||||||
out_ch=daqconfig.getEnabledOutChannels(),
|
out_ch=en_out_ch,
|
||||||
blocksize=self.daq.nFramesPerBlock,
|
blocksize=self.daq.nFramesPerBlock,
|
||||||
dtype=self.daq.getNumpyDataType(),
|
dtype=self.daq.getNumpyDataType(),
|
||||||
)
|
)
|
||||||
self.running = True
|
self.running = True
|
||||||
|
|
||||||
|
|
||||||
def streamCallback(self, indata, outdata, nframes):
|
def streamCallback(self, indata, outdata, nframes):
|
||||||
"""
|
"""
|
||||||
This is called (from a separate thread) for each block
|
This is called (from a separate thread) for each block
|
||||||
@ -165,7 +179,29 @@ class AudioStream:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
rv = self.processCallback(self, indata, outdata)
|
if indata is not None:
|
||||||
|
indata_filtered = np.empty_like(indata)
|
||||||
|
nchannels = indata.shape[1]
|
||||||
|
|
||||||
|
for i in range(nchannels):
|
||||||
|
# Filter each channel to the optional high-pass, which could also
|
||||||
|
# be an empty filter
|
||||||
|
if self.hpfs[i] is not None:
|
||||||
|
indata_float = indata[:, [i]].astype(np.float)
|
||||||
|
filtered_ch_float = self.hpfs[i].filter_(
|
||||||
|
indata_float
|
||||||
|
)
|
||||||
|
|
||||||
|
indata_filtered[:, i] = filtered_ch_float.astype(
|
||||||
|
self.streammetadata.dtype)[:, 0]
|
||||||
|
else:
|
||||||
|
# One-to-one copy
|
||||||
|
indata_filtered[:, i] = indata[:, i]
|
||||||
|
else:
|
||||||
|
indata_filtered = indata
|
||||||
|
|
||||||
|
# rv = self.processCallback(self, indata, outdata)
|
||||||
|
rv = self.processCallback(self, indata_filtered, outdata)
|
||||||
if rv != 0:
|
if rv != 0:
|
||||||
self.running <<= False
|
self.running <<= False
|
||||||
return rv
|
return rv
|
||||||
|
Loading…
Reference in New Issue
Block a user