From 10b31bd856985e7620b62924f800d15bb0756785 Mon Sep 17 00:00:00 2001 From: "stephan.roschinski" <stephan.roschinski@tuwien.ac.at> Date: Thu, 23 Feb 2023 10:40:36 +0100 Subject: [PATCH 01/22] added MCS8A box v1 --- FastComtecMCS8A/MCS8A_spcm.conf | 74 ++++++++++++++++++++++ FastComtecMCS8A/MCS8_test_measurement.SET | 65 ++++++++++++++++++++ FastComtecMCS8A/__init__.py | 0 FastComtecMCS8A/blacs_tabs.py | 17 +++++ FastComtecMCS8A/blacs_workers.py | 75 +++++++++++++++++++++++ FastComtecMCS8A/dummy.bat | 2 + FastComtecMCS8A/labscript_devices.py | 33 ++++++++++ FastComtecMCS8A/mcs8a_structures.py | 52 ++++++++++++++++ FastComtecMCS8A/register_classes.py | 16 +++++ FastComtecMCS8A/runviewer_parser.py | 0 FastComtecMCS8A/spcm_config_start.py | 36 +++++++++++ 11 files changed, 370 insertions(+) create mode 100644 FastComtecMCS8A/MCS8A_spcm.conf create mode 100644 FastComtecMCS8A/MCS8_test_measurement.SET create mode 100644 FastComtecMCS8A/__init__.py create mode 100644 FastComtecMCS8A/blacs_tabs.py create mode 100644 FastComtecMCS8A/blacs_workers.py create mode 100644 FastComtecMCS8A/dummy.bat create mode 100644 FastComtecMCS8A/labscript_devices.py create mode 100644 FastComtecMCS8A/mcs8a_structures.py create mode 100644 FastComtecMCS8A/register_classes.py create mode 100644 FastComtecMCS8A/runviewer_parser.py create mode 100644 FastComtecMCS8A/spcm_config_start.py diff --git a/FastComtecMCS8A/MCS8A_spcm.conf b/FastComtecMCS8A/MCS8A_spcm.conf new file mode 100644 index 0000000..e8b5809 --- /dev/null +++ b/FastComtecMCS8A/MCS8A_spcm.conf @@ -0,0 +1,74 @@ +[Settings] +data_dir = C:\\Users\\steph\\labscript-suite\\userlib\\user_devices\\FastComtecMCS8A\\data +dll_path = C:\\Windows\\System32\\DMCS8.dll +batch_file = C:\\Users\\steph\\labscript-suite\\userlib\\user_devices\\FastComtecMCS8A\\dummy.bat +configuration_file = C:\Users\steph\stephan\spcm_fastcomtec\MCS8_1.SET + +[MCS8A] +range=4096 +periods=2 +sweepmode=22fe2080 +fstchan=0 +holdafter=0 +streamstatus=3 +calreg0=0 +calreg1=0 +calreg2=0 +calreg3=0 +swpreset=20000000 +prena=4 +syncout=1 +cycles=18 +sequences=1 +tagbits=16 +vdac0=92c +vdac1=92c +vdac2=92c +vdac3=92c +vdac4=92c +vdac5=92c +vdac6=92c +vdac7=92c +timepreset=20.000 +digio=0 +digval=0 +autoinc=0 +savedata=0 +mpafmt=asc +sephead=1 +fmt=csv +smoothpts=5 +wndwidth=163 +wndheight=286 +sysdef=0 + +[CHN1] +range=4096 +active=1 +bitshift=0 +cftfak=2580100 +evpreset=10 +roimin=0 +roimax=4096 +caloff=0.000000 +calfact=0.400000 +calfact2=0 +calfact3=0 +calunit=nsec +caluse=1 + +[CHN2] +range=4096 +active=0 +bitshift=0 +cftfak=2580100 +evpreset=10 +roimin=0 +roimax=4096 +caloff=0.000000 +calfact=0.400000 +calfact2=0 +calfact3=0 +calunit=nsec +caluse=1 + diff --git a/FastComtecMCS8A/MCS8_test_measurement.SET b/FastComtecMCS8A/MCS8_test_measurement.SET new file mode 100644 index 0000000..9ee1a34 --- /dev/null +++ b/FastComtecMCS8A/MCS8_test_measurement.SET @@ -0,0 +1,65 @@ +[MCS8A A] 208 FW 3.104 SV 1.243 +range=4096 +periods=2 +sweepmode=22fe2080 +fstchan=0 +holdafter=0 +streamstatus=3 +calreg0=0 +calreg1=0 +calreg2=0 +calreg3=0 +swpreset=2000000 +prena=4 +syncout=1 +cycles=18 +sequences=1 +tagbits=16 +vdac0=92c +vdac1=92c +vdac2=92c +vdac3=92c +vdac4=92c +vdac5=92c +vdac6=92c +vdac7=92c +timepreset=20.000 +digio=0 +digval=0 +autoinc=0 +savedata=0 +mpafmt=csv +sephead=1 +fmt=csv +smoothpts=5 +wndwidth=163 +wndheight=286 +sysdef=0 +[CHN1] +range=4096 +active=1 +bitshift=0 +cftfak=2580100 +evpreset=10 +roimin=0 +roimax=4096 +caloff=0.000000 +calfact=0.400000 +calfact2=0 +calfact3=0 +calunit=nsec +caluse=1 +[CHN2] +range=4096 +active=0 +bitshift=0 +cftfak=2580100 +evpreset=10 +roimin=0 +roimax=4096 +caloff=0.000000 +calfact=0.400000 +calfact2=0 +calfact3=0 +calunit=nsec +caluse=1 diff --git a/FastComtecMCS8A/__init__.py b/FastComtecMCS8A/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/FastComtecMCS8A/blacs_tabs.py b/FastComtecMCS8A/blacs_tabs.py new file mode 100644 index 0000000..390ac54 --- /dev/null +++ b/FastComtecMCS8A/blacs_tabs.py @@ -0,0 +1,17 @@ +from blacs.device_base_class import DeviceTab + +class MCS8ATab(DeviceTab): + + def initialise_workers(self): + self.event_queue.logging_enabled =True + + self.create_worker( + 'main_worker', + 'user_devices.FastComtecMCS8A.blacs_workers.MCS8AWorker', + {}, + ) + self.primary_worker = 'main_worker' + + def initialise_GUI(self): + widgets,_,_ = self.auto_create_widgets() + \ No newline at end of file diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py new file mode 100644 index 0000000..d0a43f3 --- /dev/null +++ b/FastComtecMCS8A/blacs_workers.py @@ -0,0 +1,75 @@ +##################################################################### +# # +# /user_devices/FastComtecMCS8A/blacs_workers.py # +# # +# Jan 2023, Stephan Roschinski # +# # +# # +##################################################################### +from ctypes import * +from . import spcm_config_start +import subprocess +import time +from . import mcs8a_structures +from blacs.tab_base_classes import Worker + +class MCS8AWorker(Worker): + + def init(self, h5_file): + self.config = spcm_config_start.setup + self.batch_file = self.config.get_config_key('Settings', 'batch_file') + self.process = subprocess.Popen(self.batch_file) + self.h5file = h5_file # run batch file + # Do the rest + self.dll_path = self.config.get_config_key('Settings', 'dll_path') + self.dll=WinDLL(self.dll_path) #put DLL path into the conf file + print('init completed') + self.h5file = h5_file + pass + + def transition_to_buffered(self, device_name, h5_file, initial_values, fresh): + self.h5file = h5_file + return {} + + def transition_to_manual(self): + + + print('tranistion to manual') + + return {} + + def program_manual(self, values): + return {} + + def check_remote_values(self): + return True + + def abort_buffered(self): + return self.transition_to_manual() + + def abort_transition_to_buffered(self): + return True + + + + #these methods need to be implemented differently + def get_status(self): + status = ACQSTATUS() + self.dll.GetStatusData(byref(status),0) # Get status stored in the DLL + return status + + def set_settings(self): + conf_file = self.config.get_config_key('Settings', 'configuration_file') + print(conf_file) + self.dll.RunCmd(0,c_char_p(bytes('loadcnf ' + conf_file, 'utf-8'))) + + def start_measurement(self): + self.dll.RunCmd(0,c_char_p(bytes('start', 'utf-8'))) + + def stop_measurement(self): + self.dll.RunCmd(0,c_char_p(bytes('halt', 'utf-8'))) + + def save_data(self, filename): + data_dir = self.config.get_config_key('Settings', 'data_dir') + self.dll.RunCmd(0,c_char_p(bytes('mpaname=' + data_dir+filename, 'utf-8'))) + self.dll.RunCmd(0,c_char_p(bytes('savempa', 'utf-8'))) \ No newline at end of file diff --git a/FastComtecMCS8A/dummy.bat b/FastComtecMCS8A/dummy.bat new file mode 100644 index 0000000..52d9c16 --- /dev/null +++ b/FastComtecMCS8A/dummy.bat @@ -0,0 +1,2 @@ +cd C:\mcs8x64 +mcs8.exe diff --git a/FastComtecMCS8A/labscript_devices.py b/FastComtecMCS8A/labscript_devices.py new file mode 100644 index 0000000..0fd3603 --- /dev/null +++ b/FastComtecMCS8A/labscript_devices.py @@ -0,0 +1,33 @@ +##################################################################### +# # +# /user_devices/FastComtecMCS8A/labscript_devices.py # +# # +# Jan 2023, Stephan Roschinski # +# # +# # +##################################################################### +from labscript import Device, TriggerableDevice, config +import subprocess +from ctypes import * +import numpy as np + +class MCS8A(TriggerableDevice): + description = 'MCS8A' + + def __init__(self, name, parent_device, connection, batch_file, set_file, dll_path, data_dir, **kwargs): + super().__init__(name, parent_device, connection, **kwargs) + self.BLACS_connection = "SPCM" + self.batch_file = batch_file + self.set_file = set_file + self.dll_path = dll_path + self.data_dir = data_dir + + def generate_code(self, hdf5_file): + group = hdf5_file.create_group(f"devices/{self.name}") + dtypes = [("batch_file",'|S100'), ("SET_file", '|S100'), ("DLL_path", '|S100'), ("data_dir", '|S100')] + settings = np.rec.fromarrays([self.batch_file, self.set_file, self.dll_path, self.data_dir], dtype=dtypes) + group.create_dataset("Settings", data=settings) + return + + + diff --git a/FastComtecMCS8A/mcs8a_structures.py b/FastComtecMCS8A/mcs8a_structures.py new file mode 100644 index 0000000..1744076 --- /dev/null +++ b/FastComtecMCS8A/mcs8a_structures.py @@ -0,0 +1,52 @@ +from ctypes import * + +class ACQSTATUS(Structure): + _fields_ = [("started", c_ulong), #aquisition status + ("maxval", c_ulong), #maxval + ("cnt", c_double*8)] #status: runtime in msec, ofls, + #total sum, roi sum, roi rate,sweeps, + #starts + +class ACQSETTING(Structure): + _fields_ = [("range", c_long), + ("cftfak", c_long), #LOWORD: 256*cft factor(t_after_peak / t_to_peak); HIWORD:max pulse width for CFT + ("roimin", c_long), #lower ROI limit + ("roimax", c_long), #upper limit: roimin<=channel<roimax + ("nregions", c_long), #number of regions + ("caluse", c_long), #bit0: 1 if calibration used, higher bits: formula + ("calpoints", c_long),#number of calibration points + ("param", c_long), #(reserved:) for MAP and POS:LOWORD=x, HIWORD=y + ("offset", c_long), #(reserved:) zoomed MAPS: LOWORD: xoffset, HIWORD, yoffset + ("xdim", c_long), #(reserved:) x resolution of maps + ("bitshift", c_ulong),#LOWORD:Binwidth=2^(bitshift); HIWORD: Threshold for Coinc + ("active", c_long), + ("roipreset", c_double),#ROI preset value + ("dummy1", c_double), + ("dummy2", c_double), + ("dummy3", c_double)] + +class BOARDSETTING(Structure): # This is a structure type describing special MCS6 hardware settings + _fields_ = [("sweepmode", c_long), + ("prena", c_long), + ("cycles", c_long), + ("sequences", c_long), + ("syncout", c_long), + ("digio", c_long), + ("digval", c_long), + ("dac0", c_long),#DAC0 value (START) + ("dac1", c_long),#DAC1 value (STOP 1) + ("dac2", c_long),#DAC2 value (STOP 2) + ("dac3", c_long),#DAC3 value (STOP 3) + ("dac4", c_long),#DAC4 value (STOP 4) + ("dac5", c_long),#DAC5 value (STOP 5) + ("fdac", c_int), + ("tagbits", c_int), + ("extclk", c_int),#use external clock + ("maxchan", c_long), + ("serno", c_long), + ("ddruse", c_long), + ("active", c_long),#module in system + ("holdafter", c_double), + ("swpreset", c_double),#sweep preset value + ("fstchan", c_double),#acquisition delay + ("timepreset", c_double)]#time preset diff --git a/FastComtecMCS8A/register_classes.py b/FastComtecMCS8A/register_classes.py new file mode 100644 index 0000000..fb9fb73 --- /dev/null +++ b/FastComtecMCS8A/register_classes.py @@ -0,0 +1,16 @@ +##################################################################### +# # +# /user_devices/DCAMCamera/register_classes.py # +# # +# Jan 2023, Marvin Holten # +# # +# # +##################################################################### + +from labscript_devices import register_classes + +register_classes( + 'MCS8A', + BLACS_tab='user_devices.FastComtecMCS8A.blacs_tabs.MCS8ATab', + runviewer_parser=None, +) diff --git a/FastComtecMCS8A/runviewer_parser.py b/FastComtecMCS8A/runviewer_parser.py new file mode 100644 index 0000000..e69de29 diff --git a/FastComtecMCS8A/spcm_config_start.py b/FastComtecMCS8A/spcm_config_start.py new file mode 100644 index 0000000..479dead --- /dev/null +++ b/FastComtecMCS8A/spcm_config_start.py @@ -0,0 +1,36 @@ +from configobj import ConfigObj + +class config(object): + def __init__(self): + self.config = ConfigObj("C:\\Users\\steph\\labscript-suite\\userlib\\user_devices\\FastComtecMCS8A\\MCS8A_spcm.conf") + self.nr_channels = int(self.get_config_key('CHN1','active'))+int(self.get_config_key('CHN2','active')) + self.card='' + + def reload(self): + self.config.reload() + + def write(self): + self.config.write() + + def set_number_channels(self, arg): + self.nr_channels=arg + + def get_number_channels(self): + return self.nr_channels + + def set_card(self,arg): + self.card=arg + + def get_card(self): + return self.card + + def set_config_key(self, section, key, value): + self.config[section][key]=value + + def get_config_key(self, section, key): + return self.config[section][key] + +# def get_matrix_status(self, section, status): +# return self.config[section][status] + +setup = config() -- GitLab From 083c2b2004bd5b3d3227a772c108c7c9cb322f52 Mon Sep 17 00:00:00 2001 From: johannesschabbauer <johannes.schabbauer@tuwien.ac.at> Date: Thu, 23 Feb 2023 12:20:08 +0100 Subject: [PATCH 02/22] Remove unnecessary argument from init() --- FastComtecMCS8A/blacs_workers.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index d0a43f3..8eadbc6 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -15,17 +15,14 @@ from blacs.tab_base_classes import Worker class MCS8AWorker(Worker): - def init(self, h5_file): + def init(self): self.config = spcm_config_start.setup self.batch_file = self.config.get_config_key('Settings', 'batch_file') - self.process = subprocess.Popen(self.batch_file) - self.h5file = h5_file # run batch file - # Do the rest + self.process = subprocess.Popen(self.batch_file) # run batch file + # Do the rest self.dll_path = self.config.get_config_key('Settings', 'dll_path') self.dll=WinDLL(self.dll_path) #put DLL path into the conf file print('init completed') - self.h5file = h5_file - pass def transition_to_buffered(self, device_name, h5_file, initial_values, fresh): self.h5file = h5_file -- GitLab From d425d6f2b16287efe63776a628f758c5876a4437 Mon Sep 17 00:00:00 2001 From: "stephan.roschinski" <stephan.roschinski@tuwien.ac.at> Date: Thu, 23 Feb 2023 10:40:36 +0100 Subject: [PATCH 03/22] added MCS8A box v1 --- FastComtecMCS8A/blacs_workers.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index 8eadbc6..d0a43f3 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -15,14 +15,17 @@ from blacs.tab_base_classes import Worker class MCS8AWorker(Worker): - def init(self): + def init(self, h5_file): self.config = spcm_config_start.setup self.batch_file = self.config.get_config_key('Settings', 'batch_file') - self.process = subprocess.Popen(self.batch_file) # run batch file - # Do the rest + self.process = subprocess.Popen(self.batch_file) + self.h5file = h5_file # run batch file + # Do the rest self.dll_path = self.config.get_config_key('Settings', 'dll_path') self.dll=WinDLL(self.dll_path) #put DLL path into the conf file print('init completed') + self.h5file = h5_file + pass def transition_to_buffered(self, device_name, h5_file, initial_values, fresh): self.h5file = h5_file -- GitLab From 2d0233d3151004cd1b750a428f23a9561403022e Mon Sep 17 00:00:00 2001 From: johannesschabbauer <johannes.schabbauer@tuwien.ac.at> Date: Thu, 23 Feb 2023 12:20:08 +0100 Subject: [PATCH 04/22] Remove unnecessary argument from init() --- FastComtecMCS8A/blacs_workers.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index d0a43f3..8eadbc6 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -15,17 +15,14 @@ from blacs.tab_base_classes import Worker class MCS8AWorker(Worker): - def init(self, h5_file): + def init(self): self.config = spcm_config_start.setup self.batch_file = self.config.get_config_key('Settings', 'batch_file') - self.process = subprocess.Popen(self.batch_file) - self.h5file = h5_file # run batch file - # Do the rest + self.process = subprocess.Popen(self.batch_file) # run batch file + # Do the rest self.dll_path = self.config.get_config_key('Settings', 'dll_path') self.dll=WinDLL(self.dll_path) #put DLL path into the conf file print('init completed') - self.h5file = h5_file - pass def transition_to_buffered(self, device_name, h5_file, initial_values, fresh): self.h5file = h5_file -- GitLab From 6bddc11df317ddcbdb43ecf7979f787c63ab93c8 Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Wed, 22 Jan 2025 17:13:28 +0100 Subject: [PATCH 05/22] API from Benjamin and first try to get in working in labscript --- ...asurement.SET => MCS8A_default_config.SET} | 131 ++++----- FastComtecMCS8A/blacs_tabs.py | 8 +- FastComtecMCS8A/blacs_workers.py | 96 ++++--- .../docs/FastComTec_Doc_From_Manual.txt | 204 ++++++++++++++ .../docs/Fastcomtec_parameter_notes.md | 138 ++++++++++ FastComtecMCS8A/dummy.bat | 2 - FastComtecMCS8A/fastcomtec_api.py | 160 +++++++++++ FastComtecMCS8A/fastcomtec_types.py | 259 ++++++++++++++++++ FastComtecMCS8A/labscript_devices.py | 38 ++- FastComtecMCS8A/spcm_config_start.py | 36 --- .../testing/fastcomtec_plotting.py | 32 +++ FastComtecMCS8A/testing/spcm_tt_histogram.py | 130 +++++++++ FastComtecMCS8A/testing/test.py | 119 ++++++++ FastComtecMCS8A/testing/tryout.py | 21 ++ 14 files changed, 1216 insertions(+), 158 deletions(-) rename FastComtecMCS8A/{MCS8_test_measurement.SET => MCS8A_default_config.SET} (55%) create mode 100644 FastComtecMCS8A/docs/FastComTec_Doc_From_Manual.txt create mode 100644 FastComtecMCS8A/docs/Fastcomtec_parameter_notes.md delete mode 100644 FastComtecMCS8A/dummy.bat create mode 100644 FastComtecMCS8A/fastcomtec_api.py create mode 100644 FastComtecMCS8A/fastcomtec_types.py delete mode 100644 FastComtecMCS8A/spcm_config_start.py create mode 100644 FastComtecMCS8A/testing/fastcomtec_plotting.py create mode 100644 FastComtecMCS8A/testing/spcm_tt_histogram.py create mode 100644 FastComtecMCS8A/testing/test.py create mode 100644 FastComtecMCS8A/testing/tryout.py diff --git a/FastComtecMCS8A/MCS8_test_measurement.SET b/FastComtecMCS8A/MCS8A_default_config.SET similarity index 55% rename from FastComtecMCS8A/MCS8_test_measurement.SET rename to FastComtecMCS8A/MCS8A_default_config.SET index 9ee1a34..d8615c9 100644 --- a/FastComtecMCS8A/MCS8_test_measurement.SET +++ b/FastComtecMCS8A/MCS8A_default_config.SET @@ -1,65 +1,66 @@ -[MCS8A A] 208 FW 3.104 SV 1.243 -range=4096 -periods=2 -sweepmode=22fe2080 -fstchan=0 -holdafter=0 -streamstatus=3 -calreg0=0 -calreg1=0 -calreg2=0 -calreg3=0 -swpreset=2000000 -prena=4 -syncout=1 -cycles=18 -sequences=1 -tagbits=16 -vdac0=92c -vdac1=92c -vdac2=92c -vdac3=92c -vdac4=92c -vdac5=92c -vdac6=92c -vdac7=92c -timepreset=20.000 -digio=0 -digval=0 -autoinc=0 -savedata=0 -mpafmt=csv -sephead=1 -fmt=csv -smoothpts=5 -wndwidth=163 -wndheight=286 -sysdef=0 -[CHN1] -range=4096 -active=1 -bitshift=0 -cftfak=2580100 -evpreset=10 -roimin=0 -roimax=4096 -caloff=0.000000 -calfact=0.400000 -calfact2=0 -calfact3=0 -calunit=nsec -caluse=1 -[CHN2] -range=4096 -active=0 -bitshift=0 -cftfak=2580100 -evpreset=10 -roimin=0 -roimax=4096 -caloff=0.000000 -calfact=0.400000 -calfact2=0 -calfact3=0 -calunit=nsec -caluse=1 +[MCS8A A] 208 FW 3.104 SV 1.243 +range=29824 +periods=2 +sweepmode=22fd2088 +fstchan=0 +holdafter=0 +streamstatus=3 +calreg0=0 +calreg1=0 +calreg2=0 +calreg3=0 +swpreset=1 +prena=6 +syncout=0 +cycles=2 +sequences=1 +tagbits=16 +vdac0=1079c +vdac1=479c +vdac2=43b4 +vdac3=3b4 +vdac4=3b4 +vdac5=3b4 +vdac6=3b4 +vdac7=3b4 +timepreset=0.000 +digio=0 +digval=0 +autoinc=0 +savedata=1 +mpafmt=asc +sephead=1 +fmt=asc +smoothpts=5 +wndwidth=163 +wndheight=286 +sysdef=0 +showstarts=0 +[CHN1] +range=29824 +active=0 +bitshift=13 +cftfak=2580100 +evpreset=10 +roimin=0 +roimax=29824 +caloff=0.000000 +calfact=209715.200000 +calfact2=0 +calfact3=0 +calunit=nsec +caluse=1 +[CHN2] +range=29824 +active=1 +bitshift=13 +cftfak=2580100 +evpreset=10 +roimin=0 +roimax=64 +caloff=0.000000 +calfact=209715.200000 +calfact2=0 +calfact3=0 +calunit=nsec +caluse=1 diff --git a/FastComtecMCS8A/blacs_tabs.py b/FastComtecMCS8A/blacs_tabs.py index 390ac54..dabeded 100644 --- a/FastComtecMCS8A/blacs_tabs.py +++ b/FastComtecMCS8A/blacs_tabs.py @@ -4,14 +4,20 @@ class MCS8ATab(DeviceTab): def initialise_workers(self): self.event_queue.logging_enabled =True + + connection_table = self.settings['connection_table'] + props = connection_table.find_by_name(self.device_name).properties self.create_worker( 'main_worker', 'user_devices.FastComtecMCS8A.blacs_workers.MCS8AWorker', - {}, + { + "config_path": props["config_path"] + }, ) self.primary_worker = 'main_worker' def initialise_GUI(self): widgets,_,_ = self.auto_create_widgets() + self.supports_smart_programming(True) \ No newline at end of file diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index 8eadbc6..5eb4c2e 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -2,38 +2,78 @@ # # # /user_devices/FastComtecMCS8A/blacs_workers.py # # # -# Jan 2023, Stephan Roschinski # +# Jan 2025, Leolab Cavity # # # # # ##################################################################### -from ctypes import * -from . import spcm_config_start -import subprocess -import time -from . import mcs8a_structures + +from . import fastcomtec_api from blacs.tab_base_classes import Worker +from labscript import config +import os +import labscript_utils.h5_lock +import h5py +import numpy as np class MCS8AWorker(Worker): def init(self): - self.config = spcm_config_start.setup - self.batch_file = self.config.get_config_key('Settings', 'batch_file') - self.process = subprocess.Popen(self.batch_file) # run batch file - # Do the rest - self.dll_path = self.config.get_config_key('Settings', 'dll_path') - self.dll=WinDLL(self.dll_path) #put DLL path into the conf file + self.data_path_tmp = "C:\\mcs8x64\\MCS8A_measurement.tmp.mpa" + self.device = fastcomtec_api.FastComTec() + print("Connected...") + print("Status: ", self.device.get_acq_status()) + + # load (default) config + if not os.path.realpath(self.config_path) == self.config_path.replace("/","\\"): + self.config_path = os.path.dirname(os.path.realpath(__file__)) + "\\" + self.config_path + + self.device.load_config(self.config_path) + + self.smart_cache = {} + print('init completed') def transition_to_buffered(self, device_name, h5_file, initial_values, fresh): - self.h5file = h5_file + self.h5file = h5_file # save for 'transition_to_manual' + + with h5py.File(h5_file,'r') as f: + command_group = f[f"devices/{self.device_name}/commands"] + for command in command_group.attrs: + if (command in self.smart_cache and command_group.attrs[command]==self.smart_cache[command]) and not fresh: + continue + self.smart_cache[command] = command_group.attrs[command] + if command_group.attrs[command] is not None: + command = f"{command}={command_group.attrs[command]}" + self.device._send_dll_command(command) + print(f"Programmed command '{command}'") + + try: + os.remove(self.data_path_tmp) + except FileNotFoundError: + pass + self.device.erase_measruement() + self.device.start_measurement() return {} def transition_to_manual(self): + # self.device.halt_measurement() + + # load data and write to file + # .... TODO - + with h5py.File(self.h5file, 'r+') as f: + group = f.require_group(f"data/{self.device_name}/") + + # save photon counts + data = np.random.randint(0,1,size=1000) # testing + group.create_dataset("photon_counts", compression = config.compression, data=data) + + # save parameters + # for param in parameters: + group.attrs["test_param"] = 1000 print('tranistion to manual') - return {} + return True def program_manual(self, values): return {} @@ -45,28 +85,4 @@ class MCS8AWorker(Worker): return self.transition_to_manual() def abort_transition_to_buffered(self): - return True - - - - #these methods need to be implemented differently - def get_status(self): - status = ACQSTATUS() - self.dll.GetStatusData(byref(status),0) # Get status stored in the DLL - return status - - def set_settings(self): - conf_file = self.config.get_config_key('Settings', 'configuration_file') - print(conf_file) - self.dll.RunCmd(0,c_char_p(bytes('loadcnf ' + conf_file, 'utf-8'))) - - def start_measurement(self): - self.dll.RunCmd(0,c_char_p(bytes('start', 'utf-8'))) - - def stop_measurement(self): - self.dll.RunCmd(0,c_char_p(bytes('halt', 'utf-8'))) - - def save_data(self, filename): - data_dir = self.config.get_config_key('Settings', 'data_dir') - self.dll.RunCmd(0,c_char_p(bytes('mpaname=' + data_dir+filename, 'utf-8'))) - self.dll.RunCmd(0,c_char_p(bytes('savempa', 'utf-8'))) \ No newline at end of file + return True \ No newline at end of file diff --git a/FastComtecMCS8A/docs/FastComTec_Doc_From_Manual.txt b/FastComtecMCS8A/docs/FastComTec_Doc_From_Manual.txt new file mode 100644 index 0000000..afc8b50 --- /dev/null +++ b/FastComtecMCS8A/docs/FastComTec_Doc_From_Manual.txt @@ -0,0 +1,204 @@ +[MCS8A] 1004 ; Device and serial number +range=4096 ; Spectrum length +periods=2 ; Number of periods for folding +fstchan=0 ; acquisition delay = number of first bin / 64 +holdafter=0 ; hold after sweep in units of 64 basic dwelltimes +sweepmode=227ea080 ; (hex) sweepmode & 0xF: 0 = normal, + ; 1=differential (relative to first stop in sweep) + ; 4=sequential + ; 5=seq.+diff (Ch1), bit0 = differential mode + ; 9=differential to stop in Ch2, bit3 = Ch2 ref (diff.mode) + ; 0xF = Corr.+diff (Ch2) + ; bit 4: Softw. Start + ; bit 5: "Don't show" tagbits + ; bit 6: Endless + ; bit 7: Start event generation + ; bit 8: Enable Tag bits + ; bit 9: start with rising edge + ; bit 10: time under threshold for pulse width + ; bit 11: pulse width mode for any spectra with both edges enabled + ; bit 12: abandon Sweepcounter in Data + ; bit 13: "one-hot" mode with tagbits + ; bit 14: start ref (diff.mode) (MCS8) + ; bit 15: MCS8, MCS8A1: enable start input sampling + ; bit 16..bit 22 ~(input channel enable) + ; MCS8A: bit 16..bit 23 ~(input channel enable) + ; bit 26: 100 ps time resolution instead of 80 ps + ; bit 27: Folded + ; bit 28: Interleaved + ; bit 29: MCS8A: don't use trigger, use CH1 to start sweep + ; bit 30: Use only 1 decoder +calreg0=fe00 ; result of input delay calibration, l + ; lower 8 bits: start channel (always 0), + ; higher 8 bits, first channel after start +calreg1=0 ; second, third channel +calreg2=0 ; .. +calreg3=0 ; .. +swpreset=1000000 ; Sweep-Preset value +prena=4 ; bit 0: realtime preset enabled + ; bit 1: + ; bit 2: sweep preset enabled + ; bit 3: ROI preset enabled + ; bit 4: Starts preset enabled + ; bit 5: ROI2 preset enabled + ; bit 6: ROI3 preset enabled + ; bit 7: ROI4 preset enabled + ; bit 8: ROI5 preset enabled + ; bit 9: ROI6 preset enabled + ; bit 10: ROI7 preset enabled + ; bit 11: ROI8 preset enabled +syncout=0 ; LOWORD: sync out; bit 0..5 NIM syncout, bit 8..13 TTL syncout + ; bit7: NIM syncout_invert, bit15: TTL syncout_invert + ; 0="0", 1=5 MHz, 2=50 MHz, 3=100 MHz, 4=1/(80 ps or 100 ps * 64), + ; 5=1/(80 ps or 100 ps * 32), 6=PLL ref clock, 7=Input 0(Start), 8=Input 1, + ; 9=Input 2, 10..13=Input 3..7, 15=GO in, 16=Start_of_sweep, 17=Armed, + ; 18=SWEEP_ON, 19=WINDOW, 20=HOLD_OFF, 21=EOS_DEADTIME + ; 22=TIME[0],...,51=TIME[29], 52...63=SWEEP[0]..SWEEP[11] +cycles=18 ; cycles for sequential mode +sequences=1 ; for sequential mode (default 1) + ; specifies how often to repeat + ; after performing cycles acquisitions +tagbits=16 ; number of tagbits +vdac0=8fa ; (hex) dac0 value (start) voltage=2.048 * (1-vdac/2048) + ; bit 16: start with rising edge + ; (bit 14, 15) : 0=falling, 1=rising, 2=both, 3=both+CFT +vdac1=8fa ; dac1 value +vdac2=8fa ; dac2 value +vdac3=8fa ; ... +vdac4=8fa ; ... +vdac5=8fa ; ... +vdac6=8fa ; ... +vdac7=8fa ; …dac7 value +dac0v=-0.2 ; dac0 value = -0.2 V (start) +dac1v=-0.2 ; dac1 value = -0.2 V (STOP1) +.. +dac7v=-0.2 ; dac7 value in Volt +dacv=-0.1 ; set dac1..dac7 to -0.1 V, all dacs without dac0 (start) +dacv+=0.001 ; increase all dac values without dac0 by 1 mV +dac0v+=0.001 ; increase dac0 value by 1 mV (start) +rtpreset=20.000 ; Timepreset (seconds) +digio=0 ; LOWORD: Use of Dig I/O, GO Line: + ; bit 0: status dig 0..3 + ; bit 1: Output digval and increment digval after stop + ; bit 2: Invert polarity + ; bit 3: Push-Pull output, not possible + ; bit 4: Start with Input Dig 4 + ; bit 5: Start with Input GO + ; bit 8: GOWATCH + ; bit 9: GO High at Start + ; bit 10: GO Low at Stop + ; bit 11: Clear at triggered start + ; bit 12: Only triggered start +digval=0 ; digval=0..255 value for samplechanger +autoinc=0 ; 1=Enable auto increment of filename +savedata=0 ; 0=No Save at Halt + ; 1=Save at Halt + ; 2=Write list file, no save at Halt + ; 3=Write lit file, Save at Halt +mpafmt=asc ; data fromat used in MPA files + ; (dat=binary, asc=ASCII, csv=CSV) +sephead=0 ; 1=Seperated Header file (extension .MP) and + ; Data file (extension dat, asc or spe) for seperated spectra +fmt=asc ; data format used in seperated spectra (extension .MP) + ; (dat=binary, asc=ASCII, csv=CSV) +smoothpts=5 ; number of points to average for a smooth operation +wndwidth=155 ; width of server window +wndheight=273 ; height of server window +sysdef=0 ; System definition word: + ; bit0=0, bit1=0: dev#0 in system 1 + ; bit0=1, bit1=0: dev#0 in system 2 + ; bit0=0, bit1=1: dev#0 in system 3 + ; bit0=1, bit1=1: dev#0 in system 4 + ; ... + ; bit6=1, bit7=1: dev#3 in system 4 + ; bit 31: any preset stops all + +[CHN1] ; the following section concerns parameters of CHN1 +range=4096 ; Spectrum length +active=1 ; Spectrum definition words for CHN1..8: + ; active & 0xF ==0 not used + ; ==1..4 enabled, system 1..4 + ; bit 8: Enable Tag bits + ; bit 9: start with rising edge + ; bit 10: time under threshold for pulse width + ; bit 11: pulse width mode for any spectra with both edges enabled + ; Spectrum definition words for calc. Spectra: + ; active & 0xF ==3 MAP, ((x-xoffs)>>xsh) x ((y-yoffs)>>ysh) + ; bit4=1: x zoomed MAP + ; bit5=1: y zoomed MAP + ; ==5 SUM, (x + y)>>xsh + ; ==6 DIFF,(x - y + range)>>xsh + ; ==7 ANY, (for compare) + ; ==8 COPY, x + ; ==10 SW-HIS, Sweep History + ; bit 8..11 xsh, bit 12..15 ysh or bit 8..15 xsh + ; HIWORD(active) = condition no. (0=no condition) +bitshift=0 ; LOWORD: Binwidth = 2 ^ (bitshift) + ; HIWORD: Threshold for Coinc +cftfak=2580100 ; LOWORD: 256 * cft factor (t_after_peak / t_to_peak) + ; HIWORD: max pulse width for CFT +evpreset=10 ; ROI preset value +roimin=0 ; lower ROI limit +roimax=4096 ; upper limit: roimin <= channel < roimax +caloff=0.000000 ; calibration parameter: offset +calfact=0.080000 ; calibration parameter: factor +calfact2=0 +calfact3=0 +calunit=nsec ; calibration unit +caluse=1 ; bit 0=1: use calibration, higher bits: calibration formula +roi=0 9986 ;starts ; rectangular ROI from 0 to 9986 with name “starts†(lower left, upper right corner) + +[CHN2] ; the following section concerns parameters of CHN2 +... + +The following commands perform actions and therefore usually are not included in the MCS8A.SET file: + +start ; Clears the data and starts a new acquisition of system 1. + ; Further execution of the .CTL file is suspended until any + ; acquisition stops due to a preset. +halt ; Stops acquisition of system 1 if one is running. +cont ; Continues acquisition of system 1. If a time preset + ; is already reached, the time preset is prolongated + ; by the value which was valid when the “start“ command + ; was executed. Further execution of the .CTL file + ; is suspended (see start). +erase ; Clears all spectra of system 1. +savecnf ; Writes the settings into MCS8A.SET +mpaname=filename ; Defines the mpa filename +savempa ; Saves all configuration and spectra data. + ; An existing file is overwritten. +pushname ; pushes the actual mpa filename on an internal stack that can hold 4 names +popname ; pops the last mpa filename from the internal stack +loadmpa ; Loads mpa data; the filename + ; must be specified before with a command mpaname=... +addmpa ; Adds mpa data to actual spectra; the filename + ; must be specified before with a command mpaname=... +submpa ; Subtracts mpa data from actual spectra; the filename + ; must be specified before with a command mpaname=... +MC_A ; Sets actual input channel to MC_A (STOP1) for the rest of + ; the control file. +MC_B ; Sets actual multichannel analyzer to MC_B (STOP2) + ; … MC_H (ch8) +savedat ; Saves data of actual channel as separated + ; spectrum (extension .MP) An existing file + ; is overwritten. +loaddat ; Loads data of actual channel, the filename + ; must be specified before with a command datname=... +adddat ; Adds data into actual spectra; the filename + ; must be specified before with a command datname=... +subdat ; Subtracts data from actual spectra channel; the filename + ; must be specified before with a command datname=... +smooth ; Smoothes the data in actual spectra +erasedat ; Clears the data of actual spectra. +exit ; Exits the server (and MPANT) programs +alert Message ; Displays a Messagebox containing Message and an OK + ; button that must be pressed before execution can continue. +waitinfo 5000 Message ; Displays a Messagebox containing Message, an OK + ; and an END button. After the specified time (5000 msec) + ; the Messagebox vanishes and execution continues. OK + ; continues immediately, END escapes execution. +beep * ; Makes a beep. The character '*' may be replaced with '?', '!' or + ; left empty. The corresponding sound is defined in the WIN.INI + ; file in the [sounds] section. +delay 4000 ; Waits specified time (4000 msec = 4 sec). +pulse 100 ; Outputs a pulse of 100 ms duration at dig 3 \ No newline at end of file diff --git a/FastComtecMCS8A/docs/Fastcomtec_parameter_notes.md b/FastComtecMCS8A/docs/Fastcomtec_parameter_notes.md new file mode 100644 index 0000000..691701c --- /dev/null +++ b/FastComtecMCS8A/docs/Fastcomtec_parameter_notes.md @@ -0,0 +1,138 @@ +# Basic Modes of Operation + +## 1. Stop after sweep mode (p.35f) +When the MCS8A is armed it waits for a START input signal. When one occurs the sweep is started / +triggered, meaning the time starts to count. Now the arrival times of the STOP input signals relative to the +start are acquired on all channels that are enabled. A STOP event can be either a falling or a rising edge or +both. Even further signals into the START input can be acquired. Since the type of edge is detected and +marked in the acquired data even Time-over-Threshold or pulse width measurements can be accomplished. + +The data acquisition can be ended after a given time via the Time preset. In the edit field Range the length +of the spectrum can be entered. A Bin width of 1 means the highest time resolution. The Bin width can be +chosen in powers of 2 up to 1677216 times the elementary dwell time. If an Acq. Delay is specified, data are +acquired in a sweep not before the specified time. Hold after sweep allows to wait a specified time after a +sweep before the next sweep can be started. + +### 1.1. Start of measurement +#### 1.1.1. Endless mode (p.44) +mode of the measurement can be endless if the corresponding checkbox is crossed (in settings) +#### 1.1.2. Software start (p.44) +Doesn't require a start signal (checkbox in settings dialogue) + +### 1.2. Termination of measurement +terminated by any of the preset (2) conditions +#### 1.2.1. Time preset (total acquisition time) - THIS +in steps of 2.56 ns - total measurement time (in the document: "sweep and so the data acquisition ends after that time" but with sweep preset you can get multiple sweeps. So this is more like a total measurement time) +#### 1.2.2. Sweep preset (number of sweeps)- THIS ++ 48-bit sweep counter counts the real start of a new sweep rather than the completion of the sweeps ++ preset enabled: after preselected number of sweeps any further start of a sweep is prohibited +#### 1.2.3. ROI preset (number of counts) - THIS +Stop measurement after certain number of counts. ROI Preset +### 1.3. Single sweep settings +#### 1.3.1. Acquisiton delay (counting delay after START) - THIS +in steps of 2.56 ns - accept only STOP events (= signals) that arrive after the selected delay +#### 1.3.2. Length of a single sweep - THIS +The Sweep length is defined by the Range times the Bin Width. +#### 1.3.3. Hold off time (pause time after sweep)- THIS +in steps of 2.56 ns - allows new start only after this additional time has elapsed after the end of the sweep +#### 1.3.4. End of sweep dead-time - THIS +< 50ns +#### 1.3.5. Selection range lmits - THIS +Acq.Delay + TimeRange + Hold-Off ≤ 248 x 2.56 ns = 8.3 days max. + + + +## 2. Tagged spectra acquisiton (p.36) +multi detector experiment: measure which detector has fired (time resolution 5.12ns), still maintain 80 ps bin width + + + +# Measurement Examples +## 3. Pulse width measurement (p.45) +As the edge information is contained in bit 4 of the data (a 1 in that bit means falling edge) it is possible to +distinguish stop events from rising and falling signals and it is possible to analyze the pulse width of the +signals, if Both Edges are chosen. There is a choice between "Over threshold" or "Under threshold" for +the Pulse width analyzing, depending on the corresponding setting in the "inputs" dialog. To see the events +from rising and falling signals separated, just enable Pulse width in the settings dialog and set y-Range to 2 +inside the box labeled â€2D spectraâ€. You will get a two dimensional spectra with a y-dimension of 2, for y=0 it +contains the stop events from rising edges and for y=1 from falling edges. You can see here the separation +time between both edges. If you set then the y-Range to a value larger than the maximum pulse width in +channels, you will get a 2-dimensional spectra with the time of the first edge as x coordinate and the pulse +width as y coordinate (see chapter 2.5.4). (There is more text in the document ...) + +# Input Settings + +## 4. ROI Preset (p.44) +ROI Preset checkboxes marked: measurement stopped after acquiring more events than specified (events are counted only if they are within the ROI limits, i.e. >= the lower limit and < the upper limit) + +## 5. Treshold voltage (p.44) - THIS ++ Fast NIM (-0.3 V) or customized ++ Falling or Rising Edge ++ See "Measurement examples" or document p.45 for pulse width measurement + +# Electrical ratings (p.99) + +## 6. START / STOP (discriminator) inputs ++ Voltage range -2.0 … +3.0 V ++ Threshold voltage -1.0 … +1.5 V +### 6.1. Absolute maximum ratings ++ DC input current ± 80 mA ++ any discriminator input -3.0 … +4.0 V + +# Notes +## 7. Dark count histogram +### 7.1. Parameter Correspondance +#### 7.1.1. Number of repetions - Sweep preset +#### 7.1.2. Expectation value $\lambda$ - controlle by Time range +Range * bin width + +# How to set the settings +## Active channels ++ active = 1 ++ and dont forget sweepmode ! +## 8. Start event generation (???) +If start event generation is checked, a start event is inserted into the data stream and counted. + +## 9. After sweep +### 9.1. Save ++ savedata=1 +### 9.2. Auto increment data files ++ autoinc=1 + +## 10. Time Settings +### 10.1. Binning (binwidth) ++ bitshift=0 ; LOWORD: Binwidth = 2 ^ (bitshift) ++ calfact=0.400000 * Binwidth (a setting only required to propperly show the histogram in software) +### 10.2. Range (per channel) ++ range=4096 (in steps of 64) +### 10.3. Hold off time ++ holdafter=6.4 (in of 6.4 ns) +### 10.4. Acquisition delay ++ fstchan = 1 ; acquisition delay = fstchan * 6.4ns ++ caloff=6.4 * fstchan ; MPNAT software calibration for shwoing histogram +## 11. Presets +### 11.1. Prena (is HEX!) ++ prena=4 ++ ; bit 0: realtime preset enabled + ; bit 1: + ; bit 2: sweep preset enabled + ; bit 3: ROI preset enabled + ; bit 4: Starts preset enabled + ; bit 5: ROI2 preset enabled + ; bit 6: ROI3 preset enabled + ; bit 7: ROI4 preset enabled + ; bit 8: ROI5 preset enabled + ; bit 9: ROI6 preset enabled + ; bit 10: ROI7 preset enabled + ; bit 11: ROI8 preset enabled + +Examples ++ prena = 3 : time preset ++ prena = 6 : sweep preset ++ prena = 7 : sweep & time preset ++ prena = 2 : nothing +### 11.2. Time preset ++ WRONG: rtpreset=20.000; Timepreset (seconds) [from doc] ++ RIHGT: timepreset = 20.000; (seconds) [observed] +### 11.3. Sweep preset ++ swpreset=20000000 \ No newline at end of file diff --git a/FastComtecMCS8A/dummy.bat b/FastComtecMCS8A/dummy.bat deleted file mode 100644 index 52d9c16..0000000 --- a/FastComtecMCS8A/dummy.bat +++ /dev/null @@ -1,2 +0,0 @@ -cd C:\mcs8x64 -mcs8.exe diff --git a/FastComtecMCS8A/fastcomtec_api.py b/FastComtecMCS8A/fastcomtec_api.py new file mode 100644 index 0000000..2567ebf --- /dev/null +++ b/FastComtecMCS8A/fastcomtec_api.py @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- + +import ctypes +from . import fastcomtec_types +import re +from typing import NamedTuple, List + +class FastComTec(): + """ Hardware Class for the FastComtec MCS8. """ + + def __init__(self, dll_path = r'C:\Windows\System32\DMCS8.dll', **kwargs) -> None: + self.dll = ctypes.windll.LoadLibrary(dll_path) + ######################################## + # Fastcomtec Types + ######################################## + def get_acq_status(self) -> fastcomtec_types.AcqStatus: + status = fastcomtec_types.AcqStatus() + self.dll.GetStatusData(ctypes.byref(status), 0) + return status + + def get_acq_settings(self) -> fastcomtec_types.AcqSettings: + settings = fastcomtec_types.AcqSettings() + self.dll.GetSettingData(ctypes.byref(settings), 0) + return settings + + def get_board_settings(self) -> fastcomtec_types.BoardSetting: + boardsettings = fastcomtec_types.BoardSetting() + self.dll.GetMCSSetting(ctypes.byref(boardsettings), 0) + return boardsettings + + def get_acq_data(self) -> fastcomtec_types.AcqData: + return NotImplementedError # code below doesn't give data for some reason + data = fastcomtec_types.AcqData() + self.dll.GetData(ctypes.byref(data), 0) + return data + ######################################## + # DLL Interaction + ######################################## + def _send_dll_command(self, command: str) -> None: + self.dll.RunCmd(0, bytes(command, ' utf-8')) + + def _send_dll_commands(self, commands: List[str]) -> None: + for cmd in commands: + self._send_dll_command(cmd) + ######################################## + # Measurement + ######################################## + def start_measurement(self) -> None: + self._send_dll_command("start") + + def halt_measurement(self) -> None: + self._send_dll_command("halt") + + def erase_measruement(self) -> None: + self._send_dll_command("erase") + ######################################## + # Saving and Loading Data and Config + ######################################## + def load_config(self, file_path: str) -> None: + """Doesn't have to be a .SET file, can also be a .mpa file.""" + self._send_dll_command(f"loadcnf {file_path}") + + def save_config(self) -> None: + """Config stored in C:/mcs8x64/MCS8A.SET""" + self._send_dll_command("savecnf") + + def save_config_and_data_seperately(self, file_path: str, ) -> None: + """ + NOTE: + + The Config gets saved twice, but both time with CHN1 as a header . + + TOTALSUM, ROISUM, MAXVAL for Channel 2 might be totally wrong + """ + self._send_dll_command(f"datname={file_path}_CHN1") + self._send_dll_command("MC_A") + self._send_dll_command("savedat") + self._send_dll_command(f"datname={file_path}_CHN2") + self._send_dll_command("MC_B") + self._send_dll_command("savedat") + + def save_config_with_data(self, file_path: str) -> None: + self._send_dll_command(f"mpaname={file_path}") + self._send_dll_command("savempa") + ######################################## + # Change Individual Settings + ######################################## + def set_time_preset(self, time_preset_sec: float) -> None: + self._send_dll_command(f"timepreset={time_preset_sec}") + def set_sweep_preset(self, swpreset: int) -> None: + self._send_dll_command(f"timepreset={swpreset}") + + +######################################## +# Separate config and data +######################################## +def separate_config_and_data(file_path: str) -> None: + path_split_up = file_path.split(".") + path_split_up[-2] += "_config" + config_path = ".".join(path_split_up) + + path_split_up = file_path.split(".") + path_split_up[-2] += "_data" + data_path = ".".join(path_split_up) + + regex = re.compile("(TDAT)") + match = False + with open(file_path, "r") as f: + lines = f.readlines() + write_data = False + with open(config_path, "w+") as config_file: + with open(data_path, "w+") as data_file: + for line in lines: + match = regex.search(line) + if match: + write_data = True + if write_data: + data_file.write(line) + else: + config_file.write(line) + +class BoundaryInfo(NamedTuple): + boundary_id: str + line_number: int + +def separate_config_and_data_per_channel(file_path: str) -> None: + path_split_up = file_path.split(".") + path_split_up[-2] += "_config" + config_path = ".".join(path_split_up) + + # read in file + with open(file_path, "r") as f: + lines = f.readlines() + + boundaries: List[BoundaryInfo] = [] + + # find boundaries of data sections + regex_channel_data = re.compile("(TDAT)") + match = False + for i, line in enumerate(lines): + match = regex_channel_data.search(line) + if match: + boundaries.append(BoundaryInfo(line.split(",")[0][1:], i)) # exclue "[" in data_id + + # write config file + with open(config_path, "w+") as config_file: + config_file.write("".join(lines[:boundaries[0].line_number])) + + # write data files + for i in range(len(boundaries)): + path_split_up = file_path.split(".") + path_split_up[-2] += f"_data_{boundaries[i].boundary_id}" + data_path = ".".join(path_split_up) + with open(data_path, "w+") as data_file: + if (i+1) < len(boundaries): + data_file.write("".join(lines[boundaries[i].line_number:boundaries[i+1].line_number])) + else: + data_file.write("".join(lines[boundaries[i].line_number:])) + + + + \ No newline at end of file diff --git a/FastComtecMCS8A/fastcomtec_types.py b/FastComtecMCS8A/fastcomtec_types.py new file mode 100644 index 0000000..150bfef --- /dev/null +++ b/FastComtecMCS8A/fastcomtec_types.py @@ -0,0 +1,259 @@ +import ctypes # general doc about Structures: https://docs.python.org/3/library/ctypes.html#structures-and-unions + +""" +Remark to the usage of ctypes: +All Python types except integers (int), strings (str), and bytes (byte) objects +have to be wrapped in their corresponding ctypes type, so that they can be +converted to the required C data type. + +ctypes type C type Python type +---------------------------------------------------------------- +c_bool _Bool bool (1) +c_char char 1-character bytes object +c_wchar wchar_t 1-character string +c_byte char int +c_ubyte unsigned char int +c_short short int +c_ushort unsigned short int +c_int int int +c_uint unsigned int int +c_long long int +c_ulong unsigned long int +c_longlong __int64 or + long long int +c_ulonglong unsigned __int64 or + unsigned long long int +c_size_t size_t int +c_ssize_t ssize_t or + Py_ssize_t int +c_float float float +c_double double float +c_longdouble long double float +c_char_p char * + (NUL terminated) bytes object or None +c_wchar_p wchar_t * + (NUL terminated) string or None +c_void_p void * int or None + +""" +# Reconstruct the proper structure of the variables, which can be extracted +# from the header file 'struct.h'. + + +class AcqStatus(ctypes.Structure): + """ Create a structured Data type with ctypes where the dll can write into. + + This object handles and retrieves the acquisition status data from the + Fastcomtec. + + int started; // acquisition status: 1 if running, 0 else + double runtime; // running time in seconds + double totalsum; // total events + double roisum; // events within ROI + double roirate; // acquired ROI-events per second + double nettosum; // ROI sum with background subtracted + double sweeps; // Number of sweeps + double stevents; // Start Events + unsigned long maxval; // Maximum value in spectrum + """ + def __init__(self): + super().__init__() + self.started : ctypes.c_int + self.runtime : ctypes.c_double + self.totalsum : ctypes.c_double + self.roisum : ctypes.c_double + self.roirate : ctypes.c_double + self.ofls : ctypes.c_double + self.sweeps : ctypes.c_double + self.stevents : ctypes.c_double + self.maxval : ctypes.c_ulong + + _fields_ = [('started', ctypes.c_int), + ('runtime', ctypes.c_double), + ('totalsum', ctypes.c_double), + ('roisum', ctypes.c_double), + ('roirate', ctypes.c_double), + ('ofls', ctypes.c_double), + ('sweeps', ctypes.c_double), + ('stevents', ctypes.c_double), + ('maxval', ctypes.c_ulong), ] + + def __repr__(self) -> str: + return 'started : ' + str(self.started ) + '\n' \ + 'runtime : ' + str(self.runtime ) + '\n' \ + 'totalsum: ' + str(self.totalsum) + '\n' \ + 'roisum : ' + str(self.roisum ) + '\n' \ + 'roirate : ' + str(self.roirate ) + '\n' \ + 'ofls : ' + str(self.ofls ) + '\n' \ + 'sweeps : ' + str(self.sweeps ) + '\n' \ + 'stevents: ' + str(self.stevents) + '\n' \ + 'maxval : ' + str(self.maxval ) + +class AcqData(ctypes.Structure): + """ Create a structured Data type with ctypes where the dll can write into. + + This object handles and retrieves the acquisition data of the Fastcomtec. + """ + def __init__(self): + super().__init__() + self.s0 : ctypes._Pointer + self.region : ctypes._Pointer + self.comment: ctypes.c_char_p + self.cnt : ctypes._Pointer + self.hs0 : ctypes.c_int + self.hrg : ctypes.c_int + self.hcm : ctypes.c_int + self.hct : ctypes.c_int + + _fields_ = [('s0', ctypes.POINTER(ctypes.c_ulong)), + ('region', ctypes.POINTER(ctypes.c_ulong)), + ('comment', ctypes.c_char_p), + ('cnt', ctypes.POINTER(ctypes.c_double)), + ('hs0', ctypes.c_int), + ('hrg', ctypes.c_int), + ('hcm', ctypes.c_int), + ('hct', ctypes.c_int), ] + + def __repr__(self) -> str: + return 's0 : ' + str(self.s0 ) + '\n' \ + 'region : ' + str(self.region ) + '\n' \ + 'comment: ' + str(self.comment) + '\n' \ + 'cnt : ' + str(self.cnt ) + '\n' \ + 'hs0 : ' + str(self.hs0 ) + '\n' \ + 'hrg : ' + str(self.hrg ) + '\n' \ + 'hcm : ' + str(self.hcm ) + '\n' \ + 'hct : ' + str(self.hct ) + +class AcqSettings(ctypes.Structure): + def __init__(self): + super().__init__() + self.range : ctypes.c_long + self.cftfak : ctypes.c_long + self.roimin : ctypes.c_long + self.roimax : ctypes.c_long + self.nregions : ctypes.c_long + self.caluse : ctypes.c_long + self.calpoints : ctypes.c_long + self.param : ctypes.c_long + self.offset : ctypes.c_long + self.xdim : ctypes.c_long + self.bitshift : ctypes.c_ulong + self.active : ctypes.c_long + self.eventpreset : ctypes.c_double + self.dummy1 : ctypes.c_double + self.dummy2 : ctypes.c_double + self.dummy3 : ctypes.c_double + + _fields_ = [('range', ctypes.c_long), + ('cftfak', ctypes.c_long), + ('roimin', ctypes.c_long), + ('roimax', ctypes.c_long), + ('nregions', ctypes.c_long), + ('caluse', ctypes.c_long), + ('calpoints', ctypes.c_long), + ('param', ctypes.c_long), + ('offset', ctypes.c_long), + ('xdim', ctypes.c_long), + ('bitshift', ctypes.c_ulong), + ('active', ctypes.c_long), + ('eventpreset', ctypes.c_double), + ('dummy1', ctypes.c_double), + ('dummy2', ctypes.c_double), + ('dummy3', ctypes.c_double), ] + + def __repr__(self) -> str: + return 'range : ' + str(self.range ) + '\n' \ + 'cftfak : ' + str(self.cftfak ) + '\n' \ + 'roimin : ' + str(self.roimin ) + '\n' \ + 'roimax : ' + str(self.roimax ) + '\n' \ + 'nregions : ' + str(self.nregions ) + '\n' \ + 'caluse : ' + str(self.caluse ) + '\n' \ + 'calpoints : ' + str(self.calpoints ) + '\n' \ + 'param : ' + str(self.param ) + '\n' \ + 'offset : ' + str(self.offset ) + '\n' \ + 'xdim : ' + str(self.xdim ) + '\n' \ + 'bitshift : ' + str(self.bitshift ) + '\n' \ + 'active : ' + str(self.active ) + '\n' \ + 'eventpreset: ' + str(self.eventpreset) + '\n' \ + 'dummy1 : ' + str(self.dummy1 ) + '\n' \ + 'dummy2 : ' + str(self.dummy2 ) + '\n' \ + 'dummy3 : ' + str(self.dummy3 ) + +class BoardSetting(ctypes.Structure): + def __init__(self): + super().__init__() + self.sweepmode : ctypes.c_long + self.prena : ctypes.c_long + self.cycles : ctypes.c_long + self.sequences : ctypes.c_long + self.syncout : ctypes.c_long + self.digio : ctypes.c_long + self.digval : ctypes.c_long + self.dac0 : ctypes.c_long + self.dac1 : ctypes.c_long + self.dac2 : ctypes.c_long + self.dac3 : ctypes.c_long + self.dac4 : ctypes.c_long + self.dac5 : ctypes.c_long + self.fdac : ctypes.c_int + self.tagbits : ctypes.c_int + self.extclk : ctypes.c_int + self.maxchan : ctypes.c_long + self.serno : ctypes.c_long + self.ddruse : ctypes.c_long + self.active : ctypes.c_long + self.holdafter : ctypes.c_double + self.swpreset : ctypes.c_double + self.fstchan : ctypes.c_double + self.timepreset: ctypes.c_double + + _fields_ = [('sweepmode', ctypes.c_long), + ('prena', ctypes.c_long), + ('cycles', ctypes.c_long), + ('sequences', ctypes.c_long), + ('syncout', ctypes.c_long), + ('digio', ctypes.c_long), + ('digval', ctypes.c_long), + ('dac0', ctypes.c_long), + ('dac1', ctypes.c_long), + ('dac2', ctypes.c_long), + ('dac3', ctypes.c_long), + ('dac4', ctypes.c_long), + ('dac5', ctypes.c_long), + ('fdac', ctypes.c_int), + ('tagbits', ctypes.c_int), + ('extclk', ctypes.c_int), + ('maxchan', ctypes.c_long), + ('serno', ctypes.c_long), + ('ddruse', ctypes.c_long), + ('active', ctypes.c_long), + ('holdafter', ctypes.c_double), + ('swpreset', ctypes.c_double), + ('fstchan', ctypes.c_double), + ('timepreset', ctypes.c_double),] + + def __repr__(self) -> str: + return 'sweepmode : ' + str(self.sweepmode ) + '\n' \ + 'prena : ' + str(self.prena ) + '\n' \ + 'cycles : ' + str(self.cycles ) + '\n' \ + 'sequences : ' + str(self.sequences ) + '\n' \ + 'syncout : ' + str(self.syncout ) + '\n' \ + 'digio : ' + str(self.digio ) + '\n' \ + 'digval : ' + str(self.digval ) + '\n' \ + 'dac0 : ' + str(self.dac0 ) + '\n' \ + 'dac1 : ' + str(self.dac1 ) + '\n' \ + 'dac2 : ' + str(self.dac2 ) + '\n' \ + 'dac3 : ' + str(self.dac3 ) + '\n' \ + 'dac4 : ' + str(self.dac4 ) + '\n' \ + 'dac5 : ' + str(self.dac5 ) + '\n' \ + 'fdac : ' + str(self.fdac ) + '\n' \ + 'tagbits : ' + str(self.tagbits ) + '\n' \ + 'extclk : ' + str(self.extclk ) + '\n' \ + 'maxchan : ' + str(self.maxchan ) + '\n' \ + 'serno : ' + str(self.serno ) + '\n' \ + 'ddruse : ' + str(self.ddruse ) + '\n' \ + 'active : ' + str(self.active ) + '\n' \ + 'holdafter : ' + str(self.holdafter ) + '\n' \ + 'swpreset : ' + str(self.swpreset ) + '\n' \ + 'fstchan : ' + str(self.fstchan ) + '\n' \ + 'timepreset: ' + str(self.timepreset) diff --git a/FastComtecMCS8A/labscript_devices.py b/FastComtecMCS8A/labscript_devices.py index 0fd3603..e6aad99 100644 --- a/FastComtecMCS8A/labscript_devices.py +++ b/FastComtecMCS8A/labscript_devices.py @@ -6,28 +6,38 @@ # # # # ##################################################################### -from labscript import Device, TriggerableDevice, config -import subprocess -from ctypes import * -import numpy as np +from labscript import TriggerableDevice, LabscriptError, set_passed_properties class MCS8A(TriggerableDevice): description = 'MCS8A' - def __init__(self, name, parent_device, connection, batch_file, set_file, dll_path, data_dir, **kwargs): + @set_passed_properties( + property_names={"connection_table_properties":["config_path"]} + ) + def __init__(self, name, parent_device, connection, config_path="MCS8A_default_config.SET", **kwargs): super().__init__(name, parent_device, connection, **kwargs) - self.BLACS_connection = "SPCM" - self.batch_file = batch_file - self.set_file = set_file - self.dll_path = dll_path - self.data_dir = data_dir + self.BLACS_connection = "SPCM MCS8A" + self.config_path = config_path + self.commands = {} + + def do_checks(self): + if len(self.trigger_device.triggerings) > 1: + raise NotImplementedError("Only 1 acquistuin allowed, working on code!") + + def set_dll_command(self, command, value = None): + if command in self.commands: + raise LabscriptError("Do not set the same config command twice!") + self.commands[command] = value def generate_code(self, hdf5_file): group = hdf5_file.create_group(f"devices/{self.name}") - dtypes = [("batch_file",'|S100'), ("SET_file", '|S100'), ("DLL_path", '|S100'), ("data_dir", '|S100')] - settings = np.rec.fromarrays([self.batch_file, self.set_file, self.dll_path, self.data_dir], dtype=dtypes) - group.create_dataset("Settings", data=settings) - return + commands_group = group.create_group("commands") + for command,value in self.commands.items(): + commands_group.attrs[command] = value + + self.do_checks() + + super().generate_code(hdf5_file) diff --git a/FastComtecMCS8A/spcm_config_start.py b/FastComtecMCS8A/spcm_config_start.py deleted file mode 100644 index 479dead..0000000 --- a/FastComtecMCS8A/spcm_config_start.py +++ /dev/null @@ -1,36 +0,0 @@ -from configobj import ConfigObj - -class config(object): - def __init__(self): - self.config = ConfigObj("C:\\Users\\steph\\labscript-suite\\userlib\\user_devices\\FastComtecMCS8A\\MCS8A_spcm.conf") - self.nr_channels = int(self.get_config_key('CHN1','active'))+int(self.get_config_key('CHN2','active')) - self.card='' - - def reload(self): - self.config.reload() - - def write(self): - self.config.write() - - def set_number_channels(self, arg): - self.nr_channels=arg - - def get_number_channels(self): - return self.nr_channels - - def set_card(self,arg): - self.card=arg - - def get_card(self): - return self.card - - def set_config_key(self, section, key, value): - self.config[section][key]=value - - def get_config_key(self, section, key): - return self.config[section][key] - -# def get_matrix_status(self, section, status): -# return self.config[section][status] - -setup = config() diff --git a/FastComtecMCS8A/testing/fastcomtec_plotting.py b/FastComtecMCS8A/testing/fastcomtec_plotting.py new file mode 100644 index 0000000..98d2dfe --- /dev/null +++ b/FastComtecMCS8A/testing/fastcomtec_plotting.py @@ -0,0 +1,32 @@ +import matplotlib.pyplot as plt +import numpy as np + +def plot_raw_histogram(config_path: str, data_path: str) -> None: + data = np.genfromtxt(data_path, dtype=np.int64, skip_header=1, delimiter='\n') + print(data) + plt.plot(data) + plt.xlabel("bin") + plt.ylabel("count") + plt.show() + +def plot_raw_histograms(config_path: str, data_path_1: str, data_path_2: str) -> None: + fig, axs = plt.subplots(2, 1) + data_1 = np.genfromtxt(data_path_1, dtype=np.int64, skip_header=1, delimiter='\n') + data_2 = np.genfromtxt(data_path_2, dtype=np.int64, skip_header=1, delimiter='\n') + x_values = (76.8/0.4 + np.arange(0, len(data_1))) * 0.4 + axs[0].plot(x_values, data_1, color = "blue") + axs[0].set_xlabel("$\Delta t$ (ns)") + axs[0].set_ylabel("count") + axs[0].set_xlim(76.8, (4096 + 76.8/0.4)*0.4) + axs[0].set_xticks(np.arange(100, 1800, 100), [str(n) for n in np.arange(100, 1800, 100)]) + axs[0].set_title("CHN1, 10 MHz fast NIM") + axs[1].plot(data_2, color = "skyblue") + axs[1].set_xlabel("bin") + axs[1].set_ylabel("count") + axs[1].set_xlim(0, 4096) + axs[1].set_title("CHN2, 10 MHz $V_{pp} = 1 V$") + print("Sum CHN1", data_1.sum()) + print("Max CHN1", data_1.max()) + print("Sum CHN2", data_2.sum()) + print("Max CHN2", data_2.max()) + plt.show() \ No newline at end of file diff --git a/FastComtecMCS8A/testing/spcm_tt_histogram.py b/FastComtecMCS8A/testing/spcm_tt_histogram.py new file mode 100644 index 0000000..e424daa --- /dev/null +++ b/FastComtecMCS8A/testing/spcm_tt_histogram.py @@ -0,0 +1,130 @@ +import matplotlib.pyplot as plt +import numpy as np +import fastcomtec_api + +def printkw(**keywordarguments) -> None: + for kw in keywordarguments: + print(kw, f"{keywordarguments[kw]:e}") + +def print_kw_enum(**keywordarguments) -> None: + for kw in keywordarguments: + print(kw) + for i, el in enumerate(keywordarguments[kw]): + print(f"{i:>2}:", el) + print("----------------------------") + +def tt_calculations(): + bin_widths = 2**np.arange(0, 25) + fundamental_time_resolution = 400*10**(-12) + time_resolutions = fundamental_time_resolution * bin_widths + bins_per_second = 1 / time_resolutions + + print_kw_enum(bin_widths = bin_widths, time_res = time_resolutions, bins_per_s = bins_per_second) + + bin_selection = 24 + total_measurement_duration_sec = 300 + + printkw(bins = bins_per_second[bin_selection] * total_measurement_duration_sec) + + # print_kw(total_time = 29824*time_resolutions[22]) + +def calculate_dark_count_histogram(file: str, name: str, total_time_sec: int, num_measurements: int): + + data = np.loadtxt(file, dtype=np.int32, skiprows=1) + # print(data.shape) + data_50s_split = np.array_split(data, num_measurements) + counts_per_measurement = [] + for ar in data_50s_split: + # print(ar.shape) + counts_per_measurement.append(ar.sum()) + bins=np.arange(np.array(counts_per_measurement).max() + 1) + plt.hist(counts_per_measurement, bins, density=True, edgecolor='black', linewidth=1.2) + plt.xticks(bins+0.5, [str(b) for b in bins]) + plt.title(f"SPCM {name} Histogram\n{num_measurements} $\cdot$ {total_time_sec/num_measurements}s measurements") + plt.ylabel("P(n)") + plt.xlabel("n") + plt.show() + +def calculate_test_diode_histogram(file: str, name: str, total_time_sec: int, num_measurements: int): + print("Loading data ...") + data = np.loadtxt(file, dtype=np.int32, skiprows=1) + # print(data.shape) + data_50s_split = np.array_split(data, num_measurements) + counts_per_measurement = [] + print("Evaluating counts ...") + for ar in data_50s_split: + # print(ar.shape) + counts_per_measurement.append(ar.sum()) + min_count = np.array(counts_per_measurement).min() + max_count = np.array(counts_per_measurement).max() + 1 + # bins=np.arange(max_count) + bins = np.linspace(min_count, max_count + 1, num=25) + bin_width = (max_count - min_count)/25 + print("Plotting histogram ...") + plt.hist(counts_per_measurement, bins, edgecolor='black', linewidth=1.2) + plt.xticks(bins+bin_width * 0.5, [f"{int(b):.5e}" for b in bins], rotation=90) + plt.title(f"SPCM {name} Histogram\n{num_measurements} $\cdot$ {total_time_sec/num_measurements}s measurements") + plt.ylabel("P(n)") + plt.xlabel("n") + print("Done") + plt.show() + print("Exiting") + +def calculate_darkcount_histogram_tripple(): + data_100_0 = np.loadtxt(r"C:\Users\admin\benjamin_code\FastComTec_self\measurements\100s_darkcount_data_TDAT1.mpa", dtype=np.int32, skiprows=1) + print(data_100_0.shape) + data_100_1 = np.loadtxt(r"C:\Users\admin\benjamin_code\FastComTec_self\measurements\100s-1_darkcount_data_TDAT1.mpa", dtype=np.int32, skiprows=1) + print(data_100_1.shape) + data_100_2 = np.loadtxt(r"C:\Users\admin\benjamin_code\FastComTec_self\measurements\100s-2_darkcount_data_TDAT1.mpa", dtype=np.int32, skiprows=1) + print(data_100_2.shape) + data_combined = np.hstack((data_100_0, data_100_1, data_100_2)) + seconds = 300 + data_split = np.array_split(data_combined, seconds) + + counts_per_sec = [] + for ar in data_split: + # print(ar.shape) + counts_per_sec.append(ar.sum()) + # print(len(data_split)) + bins=np.arange(25) + plt.hist(counts_per_sec, bins, density=True) + plt.xticks(bins, [str(b) for b in bins]) + plt.title(f"SPCM Dark Count Histogram ({seconds}x1s measurements)") + plt.ylabel("P(n)") + plt.xlabel("n") + plt.tight_layout() + plt.show() + +def light_calculations(): + P0 = 2.5 * 10** (-3) + wavelen = 780 * 10**(-9) + h = 6 * 10**(-34) + c = 3 * 10**8 + photon_energy = c/wavelen * h + num_photons = P0 / photon_energy + attenuation_dB = -5 + printkw(photon_energy = photon_energy) + printkw(num_photons = num_photons) + printkw(attenuated = num_photons * 10**attenuation_dB) + + +plt.rcParams.update({'figure.autolayout': True}) + +### Split file +# fastcomtec_api.separate_config_and_data_per_channel(r"C:\Users\admin\benjamin_code\FastComTec_self\measurements\Thorlabs Laser Diode\50s-2_5OD.mpa") + +### Dark counts +# calculate_dark_count_histogram(r"C:\Users\admin\benjamin_code\FastComTec_self\measurements\Dark counts\50s_darkcount_data_TDAT1.mpa" , "Dark Count", 50, 50) +# calculate_dark_count_histogram(r"C:\Users\admin\benjamin_code\FastComTec_self\measurements\Dark counts\100s_darkcount_data_TDAT1.mpa", "Dark Count", 100, 100) +# calculate_darkcount_histogram_tripple() + +### Thorlabs Diode +light_calculations() +# calculate_test_diode_histogram(r"C:\Users\admin\benjamin_code\FastComTec_self\measurements\Thorlabs Laser Diode\50s_5OD_data_TDAT1.mpa", "780nm Diode Counts", 50, 50) + + + +### Calculations +# tt_calculations() +# light_calculations() + diff --git a/FastComtecMCS8A/testing/test.py b/FastComtecMCS8A/testing/test.py new file mode 100644 index 0000000..acb6fca --- /dev/null +++ b/FastComtecMCS8A/testing/test.py @@ -0,0 +1,119 @@ +import fastcomtec_api +import time +import fastcomtec_plotting + + +######################################################## +### Basics +######################################################## +def load_dll(): + print("--- Load Dll ---") + device = fastcomtec_api.FastComTec() + print(device.get_acq_status()) + +def start_halt_erase(): + print("--- Start Halt Erase ---") + device = fastcomtec_api.FastComTec() + device.erase_measruement() + device.start_measurement() + print(device.get_acq_status()) + print() + time.sleep(5) + device.halt_measurement() + print(device.get_acq_status()) + print() + +def start_halt_wo_erase(): + print("--- Start Halt wo Erase ---") + device = fastcomtec_api.FastComTec() + print(device.get_acq_status()) + print() + device.start_measurement() + time.sleep(5) + device.halt_measurement() + print(device.get_acq_status()) + print() + +def load_cfg(): + device = fastcomtec_api.FastComTec() + device.load_config("C:/Users/admin/benjamin_code/FastComTec_self/configs/ExampleMeasurement1_config.mpa") +######################################################## +### Fastcomtec Types +######################################################## +def test_get_data(): + device = fastcomtec_api.FastComTec() + device.start_measurement() + data = device.get_acq_data() + device.halt_measurement() + print(data) + +def get_fastcomtec_types(): + print("--- FastComTec types ----") + device = fastcomtec_api.FastComTec() + print("--- AcqSettings ---") + print(device.get_acq_settings()) + print("--- AcqStatus ---") + print(device.get_acq_status()) + print("--- BoardSettings ---") + print(device.get_board_settings()) +######################################################## +### Example measurements +######################################################## +def two_channel_measurement(): + device = fastcomtec_api.FastComTec() + device.load_config("C:/Users/admin/benjamin_code/FastComTec_self/configs/TwoChannelMeasurement_config.mpa") + device.erase_measruement() + device._send_dll_command("MC_A") + device.start_measurement() + time.sleep(1) + device.halt_measurement() + device.save_config_with_data("C:/Users/admin/benjamin_code/FastComTec_self/measurements/TwoChannelMeasurement.mpa") + fastcomtec_api.separate_config_and_data_per_channel("C:/Users/admin/benjamin_code/FastComTec_self/measurements/TwoChannelMeasurement.mpa") + +def example_measurement_1(): + device = fastcomtec_api.FastComTec() + device.load_config("C:/Users/admin/benjamin_code/FastComTec_self/configs/ExampleMeasurement1_config.mpa") + device.erase_measruement() + # device._send_dll_command("MC_A") + device.start_measurement() + time.sleep(1) + device.halt_measurement() + output_file = "C:/Users/admin/benjamin_code/FastComTec_self/test_example.mpa" + device.save_config_with_data(output_file) + # device.save_config_and_data_seperately("C:/Users/admin/benjamin_code/FastComTec_self/test_example") + fastcomtec_api.separate_config_and_data_per_channel(output_file) + +def load_cfg_and_perform_measurement(config_path_full: str, output_path_full: str, measurement_time: float): + device = fastcomtec_api.FastComTec() + device.load_config(config_path_full) + device.erase_measruement() + # device._send_dll_command("MC_A") + device.start_measurement() + time.sleep(measurement_time) + device.halt_measurement() + device.save_config_with_data(output_path_full) + fastcomtec_api.separate_config_and_data_per_channel(output_path_full) + +######################################################## +### Histogram plotting +######################################################## +def print_data(): + fastcomtec_plotting.plot_raw_histogram("test_example_config.mpa", "test_example_data.mpa") + fastcomtec_plotting.plot_raw_histograms("test_example_config.mpa", "test_example_data_TDAT0.mpa", "test_example_data_TDAT1.mpa") + +def safe_config(): + device = fastcomtec_api.FastComTec() + device.save_config() + +def custom_measurement(): + load_cfg_and_perform_measurement( + config_path_full="C:/Users/admin/benjamin_code/FastComTec_self/configs/test.mpa", + output_path_full="C:/Users/admin/benjamin_code/FastComTec_self/measurements/TwoChannelMeasurement.mpa", + measurement_time=1 + ) + +def run_tests(): + device = fastcomtec_api.FastComTec() + device._send_dll_command("bitshift=32") + device.save_config() +run_tests() diff --git a/FastComtecMCS8A/testing/tryout.py b/FastComtecMCS8A/testing/tryout.py new file mode 100644 index 0000000..a41c0b6 --- /dev/null +++ b/FastComtecMCS8A/testing/tryout.py @@ -0,0 +1,21 @@ + +import ctypes as ct + +# storing a ctypes long value +value_1 = ct.c_long(10) + +# using pointer() method we are pointing to the +# value_1 variable and storing it in ptr +ptr = ct.pointer(value_1) + + +print("-----") +print(ptr.contents) +print(ptr[0]) +print(ptr[1]) +print("-----") +print(id(ptr)) +print(id(ptr[0])) +print(id(ptr[1])) +ptr = ptr + 1 +print(id(ptr)) \ No newline at end of file -- GitLab From 3fe4991e6bf06028ea12c09fbd360158fb5a4176 Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Wed, 22 Jan 2025 17:15:59 +0100 Subject: [PATCH 06/22] Deleted unused file --- FastComtecMCS8A/MCS8A_spcm.conf | 74 --------------------------------- 1 file changed, 74 deletions(-) delete mode 100644 FastComtecMCS8A/MCS8A_spcm.conf diff --git a/FastComtecMCS8A/MCS8A_spcm.conf b/FastComtecMCS8A/MCS8A_spcm.conf deleted file mode 100644 index e8b5809..0000000 --- a/FastComtecMCS8A/MCS8A_spcm.conf +++ /dev/null @@ -1,74 +0,0 @@ -[Settings] -data_dir = C:\\Users\\steph\\labscript-suite\\userlib\\user_devices\\FastComtecMCS8A\\data -dll_path = C:\\Windows\\System32\\DMCS8.dll -batch_file = C:\\Users\\steph\\labscript-suite\\userlib\\user_devices\\FastComtecMCS8A\\dummy.bat -configuration_file = C:\Users\steph\stephan\spcm_fastcomtec\MCS8_1.SET - -[MCS8A] -range=4096 -periods=2 -sweepmode=22fe2080 -fstchan=0 -holdafter=0 -streamstatus=3 -calreg0=0 -calreg1=0 -calreg2=0 -calreg3=0 -swpreset=20000000 -prena=4 -syncout=1 -cycles=18 -sequences=1 -tagbits=16 -vdac0=92c -vdac1=92c -vdac2=92c -vdac3=92c -vdac4=92c -vdac5=92c -vdac6=92c -vdac7=92c -timepreset=20.000 -digio=0 -digval=0 -autoinc=0 -savedata=0 -mpafmt=asc -sephead=1 -fmt=csv -smoothpts=5 -wndwidth=163 -wndheight=286 -sysdef=0 - -[CHN1] -range=4096 -active=1 -bitshift=0 -cftfak=2580100 -evpreset=10 -roimin=0 -roimax=4096 -caloff=0.000000 -calfact=0.400000 -calfact2=0 -calfact3=0 -calunit=nsec -caluse=1 - -[CHN2] -range=4096 -active=0 -bitshift=0 -cftfak=2580100 -evpreset=10 -roimin=0 -roimax=4096 -caloff=0.000000 -calfact=0.400000 -calfact2=0 -calfact3=0 -calunit=nsec -caluse=1 - -- GitLab From a5105cc514d0c7d744da4f8f3e54f84418102553 Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Thu, 23 Jan 2025 11:41:23 +0100 Subject: [PATCH 07/22] Changed becasue the range is [0,2) --- FastComtecMCS8A/blacs_workers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index 5eb4c2e..d22c004 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -65,8 +65,8 @@ class MCS8AWorker(Worker): group = f.require_group(f"data/{self.device_name}/") # save photon counts - data = np.random.randint(0,1,size=1000) # testing - group.create_dataset("photon_counts", compression = config.compression, data=data) + data = np.random.randint(0,2,size=1000) # testing + group.create_dataset("photon_counts", compression = config.compression, data=data) # maybe save as binary dtype # save parameters # for param in parameters: -- GitLab From f6266890bb2701986dc68370ea085d5f07aded77 Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Thu, 23 Jan 2025 14:49:06 +0100 Subject: [PATCH 08/22] for difference --- FastComtecMCS8A/test_config.SET | 66 +++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 FastComtecMCS8A/test_config.SET diff --git a/FastComtecMCS8A/test_config.SET b/FastComtecMCS8A/test_config.SET new file mode 100644 index 0000000..f6cb2a9 --- /dev/null +++ b/FastComtecMCS8A/test_config.SET @@ -0,0 +1,66 @@ +[MCS8A A] 208 FW 3.104 SV 1.243 +range=29824 +periods=2 +sweepmode=22fd2088 +fstchan=0 +holdafter=0 +streamstatus=3 +calreg0=0 +calreg1=0 +calreg2=0 +calreg3=0 +swpreset=1 +prena=6 +syncout=0 +cycles=2 +sequences=1 +tagbits=16 +vdac0=1079c +vdac1=479c +vdac2=43b4 +vdac3=3b4 +vdac4=3b4 +vdac5=3b4 +vdac6=3b4 +vdac7=3b4 +timepreset=0.000 +digio=0 +digval=0 +autoinc=0 +savedata=1 +mpafmt=asc +sephead=1 +fmt=asc +smoothpts=5 +wndwidth=163 +wndheight=286 +sysdef=0 +showstarts=0 +[CHN1] +range=29824 +active=0 +bitshift=11 +cftfak=2580100 +evpreset=10 +roimin=0 +roimax=29824 +caloff=0.000000 +calfact=52428.800000 +calfact2=0 +calfact3=0 +calunit=nsec +caluse=1 +[CHN2] +range=29824 +active=1 +bitshift=11 +cftfak=2580100 +evpreset=10 +roimin=0 +roimax=64 +caloff=0.000000 +calfact=52428.800000 +calfact2=0 +calfact3=0 +calunit=nsec +caluse=1 -- GitLab From 6be6276f3f1eadd7c4c5389ea6f0a31aab09e06e Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Fri, 24 Jan 2025 13:47:32 +0100 Subject: [PATCH 09/22] new default config --- FastComtecMCS8A/MCS8A_default_config.SET | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/FastComtecMCS8A/MCS8A_default_config.SET b/FastComtecMCS8A/MCS8A_default_config.SET index d8615c9..69cbb7c 100644 --- a/FastComtecMCS8A/MCS8A_default_config.SET +++ b/FastComtecMCS8A/MCS8A_default_config.SET @@ -1,7 +1,7 @@ [MCS8A A] 208 FW 3.104 SV 1.243 -range=29824 +range=15040 periods=2 -sweepmode=22fd2088 +sweepmode=22fd2098 fstchan=0 holdafter=0 streamstatus=3 @@ -27,7 +27,7 @@ timepreset=0.000 digio=0 digval=0 autoinc=0 -savedata=1 +savedata=0 mpafmt=asc sephead=1 fmt=asc @@ -37,29 +37,29 @@ wndheight=286 sysdef=0 showstarts=0 [CHN1] -range=29824 +range=15040 active=0 -bitshift=13 +bitshift=11 cftfak=2580100 evpreset=10 roimin=0 -roimax=29824 +roimax=15040 caloff=0.000000 -calfact=209715.200000 +calfact=52428.800000 calfact2=0 calfact3=0 calunit=nsec caluse=1 [CHN2] -range=29824 +range=15040 active=1 -bitshift=13 +bitshift=11 cftfak=2580100 evpreset=10 roimin=0 -roimax=64 +roimax=7040 caloff=0.000000 -calfact=209715.200000 +calfact=52428.800000 calfact2=0 calfact3=0 calunit=nsec -- GitLab From 8484d83a2abd525feb3d5a5caa4e2f13c1dcc76d Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Fri, 24 Jan 2025 18:13:29 +0100 Subject: [PATCH 10/22] periodic SPCM measurements in labscript are working with these configurations --- FastComtecMCS8A/blacs_workers.py | 37 ++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index d22c004..61a5473 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -18,24 +18,31 @@ import numpy as np class MCS8AWorker(Worker): def init(self): - self.data_path_tmp = "C:\\mcs8x64\\MCS8A_measurement.tmp.mpa" + self.base_dir = "C:/mcs8x64/" + self.data_mame_tmp = "CHN2_tmp" + self.data_path_tmp = self.base_dir + self.data_mame_tmp + ".dat" self.device = fastcomtec_api.FastComTec() print("Connected...") - print("Status: ", self.device.get_acq_status()) + # print("Status: ", self.device.get_acq_status()) # load (default) config if not os.path.realpath(self.config_path) == self.config_path.replace("/","\\"): self.config_path = os.path.dirname(os.path.realpath(__file__)) + "\\" + self.config_path - - self.device.load_config(self.config_path) - + print("test123") + # self.device.load_config(self.config_path) + print(f"Data file path: {self.data_mame_tmp}") + # self.device._send_dll_command(f"mpaname={self.data_path_tmp}") # mpa might be slow + self.device._send_dll_command("MC_B") + self.device._send_dll_command(f"datname={self.data_mame_tmp}") + self.device._send_dll_command("fmt=dat") self.smart_cache = {} print('init completed') def transition_to_buffered(self, device_name, h5_file, initial_values, fresh): + self.h5file = h5_file # save for 'transition_to_manual' - + """ with h5py.File(h5_file,'r') as f: command_group = f[f"devices/{self.device_name}/commands"] for command in command_group.attrs: @@ -44,14 +51,19 @@ class MCS8AWorker(Worker): self.smart_cache[command] = command_group.attrs[command] if command_group.attrs[command] is not None: command = f"{command}={command_group.attrs[command]}" + print(f"sending: {command}") self.device._send_dll_command(command) print(f"Programmed command '{command}'") - + """ try: - os.remove(self.data_path_tmp) + os.remove(self.base_dir + self.data_mame_tmp + ".mp") except FileNotFoundError: pass - self.device.erase_measruement() + + # self.device.erase_measruement() + # self.device._send_dll_command("bitshift=11") + ran = 256 + self.device._send_dll_command(f"range={ran}") self.device.start_measurement() return {} @@ -60,12 +72,15 @@ class MCS8AWorker(Worker): # load data and write to file # .... TODO - + self.device._send_dll_command("savedat") with h5py.File(self.h5file, 'r+') as f: group = f.require_group(f"data/{self.device_name}/") # save photon counts - data = np.random.randint(0,2,size=1000) # testing + # data = np.random.randint(0,2,size=1000) # testing + # data = np.loadtxt(self.data_path_tmp) + data = np.fromfile(self.data_path_tmp, dtype=np.uint32) + print(data.sum()) group.create_dataset("photon_counts", compression = config.compression, data=data) # maybe save as binary dtype # save parameters -- GitLab From 9821f3d4a474259976377f30538f68508f252076 Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Mon, 27 Jan 2025 15:47:13 +0100 Subject: [PATCH 11/22] Changed default config for SPCM measurements: software start, 1.7s, highest binning, SPCM on channel 2. --- FastComtecMCS8A/MCS8A_default_config.SET | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/FastComtecMCS8A/MCS8A_default_config.SET b/FastComtecMCS8A/MCS8A_default_config.SET index 69cbb7c..4d9575d 100644 --- a/FastComtecMCS8A/MCS8A_default_config.SET +++ b/FastComtecMCS8A/MCS8A_default_config.SET @@ -1,5 +1,5 @@ [MCS8A A] 208 FW 3.104 SV 1.243 -range=15040 +range=256 periods=2 sweepmode=22fd2098 fstchan=0 @@ -30,36 +30,36 @@ autoinc=0 savedata=0 mpafmt=asc sephead=1 -fmt=asc +fmt=dat smoothpts=5 wndwidth=163 wndheight=286 sysdef=0 showstarts=0 [CHN1] -range=15040 +range=256 active=0 -bitshift=11 +bitshift=18 cftfak=2580100 evpreset=10 roimin=0 -roimax=15040 +roimax=256 caloff=0.000000 -calfact=52428.800000 +calfact=6710886.400000 calfact2=0 calfact3=0 calunit=nsec caluse=1 [CHN2] -range=15040 +range=256 active=1 -bitshift=11 +bitshift=18 cftfak=2580100 evpreset=10 roimin=0 -roimax=7040 +roimax=200 caloff=0.000000 -calfact=52428.800000 +calfact=6710886.400000 calfact2=0 calfact3=0 calunit=nsec -- GitLab From 05562253565dc6da0ee763673ff657b6ec30d521 Mon Sep 17 00:00:00 2001 From: Runner PC Cavity Lab <johannes.schabbauer@tuwien.ac.at> Date: Wed, 29 Jan 2025 18:00:14 +0100 Subject: [PATCH 12/22] added comments --- FastComtecMCS8A/blacs_workers.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index 61a5473..faa5e9d 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -61,9 +61,11 @@ class MCS8AWorker(Worker): pass # self.device.erase_measruement() - # self.device._send_dll_command("bitshift=11") - ran = 256 - self.device._send_dll_command(f"range={ran}") + ######################################## + ### TIME DIGITIZER PARAMETERS + ######################################## + # self.device._send_dll_command("bitshift=11") # See documentation on bitshift (C:\Users\labuser\labscript-suite\userlib\user_devices\FastComtecMCS8A\docs) + self.device._send_dll_command(f"range=256") # See documentation on bitshift (C:\Users\labuser\labscript-suite\userlib\user_devices\FastComtecMCS8A\docs) self.device.start_measurement() return {} -- GitLab From c7ec65efbc6c9ba864f125a6e7390c970961d5b6 Mon Sep 17 00:00:00 2001 From: Runner PC Cavity Lab <johannes.schabbauer@tuwien.ac.at> Date: Wed, 29 Jan 2025 18:00:44 +0100 Subject: [PATCH 13/22] removed whitespace --- FastComtecMCS8A/fastcomtec_api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/FastComtecMCS8A/fastcomtec_api.py b/FastComtecMCS8A/fastcomtec_api.py index 2567ebf..da549a7 100644 --- a/FastComtecMCS8A/fastcomtec_api.py +++ b/FastComtecMCS8A/fastcomtec_api.py @@ -9,7 +9,7 @@ class FastComTec(): """ Hardware Class for the FastComtec MCS8. """ def __init__(self, dll_path = r'C:\Windows\System32\DMCS8.dll', **kwargs) -> None: - self.dll = ctypes.windll.LoadLibrary(dll_path) + self.dll = ctypes.windll.LoadLibrary(dll_path) ######################################## # Fastcomtec Types ######################################## @@ -156,5 +156,3 @@ def separate_config_and_data_per_channel(file_path: str) -> None: data_file.write("".join(lines[boundaries[i].line_number:])) - - \ No newline at end of file -- GitLab From 1c225203eaf7651bceb0ff23957cfa3600316974 Mon Sep 17 00:00:00 2001 From: Runner PC Cavity Lab <johannes.schabbauer@tuwien.ac.at> Date: Thu, 30 Jan 2025 11:22:50 +0100 Subject: [PATCH 14/22] Added settings to hp5 --- FastComtecMCS8A/blacs_workers.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index faa5e9d..670ad98 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -64,8 +64,10 @@ class MCS8AWorker(Worker): ######################################## ### TIME DIGITIZER PARAMETERS ######################################## - # self.device._send_dll_command("bitshift=11") # See documentation on bitshift (C:\Users\labuser\labscript-suite\userlib\user_devices\FastComtecMCS8A\docs) - self.device._send_dll_command(f"range=256") # See documentation on bitshift (C:\Users\labuser\labscript-suite\userlib\user_devices\FastComtecMCS8A\docs) + self.range_dec = "1024" + self.bitshift_hex = "e" + self.device._send_dll_command(f"range={self.range_dec}") # See documentation on bitshift (C:\Users\labuser\labscript-suite\userlib\user_devices\FastComtecMCS8A\docs) + self.device._send_dll_command(f"bitshift={self.bitshift_hex}") # See documentation on bitshift (C:\Users\labuser\labscript-suite\userlib\user_devices\FastComtecMCS8A\docs) self.device.start_measurement() return {} @@ -87,7 +89,9 @@ class MCS8AWorker(Worker): # save parameters # for param in parameters: - group.attrs["test_param"] = 1000 + group.attrs["test_param"] = 1000 + group.attrs["range_dec"] = self.range_dec + group.attrs["bitshift_hex"] = self.bitshift_hex print('tranistion to manual') return True -- GitLab From d73ccdaeeb40dc8ebf3566eb7740d9395ae01b00 Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Mon, 3 Feb 2025 10:40:36 +0100 Subject: [PATCH 15/22] Fastcomtech: Work in progress from last week --- FastComtecMCS8A/MCS8A_default_config.SET | 8 +- FastComtecMCS8A/blacs_workers.py | 93 ++++++++++++------------ FastComtecMCS8A/fastcomtec_api.py | 5 +- FastComtecMCS8A/labscript_devices.py | 11 +++ 4 files changed, 65 insertions(+), 52 deletions(-) diff --git a/FastComtecMCS8A/MCS8A_default_config.SET b/FastComtecMCS8A/MCS8A_default_config.SET index 4d9575d..0484c1c 100644 --- a/FastComtecMCS8A/MCS8A_default_config.SET +++ b/FastComtecMCS8A/MCS8A_default_config.SET @@ -1,10 +1,10 @@ [MCS8A A] 208 FW 3.104 SV 1.243 range=256 periods=2 -sweepmode=22fd2098 +sweepmode=22fd2088 fstchan=0 holdafter=0 -streamstatus=3 +streamstatus=0 calreg0=0 calreg1=0 calreg2=0 @@ -27,8 +27,8 @@ timepreset=0.000 digio=0 digval=0 autoinc=0 -savedata=0 -mpafmt=asc +savedata=2 +mpafmt=dat sephead=1 fmt=dat smoothpts=5 diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index 670ad98..e927992 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -14,13 +14,20 @@ import os import labscript_utils.h5_lock import h5py import numpy as np +import time class MCS8AWorker(Worker): def init(self): + self.start = time.perf_counter() self.base_dir = "C:/mcs8x64/" self.data_mame_tmp = "CHN2_tmp" - self.data_path_tmp = self.base_dir + self.data_mame_tmp + ".dat" + + # ONLY FOR MPA AND AUTOSAVE + self.data_path_tmp = self.base_dir + self.data_mame_tmp + ".mpa" + # ONLY FOR DAT WITHOUT AUTOSAVE + # self.data_path_tmp = self.base_dir + self.data_mame_tmp + self.device = fastcomtec_api.FastComTec() print("Connected...") # print("Status: ", self.device.get_acq_status()) @@ -28,21 +35,44 @@ class MCS8AWorker(Worker): # load (default) config if not os.path.realpath(self.config_path) == self.config_path.replace("/","\\"): self.config_path = os.path.dirname(os.path.realpath(__file__)) + "\\" + self.config_path - print("test123") - # self.device.load_config(self.config_path) - print(f"Data file path: {self.data_mame_tmp}") + self.config_path = self.config_path.replace("\\", "/") + + print("Load config...", self.config_path) + self.device.load_config(self.config_path) + + + # print(f"Data file path: {self.data_path_tmp}") + + # ONLY FOR MPA AND AUTOSAVE # self.device._send_dll_command(f"mpaname={self.data_path_tmp}") # mpa might be slow - self.device._send_dll_command("MC_B") - self.device._send_dll_command(f"datname={self.data_mame_tmp}") - self.device._send_dll_command("fmt=dat") + self.device._send_dll_command("mpafmt=dat") + self.device._send_dll_command("savedata=2") + + # ONLY FOR DAT WITHOUT AUTOSAVE + # self.device._send_dll_command(f"datname={self.data_mame_tmp}") + # self.device._send_dll_command("fmt=dat") self.smart_cache = {} print('init completed') def transition_to_buffered(self, device_name, h5_file, initial_values, fresh): - + print(f"Cycle time: {time.perf_counter()-self.start:.1f} s") + self.start = time.perf_counter() + self.h5file = h5_file # save for 'transition_to_manual' - """ + self.mpaname = h5_file.replace("\\","/").replace(".h5",".mpa") + self.device._send_dll_command(f"mpaname={self.mpaname}") + + while True: + status = self.device.get_acq_status() + status = getattr(status,"started") + if status!=1: + break + print("Waiting for device to stop running (from previous shot)...", end="\r") + time.sleep(0.1) + + self.device.halt_measurement() + with h5py.File(h5_file,'r') as f: command_group = f[f"devices/{self.device_name}/commands"] for command in command_group.attrs: @@ -51,49 +81,15 @@ class MCS8AWorker(Worker): self.smart_cache[command] = command_group.attrs[command] if command_group.attrs[command] is not None: command = f"{command}={command_group.attrs[command]}" - print(f"sending: {command}") self.device._send_dll_command(command) print(f"Programmed command '{command}'") - """ - try: - os.remove(self.base_dir + self.data_mame_tmp + ".mp") - except FileNotFoundError: - pass - - # self.device.erase_measruement() - ######################################## - ### TIME DIGITIZER PARAMETERS - ######################################## - self.range_dec = "1024" - self.bitshift_hex = "e" - self.device._send_dll_command(f"range={self.range_dec}") # See documentation on bitshift (C:\Users\labuser\labscript-suite\userlib\user_devices\FastComtecMCS8A\docs) - self.device._send_dll_command(f"bitshift={self.bitshift_hex}") # See documentation on bitshift (C:\Users\labuser\labscript-suite\userlib\user_devices\FastComtecMCS8A\docs) + + # self.device.erase_measruement() # Don't erase because this takes super long for some reason self.device.start_measurement() + return {} def transition_to_manual(self): - # self.device.halt_measurement() - - # load data and write to file - # .... TODO - self.device._send_dll_command("savedat") - with h5py.File(self.h5file, 'r+') as f: - group = f.require_group(f"data/{self.device_name}/") - - # save photon counts - # data = np.random.randint(0,2,size=1000) # testing - # data = np.loadtxt(self.data_path_tmp) - data = np.fromfile(self.data_path_tmp, dtype=np.uint32) - print(data.sum()) - group.create_dataset("photon_counts", compression = config.compression, data=data) # maybe save as binary dtype - - # save parameters - # for param in parameters: - group.attrs["test_param"] = 1000 - group.attrs["range_dec"] = self.range_dec - group.attrs["bitshift_hex"] = self.bitshift_hex - print('tranistion to manual') - return True def program_manual(self, values): @@ -106,4 +102,7 @@ class MCS8AWorker(Worker): return self.transition_to_manual() def abort_transition_to_buffered(self): - return True \ No newline at end of file + return True + + # def shutdown(self): + # self.device._send_dll_command("exit") \ No newline at end of file diff --git a/FastComtecMCS8A/fastcomtec_api.py b/FastComtecMCS8A/fastcomtec_api.py index da549a7..75c580a 100644 --- a/FastComtecMCS8A/fastcomtec_api.py +++ b/FastComtecMCS8A/fastcomtec_api.py @@ -1,7 +1,10 @@ # -*- coding: utf-8 -*- import ctypes -from . import fastcomtec_types +try: + from . import fastcomtec_types +except ImportError: + import fastcomtec_types import re from typing import NamedTuple, List diff --git a/FastComtecMCS8A/labscript_devices.py b/FastComtecMCS8A/labscript_devices.py index e6aad99..38da0e8 100644 --- a/FastComtecMCS8A/labscript_devices.py +++ b/FastComtecMCS8A/labscript_devices.py @@ -7,6 +7,7 @@ # # ##################################################################### from labscript import TriggerableDevice, LabscriptError, set_passed_properties +import numpy as np class MCS8A(TriggerableDevice): description = 'MCS8A' @@ -29,6 +30,16 @@ class MCS8A(TriggerableDevice): raise LabscriptError("Do not set the same config command twice!") self.commands[command] = value + def set_binwidth_and_duration(self, binwidth, duration): + """ + Parameters: + """ + bitshift = round(np.log2(binwidth/400e-12)) + self.set_dll_command(f"bitshift", hex(bitshift)[2:]) + + range_dec = duration/binwidth//64 * 64 + self.set_dll_command("range", str(int(range_dec))) + def generate_code(self, hdf5_file): group = hdf5_file.create_group(f"devices/{self.name}") commands_group = group.create_group("commands") -- GitLab From 8b525b1934142d4765fe71af91d4b61f70615554 Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Mon, 3 Feb 2025 19:02:58 +0100 Subject: [PATCH 16/22] Fastcomtec: New device code to let MCS8A run for long time and save trigger signals instead of starting and stopping for each shot. --- FastComtecMCS8A/MCS8A_default_config.SET | 10 +-- FastComtecMCS8A/blacs_workers.py | 96 +++++++++++------------- FastComtecMCS8A/labscript_devices.py | 39 ++++------ FastComtecMCS8A/test_config.SET | 66 ---------------- 4 files changed, 62 insertions(+), 149 deletions(-) delete mode 100644 FastComtecMCS8A/test_config.SET diff --git a/FastComtecMCS8A/MCS8A_default_config.SET b/FastComtecMCS8A/MCS8A_default_config.SET index 0484c1c..69b9905 100644 --- a/FastComtecMCS8A/MCS8A_default_config.SET +++ b/FastComtecMCS8A/MCS8A_default_config.SET @@ -1,7 +1,7 @@ [MCS8A A] 208 FW 3.104 SV 1.243 -range=256 +range=44736 periods=2 -sweepmode=22fd2088 +sweepmode=22fc2088 fstchan=0 holdafter=0 streamstatus=0 @@ -37,8 +37,8 @@ wndheight=286 sysdef=0 showstarts=0 [CHN1] -range=256 -active=0 +range=44736 +active=1 bitshift=18 cftfak=2580100 evpreset=10 @@ -51,7 +51,7 @@ calfact3=0 calunit=nsec caluse=1 [CHN2] -range=256 +range=44736 active=1 bitshift=18 cftfak=2580100 diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index e927992..462484f 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -9,83 +9,73 @@ from . import fastcomtec_api from blacs.tab_base_classes import Worker -from labscript import config +from labscript_utils.labconfig import LabConfig import os import labscript_utils.h5_lock import h5py import numpy as np import time +from datetime import datetime class MCS8AWorker(Worker): def init(self): - self.start = time.perf_counter() - self.base_dir = "C:/mcs8x64/" - self.data_mame_tmp = "CHN2_tmp" - - # ONLY FOR MPA AND AUTOSAVE - self.data_path_tmp = self.base_dir + self.data_mame_tmp + ".mpa" - # ONLY FOR DAT WITHOUT AUTOSAVE - # self.data_path_tmp = self.base_dir + self.data_mame_tmp + self.base_dir = LabConfig().get("DEFAULT","experiment_shot_storage") + "\\MCS8A" + os.makedirs(self.base_dir, exist_ok=True) # create MCS8A folder if it doesn't exist yet self.device = fastcomtec_api.FastComTec() - print("Connected...") # print("Status: ", self.device.get_acq_status()) + self.device.halt_measurement() # load (default) config if not os.path.realpath(self.config_path) == self.config_path.replace("/","\\"): self.config_path = os.path.dirname(os.path.realpath(__file__)) + "\\" + self.config_path self.config_path = self.config_path.replace("\\", "/") - print("Load config...", self.config_path) self.device.load_config(self.config_path) - - # print(f"Data file path: {self.data_path_tmp}") - # ONLY FOR MPA AND AUTOSAVE - # self.device._send_dll_command(f"mpaname={self.data_path_tmp}") # mpa might be slow + # Save data only in list file and in binary format self.device._send_dll_command("mpafmt=dat") self.device._send_dll_command("savedata=2") - # ONLY FOR DAT WITHOUT AUTOSAVE - # self.device._send_dll_command(f"datname={self.data_mame_tmp}") - # self.device._send_dll_command("fmt=dat") - self.smart_cache = {} - - print('init completed') + # Set how long each measurement lasts, i.e. how ofen a new file is started. + # We don't want it too long, because then the single files get huge, but we also don't + # want it too often, because each stopping and starting takes some time. + self.maxduration = 300 # seconds + self.device._send_dll_command("bitshift=18") # 400ps * 2^0x18 = 0.0067 s + range_int = np.ceil(self.maxduration / (400e-12 * 2**0x18) / 64) * 64 + self.device._send_dll_command(f"range={int(range_int)}") + + def get_new_mpapath(self): + mpaname = self.device_name + "_" + datetime.now().strftime("%Y-%d-%m_%H-%M-%S") + ".mpa" + return self.base_dir + "\\" + mpaname def transition_to_buffered(self, device_name, h5_file, initial_values, fresh): - print(f"Cycle time: {time.perf_counter()-self.start:.1f} s") - self.start = time.perf_counter() - - self.h5file = h5_file # save for 'transition_to_manual' - self.mpaname = h5_file.replace("\\","/").replace(".h5",".mpa") - self.device._send_dll_command(f"mpaname={self.mpaname}") - - while True: - status = self.device.get_acq_status() - status = getattr(status,"started") - if status!=1: - break - print("Waiting for device to stop running (from previous shot)...", end="\r") - time.sleep(0.1) - - self.device.halt_measurement() - - with h5py.File(h5_file,'r') as f: - command_group = f[f"devices/{self.device_name}/commands"] - for command in command_group.attrs: - if (command in self.smart_cache and command_group.attrs[command]==self.smart_cache[command]) and not fresh: - continue - self.smart_cache[command] = command_group.attrs[command] - if command_group.attrs[command] is not None: - command = f"{command}={command_group.attrs[command]}" - self.device._send_dll_command(command) - print(f"Programmed command '{command}'") - - # self.device.erase_measruement() # Don't erase because this takes super long for some reason - self.device.start_measurement() + # If device is already started, we leave it running and just save the datapath and index in the h5file. + # If the device is not running, or already longer than the defined duration, send the stop command, set new datafile and start new measurement. + # TODO: How to handle parameters (start new measurment when they changed, or treat parameters statically) + if getattr(self.device.get_acq_status(),"started") != 1 or time.perf_counter()-getattr(self,"start_time",-self.maxduration)>self.maxduration: + time.sleep(1) # TODO: Do we need this wait? What's the minumum? + self.device.halt_measurement() + mpapath = self.get_new_mpapath() + self.device._send_dll_command(f"mpaname={mpapath}") + self.datapath = mpapath.replace(".mpa",".lst") + self.dataindex = 0 + print("Set filename...", self.datapath.split("\\")[-1]) + + # self.device.erase_measruement() # Don't erase because this takes super long for some reason + self.device.start_measurement() + self.start_time = time.perf_counter() + time.sleep(0.2) # some buffer time to make sure the measurement already started for sure + # TODO: What's the minimum time to wait here? + + # Save where to find the measurement data (which file and within file) + with h5py.File(h5_file,'r+') as f: + group = f[f"devices/{self.device_name}"] + group.attrs["datapath"] = self.datapath + group.attrs["dataindex"] = self.dataindex + self.dataindex += group.attrs["n_trigger"] return {} @@ -104,5 +94,5 @@ class MCS8AWorker(Worker): def abort_transition_to_buffered(self): return True - # def shutdown(self): - # self.device._send_dll_command("exit") \ No newline at end of file + def shutdown(self): + self.device._send_dll_command("exit") \ No newline at end of file diff --git a/FastComtecMCS8A/labscript_devices.py b/FastComtecMCS8A/labscript_devices.py index 38da0e8..078e95c 100644 --- a/FastComtecMCS8A/labscript_devices.py +++ b/FastComtecMCS8A/labscript_devices.py @@ -2,11 +2,11 @@ # # # /user_devices/FastComtecMCS8A/labscript_devices.py # # # -# Jan 2023, Stephan Roschinski # +# Feb. 2024, Leonard Group # # # # # ##################################################################### -from labscript import TriggerableDevice, LabscriptError, set_passed_properties +from labscript import TriggerableDevice, LabscriptError, set_passed_properties, config import numpy as np class MCS8A(TriggerableDevice): @@ -19,34 +19,23 @@ class MCS8A(TriggerableDevice): super().__init__(name, parent_device, connection, **kwargs) self.BLACS_connection = "SPCM MCS8A" self.config_path = config_path - self.commands = {} + self.labels = {} - def do_checks(self): - if len(self.trigger_device.triggerings) > 1: - raise NotImplementedError("Only 1 acquistuin allowed, working on code!") - - def set_dll_command(self, command, value = None): - if command in self.commands: - raise LabscriptError("Do not set the same config command twice!") - self.commands[command] = value - - def set_binwidth_and_duration(self, binwidth, duration): - """ - Parameters: - """ - bitshift = round(np.log2(binwidth/400e-12)) - self.set_dll_command(f"bitshift", hex(bitshift)[2:]) - - range_dec = duration/binwidth//64 * 64 - self.set_dll_command("range", str(int(range_dec))) + def acquire(self, t, duration, trigger_duration=None, label=""): + if trigger_duration is None: + trigger_duration = duration + self.labels[t] = label + self.trigger(t,trigger_duration) def generate_code(self, hdf5_file): group = hdf5_file.create_group(f"devices/{self.name}") - commands_group = group.create_group("commands") - for command,value in self.commands.items(): - commands_group.attrs[command] = value + labels_group = group.create_group("labels") + for t,label in self.labels.items(): + labels_group.attrs[str(t)] = label - self.do_checks() + group.attrs["n_trigger"] = len(self.trigger_device.triggerings) + durations = [triggering[1] for triggering in self.trigger_device.triggerings] + group.create_dataset("durations", compression = config.compression, data = durations) super().generate_code(hdf5_file) diff --git a/FastComtecMCS8A/test_config.SET b/FastComtecMCS8A/test_config.SET deleted file mode 100644 index f6cb2a9..0000000 --- a/FastComtecMCS8A/test_config.SET +++ /dev/null @@ -1,66 +0,0 @@ -[MCS8A A] 208 FW 3.104 SV 1.243 -range=29824 -periods=2 -sweepmode=22fd2088 -fstchan=0 -holdafter=0 -streamstatus=3 -calreg0=0 -calreg1=0 -calreg2=0 -calreg3=0 -swpreset=1 -prena=6 -syncout=0 -cycles=2 -sequences=1 -tagbits=16 -vdac0=1079c -vdac1=479c -vdac2=43b4 -vdac3=3b4 -vdac4=3b4 -vdac5=3b4 -vdac6=3b4 -vdac7=3b4 -timepreset=0.000 -digio=0 -digval=0 -autoinc=0 -savedata=1 -mpafmt=asc -sephead=1 -fmt=asc -smoothpts=5 -wndwidth=163 -wndheight=286 -sysdef=0 -showstarts=0 -[CHN1] -range=29824 -active=0 -bitshift=11 -cftfak=2580100 -evpreset=10 -roimin=0 -roimax=29824 -caloff=0.000000 -calfact=52428.800000 -calfact2=0 -calfact3=0 -calunit=nsec -caluse=1 -[CHN2] -range=29824 -active=1 -bitshift=11 -cftfak=2580100 -evpreset=10 -roimin=0 -roimax=64 -caloff=0.000000 -calfact=52428.800000 -calfact2=0 -calfact3=0 -calunit=nsec -caluse=1 -- GitLab From 7ed2e33ff206a6a9cd83f6062b82d85e22b1870b Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Tue, 4 Feb 2025 16:00:48 +0100 Subject: [PATCH 17/22] Fastcomtec: Checked "sweep counter in data not needed", we increase bits for timedata in lst file --- FastComtecMCS8A/MCS8A_default_config.SET | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FastComtecMCS8A/MCS8A_default_config.SET b/FastComtecMCS8A/MCS8A_default_config.SET index 69b9905..2d4fc21 100644 --- a/FastComtecMCS8A/MCS8A_default_config.SET +++ b/FastComtecMCS8A/MCS8A_default_config.SET @@ -1,7 +1,7 @@ [MCS8A A] 208 FW 3.104 SV 1.243 range=44736 periods=2 -sweepmode=22fc2088 +sweepmode=22fc3088 fstchan=0 holdafter=0 streamstatus=0 -- GitLab From 70f9c60257d0820334c58b79c7d04d1588b7a07c Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Tue, 4 Feb 2025 19:25:23 +0100 Subject: [PATCH 18/22] Improven timitgs when Fastcomtec is stopped and started (maybe not completely working) --- FastComtecMCS8A/blacs_workers.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index 462484f..e8f5a77 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -25,6 +25,8 @@ class MCS8AWorker(Worker): self.device = fastcomtec_api.FastComTec() # print("Status: ", self.device.get_acq_status()) + if getattr(self.device.get_acq_status(),"started") == 1: + time.sleep(1) self.device.halt_measurement() # load (default) config @@ -42,7 +44,7 @@ class MCS8AWorker(Worker): # Set how long each measurement lasts, i.e. how ofen a new file is started. # We don't want it too long, because then the single files get huge, but we also don't # want it too often, because each stopping and starting takes some time. - self.maxduration = 300 # seconds + self.maxduration = 600 # seconds self.device._send_dll_command("bitshift=18") # 400ps * 2^0x18 = 0.0067 s range_int = np.ceil(self.maxduration / (400e-12 * 2**0x18) / 64) * 64 self.device._send_dll_command(f"range={int(range_int)}") @@ -55,8 +57,13 @@ class MCS8AWorker(Worker): # If device is already started, we leave it running and just save the datapath and index in the h5file. # If the device is not running, or already longer than the defined duration, send the stop command, set new datafile and start new measurement. # TODO: How to handle parameters (start new measurment when they changed, or treat parameters statically) - if getattr(self.device.get_acq_status(),"started") != 1 or time.perf_counter()-getattr(self,"start_time",-self.maxduration)>self.maxduration: - time.sleep(1) # TODO: Do we need this wait? What's the minumum? + with h5py.File(h5_file,'r') as f: + stop_time = f["devices/" + f["connection table"].attrs["master_pseudoclock"]].attrs["stop_time"] + if getattr(self.device.get_acq_status(),"started") != 1 or time.perf_counter()-getattr(self,"start_time",-self.maxduration)>self.maxduration-stop_time-0.1: + while getattr(self.device.get_acq_status(),"started") == 1: + # If the current measurement doesn't have enought time left for this shot, wait until measurment ends. + time.sleep(0.1) + print("Wait until last measurement stopped...", end="\r") self.device.halt_measurement() mpapath = self.get_new_mpapath() self.device._send_dll_command(f"mpaname={mpapath}") @@ -67,7 +74,7 @@ class MCS8AWorker(Worker): # self.device.erase_measruement() # Don't erase because this takes super long for some reason self.device.start_measurement() self.start_time = time.perf_counter() - time.sleep(0.2) # some buffer time to make sure the measurement already started for sure + time.sleep(0.1) # some buffer time to make sure the measurement already started for sure # TODO: What's the minimum time to wait here? # Save where to find the measurement data (which file and within file) -- GitLab From f3af7959b7a4fcec1d3cae2fcca57cf552af6ca6 Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Wed, 5 Feb 2025 13:02:07 +0100 Subject: [PATCH 19/22] Fastcomtec: 600s Sweeps work with these wait times, don't know if we can wait shorter --- FastComtecMCS8A/blacs_workers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index e8f5a77..dac0f3d 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -59,7 +59,7 @@ class MCS8AWorker(Worker): # TODO: How to handle parameters (start new measurment when they changed, or treat parameters statically) with h5py.File(h5_file,'r') as f: stop_time = f["devices/" + f["connection table"].attrs["master_pseudoclock"]].attrs["stop_time"] - if getattr(self.device.get_acq_status(),"started") != 1 or time.perf_counter()-getattr(self,"start_time",-self.maxduration)>self.maxduration-stop_time-0.1: + if getattr(self.device.get_acq_status(),"started") != 1 or time.perf_counter()-getattr(self,"start_time",-self.maxduration)>self.maxduration-stop_time-0.5: while getattr(self.device.get_acq_status(),"started") == 1: # If the current measurement doesn't have enought time left for this shot, wait until measurment ends. time.sleep(0.1) @@ -74,7 +74,7 @@ class MCS8AWorker(Worker): # self.device.erase_measruement() # Don't erase because this takes super long for some reason self.device.start_measurement() self.start_time = time.perf_counter() - time.sleep(0.1) # some buffer time to make sure the measurement already started for sure + time.sleep(1) # some buffer time to make sure the measurement already started for sure # TODO: What's the minimum time to wait here? # Save where to find the measurement data (which file and within file) -- GitLab From 7597685d30742df94658fd6f94017982fa114fe5 Mon Sep 17 00:00:00 2001 From: Leolab Cavity CAD PC <quantuminfo.leolab@gmail.com> Date: Wed, 5 Feb 2025 17:37:04 +0100 Subject: [PATCH 20/22] Fastcomtec: Added SPCM gate --- FastComtecMCS8A/labscript_devices.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/FastComtecMCS8A/labscript_devices.py b/FastComtecMCS8A/labscript_devices.py index 078e95c..a435fb8 100644 --- a/FastComtecMCS8A/labscript_devices.py +++ b/FastComtecMCS8A/labscript_devices.py @@ -6,8 +6,7 @@ # # # # ##################################################################### -from labscript import TriggerableDevice, LabscriptError, set_passed_properties, config -import numpy as np +from labscript import TriggerableDevice, Trigger, set_passed_properties, config class MCS8A(TriggerableDevice): description = 'MCS8A' @@ -15,17 +14,24 @@ class MCS8A(TriggerableDevice): @set_passed_properties( property_names={"connection_table_properties":["config_path"]} ) - def __init__(self, name, parent_device, connection, config_path="MCS8A_default_config.SET", **kwargs): + def __init__(self, name, parent_device, connection, SPCM_gate_parent=None, SPCM_gate_connection=None, config_path="MCS8A_default_config.SET", **kwargs): super().__init__(name, parent_device, connection, **kwargs) self.BLACS_connection = "SPCM MCS8A" self.config_path = config_path self.labels = {} + if SPCM_gate_parent is not None: + self.SPCM_gate = Trigger( + f"{name}_SPCM_gate", SPCM_gate_parent, SPCM_gate_connection, trigger_edge_type="falling" + ) + def acquire(self, t, duration, trigger_duration=None, label=""): if trigger_duration is None: trigger_duration = duration self.labels[t] = label self.trigger(t,trigger_duration) + if hasattr(self,"SPCM_gate"): + self.SPCM_gate.trigger(t,duration) def generate_code(self, hdf5_file): group = hdf5_file.create_group(f"devices/{self.name}") -- GitLab From 873aea94a4248d4d8f70f7ac463f95187a609e37 Mon Sep 17 00:00:00 2001 From: Runner PC Cavity Lab <johannes.schabbauer@tuwien.ac.at> Date: Thu, 6 Feb 2025 16:45:24 +0100 Subject: [PATCH 21/22] SPCM: Increased waiting time, 10m measurements work with this settings now --- FastComtecMCS8A/MCS8A_default_config.SET | 7 +++---- FastComtecMCS8A/blacs_workers.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/FastComtecMCS8A/MCS8A_default_config.SET b/FastComtecMCS8A/MCS8A_default_config.SET index 2d4fc21..2f611f4 100644 --- a/FastComtecMCS8A/MCS8A_default_config.SET +++ b/FastComtecMCS8A/MCS8A_default_config.SET @@ -1,5 +1,5 @@ [MCS8A A] 208 FW 3.104 SV 1.243 -range=44736 +range=89408 periods=2 sweepmode=22fc3088 fstchan=0 @@ -35,9 +35,8 @@ smoothpts=5 wndwidth=163 wndheight=286 sysdef=0 -showstarts=0 [CHN1] -range=44736 +range=89408 active=1 bitshift=18 cftfak=2580100 @@ -51,7 +50,7 @@ calfact3=0 calunit=nsec caluse=1 [CHN2] -range=44736 +range=89408 active=1 bitshift=18 cftfak=2580100 diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index dac0f3d..d7c93d2 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -59,7 +59,7 @@ class MCS8AWorker(Worker): # TODO: How to handle parameters (start new measurment when they changed, or treat parameters statically) with h5py.File(h5_file,'r') as f: stop_time = f["devices/" + f["connection table"].attrs["master_pseudoclock"]].attrs["stop_time"] - if getattr(self.device.get_acq_status(),"started") != 1 or time.perf_counter()-getattr(self,"start_time",-self.maxduration)>self.maxduration-stop_time-0.5: + if getattr(self.device.get_acq_status(),"started") != 1 or time.perf_counter()-getattr(self,"start_time",-self.maxduration)>self.maxduration-stop_time-2: while getattr(self.device.get_acq_status(),"started") == 1: # If the current measurement doesn't have enought time left for this shot, wait until measurment ends. time.sleep(0.1) -- GitLab From c07ab61428333789d8506dc1cfbd8f162de4a057 Mon Sep 17 00:00:00 2001 From: Runner PC Cavity Lab <johannes.schabbauer@tuwien.ac.at> Date: Thu, 13 Feb 2025 10:01:43 +0100 Subject: [PATCH 22/22] Fastcomtec: Incresed wait times again, still don't know for sure what is the optimal (and most robust) delay --- FastComtecMCS8A/blacs_workers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/FastComtecMCS8A/blacs_workers.py b/FastComtecMCS8A/blacs_workers.py index d7c93d2..538e683 100644 --- a/FastComtecMCS8A/blacs_workers.py +++ b/FastComtecMCS8A/blacs_workers.py @@ -28,6 +28,7 @@ class MCS8AWorker(Worker): if getattr(self.device.get_acq_status(),"started") == 1: time.sleep(1) self.device.halt_measurement() + self.dataindex=0 # load (default) config if not os.path.realpath(self.config_path) == self.config_path.replace("/","\\"): @@ -59,7 +60,9 @@ class MCS8AWorker(Worker): # TODO: How to handle parameters (start new measurment when they changed, or treat parameters statically) with h5py.File(h5_file,'r') as f: stop_time = f["devices/" + f["connection table"].attrs["master_pseudoclock"]].attrs["stop_time"] - if getattr(self.device.get_acq_status(),"started") != 1 or time.perf_counter()-getattr(self,"start_time",-self.maxduration)>self.maxduration-stop_time-2: + if getattr(self.device.get_acq_status(),"started") != 1 or time.perf_counter()-getattr(self,"start_time",-self.maxduration)>self.maxduration-stop_time-3: + if self.dataindex==0: + self.device.halt_measurement() while getattr(self.device.get_acq_status(),"started") == 1: # If the current measurement doesn't have enought time left for this shot, wait until measurment ends. time.sleep(0.1) @@ -74,7 +77,7 @@ class MCS8AWorker(Worker): # self.device.erase_measruement() # Don't erase because this takes super long for some reason self.device.start_measurement() self.start_time = time.perf_counter() - time.sleep(1) # some buffer time to make sure the measurement already started for sure + time.sleep(2) # some buffer time to make sure the measurement already started for sure # TODO: What's the minimum time to wait here? # Save where to find the measurement data (which file and within file) -- GitLab