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: 2 additions & 0 deletions src/pycbsdk/cbhw/device/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ def __init__(self, params: Params):
lambda: CBChannelType.Any
), # Filled in upon receiving device config.
"proc_chans": 0,
"instrument": -1,
"channel_infos": {},
"group_infos": {},
"group_nchans": {},
"sysfreq": None, # Should be 30_000 for legacy or 1e9 for Gemini PTP
}
# Init group_callbacks dict with an empty list for each supported smp grp (1-5:SMP; 6:RAW)
Expand Down
8 changes: 7 additions & 1 deletion src/pycbsdk/cbhw/device/nsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ def _handle_sysrep(self, pkt):
if b_general or pkt.header.type == CBPacketType.SYSREPRUNLEV:
self._config["runlevel"] = CBRunLevel(pkt.runlevel)
self._config["sysfreq"] = pkt.sysfreq
self._config["instrument"] = pkt.header.instrument
self._config_events["sysrep"].set()
if b_general or pkt.header.type == CBPacketType.SYSREPRUNLEV:
if self._config["runlevel"] == CBRunLevel.STANDBY:
Expand All @@ -396,7 +397,10 @@ def _handle_sysrep(self, pkt):
def _handle_chaninfo(self, pkt):
# If this config packet is limited in scope then it might have some garbage data in its out-of-scope payload.
# We should update our config, but only the parts that this REP packet is scoped to.
if pkt.header.type in [CBPacketType.CHANREP]:
if pkt.header.instrument != self._config["instrument"]:
# Gemini system returns channel info for all instruments.
pass
elif pkt.header.type in [CBPacketType.CHANREP]:
# Full scope; overwrite our config.
self._config["channel_infos"][pkt.chan] = copy.copy(pkt)
self._config["channel_types"][pkt.chan] = get_chantype_from_chaninfo(pkt)
Expand Down Expand Up @@ -457,6 +461,7 @@ def _handle_groupinfo(self, pkt):
else:
chan_list = set()
self._config["group_infos"][pkt.group] = chan_list
self._config["group_nchans"][pkt.group] = len(chan_list)

def _handle_configall(self, pkt):
if pkt.header.dlen > 0:
Expand Down Expand Up @@ -1017,6 +1022,7 @@ def get_config(
self._config["channel_infos"] = {}
# Do not clear sysfreq if we already have it as this cannot change.
self._config["sysfreq"] = self._config.get("sysfreq", None)
# self._config["instrument"] = -1
time.sleep(0.1)
pkt = self.packet_factory.make_packet(
None,
Expand Down
8 changes: 8 additions & 0 deletions src/pycbsdk/cbhw/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,13 @@ def run(self) -> None:
b_debug_unknown = True # If there are no callbacks and it's not a group or event packet, then debug.

# See if we have any callbacks registered for this type of packet.
b_grp = False
if chid & CBSpecialChan.CONFIGURATION:
callbacks = self._device.config_callbacks[pkt_type]
elif chid == CBSpecialChan.GROUP:
# This is a sample group packet. The pkt_type is actually the sample group id (1-6)
if pkt_type in self._device.group_callbacks:
b_grp = True
callbacks = self._device.group_callbacks[pkt_type]
else:
# Known bug https://blackrockengineering.atlassian.net/browse/CSCI-95
Expand All @@ -106,6 +108,12 @@ def run(self) -> None:
pkt = self._packet_factory.make_packet(
data, chid=chid, pkt_type=pkt_type, chantype=chantype
)
if b_grp:
# Note: pkt.data length is always a multiple of 4 bytes = 32 bits = 2 channels * 16 bits.
n_chans = self._device.config["group_nchans"][pkt_type]
if n_chans % 2 != 0:
# Odd number of channels enabled then we have an extra 16 bits of data.
pkt.data = pkt.data[:n_chans]
# Between the time the callbacks are grabbed above and the time we actually call them,
# it's possible for the client to unregister the callback.
# Thus it's very important that a callback is unregistered and some time is allowed to pass
Expand Down
15 changes: 9 additions & 6 deletions src/pycbsdk/examples/group_sample_intervals.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@


class DummyApp:
def __init__(self, duration=21.0, t_step=1 / 30_000):
def __init__(self, nchans: int, duration=21.0, t_step=1 / 30_000):
n_samples = int(np.ceil(duration * 30_000))
self._nchans = nchans
self._t_step = t_step
self._buffer = np.zeros((n_samples, 2), dtype=np.int16)
self._buffer = np.zeros((n_samples, nchans), dtype=np.int16)
self._ts = np.zeros((n_samples,), dtype=np.int64)
self._write_index = 0
self._last_time = 0

def handle_frame(self, pkt):
if self._write_index < self._buffer.shape[0]:
self._buffer[self._write_index, :] = memoryview(pkt.data[:4])
self._buffer[self._write_index, :] = memoryview(pkt.data[:self._nchans])
self._ts[self._write_index] = pkt.header.time
self._write_index += 1

Expand All @@ -41,6 +42,7 @@ def finish(self):
def main(
duration: float = 11.0,
smpgroup: int = 6,
nchans: int = 2,
inst_addr: str = "",
inst_port: int = 51002,
client_addr: str = "",
Expand Down Expand Up @@ -81,11 +83,12 @@ def main(
for chtype in [CBChannelType.FrontEnd, CBChannelType.AnalogIn]:
cbsdk.set_all_channels_disable(nsp_obj, chtype)

# Enable channel 1 at smpgroup. For smpgroup < 5, this also updates the smpfilter.
_ = cbsdk.set_channel_config(nsp_obj, 1, "smpgroup", smpgroup)
# Enable channels 1 & 2 at smpgroup. For smpgroup < 5, this also updates the smpfilter.
for ch in range(1, nchans + 1):
_ = cbsdk.set_channel_config(nsp_obj, ch, "smpgroup", smpgroup)

# Create a dummy app.
app = DummyApp(duration=duration, t_step=1 / config["sysfreq"])
app = DummyApp(nchans, duration=duration, t_step=1 / config["sysfreq"])

time.sleep(2.0)

Expand Down
Loading