diff --git a/src/pycbsdk/cbhw/device/nsp.py b/src/pycbsdk/cbhw/device/nsp.py index 97b12f2..ca95b24 100644 --- a/src/pycbsdk/cbhw/device/nsp.py +++ b/src/pycbsdk/cbhw/device/nsp.py @@ -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) @@ -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. @@ -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 @@ -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() @@ -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"] @@ -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, diff --git a/src/pycbsdk/cbhw/packet/packets.py b/src/pycbsdk/cbhw/packet/packets.py index b0ffc65..9240f8e 100644 --- a/src/pycbsdk/cbhw/packet/packets.py +++ b/src/pycbsdk/cbhw/packet/packets.py @@ -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), ) @@ -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 diff --git a/src/pycbsdk/examples/group_sample_intervals.py b/src/pycbsdk/examples/group_sample_intervals.py index 7019080..e2e0d61 100644 --- a/src/pycbsdk/examples/group_sample_intervals.py +++ b/src/pycbsdk/examples/group_sample_intervals.py @@ -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 @@ -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)." ) @@ -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) diff --git a/src/pycbsdk/examples/print_rates.py b/src/pycbsdk/examples/print_rates.py index b059057..8588280 100644 --- a/src/pycbsdk/examples/print_rates.py +++ b/src/pycbsdk/examples/print_rates.py @@ -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)