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] 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