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