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
40 changes: 29 additions & 11 deletions src/pycbsdk/cbhw/device/nsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,13 @@ def _register_basic_callbacks(self):

self.register_config_callback(CBPacketType.CHANREPAINP, self._handle_chaninfo)
self.register_config_callback(CBPacketType.CHANREPSPKTHR, self._handle_chaninfo)
self.register_config_callback(
CBPacketType.CHANREPNTRODEGROUP, self._handle_chaninfo
)
self.register_config_callback(CBPacketType.CHANREPDISP, self._handle_chaninfo)
self.register_config_callback(
CBPacketType.CHANREPUNITOVERRIDES, self._handle_chaninfo
)

self.register_config_callback(CBPacketType.GROUPREP, self._handle_groupinfo)
self.register_config_callback(CBPacketType.PROCREP, self._handle_procinfo)
Expand Down Expand Up @@ -397,8 +404,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.instrument != self._config["instrument"]:
# Gemini system returns channel info for all instruments.
if (pkt.header.instrument != self._config["instrument"]) or (
pkt.chan > self._config["proc_chans"]
):
# Drop channels that do not belong to this instrument
pass
elif pkt.header.type in [CBPacketType.CHANREP]:
# Full scope; overwrite our config.
Expand Down Expand Up @@ -430,8 +439,8 @@ def _handle_chaninfo(self, pkt):
# self._config["channel_infos"][pkt.chan].union.a.moninst = pkt.moninst
# self._config["channel_infos"][pkt.chan].union.a.monchan = pkt.monchan
elif pkt.header.type == CBPacketType.CHANREPSCALE:
self._config["channel_infos"][pkt.chan].scalein = pkt.scalein
self._config["channel_infos"][pkt.chan].scaleout = pkt.scaleout
self._config["channel_infos"][pkt.chan].scalin = pkt.scalin
self._config["channel_infos"][pkt.chan].scalout = pkt.scalout
elif pkt.header.type == CBPacketType.CHANREPDINP:
# TODO: NOTE: Need extra check if this is for serial or digital?
self._config["channel_infos"][pkt.chan].dinpopts = pkt.dinpopts
Expand All @@ -443,14 +452,21 @@ def _handle_chaninfo(self, pkt):
elif pkt.header.type == CBPacketType.CHANREPLABEL:
self._config["channel_infos"][pkt.chan].label = pkt.label
self._config["channel_infos"][pkt.chan].userflags = pkt.userflags
elif pkt.header.type == CBPacketType.CHANSETSPKTHR:
# TODO: from CHANREPSPKTHR, .spkthrlevel
elif pkt.header.type in [
CBPacketType.CHANSETSPKTHR,
CBPacketType.CHANREPSPKTHR,
]:
self._config["channel_infos"][pkt.chan].spkthrlevel = pkt.spkthrlevel

elif pkt.header.type == CBPacketType.CHANREPNTRODEGROUP:
# TODO: from use pkt.spkgroup
pass
elif pkt.header.type == CBPacketType.CHANREPDISP:
# TODO: Use .smpdispmin, .smpdispmax, .spkdispmax, .lncdispmax
pass
elif pkt.header.type == CBPacketType.CHANREPUNITOVERRIDES:
# TODO: Use .unitmapping
pass
else:
# TODO: from CHANREPNTRODEGROUP, .spkgroup
# TODO: from CHANREPDISP, .smpdispmin, .smpdispmax, .spkdispmax, .lncdispmax
# TODO: from CHANREPUNITOVERRIDES, .unitmapping
pass
# print(f"handled chaninfo {pkt.chan} of type {hex(pkt.header.type)}")
self._config_events["chaninfo"].set()
Expand Down Expand Up @@ -479,6 +495,8 @@ def _handle_nplay(self, pkt):

def _handle_procmon(self, pkt):
arrival_time = time.time()
# Note: There's about 0.57 msec from when procmon is sent to when it is received.
# so we could make sys_time = arrival_time - 570_000e-9
update_interval = max(pkt.header.time - self._monitor_state["time"], 1)
pkt_delta = self.pkts_received - self._monitor_state["pkts_received"]

Expand All @@ -493,7 +511,7 @@ def _handle_procmon(self, pkt):
f";\tcounter - {pkt.counter if has_counter else 'N/A'}"
f";\tdelta - {pkt_delta}"
f";\tsent - {pkt.sentpkts}"
f";\trate (pkt/samp) - {pkt_delta/update_interval}"
f";\trate (pkt/samp) - {pkt_delta / update_interval}"
)
self._monitor_state = {
"counter": pkt.counter if has_counter else -1,
Expand Down
4 changes: 2 additions & 2 deletions src/pycbsdk/cbhw/packet/packets.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,7 +530,7 @@ def sizes(self) -> list[int]:
# Convert coords from uint16 to half as many uint32
# TODO: Use numpy from buffer
return struct.unpack(
f"<{len(self.coords)//2}L",
f"<{len(self.coords) // 2}L",
struct.pack(f"<{len(self.coords)}H", self._array),
)

Expand All @@ -540,7 +540,7 @@ def sizes(self, insizes: list[int]):
assert n_elems <= (self.max_elements // 2)
# TODO: Use numpy buffer
self.coords = struct.unpack(
f"<{len(insizes)*2}H", struct.pack(f"<{len(insizes)}L", insizes)
f"<{len(insizes) * 2}H", struct.pack(f"<{len(insizes)}L", insizes)
)

@property
Expand Down
15 changes: 11 additions & 4 deletions src/pycbsdk/examples/group_sample_intervals.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def __init__(self, nchans: int, duration=21.0, t_step=1 / 30_000):

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

Expand All @@ -35,7 +35,7 @@ def finish(self):
s_elapsed = ts_elapsed * self._t_step
n_samps = np.sum(b_ts)
print(
f"Collected {n_samps} samples in {s_elapsed} s\t({n_samps/s_elapsed:.2f} Hz)."
f"Collected {n_samps} samples in {s_elapsed} s\t({n_samps / s_elapsed:.2f} Hz)."
)


Expand Down Expand Up @@ -83,12 +83,19 @@ def main(
for chtype in [CBChannelType.FrontEnd, CBChannelType.AnalogIn]:
cbsdk.set_all_channels_disable(nsp_obj, chtype)

# Enable channels 1 & 2 at smpgroup. For smpgroup < 5, this also updates the smpfilter.
# Enable first nchans 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)

# Calculate the clock step (I hate this)
if inst_addr and int(inst_addr.split(".")[-1]) in [200, 201, 202, 203, 203]:
# Note: This misses Gemini NSP!
t_step = 1 / 1e9
else:
t_step = 1 / config["sysfreq"]

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

time.sleep(2.0)

Expand Down
9 changes: 8 additions & 1 deletion src/pycbsdk/examples/print_rates.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,15 @@ def main(
]
n_chans = sum(b_spk)

# Calculate the clock step (I hate this)
if inst_addr and int(inst_addr.split(".")[-1]) in [200, 201, 202, 203, 203]:
# Note: This misses Gemini NSP!
t_step = 1 / 1e9
else:
t_step = 1 / config["sysfreq"]

# Create the dummy app.
app = DummyApp(n_chans, history=update_interval, tstep=1 / config["sysfreq"])
app = DummyApp(n_chans, history=update_interval, tstep=t_step)
# Register callbacks to update the app's state when appropriate packets are received.
_ = cbsdk.register_spk_callback(nsp_obj, app.update_state)

Expand Down
Loading