This tutorial explains how to perform photon counting using an NI card in Python.
Photon counting is the process of counting the number of photons detected by a photon detector. NI cards provide hardware support for photon counting, which can be used with Python to easily perform photon counting for various applications, such as fluorescence lifetime imaging microscopy (FLIM).
In order to get started with photon counting using an NI card in Python, you will need:
Performing photon counting using an NI card involves creating a task, configuring the channels and timing, starting the task, and reading the acquired data. Here's an example script:
import numpy as np import os import time import PyDAQmx as daq def acquire_signal(channel_name, file_path, save_data=True, acquisition_time=1, laser_length=200e-6, resolution=0.5e-6): """ Acquires a signal from a DAQ device and saves the data to a file. Parameters: channel_name (str): The name of the DAQ channel to acquire data from. file_path (str): The path to the file where the data should be saved. save_data (bool): Whether to save the acquired data to a file. Defaults to True. acquisition_time (float): The duration of the acquisition in seconds. Defaults to 1. laser_length (float): The length of the laser pulse in seconds. Defaults to 200e-6. resolution (float): The resolution of the acquisition in seconds. Defaults to 0.5e-6. Returns: A tuple containing the acquired data and the time scale of the data. """ # Convert laser_length and resolution to number of samples laser_samples = int(laser_length / resolution) resolution_samples = int(resolution * 1e9) # convert to nanoseconds # Initialize variables start_time = time.time() print(f"Acquiring signal from {channel_name}...\n") data = {} flag = False flag2 = True split = 2 while not flag: # Split the acquisition time into smaller chunks if acquisition_time > split: acquisition_time_sub = split acquisition_time -= split else: acquisition_time_sub = acquisition_time flag = True # Initialize DAQ tasks counter1 = daq.Task() counter2 = daq.Task() clock = daq.Task() # Configure clock task to generate a pulse train clock_channel = '/Dev1/Ctr2' clock.CreateCOPulseChanFreq(clock_channel, "myClockTask", daq.DAQmx_Val_Hz, daq.DAQmx_Val_Low, 0, 1 / float(resolution), 0.5) # Set the clock task to start and stop with a digital pause trigger clock.SetPauseTrigType(daq.DAQmx_Val_DigLvl) clock.SetDigLvlPauseTrigSrc("PFI2") clock.SetDigLvlPauseTrigWhen(daq.DAQmx_Val_Low) # Configure counter tasks to measure laser and sync signals laser_channel = '/Dev1/Ctr0' sync_channel = '/Dev1/Ctr1' counter1.CreateCISemiPeriodChan(laser_channel, 'Laser', 0, 2, daq.DAQmx_Val_Ticks, '') counter2.CreateCISemiPeriodChan(sync_channel, 'Sync', 0, 2, daq.DAQmx_Val_Ticks, '') # Set the counter tasks to start and stop with a digital edge trigger counter1.SetArmStartTrigType(daq.DAQmx_Val_DigEdge) counter1.SetDigEdgeArmStartTrigSrc("PFI2") counter1.SetDigEdgeArmStartTrigEdge(daq.DAQmx_Val_Rising) counter2.SetArmStartTrigType(daq.DAQmx_Val_DigEdge) counter2.SetDigEdgeArmStartTrigSrc("PFI2") counter2.SetDigEdgeArmStartTrigEdge(daq.DAQmx_Val_Rising) # Set the counter tasks to use the clock task as the timebase counter1.SetCITimebaseSrc(laser_channel, clock_channel) counter2.SetCITimebaseSrc(sync_channel, clock_channel) # Configure counter tasks for continuous sampling buffer_size = 2 ** 25 counter1.CfgImplicitTiming(daq.DAQmx_Val_ContSamps, buffer_size) counter2.CfgImplicitTiming(daq.DAQmx_Val_ContSamps, buffer_size) # Start DAQ tasks try: clock.StartTask() counter1.StartTask() counter2.StartTask() except Exception as e: print('Exception occurred during task initialization') print(e) clock.StopTask() counter1.StopTask() counter2.StopTask() return None, None # Read laser and sync signals n_samples = int(acquisition_time_sub / resolution) laser_data = np.empty((1, 2 * n_samples), dtype=np.uint32) sync_data = np.empty((1, 2 * n_samples), dtype=np.uint32) n_read_samples = daq.int32() counter1.ReadCounterU32(2 * n_samples, 100, laser_data[0], 2 * n_samples, byref(n_read_samples), None) counter2.ReadCounterU32(2 * n_samples, 100, sync_data[0], 2 * n_samples, byref(n_read_samples), None) # Stop and clear DAQ tasks try: clock.StopTask() clock.ClearTask() counter1.StopTask() counter1.ClearTask() counter2.StopTask() counter2.ClearTask() except: pass # Extract laser pulses from laser signal sync_threshold = 0.5 sync_indices = np.argwhere(sync_data > sync_threshold) array_size = np.min(np.diff(np.transpose(sync_indices))) - 1 pulse_data = np.zeros(array_size) if flag2: main_data = np.zeros(array_size) flag2 = False for i in range(np.size(sync_indices) - 2): if i != int(np.size(sync_indices)) - 1: pulse_data += laser_data[0, int(sync_indices[i])+1:int(sync_indices[i])+array_size+1] main_data += pulse_data print("Signal of {} pulses acquired in {:.2f}s.\n".format(np.size(sync_indices), time.time()-start_time)) # Create dictionary of acquired data data = {} data[chan_name] = list(main_data) data['TimeScale'] = list(resolution * np.arange(np.size(main_data))) # Save data to file if save: os.makedirs(os.path.dirname(filepath), exist_ok=True) with open(filepath, 'a') as f: print(data, file=f) time.sleep(1) return data[chan_name], data['TimeScale']
Photon counting with an NI card is a powerful tool for measuring the intensity of light sources or performing fluorescence lifetime imaging microscopy. With Python, you can easily set up and control photon counting tasks. We hope this tutorial has been helpful in getting you started with photon counting using an NI card in Python.