diff --git a/README.md b/README.md index 1a359a0..3b65f19 100644 --- a/README.md +++ b/README.md @@ -186,7 +186,8 @@ acquisition_settings = AcquisitionSettings( enabled_channels=[0, 1, 2, 3], vertical_ranges_in_mv=[200, 200, 200, 200], vertical_offsets_in_percent=[0, 0, 0, 0], - timestamping_enabled=True + timestamping_enabled=True, + batch_size=1 ) card.configure_trigger(trigger_settings) diff --git a/src/example_scripts/connect_to_star_hub.py b/src/example_scripts/connect_to_star_hub.py deleted file mode 100644 index 5cb4324..0000000 --- a/src/example_scripts/connect_to_star_hub.py +++ /dev/null @@ -1,43 +0,0 @@ -from spectrumdevice.devices.mocks import MockSpectrumDigitiserCard, MockSpectrumDigitiserStarHub -from spectrumdevice.devices.digitiser import SpectrumDigitiserCard -from spectrumdevice.devices.digitiser import SpectrumDigitiserStarHub -from spectrumdevice.settings import ModelNumber - - -def star_hub_example( - mock_mode: bool, num_cards: int, master_card_index: int, ip_address: str -) -> SpectrumDigitiserStarHub: - - if not mock_mode: - child_cards = [] - for n in range(num_cards): - # Connect to each card in the hub. - child_cards.append(SpectrumDigitiserCard(device_number=n, ip_address=ip_address)) - # Connect to the hub itself - return SpectrumDigitiserStarHub(device_number=0, child_cards=child_cards, master_card_index=master_card_index) - else: - mock_child_cards = [] - for n in range(num_cards): - # Create a mock device for each card in the hub - mock_child_cards.append( - MockSpectrumDigitiserCard( - device_number=n, - model=ModelNumber.TYP_M2P5966_X4, - mock_source_frame_rate_hz=10.0, # Mock devices need to be provided with a mock source frame rate - num_modules=2, # (For real devices, this and num_channels_per_module are read from the hardware). - num_channels_per_module=4, - ) - ) - # Create a mock hub containing the above devices - return MockSpectrumDigitiserStarHub( - device_number=0, child_cards=mock_child_cards, master_card_index=master_card_index - ) - - -if __name__ == "__main__": - hub = star_hub_example(mock_mode=True, num_cards=2, master_card_index=1, ip_address="169.254.45.181") - print(f"{hub} contains {len(hub.channels)} channels in total:") - for channel in hub.channels: - print(channel) - hub.reset() - hub.disconnect() diff --git a/src/example_scripts/continuous_multi_fifo_mode.py b/src/example_scripts/continuous_multi_fifo_mode.py index 708f5ba..f4094eb 100644 --- a/src/example_scripts/continuous_multi_fifo_mode.py +++ b/src/example_scripts/continuous_multi_fifo_mode.py @@ -18,11 +18,12 @@ def continuous_multi_fifo_example( mock_mode: bool, - acquisition_duration_in_seconds: float, + time_to_keep_acquiring_for_in_seconds: float, + batch_size: int, trigger_source: TriggerSource, device_number: int, ip_address: Optional[str] = None, - acquisition_length: int = 400, + single_acquisition_length_in_samples: int = 400, ) -> List[Measurement]: if not mock_mode: @@ -48,14 +49,15 @@ def continuous_multi_fifo_example( # Acquisition settings acquisition_settings = AcquisitionSettings( acquisition_mode=AcquisitionMode.SPC_REC_FIFO_MULTI, - sample_rate_in_hz=40000000, - acquisition_length_in_samples=acquisition_length, + sample_rate_in_hz=5000, + acquisition_length_in_samples=single_acquisition_length_in_samples, pre_trigger_length_in_samples=0, timeout_in_ms=5000, enabled_channels=[0], vertical_ranges_in_mv=[200], vertical_offsets_in_percent=[0], timestamping_enabled=True, + batch_size=batch_size, ) try: @@ -69,7 +71,7 @@ def continuous_multi_fifo_example( # Retrieve streamed waveform data until desired time has elapsed measurements_list = [] - while (monotonic() - start_time) < acquisition_duration_in_seconds: + while (monotonic() - start_time) < time_to_keep_acquiring_for_in_seconds: measurements_list += [ Measurement(waveforms=frame, timestamp=card.get_timestamp()) for frame in card.get_waveforms() @@ -95,10 +97,13 @@ def continuous_multi_fifo_example( measurements = continuous_multi_fifo_example( mock_mode=False, - acquisition_duration_in_seconds=0.5, + time_to_keep_acquiring_for_in_seconds=2, # continuous acquisition will stop after this many seconds (after + # waiting for the final batch to be acquired and transferred). Make sure you expect to receive (and finish + # transferring) at least 1 batch in this time! otherwise you will likely receive a timeout error + batch_size=5, # number of measurements to acquire before they are returned by get_waveforms() trigger_source=TriggerSource.SPC_TMASK_EXT0, device_number=1, - ip_address="169.254.45.181", + ip_address="169.254.13.35", ) # Plot waveforms diff --git a/src/example_scripts/finite_multi_fifo_mode.py b/src/example_scripts/finite_multi_fifo_mode.py index ead8219..ba25263 100644 --- a/src/example_scripts/finite_multi_fifo_mode.py +++ b/src/example_scripts/finite_multi_fifo_mode.py @@ -17,6 +17,7 @@ def finite_multi_fifo_example( mock_mode: bool, num_measurements: int, + batch_size: int, trigger_source: TriggerSource, device_number: int, ip_address: Optional[str] = None, @@ -54,7 +55,7 @@ def finite_multi_fifo_example( vertical_ranges_in_mv=[200], vertical_offsets_in_percent=[0], timestamping_enabled=True, - batch_size=5, + batch_size=batch_size, ) # Apply settings @@ -72,12 +73,14 @@ def finite_multi_fifo_example( from matplotlib.pyplot import plot, show, figure, title + # Only a few parameters are included as arguments here. See contents of the example function for other settings measurements = finite_multi_fifo_example( mock_mode=False, - num_measurements=5, + num_measurements=10, # number of waveforms to acquire from each enabled channel + batch_size=5, # number of measurements to acquire before they are returned by get_waveforms() trigger_source=TriggerSource.SPC_TMASK_EXT0, device_number=1, - ip_address="169.254.45.181", + ip_address="169.254.13.35", ) # Plot waveforms diff --git a/src/example_scripts/standard_single_mode.py b/src/example_scripts/standard_single_mode.py index 2a1c6e2..4be116f 100644 --- a/src/example_scripts/standard_single_mode.py +++ b/src/example_scripts/standard_single_mode.py @@ -73,7 +73,7 @@ def standard_single_mode_example( from matplotlib.pyplot import plot, show, xlabel, tight_layout, ylabel meas = standard_single_mode_example( - mock_mode=False, trigger_source=TriggerSource.SPC_TMASK_EXT0, device_number=1, ip_address="169.254.45.181" + mock_mode=False, trigger_source=TriggerSource.SPC_TMASK_EXT0, device_number=1, ip_address="169.254.13.35" ) # Plot waveforms diff --git a/src/example_scripts/star_hub_example.py b/src/example_scripts/star_hub_example.py new file mode 100644 index 0000000..b044bb9 --- /dev/null +++ b/src/example_scripts/star_hub_example.py @@ -0,0 +1,100 @@ +from spectrumdevice.devices.mocks import MockSpectrumDigitiserCard, MockSpectrumDigitiserStarHub +from spectrumdevice.devices.digitiser import SpectrumDigitiserCard +from spectrumdevice.devices.digitiser import SpectrumDigitiserStarHub +from spectrumdevice.settings import ( + ModelNumber, + TriggerSettings, + TriggerSource, + ExternalTriggerMode, + AcquisitionMode, + AcquisitionSettings, +) + + +def connect_to_star_hub_example( + mock_mode: bool, num_cards: int, master_card_index: int, ip_address: str +) -> SpectrumDigitiserStarHub: + + if not mock_mode: + child_cards = [] + for n in range(num_cards): + # Connect to each card in the hub. + child_cards.append(SpectrumDigitiserCard(device_number=n, ip_address=ip_address)) + # Connect to the hub itself + return SpectrumDigitiserStarHub(device_number=0, child_cards=child_cards, master_card_index=master_card_index) + else: + mock_child_cards = [] + for n in range(num_cards): + # Create a mock device for each card in the hub + mock_child_cards.append( + MockSpectrumDigitiserCard( + device_number=n, + model=ModelNumber.TYP_M2P5966_X4, + mock_source_frame_rate_hz=10.0, # Mock devices need to be provided with a mock source frame rate + num_modules=2, # (For real devices, this and num_channels_per_module are read from the hardware). + num_channels_per_module=4, + ) + ) + # Create a mock hub containing the above devices + return MockSpectrumDigitiserStarHub( + device_number=0, child_cards=mock_child_cards, master_card_index=master_card_index + ) + + +if __name__ == "__main__": + + from matplotlib.pyplot import figure, title, plot, show + + num_measurements = 5 + hub = connect_to_star_hub_example(mock_mode=False, num_cards=2, master_card_index=1, ip_address="169.254.13.35") + + print(f"{hub} contains {len(hub.channels)} channels in total:") + for channel in hub.channels: + print(channel) + + # Trigger settings + trigger_settings = TriggerSettings( + trigger_sources=[TriggerSource.SPC_TMASK_EXT0], + external_trigger_mode=ExternalTriggerMode.SPC_TM_POS, + external_trigger_level_in_mv=1000, + ) + + # Acquisition settings + acquisition_settings = AcquisitionSettings( + acquisition_mode=AcquisitionMode.SPC_REC_FIFO_MULTI, + sample_rate_in_hz=40000000, + acquisition_length_in_samples=400, + pre_trigger_length_in_samples=0, + timeout_in_ms=1000, + enabled_channels=[0, 8], # at least 1 channel from each child card must be enabled + vertical_ranges_in_mv=[200], + vertical_offsets_in_percent=[0], + timestamping_enabled=True, + batch_size=5, + ) + + # Apply settings + hub.configure_trigger(trigger_settings) + hub.configure_acquisition(acquisition_settings) + + # Execute acquisition + measurements = hub.execute_finite_fifo_acquisition(num_measurements) + + # Plot waveforms + for n, measurement in enumerate(measurements): + figure() + title(f"Measurement {n}") + for wfm in measurement.waveforms: + plot(wfm) + + ts_format = "%Y-%m-%d %H:%M:%S.%f" + print(f"Completed {len(measurements)} measurements each containing {len(measurements[0].waveforms)} waveforms.") + print(f"Waveforms had the following shape: {measurements[0].waveforms[0].shape}") + print(f"and the following timestamps:") + for measurement in measurements: + print(measurement.timestamp.strftime(ts_format) if measurement.timestamp else "Timestamping disabled") + + hub.reset() + hub.disconnect() + + show() diff --git a/src/spectrumdevice/devices/digitiser/abstract_spectrum_digitiser.py b/src/spectrumdevice/devices/digitiser/abstract_spectrum_digitiser.py index 58de93f..635fe21 100644 --- a/src/spectrumdevice/devices/digitiser/abstract_spectrum_digitiser.py +++ b/src/spectrumdevice/devices/digitiser/abstract_spectrum_digitiser.py @@ -31,7 +31,7 @@ def configure_acquisition(self, settings: AcquisitionSettings) -> None: if settings.batch_size > 1 and settings.acquisition_mode == AcquisitionMode.SPC_REC_STD_SINGLE: raise ValueError("In standard single mode, only 1 acquisition can be downloaded at a time.") self._acquisition_mode = settings.acquisition_mode - self._batch_size = settings.batch_size + self.set_batch_size(settings.batch_size) self.set_acquisition_mode(settings.acquisition_mode) self.set_sample_rate_in_hz(settings.sample_rate_in_hz) self.set_acquisition_length_in_samples(settings.acquisition_length_in_samples) @@ -101,14 +101,14 @@ def execute_finite_fifo_acquisition(self, num_measurements: int) -> List[Measure timestamp attribute, which (if timestamping was enabled in acquisition settings) contains the time at which the acquisition was triggered. """ - if (num_measurements % self._batch_size) != 0: + if (num_measurements % self.batch_size) != 0: raise ValueError( "Number of measurements in a finite FIFO acquisition must be a multiple of the " " batch size configured using AbstractSpectrumDigitiser.configure_acquisition()." ) self.execute_continuous_fifo_acquisition() measurements = [] - for _ in range(num_measurements // self._batch_size): + for _ in range(num_measurements // self.batch_size): measurements += [ Measurement(waveforms=frame, timestamp=self.get_timestamp()) for frame in self.get_waveforms() ] diff --git a/src/spectrumdevice/devices/digitiser/digitiser_card.py b/src/spectrumdevice/devices/digitiser/digitiser_card.py index 8855e82..77e3ce3 100644 --- a/src/spectrumdevice/devices/digitiser/digitiser_card.py +++ b/src/spectrumdevice/devices/digitiser/digitiser_card.py @@ -64,6 +64,7 @@ def __init__(self, device_number: int = 0, ip_address: Optional[str] = None): raise SpectrumCardIsNotADigitiser(self.type) self._acquisition_mode = self.acquisition_mode self._timestamper: Optional[Timestamper] = None + self._batch_size = 1 def _init_channels(self) -> Sequence[SpectrumDigitiserChannelInterface]: num_modules = self.read_spectrum_device_register(SPC_MIINST_MODULES) @@ -249,6 +250,13 @@ def set_acquisition_mode(self, mode: AcquisitionMode) -> None: mode (`AcquisitionMode`): The desired acquisition mode.""" self.write_to_spectrum_device_register(SPC_CARDMODE, mode.value) + @property + def batch_size(self) -> int: + return self._batch_size + + def set_batch_size(self, batch_size: int) -> None: + self._batch_size = batch_size + def define_transfer_buffer(self, buffer: Optional[Sequence[TransferBuffer]] = None) -> None: """Create or provide a `TransferBuffer` object for receiving acquired samples from the device. diff --git a/src/spectrumdevice/devices/digitiser/digitiser_interface.py b/src/spectrumdevice/devices/digitiser/digitiser_interface.py index 4f58edc..0940427 100644 --- a/src/spectrumdevice/devices/digitiser/digitiser_interface.py +++ b/src/spectrumdevice/devices/digitiser/digitiser_interface.py @@ -106,3 +106,12 @@ def acquisition_mode(self) -> AcquisitionMode: @abstractmethod def set_acquisition_mode(self, mode: AcquisitionMode) -> None: raise NotImplementedError() + + @property + @abstractmethod + def batch_size(self) -> int: + raise NotImplementedError() + + @abstractmethod + def set_batch_size(self, batch_size: int) -> None: + raise NotImplementedError() diff --git a/src/spectrumdevice/devices/digitiser/digitiser_star_hub.py b/src/spectrumdevice/devices/digitiser/digitiser_star_hub.py index 36d29fe..515ce39 100644 --- a/src/spectrumdevice/devices/digitiser/digitiser_star_hub.py +++ b/src/spectrumdevice/devices/digitiser/digitiser_star_hub.py @@ -96,7 +96,7 @@ def _get_waveforms(digitiser_card: SpectrumDigitiserCard) -> None: thread.join() waveform_sets_all_cards_ordered = [] - for n in range(self._batch_size): + for n in range(self.batch_size): waveforms_in_this_batch = [] for card in self._child_cards: waveforms_in_this_batch += card_ids_and_waveform_sets[str(card)][n] @@ -178,3 +178,14 @@ def set_acquisition_mode(self, mode: AcquisitionMode) -> None: mode (`AcquisitionMode`): The desired acquisition mode.""" for d in self._child_cards: cast(SpectrumDigitiserCard, d).set_acquisition_mode(mode) + + @property + def batch_size(self) -> int: + batch_sizes = [] + for d in self._child_cards: + batch_sizes.append(cast(SpectrumDigitiserCard, d).batch_size) + return check_settings_constant_across_devices(batch_sizes, __name__) + + def set_batch_size(self, batch_size: int) -> None: + for d in self._child_cards: + cast(SpectrumDigitiserCard, d).set_batch_size(batch_size) diff --git a/src/tests/configuration.py b/src/tests/configuration.py index 5e04584..07e1623 100644 --- a/src/tests/configuration.py +++ b/src/tests/configuration.py @@ -17,7 +17,7 @@ class SpectrumTestMode(Enum): # Set IP address of real spectrum device (for use if TestMode.REAL_HARDWARE is set above). Set to None to run tests on # a local (PCIe) card. -TEST_DEVICE_IP = "169.254.45.181" +TEST_DEVICE_IP = "169.254.13.35" # Set the device number to connect to for real hardware tests. If using a star hub (e.g. netbox), this should be the # STAR_HUB_MASTER_CARD_INDEX. If you only have a single, local (PCIe) card, set to 0. TEST_DEVICE_NUMBER = 1 diff --git a/src/tests/test_integration.py b/src/tests/test_integration.py index 0202ec8..0fd4bb1 100644 --- a/src/tests/test_integration.py +++ b/src/tests/test_integration.py @@ -5,7 +5,7 @@ import pytest from numpy import array, concatenate -from example_scripts.connect_to_star_hub import star_hub_example +from example_scripts.star_hub_example import connect_to_star_hub_example from example_scripts.continuous_averaging_fifo_mode import continuous_averaging_multi_fifo_example from example_scripts.continuous_multi_fifo_mode import continuous_multi_fifo_example from example_scripts.finite_multi_fifo_mode import finite_multi_fifo_example @@ -57,6 +57,7 @@ def test_finite_multi_fifo_mode(self) -> None: measurements = finite_multi_fifo_example( mock_mode=self._single_card_mock_mode, num_measurements=5, + batch_size=5, trigger_source=INTEGRATION_TEST_TRIGGER_SOURCE, device_number=TEST_DEVICE_NUMBER, ip_address=TEST_DEVICE_IP, @@ -68,11 +69,12 @@ def test_finite_multi_fifo_mode(self) -> None: def test_continuous_multi_fifo_mode(self) -> None: measurements = continuous_multi_fifo_example( mock_mode=self._single_card_mock_mode, - acquisition_duration_in_seconds=0.5, + time_to_keep_acquiring_for_in_seconds=0.5, + batch_size=1, trigger_source=INTEGRATION_TEST_TRIGGER_SOURCE, device_number=TEST_DEVICE_NUMBER, ip_address=TEST_DEVICE_IP, - acquisition_length=ACQUISITION_LENGTH, + single_acquisition_length_in_samples=ACQUISITION_LENGTH, ) self._asserts_for_fifo_mode(measurements) @@ -108,11 +110,11 @@ def setUp(self) -> None: self._star_hub_mock_mode = STAR_HUB_TEST_MODE == SpectrumTestMode.MOCK_HARDWARE def test_star_hub(self) -> None: - hub = star_hub_example( + hub = connect_to_star_hub_example( mock_mode=self._star_hub_mock_mode, num_cards=NUM_CARDS_IN_STAR_HUB, master_card_index=STAR_HUB_MASTER_CARD_INDEX, - ip_address="169.254.45.181", + ip_address=TEST_DEVICE_IP, ) self.assertEqual(len(hub.channels), NUM_CHANNELS_PER_MODULE * NUM_MODULES_PER_CARD * NUM_CARDS_IN_STAR_HUB) self.assertEqual(len(hub._child_cards), NUM_CARDS_IN_STAR_HUB)