└── naqs_devices_HeliCam/
├── .gitignore
├── pyproject.toml
├── README.md
├── LICENSE.txt
├── CITATION.cff
├── docs/
│ ├── conf.py
│ ├── make.bat
│ ├── Makefile
│ └── index.rst
└── src/naqs_devices/
└── HeliCam/
├── __init__.py
├── blacs_tabs.py
├── blacs_workers.py
├── labscript_devices.py
├── register_classes.py
└── runviewer_parsers.py # TODO?
The connection table below will initialize the camera's settings, but each can be changed per/in shot with say, a runmanager global in your experiment script.
from labscript import *
from naqs_devices.HeliCam.labscript_devices import HeliCam
from labscript_devices.PrawnBlaster.labscript_devices import PrawnBlaster
from labscript_devices.PrawnDO.labscript_devices import PrawnDO
prawn = PrawnBlaster(
name="prawn",
com_port="COM5",
pico_board="pico2",
num_pseudoclocks=1,
clock_frequency=150e6,
)
prawn_do = PrawnDO(
name="prawn_do",
com_port="COM3",
clock_line=prawn.clocklines[0],
external_clock=False,
)
settings = {
"SensTqp": 4095, # Demod freq = 2121 Hz
"SensNFrames": 16, # Number of frames per burst
"SensNavM2": 1, # Proportional to num demod cycles
"CamMode": 0, # RawIQ
"DdsGain": 2, # Gain of 1, level 2 of 3
"BSEnable": 0, # Background subtraction
"TrigFreeExtN": 0, # External acquisition timing (here from the PrawnDO)
'ExtTqp': 0, # Internal Tqp
'EnTrigOnPos': 0, # No translation stage
"TrigExtSrcSel": 0, # Default ext trigger source
"AcqStop": 0, # Ensure acquisition is running
"EnSynFOut": 1, # Provides signal at demod freq on OUT3
}
manual_settings = settings.copy()
manual_settings["TrigFreeExtN"] = 1 # Manual mode requires internal timing
camera = HeliCam(
name="helicam",
parent_device=prawn_do.outputs,
connection="do0",
serial_number="008650",
camera_attributes=settings,
manual_mode_camera_attributes=manual_settings,
trigger_duration=2000e-6,
)
if __name__ == "__main__":
start()
stop(1)In ExtTqp mode, the sampling time schedule is a square wave input on IN3 (the
encoder port on the HeliDriver). In this mode, the SensTqp register assigns the
integration duration of a single sample, where you must follow:
where SensTqp register is only required to
initialize the device, and will be updated during the shot.
from labscript import *
from naqs_devices.HeliCam.labscript_devices import HeliCam
from labscript_devices.PrawnBlaster.labscript_devices import PrawnBlaster
from labscript_devices.PrawnDO.labscript_devices import PrawnDO
prawn = PrawnBlaster(
name="prawn",
com_port="COM5",
pico_board="pico2",
num_pseudoclocks=1,
clock_frequency=150e6,
)
prawn_do = PrawnDO(
name="prawn_do",
com_port="COM3",
clock_line=prawn.clocklines[0],
external_clock=False,
)
settings = {
"SensTqp": 4095, # Demod freq = 2121 Hz
"SensNFrames": 16, # Number of frames per burst
"SensNavM2": 1, # Proportional to num demod cycles
"CamMode": 0, # RawIQ
"DdsGain": 2, # Gain of 1, level 2 of 3
"BSEnable": 0, # Background subtraction
"TrigFreeExtN": 0, # External acquisition timing (here from the PrawnDO)
'ExtTqp': 1, # External Tqp
'ExtTqpPuls': 1, # Enable PhiA and PhiB on Encoder
'EnTrigOnPos': 0, # No translation stage
"TrigExtSrcSel": 0, # Default ext trigger source
"AcqStop": 0, # Ensure acquisition is running
"EnSynFOut": 0, # Has to be off for ExtTqp mode
}
manual_settings = settings.copy()
manual_settings["TrigFreeExtN"] = 1 # Manual mode requires internal timing
camera = HeliCam(
name="helicam",
parent_device=prawn_do.outputs,
connection="do0",
serial_number="008650",
camera_attributes=settings,
manual_mode_camera_attributes=manual_settings,
trigger_duration=2000e-6,
)
if __name__ == "__main__":
start()
stop(1)In order to not have to manually find the SensTqp register to correspond to
your desired demodulation frequency, use a runmanager global and the
frequency_to_tqp method. The worker will then set the settings before the
shot runs.
# with the same contents as the above connection table(s), as per the labscript paradigm
if __name__ == "__main__":
# Capital variables are runmanager globals
start()
camera.camera_attributes["SensNFrames"] = N_FRAMES
camera.camera_attributes["SensNavM2"] = SENSNAVM2
# Only necessary to provide SensTqp when demodulation freq is internal
camera.camera_attributes["SensTqp"] = camera.frequency_to_tqp(DEMOD_FREQ)
print(f"RUNMANAGER says: {camera.camera_attributes}")
t = 0
for i in range(N_ACQUISITIONS):
t += DELTA_T
camera.expose(t, f"snap{i}")
add_time_marker(t, f"snap{i}", verbose=True)
t += 1
stop(t)# view_heli_images.py
import lyse
from pathlib import Path
import matplotlib.pyplot as plt
import numpy as np
df = lyse.data()
h5_path = df.filepath.iloc[-1]
run = lyse.Run(h5_path)
run_globals = run.get_globals()
N = run_globals.get("N_ACQUISITIONS", 1)
cols = int(np.ceil(np.sqrt(N)))
rows = int(np.ceil(N / cols))
fig, axes = plt.subplots(rows, cols, figsize=(15, 15))
axes = axes.flatten()
for i in range(N):
ims = run.get_image('helicam', f'snap{i}', 'frame')
axes[i].imshow(ims, cmap='gray')
axes[i].set_title(f'frame {i + 1}')
axes[i].axis('off')
for j in range(N, len(axes)):
axes[j].axis('off')
plt.tight_layout()This labscript device driver is made for the Heliotis HeliCam C3 Lock-In Camera. The important user controllable parameters in the GUI are:
| Parameter | Explanation | Range |
|---|---|---|
SensNFrames |
The number of frames in a fully integrated image, called a volume | (1, 511) |
SensNavM2 |
The number of demodulation cycles per frame SensNavM2*2 + 2 |
(0, 255) |
SensTqp |
The time quarter period of the sensor demodulation stage | (0, 4095) |
Note that the demodulation frequency
The BLACS tab provides snap and continuous acquisitions. The logs will report
many calls to the LibHeLIC's Acquire(), which returns either a negative value (usually -116)
or the size of the buffer allocated.
In testing, we have found that the C3's purported framerate upper limit of 3800 fps is not always achievable given settings that follow the stated specification of:
where SensNFrames,
SensNavM2, and SensTqp in an attempt to characterize the limits to both
framerate and acquisition rate. The method employs a binary search algorithm and
reports the fastest successful framerate and acquisition rate. Due to the nature
of the algorithm's convergence, one may possibly find faster parameters by increasing
either the tolerance or number of desired iterations.