From 017676a08f82576d2af417c576c8153b13e290eb Mon Sep 17 00:00:00 2001
From: Stephan Philips <s.g.j.philips@tudelft.nl>
Date: Wed, 3 Jun 2020 10:53:39 +0200
Subject: [PATCH] added d5a

---
 core_tools.egg-info/SOURCES.txt               |   6 +-
 .../data_getter/scan_generator_Keysight.py    |   4 +-
 .../GUI/keysight_videomaps/liveplotting.py    |   2 +-
 .../HVI/__pycache__/__init__.cpython-37.pyc   | Bin 142 -> 135 bytes
 .../HVI_charge_stability_diagram.py           |  26 +-
 ...VI_charge_stability_diagram.cpython-37.pyc | Bin 3990 -> 4056 bytes
 .../__pycache__/__init__.cpython-37.pyc       | Bin 184 -> 160 bytes
 .../HVI/single_shot_exp/HVI_single_shot.py    |  34 +--
 .../HVI_single_shot.cpython-37.pyc            | Bin 2767 -> 3079 bytes
 .../__pycache__/__init__.cpython-37.pyc       | Bin 158 -> 151 bytes
 .../__pycache__/__init__.cpython-37.pyc       | Bin 138 -> 131 bytes
 core_tools/drivers/D5a.py                     | 150 +++++++++
 core_tools/drivers/M3102A.py                  | 285 +++++++++---------
 core_tools/drivers/M3102_firmware_loader.py   |  33 ++
 .../__pycache__/harware.cpython-37.pyc        | Bin 5155 -> 5148 bytes
 .../__pycache__/__init__.cpython-37.pyc       | Bin 145 -> 138 bytes
 .../pulse_lib_sweep.cpython-37.pyc            | Bin 8825 -> 8818 bytes
 core_tools/sweeps/exp_readout_runner.py       | 145 +++++++++
 core_tools/utility/dig_utility.py             |   6 +-
 .../utility/digitizer_param_conversions.py    | 210 +++++++++++++
 core_tools/utility/powerpoint.py              |   2 +-
 example_T2Hahn.py                             |  48 +++
 .../__pycache__/__init__.cpython-37.pyc       | Bin 69531 -> 69524 bytes
 23 files changed, 774 insertions(+), 177 deletions(-)
 create mode 100644 core_tools/drivers/D5a.py
 create mode 100644 core_tools/drivers/M3102_firmware_loader.py
 create mode 100644 core_tools/sweeps/exp_readout_runner.py
 create mode 100644 core_tools/utility/digitizer_param_conversions.py
 create mode 100644 example_T2Hahn.py

diff --git a/core_tools.egg-info/SOURCES.txt b/core_tools.egg-info/SOURCES.txt
index 57af58f2..87e3eea8 100644
--- a/core_tools.egg-info/SOURCES.txt
+++ b/core_tools.egg-info/SOURCES.txt
@@ -1,3 +1,4 @@
+README.md
 setup.py
 core_tools/__init__.py
 core_tools.egg-info/PKG-INFO
@@ -34,13 +35,8 @@ core_tools/GUI/param_viewer/param_viewer_GUI_window.py
 core_tools/HVI/__init__.py
 core_tools/HVI/charge_stability_diagram/HVI_charge_stability_diagram.py
 core_tools/HVI/charge_stability_diagram/__init__.py
-core_tools/HVI/charge_stability_diagram_fast/HVI_charge_stability_diagram.py
-core_tools/HVI/charge_stability_diagram_fast/__init__.py
 core_tools/HVI/single_shot_exp/HVI_single_shot.py
 core_tools/HVI/single_shot_exp/__init__.py
-core_tools/HVI/single_shot_exp_SD_corr/HVI_single_shot.py
-core_tools/HVI/single_shot_exp_SD_corr/__init__.py
-core_tools/HVI/single_shot_exp_SD_corr/generate_SD_corr_sequence.py
 core_tools/sweeps/__init__.py
 core_tools/sweeps/pulse_lib_sweep.py
 core_tools/sweeps/Modulated_scans/DEMOD_tests.py
diff --git a/core_tools/GUI/keysight_videomaps/data_getter/scan_generator_Keysight.py b/core_tools/GUI/keysight_videomaps/data_getter/scan_generator_Keysight.py
index 5d003b66..efa219dd 100644
--- a/core_tools/GUI/keysight_videomaps/data_getter/scan_generator_Keysight.py
+++ b/core_tools/GUI/keysight_videomaps/data_getter/scan_generator_Keysight.py
@@ -5,8 +5,8 @@ Created on Fri Aug  9 16:50:02 2019
 @author: V2
 """
 from qcodes import MultiParameter
-from projects.keysight_measurement.HVI.ChargeStabilityDiagram.HVI_charge_stability_diagram import load_HVI, set_and_compile_HVI, excute_HVI, HVI_ID
-from projects.keysight_measurement.M3102A import DATA_MODE
+from core_tools.HVI.charge_stability_diagram.HVI_charge_stability_diagram import load_HVI, set_and_compile_HVI, excute_HVI, HVI_ID
+from core_tools.drivers.M3102A import DATA_MODE
 import matplotlib.pyplot as plt
 import numpy as np
 import time
diff --git a/core_tools/GUI/keysight_videomaps/liveplotting.py b/core_tools/GUI/keysight_videomaps/liveplotting.py
index 359c2ab3..a1bf6fdb 100644
--- a/core_tools/GUI/keysight_videomaps/liveplotting.py
+++ b/core_tools/GUI/keysight_videomaps/liveplotting.py
@@ -13,7 +13,7 @@ from .data_getter import scan_generator_Virtual
 from .plotter.plotting_functions import _1D_live_plot, _2D_live_plot
 from qcodes import MultiParameter
 from qcodes.measure import Measure
-from qtt.utilities.tools import addPPTslide
+from core_tools.utility.powerpoint import addPPTslide
 import time
 import logging
 
diff --git a/core_tools/HVI/__pycache__/__init__.cpython-37.pyc b/core_tools/HVI/__pycache__/__init__.cpython-37.pyc
index af264deeb0e449be89943581b48da81e7bec6a57..e4c2bbbd43a89f35f89686d4f261a45c61a94348 100644
GIT binary patch
delta 45
zcmeBUY-i+l;^pOH0D?W%hbD4ch#4eX#grMvC+DZ6#w6z#rN)=!=jRkpOwa=W20#s~

delta 52
zcmZo?>|^A1;^pOH0D@o2TPJc`$eJfx#Z=^^#3$#cq{gJAmZj$87v!eqm4F2%M(P0o
DeJBvm

diff --git a/core_tools/HVI/charge_stability_diagram/HVI_charge_stability_diagram.py b/core_tools/HVI/charge_stability_diagram/HVI_charge_stability_diagram.py
index f9090862..949d19ae 100644
--- a/core_tools/HVI/charge_stability_diagram/HVI_charge_stability_diagram.py
+++ b/core_tools/HVI/charge_stability_diagram/HVI_charge_stability_diagram.py
@@ -8,7 +8,7 @@ try:
 except:
     warnings.warn("\nAttemting to use a file that needs Keysight AWG libraries. Please install if you need them.\n")
 
-import V2_software.HVI_files.charge_stability_diagram as ct
+import core_tools.HVI.charge_stability_diagram as ct
 import time
 
 HVI_ID = "HVI_charge_stability_diagram.HVI"
@@ -33,12 +33,12 @@ def load_HVI(AWGs, channel_map, *args,**kwargs):
 	HVI = keysightSD1.SD_HVI()
 	a = HVI.open(ct.__file__[:-11] + "HVI_charge_stability_diagram.HVI")
 
-	error = HVI.assignHardwareWithUserNameAndSlot("Module 0",0,2)
-	error = HVI.assignHardwareWithUserNameAndSlot("Module 1",0,3)
-	error = HVI.assignHardwareWithUserNameAndSlot("Module 2",0,4)
-	error = HVI.assignHardwareWithUserNameAndSlot("Module 3",0,5)
-	error = HVI.assignHardwareWithUserNameAndSlot("Module 4",0,6)
-	
+	error1 = HVI.assignHardwareWithUserNameAndSlot("Module 0",0,2)
+	error2 = HVI.assignHardwareWithUserNameAndSlot("Module 1",0,3)
+	error3 = HVI.assignHardwareWithUserNameAndSlot("Module 2",0,4)
+	error4 = HVI.assignHardwareWithUserNameAndSlot("Module 3",0,5)
+	error5 = HVI.assignHardwareWithUserNameAndSlot("Module 4",0,6)
+	print(a, error1, error2, error3, error4, error5)
 	HVI.compile()
 	HVI.load()
 
@@ -87,7 +87,7 @@ def excute_HVI(HVI, AWGs, channel_map, playback_time, n_rep, *args, **kwargs):
 	length = int(playback_time/10 + 20)
 
 	for awgname, awg in AWGs.items():
-		awg.writeRegisterByNumber(3, int(length))
+		awg.awg.writeRegisterByNumber(3, int(length))
 
 	dig = kwargs['digitizer'] 
 	t_single_point = kwargs['t_measure']
@@ -95,12 +95,12 @@ def excute_HVI(HVI, AWGs, channel_map, playback_time, n_rep, *args, **kwargs):
 
 	t_single_point_formatted = int((t_single_point)/10) # divide by 10 since 100MHz clock (160 ns HVI overhead)
 
-	dig.writeRegisterByNumber(2, npt)
-	dig.writeRegisterByNumber(3, t_single_point_formatted)
-	
+	dig.SD_AIN.writeRegisterByNumber(2, npt)
+	dig.SD_AIN.writeRegisterByNumber(3, t_single_point_formatted)
+
 	if 'averaging' in kwargs:
-		dig.set_meas_time(kwargs['t_measure'])
-		dig.set_MAV_filter(16,1)
+		dig.set_meas_time(kwargs['t_measure'], fourchannel = True)
+		dig.set_MAV_filter(16,1, fourchannel = True)
 
 	HVI.start()
 
diff --git a/core_tools/HVI/charge_stability_diagram/__pycache__/HVI_charge_stability_diagram.cpython-37.pyc b/core_tools/HVI/charge_stability_diagram/__pycache__/HVI_charge_stability_diagram.cpython-37.pyc
index 86e01ff890b58bae64f67c5126c2de13adcca9b7..025292859ab786d3674da877c1187e7e8d03aca9 100644
GIT binary patch
delta 874
zcmY*Y&rcIU6yDiwx7+TI5`xf@2!=p$6U7Lkk;Ev7U@@uuP-94OQ#wN_w7bo8k&rB2
z^x%aUnP@y|d(?x8;b!86#FKx3X5!5gi6?Jz^39egZZhASZ{FLNZ@#ztviDoBIboU!
z37#KM_8<KIVEVFU0*k@A4Xgf>J(FK&<AK`HaRMW(H<j%~$P8d6Fg;`zup}_UH-cE8
z1@Rye=z;NAMH1xArW8hN4=XPaO0vU9qbpjYW!LAiqF1ezD}+ZXUIE)Cdl$WSS0$8s
z^gM6H;iEWm6fYdb(YF%q2bG6Q#j*UuVW;R}k}rCcIKJmq>c_V??`8Asc_hVh>N(WI
z9;w}N)Q&Wy$uzU~M%_o3L?b&&PeR~kty0)pE)>@tzg#6e>N=FvXc|(qTY#u-!2o-u
z^&^{o($eT7`>LJl$^@u^n$o1S)SZ{N6X1`&CN~sj#4paTMH&j$0;?&59Y)9^K~gXb
z7O$)HJ)A}Z6{~3p?ACW$bt)ES3$DO>QX?kp#O8uzTYFFA-_g_^8*zQb+n~kOg6opX
zK^q9n*&0{CVX6V)N@26aPi)e%Pv%LfT=xl`*vf5GmkH(S!lW~v&9NQbo)Pz_T>?@9
z1_hiJ@b~sS;({AI0xfD>tq`~5ukskQcMDZQN1&D~xLo49Mn{IHe5YP^OBLeOJka8-
zc(hvZeS-h<aLnlI?37^|Fb_kvkOlj8)F#t$MvePuk$o{Pt68C9h9%5W+u*+;55Z)I
z#AIWw(WSyEe>OYP8BuQw7#Er}#}NV@XOaCf&qlAq*&^$=&OrA$YdAS63U3IQ3NyRb
uXx}vKLUSgw)AWkS-4Y;v3_2@djvZK6dxSk;0LBhzId-q`D;Z!@$=N@P?CW~~

delta 784
zcmY+C&ubGw6vuaVvy)BtM~d2*lw#AGgF#y%SWBy7Eu}v|R0tF;Tb3oWNu$Z`GP`Xr
zTfFK)1c!Q*%~9|oy@?*Qe}Eo5$Y2j1{TKA&ytIV6FdyFc&6{~|-<u!fugA?r(;Sk-
zS3la{eD&UpWh*BX7VExP5oa&uH?vxTQ=JSEMD(7rcdQJd%z?6m%AY8sCm~6kFb{T=
zeS|D#sOfOF5p=_L$K#n!;F4;N9jXg=RgcnuzLn^>$n|HtF>mkGoJK%AI||$WR7iuz
zUL%g|?Z~5%9mQU_<@)w|&543$yz5fWUR_^vn&R5XX5Y*r3-MJn#!@_*l{KWvbmHI;
ze?b~tUsdTCd!dbv6osHA0Jx7)W*@Z*s<2;L3B6-~wem<MK`H7<OVVc1miLDC^h8f(
zR?A&qPGzDcR!<iHXb3x#<iRGn1V6zv6+R`w1ec^9>PKXtZ~bT~RiQIPuS-?q;6v^w
zI`ey2lyI+#xzhGystLh9=|y&=UwS$Q#T5YO0h54>fI<B{<5Mr>8R!eg9nWvYTN_z-
zx8=KCk1N993Td}Ctk{X#eyig-VIb0Q#Si0+;{Q&k8PKj9#~wN9iVCU9?71;nI0wBH
z-85ta8N#*%=`8zfJVcz0nsayoV(+j=W~(rLYB<w_;jph}$>h0#=0!2B64R`+r3pA&
z0n7?;&2gZ5$KmXnHKophk2S3;A``P}`FRMu0l3-U`EJcj-U18Pmz<@w6*>cZi-2Xo
e3SgC8&fmTOz2jMz5J!}8YV`d_P-f5a5B>q%*3l9G

diff --git a/core_tools/HVI/charge_stability_diagram/__pycache__/__init__.cpython-37.pyc b/core_tools/HVI/charge_stability_diagram/__pycache__/__init__.cpython-37.pyc
index dca38e504ef20a07126f6bdee1ad32042b8a70d8..a1d7f140b103c6c8d3f43764e3f5466a6d5dd128 100644
GIT binary patch
delta 61
zcmdnNxPXzziI<m)0SNY3ABvmEW3K9%Y!y>x6rY@*k{XkoUz8eOlAoVbjLh^1^PJdV
F0RS%A6n6js

delta 85
zcmZ3$xPy_$iI<m)0SI2YUW}Q@V{V(6Y!y>x6rY@*k{VN7keR1jP?VpQnp{#GQ(Bx_
nR2)-Wl3I|Fm=_af6knX5R#Ki=lp5m^<{6)snUh*PF~b4?u3R2d

diff --git a/core_tools/HVI/single_shot_exp/HVI_single_shot.py b/core_tools/HVI/single_shot_exp/HVI_single_shot.py
index e87c7ce3..dacc568d 100644
--- a/core_tools/HVI/single_shot_exp/HVI_single_shot.py
+++ b/core_tools/HVI/single_shot_exp/HVI_single_shot.py
@@ -4,7 +4,7 @@ define a function that loads the HVI file that will be used thoughout the experi
 import keysightSD1
 import core_tools.HVI.single_shot_exp as ct
 
-HVI_ID = "HVI_single_shot_test.HVI"
+HVI_ID = "HVI_single_shot.HVI"
 
 
 def load_HVI(AWGs, channel_map, *args,**kwargs):
@@ -24,7 +24,7 @@ def load_HVI(AWGs, channel_map, *args,**kwargs):
 
 			
 	HVI = keysightSD1.SD_HVI()
-	error = HVI.open(ct.__file__[:-11] + "HVI_single_shot_test.HVI")
+	error = HVI.open(ct.__file__[:-11] + "HVI_single_shot.HVI")
 	print(error)
 
 	error = HVI.assignHardwareWithUserNameAndSlot("Module 0",0,2)
@@ -33,6 +33,10 @@ def load_HVI(AWGs, channel_map, *args,**kwargs):
 	print(error)
 	error = HVI.assignHardwareWithUserNameAndSlot("Module 2",0,4)
 	print(error)
+	error = HVI.assignHardwareWithUserNameAndSlot("Module 3",0,5)
+	print(error)
+	error = HVI.assignHardwareWithUserNameAndSlot("Module 4",0,6)
+	print(error)
 
 	error = HVI.compile()
 	print(error)
@@ -87,23 +91,19 @@ def excute_HVI(HVI, AWGs, channel_map, playback_time, n_rep, *args, **kwargs):
 
 	for awgname, awg in AWGs.items():
 		err = awg.awg.writeRegisterByNumber(2, int(nrep))
-		print(err)
 		err = awg.awg.writeRegisterByNumber(3, int(length))
-		print(err)
-
-	# dig = kwargs['digitizer'] 
-	# dig_wait = kwargs['dig_wait']
-
-	# delay_1 = int(dig_wait/10)
-	# dig.writeRegisterByNumber(2, int(nrep))
-	# dig.writeRegisterByNumber(4, int(delay_1))
-	# dig.writeRegisterByNumber(3, int(length-delay_1-2))
 
-	# if 'averaging' in kwargs:
-	# 	dig.set_meas_time(kwargs['t_measure'])
-	# 	dig.set_MAV_filter(16,1)
+	dig = kwargs['digitizer'] 
+	dig_wait = kwargs['dig_wait']
+	print(dig_wait)
+	delay_1 = int(dig_wait/10)
+	err = dig.SD_AIN.writeRegisterByNumber(2, int(nrep))
+	err = dig.SD_AIN.writeRegisterByNumber(4, int(delay_1))
+	err = dig.SD_AIN.writeRegisterByNumber(3, int(length-delay_1-2))
+
+	if 'averaging' in kwargs:
+		dig.set_meas_time(kwargs['t_measure'], fourchannel=True)
+		dig.set_MAV_filter(16,1, fourchannel=True)
 	# start sequence
 	err = AWGs['AWG1'].awg.writeRegisterByNumber(1, 0)
-	print(err)
 	err = AWGs['AWG1'].awg.writeRegisterByNumber(0,int(1))
-	print(err)
diff --git a/core_tools/HVI/single_shot_exp/__pycache__/HVI_single_shot.cpython-37.pyc b/core_tools/HVI/single_shot_exp/__pycache__/HVI_single_shot.cpython-37.pyc
index e6205378733f97db7ef69f3e3ed3f863a0013a72..11561082c9b27a133d61f22a935dc0989b3894a3 100644
GIT binary patch
delta 956
zcmZ8g%}*0S6rbsC_p9A)DUq}^VnIx-G2ufKzfL72hzWSW$OV|JJ3}cfyExqfl5IF}
zFlszBd-DW;fTteyq(=|j_dudIj^2#M#5Y?5gxSoSdB6AOH*ep2bA07>(V8+%1Ht(5
z_Up=%T*vyH>wo-wmNXk)-DRY?=66oYsav@7=lE)tW+|rTk<?a$OsF1Uj6&rwOD#Zi
z(I;HP`R-plgfBBhW71@KPn+{;%Vm{2r)jwEOuc)mbXO!Dmn(q&o^c*7ZBG)%r)<gb
zIU|AZyUpm0T*KqhH+d-fE)U?5?l1WZz9$Y;(|ib)*xYjMS1)X5g9MFD7N<SJ*%luJ
z6E6!<ITORtPo;SC0qD&X2$bPBKsq8OF>){!m``Cg4IE9V<^B61KEy|88ZF;mO11U2
z5gO~MLy1ZO5?MG(wKM+_84#_9yc}7C?9g-xwX?7)u$qA!nj&l9Wl~&52e@s8+4b}#
zt{$R-mnFFNfxZVN^r2NmR4T$@aWU0GWkca7;Gw`ZdI?d3X5OcuG}(0(3T7s{1UjZx
z2_=D>G?xT@j^qMFA+&@szksTJ_B)J_p}W3KyC>=gyrR=ay%99_7>_mBk{!DdaFMlG
zx3?L$>(DT<9*|9DH(Q)NtKvAb>bJPFW_uoUPxIi)MaE;fwm37vfga6QwODF+0oS6A
zUfJ&#R%KzD)Mn>nv&nj7y^2~wovS@3s|`0`yqZhu&WnPzB*b+gMuj-5dMta;1+n5X
zuO6($s=ZV9>`lhU!7G*+=dlET!mC16DO-jXA`=%|MHmdW>$Cz!;87$!F#;emG@MTr
zZ~+gbG%UU>><f4hv_fLxkHD*H#3~`wcL?E`XvP?wN_sVS{<Z?bcDHz+xJMDMZg!dr
W{-h-niVM+Hgkd=PXk3%Vw1I!%X7REB

delta 718
zcmZuv&1(}u6rZ<~O*Wg4YHZVlgr<!>GzfwP4~mzDf-OQsMSF=3>CO<>=F8csO(7vd
z4jv`!T^hu{AU6?&{sTR`ckg=f;6Z$|RcOS4_nVL3@4cD#-n_}3Ev2ieludAae*5v+
z`}8FJX)(9+WS4nvr`ZzBd)`${32*YHVEiJJA7}n|*o5S4-B^MD3jGIkb2g<>D6e49
zpEc)pz2D-@>2gu$;+1H1du`EC;eme|*;?Gtm&Y?k7K%`$7UaM({F0f6)!8lc6h=Bs
z#gs*yf>f{7INonK2TZwb5ttn&MNj6Cl6ehUdk!Tnzv2nS8XbLmyXRsynAlAuJOhCw
z<W)q+)N33`v?$trH18dPB7?{nM#C5`D2D>!#xfbh(CY6D-~^^*n>@W!H-_<{J+NOG
zCzMlE#>Ox;h6xoJB*p;b4--MgnrB{?_s9UanJ1xdl*i`${xSv1@c}$K@64@7q!ORK
zwmtxhZ0+5>A$3X;5NZ`Opl(M=olHO*N6ldANV-Zq6iwGtLROD!hwXhK<z*C9l3`_K
zR=Y22Skq9}a9&scn_YOUn-oy=`M1Qj=sSlhtn5A1csdTTEx2T4AOjX!GOgz<$m_^d
z>>$P%?>dahxIap)SHr$8T>NdK-;)cd4WjxV?rzIf+=RUfLtJ=4*zS`5HBqA1tn6=)
Cm8r}C

diff --git a/core_tools/HVI/single_shot_exp/__pycache__/__init__.cpython-37.pyc b/core_tools/HVI/single_shot_exp/__pycache__/__init__.cpython-37.pyc
index f1adf6021ec9e7217b4e4cc083e02aa377af54c3..a0ccd6ef1aadb3bc5a81e6231ed0a2fd5d30a2f5 100644
GIT binary patch
delta 45
zcmbQoIGvH(iI<m)0SNY3ADYN*A?A>56;oyupPZkP8k3w~lp0@>pPy4aF~JT14PXuH

delta 52
zcmbQvIFFIriI<m)0SJC6Z=J|(A?u!O6;qLu5}%x(k{XkeT9%rVUyz%cR{|E87-<Ip
Dg_sc!

diff --git a/core_tools/__pycache__/__init__.cpython-37.pyc b/core_tools/__pycache__/__init__.cpython-37.pyc
index 67a257e43c02d86619b43d0eafc7d3dfd5a51709..f5eacf945784c6cb047c3403090c6003467ba161 100644
GIT binary patch
delta 45
zcmeBTY-Z$k;^pOH0D?W%hbD4ci0LF-#grMvC+DZ6#w6z#rN)=!=jRkpOwa-V1b7Xb

delta 52
zcmZo>>|*40;^pOH0D@o2TPJc`$QmbG#Z=^^#3$#cq{gJAmZj$87v!eqm4F2%Mrr{7
Ddkhf2

diff --git a/core_tools/drivers/D5a.py b/core_tools/drivers/D5a.py
new file mode 100644
index 00000000..3c090ab6
--- /dev/null
+++ b/core_tools/drivers/D5a.py
@@ -0,0 +1,150 @@
+from qcodes.instrument.base import Instrument
+from qcodes.utils.validators import Enum, Numbers
+import numpy as np
+
+try:
+    from spirack import D5a_module
+except ImportError:
+    raise ImportError(('The D5a_module class could not be found. '
+                       'Try installing it using pip install spirack'))
+
+from functools import partial
+
+
+class D5a(Instrument):
+    """
+    Qcodes driver for the D5a DAC SPI-rack module.
+
+    functions:
+    -   set_dacs_zero   set all DACs to zero voltage
+
+    parameters:
+    -   dacN:       get and set DAC voltage
+    -   stepsizeN   get the minimum step size corresponding to the span
+    -   spanN       get and set the DAC span: '4v uni', '4v bi', or '2.5v bi'
+
+    where N is the DAC number from 1 up to 16
+
+    """
+
+    def __init__(self, name, spi_rack, module, inter_delay=0.1, dac_step=10e-3,
+                 reset_voltages=False, mV=False, number_dacs=16, **kwargs):
+        """ Create instrument for the D5a module.
+
+        The D5a module works with volts as units. For backward compatibility
+        there is the option to allow mV for the dacX parameters.
+
+        The output span of the DAC module can be changed with the spanX
+        command. Be carefull when executing this command with a sample
+        connected as voltage jumps can occur.
+
+        Args:
+            name (str): name of the instrument.
+
+            spi_rack (SPI_rack): instance of the SPI_rack class as defined in
+                the spirack package. This class manages communication with the
+                individual modules.
+
+            module (int): module number as set on the hardware.
+            inter_delay (float): time in seconds, passed to dac parameters of the object
+            dac_step (float): max step size (V or mV), passed to dac parameters of the object
+            reset_voltages (bool): passed to D5a_module constructor
+            mV (bool): if True, then use mV as units in the dac parameters
+            number_dacs (int): number of DACs available. This is 8 for the D5mux
+        """
+        super().__init__(name, **kwargs)
+
+        self.d5a = D5a_module(spi_rack, module, reset_voltages=reset_voltages)
+        self._number_dacs = number_dacs
+
+        self._span_set_map = {
+            '4v uni': 0,
+            '4v bi': 2,
+            '2v bi': 4,
+        }
+
+        self._span_get_map = {v: k for k, v in self._span_set_map.items()}
+
+        self.add_function('set_dacs_zero', call_cmd=self._set_dacs_zero,
+                          docstring='Reset all dacs to zero voltage. No ramping is performed.')
+
+        if mV:
+            self._gain = 1e3
+            unit = 'mV'
+        else:
+            self._gain = 1
+            unit = 'V'
+
+        # make a cache, to get a numerically not rounded value.
+        self.voltage_cache = np.zeros([number_dacs])
+        for i in range(number_dacs):
+            self.voltage_cache[i] = self.__get_dac(i)
+
+        for i in range(self._number_dacs):
+            validator = self._get_validator(i)
+
+            self.add_parameter('dac{}'.format(i + 1),
+                               label='DAC {}'.format(i + 1),
+                               get_cmd=partial(self._get_dac, i),
+                               set_cmd=partial(self._set_dac, i),
+                               unit=unit,
+                               vals=validator,
+                               step=dac_step,
+                               inter_delay=inter_delay)
+
+            self.add_parameter('stepsize{}'.format(i + 1),
+                               get_cmd=partial(self.d5a.get_stepsize, i),
+                               unit='V',
+                               docstring='Returns the smallest voltage step of the DAC.')
+
+            self.add_parameter('span{}'.format(i + 1),
+                               get_cmd=partial(self._get_span, i),
+                               set_cmd=partial(self._set_span, i),
+                               vals=Enum(*self._span_set_map.keys()),
+                               docstring='Change the output span of the DAC. This command also updates the validator.')
+
+    def set_dac_unit(self, unit: str) -> None:
+        """Set the unit of dac parameters"""
+        allowed_values = Enum('mV', 'V')
+        allowed_values.validate(unit)
+        self._gain = {'V': 1, 'mV': 1e3}[unit]
+        for i in range(1, self._number_dacs + 1):
+            setattr(self.parameters[f'dac{i}'], 'unit', unit)
+            setattr(self.parameters[f'dac{i}'], 'vals', self._get_validator(i - 1))
+
+    def _set_dacs_zero(self):
+        for i in range(self._number_dacs):
+            self._set_dac(i, 0.0)
+
+    def _set_dac(self, dac, value):
+        self.voltage_cache[dac] = value
+        return self.d5a.set_voltage(dac, value / self._gain)
+
+    def _get_dac(self, dac):
+        return self.voltage_cache[dac]
+    
+    def __get_dac(self, dac):
+        return self._gain * self.d5a.voltages[dac]
+
+    def _get_span(self, dac):
+        return self._span_get_map[self.d5a.span[dac]]
+
+    def _set_span(self, dac, span_str):
+        self.d5a.change_span_update(dac, self._span_set_map[span_str])
+        self.parameters['dac{}'.format(
+            dac + 1)].vals = self._get_validator(dac)
+
+    def _get_validator(self, dac):
+        span = self.d5a.span[dac]
+
+        if span == D5a_module.range_2V_bi:
+            validator = Numbers(-2 * self._gain, 2 * self._gain)
+        elif span == D5a_module.range_4V_bi:
+            validator = Numbers(-4 * self._gain, 4 * self._gain)
+        elif span == D5a_module.range_4V_uni:
+            validator = Numbers(0, 4 * self._gain)
+        else:
+            msg = 'The found DAC span of {} does not correspond to a known one'
+            raise Exception(msg.format(span))
+
+        return validator
diff --git a/core_tools/drivers/M3102A.py b/core_tools/drivers/M3102A.py
index e33a5eed..ced545c3 100644
--- a/core_tools/drivers/M3102A.py
+++ b/core_tools/drivers/M3102A.py
@@ -1,6 +1,5 @@
 from qcodes import Instrument, MultiParameter
 from dataclasses import dataclass
-from si_prefix import si_format
 import warnings
 import logging
 import time
@@ -62,7 +61,6 @@ class line_trace(MultiParameter):
     """
     def __init__(self, name, instrument, inst_name , raw=False):
         self.my_instrument = instrument
-        self.name = name
         super().__init__(name=inst_name,
                          names = (name +'_ch1', name +'_ch2'),
                          shapes=((1,),(1,)),
@@ -85,11 +83,10 @@ class line_trace(MultiParameter):
         """
         generate channels mask for start multiple control functions
         """
-        channel_mask = list('0000') # TODO make general version for n channels
+        channel_mask = 0
 
         for i in self.channels:
-            channel_mask[-i] = '1'
-        channel_mask = int(''.join(channel_mask), 2)
+            channel_mask += 1 << (i - 1)
 
         return channel_mask
 
@@ -102,104 +99,133 @@ class line_trace(MultiParameter):
 
         return self.get_data()
 
-    def get_data(self):
-        """
-        Get data from the cards
 
-        TODO : IMPLEMENT BUFFERING METHODS for faster data transfers.
-        """
+    def _read_channel_data(self, channel_number, channel_data_raw):
+        start = time.perf_counter()
+        i = 0
+        points_aquired = 0
+        while points_aquired < len(channel_data_raw):
+            np_ready = self.my_instrument.SD_AIN.DAQcounterRead(channel_number)
+            check_error(np_ready)
+
+            if np_ready + points_aquired > len(channel_data_raw):
+                np_ready = len(channel_data_raw) - points_aquired
+                logging.error("more data points in digitizer ram then what is being collected.")
+
+
+            if np_ready > 0:
+                # Always read with a timeout to prevent infinite blocking of HW (and reboot of system).
+                # There are np_ready points available. This can be read in 1 second.
+                req_points = self.my_instrument.SD_AIN.DAQread(channel_number, np_ready, 1000)
+                check_error(req_points)
+                if not type(req_points) is int and len(req_points) != np_ready:
+                    logging.error(f'DAQread failure. ready:{np_ready} read:{len(req_points)}')
+
+                channel_data_raw[points_aquired: points_aquired + np_ready] = req_points
+                points_aquired = points_aquired + np_ready
+                i = 0
+
+            if np_ready == 0:
+                i+=1
+                time.sleep(0.001)
+                if i > 100:
+                    logging.error(f"digitizer did not manage to collect enough data points for channel {channel_number}, "
+                                  f"returning zeros. ({points_aquired})")
+                    break
+
+        logging.info(f'channel {channel_number}: retrieved {points_aquired} points in {(time.perf_counter()-start)*1000:3.1f} ms')
+
+
+    def _get_data_normal(self):
         data_out = tuple()
 
         for channel_property in self.my_instrument.channel_properties.values():
-            start = time.perf_counter()
-            if self.my_instrument.mode == MODES.AVERAGE and channel_property.number in [2,4]:
-                continue
             if channel_property.active == False:
                 continue
 
             # make flat data structures.
-            if self.my_instrument.mode == MODES.AVERAGE:
-                channel_data_raw = np.zeros( [ channel_property.cycles*10], np.uint16)
-            else:
-                channel_data_raw = np.zeros( [ channel_property.cycles*channel_property.points_per_cycle], np.double)
-
-            # get data out of the buffer
-            np_ready = self.my_instrument.SD_AIN.DAQcounterRead(channel_property.number)
-            check_error(np_ready)
-
-            i = 0
-            points_aquired = 0
-            while points_aquired < len(channel_data_raw):
-#                print('np_ready = %i' % np_ready)
-                if np_ready + points_aquired > len(channel_data_raw):
-                    np_ready = len(channel_data_raw) - points_aquired
-                    logging.error("more data points in digitizer ram then what is being collected.")
+            channel_data_raw = np.zeros([channel_property.cycles*channel_property.points_per_cycle], np.double)
+
+            self._read_channel_data(channel_property.number, channel_data_raw)
+
+            # format the data with correct amplitude
+            # convert 16-bit to relative scale (-1.0 .. 1.0)
+            f = 1 / 32768
+            # multiply with the relevant channel amplitude (standard in volt -> mV!)
+            f *= channel_property.full_scale*1000
+            # inplace multiplication on numpy array is fast
+            channel_data_raw *= f
+
+            # reshape for [repetitions, time] and average
+            channel_data_raw = channel_data_raw.reshape([channel_property.cycles, channel_property.points_per_cycle])
+
+            if self.my_instrument.data_mode == DATA_MODE.FULL:
+                data_out += (channel_data_raw, )
+            elif self.my_instrument.data_mode == DATA_MODE.AVERAGE_TIME:
+                data_out += (np.average(channel_data_raw, axis = 1), )
+            elif self.my_instrument.data_mode == DATA_MODE.AVERAGE_CYCLES:
+                data_out += (np.average(channel_data_raw, axis = 0), )
+            elif self.my_instrument.data_mode == DATA_MODE.AVERAGE_TIME_AND_CYCLES:
+                data_out += (np.average(channel_data_raw), )
 
+        return data_out
 
-                if np_ready > 0:
-                    # Always read with a timeout to prevent infinite blocking of HW (and reboot of system).
-                    # There are np_ready points available. This can be read in 1 second.
-                    req_points = self.my_instrument.SD_AIN.DAQread(channel_property.number, np_ready, 1000)
-                    check_error(req_points)
-                    if not type(req_points) is int and len(req_points) != np_ready:
-                        logging.error(f'DAQread failure. ready:{np_ready} read:{len(req_points)}')
 
-                    channel_data_raw[points_aquired: points_aquired + np_ready] = req_points
-                    points_aquired = points_aquired + np_ready
-                    i = 0
+    def _get_data_average(self):
+        data_out = tuple()
 
-                if np_ready == 0:
-                    i+=1
-                    time.sleep(0.001)
-                    if i > 100:
-                        logging.error(f"digitizer did not manage to collect enough data points, returning zeros. {points_aquired}, {channel_property}")
-                        break
+         # note that we are acquirering two channels at the same time in this mode.
+        for channel_property in self.my_instrument.channel_properties.values():
+            # averaging mode: channels are read in pairs.
+            if channel_property.number in [2,4]:
+                # even numbers are read with odd channel.
+                continue
+            if channel_property.number not in self.channels and channel_property.number+1 not in self.channels:
+                # don't read anything if both channels not active.
+                continue
 
-                np_ready = self.my_instrument.SD_AIN.DAQcounterRead(channel_property.number)
+            # make flat data structures.
+            channel_data_raw = np.zeros([channel_property.cycles*10], np.uint16)
 
-            logging.info(f'channel {channel_property.number}: retrieved {points_aquired} points in {(time.perf_counter()-start)*1000:3.1f} ms')
+            self._read_channel_data(channel_property.number, channel_data_raw)
 
             # format the data
-            if self.my_instrument.mode == MODES.AVERAGE:
-                # note that we are acquirering two channels at the same time in this mode..
-                channel_data_raw = channel_data_raw.reshape([channel_property.cycles, 10]).transpose().astype(np.int32)
-                channel_data = np.empty([2,channel_property.cycles])
-                channel_data[0] = ((channel_data_raw[1] & 2**16-1) << 16) | (channel_data_raw[0] & 2**16-1)
-                channel_data[1] = ((channel_data_raw[3] & 2**16-1) << 16) | (channel_data_raw[2] & 2**16-1)
-
-                # correct amplitude,
-                channel_data[0] *= 5 * 2/(channel_property.t_measure-160)*channel_property.full_scale / 2**15 # outputs V, 5 for the aquisition in blocks of 5
-                channel_data[1] *= 5 * 2/(channel_property.t_measure-160)*channel_property.full_scale / 2**15 # outputs V
-
-                # make sure only the data of the wanted channel is returned.
-                if channel_property.number in self.channels:
-                    if self.my_instrument.data_mode in [DATA_MODE.AVERAGE_CYCLES, DATA_MODE.AVERAGE_TIME_AND_CYCLES]:
-                       data_out += (np.average(channel_data[0]), )
-                    else:
-                        data_out += (channel_data[0], )
-
-                if channel_property.number + 1 in self.channels:
-                    if self.my_instrument.data_mode in [DATA_MODE.AVERAGE_CYCLES, DATA_MODE.AVERAGE_TIME_AND_CYCLES]:
-                       data_out += (np.average(channel_data[1]), )
-                    else:
-                        data_out += (channel_data[1], )
-            else:
-                # correct amplitude,
-                channel_data_raw /= 32768. #convert bit to relative scale (-1/1)
-                channel_data_raw *= channel_property.full_scale*1000 #multiply with the relevant channel amplitude (standard in volt -> mV!)
-                channel_data_raw = channel_data_raw.reshape([channel_property.cycles, channel_property.points_per_cycle])
-
-                if self.my_instrument.data_mode == DATA_MODE.FULL:
-                    data_out += (channel_data_raw, )
-                elif self.my_instrument.data_mode == DATA_MODE.AVERAGE_TIME:
-                    data_out += (np.average(channel_data_raw, axis = 1), )
-                elif self.my_instrument.data_mode == DATA_MODE.AVERAGE_CYCLES:
-                    data_out += (np.average(channel_data_raw, axis = 0), )
-                elif self.my_instrument.data_mode == DATA_MODE.AVERAGE_TIME_AND_CYCLES:
-                    data_out += (np.average(channel_data_raw), )
+            channel_data_raw = channel_data_raw.reshape([channel_property.cycles, 10]).transpose().astype(np.int32)
+            channel_data = np.empty([2,channel_property.cycles])
+            channel_data[0] = ((channel_data_raw[1] & 2**16-1) << 16) | (channel_data_raw[0] & 2**16-1)
+            channel_data[1] = ((channel_data_raw[3] & 2**16-1) << 16) | (channel_data_raw[2] & 2**16-1)
+
+            # correct amplitude,
+            # outputs V, 5 for the aquisition in blocks of 5
+            channel_data[0] *= 5 * 2/(channel_property.t_measure-160)*channel_property.full_scale / 2**15
+            channel_data[1] *= 5 * 2/(channel_property.t_measure-160)*channel_property.full_scale / 2**15
+
+            # only add the data of the selected channels.
+            if channel_property.number in self.channels:
+                if self.my_instrument.data_mode in [DATA_MODE.AVERAGE_CYCLES, DATA_MODE.AVERAGE_TIME_AND_CYCLES]:
+                   data_out += (np.average(channel_data[0]), )
+                else:
+                    data_out += (channel_data[0], )
+
+            if channel_property.number + 1 in self.channels:
+                if self.my_instrument.data_mode in [DATA_MODE.AVERAGE_CYCLES, DATA_MODE.AVERAGE_TIME_AND_CYCLES]:
+                   data_out += (np.average(channel_data[1]), )
+                else:
+                    data_out += (channel_data[1], )
 
         return data_out
 
+
+    def get_data(self):
+        """
+        Get data from the cards
+        """
+        if self.my_instrument.mode == MODES.NORMAL:
+            return self._get_data_normal()
+        else:
+            return self._get_data_average()
+
+
     def start_digitizers(self):
         # start digizers.
         self.my_instrument.daq_start_multiple(self.channel_mask)
@@ -277,13 +303,11 @@ class SD_DIG(Instrument):
         super().__init__(name)
         """
         init keysight digitizer
-
         Args:
             name (str) : name of the digitizer
             chassis (int) : chassis number
             slot (int) : slot in the chassis where the digitizer is.
             n_channels (int) : number of channels on the digitizer card.
-
         NOTE: channels start for number 1! (e.g. channel 1, channel 2, channel 3, channel 4)
         """
         self.SD_AIN = keysightSD1.SD_AIN()
@@ -350,7 +374,7 @@ class SD_DIG(Instrument):
     def set_operating_mode(self, operation_mode):
         """
         Modes for operation
-
+        Only affects daq start and daq trigger in get_raw().
         Args:
             operation_mode (int) : mode of operation
                 0 : use software triggers (does call start and trigger in software)
@@ -362,7 +386,6 @@ class SD_DIG(Instrument):
     def set_active_channels(self, channels):
         """
         set the active channels:
-
         Args:
             channels (list) : channels numbers that need to be used
         """
@@ -393,7 +416,6 @@ class SD_DIG(Instrument):
     def set_daq_settings(self, channel, n_cycles, t_measure, sample_rate = 500e6, DAQ_trigger_delay = 0, DAQ_trigger_mode = 1):
         """
         quickset for the daq settings
-
         Args:
             n_cycles (int) : number of trigger to record.
             t_measure (float) : time to measure (unit : ns)
@@ -455,7 +477,6 @@ class SD_DIG(Instrument):
     def daq_flush(self, daq, verbose=False):
         """
         Flush the specified DAQ
-
         Args:
             daq (int)       : the DAQ you are flushing
         """
@@ -463,7 +484,6 @@ class SD_DIG(Instrument):
 
     def daq_stop(self, daq, verbose=False):
         """ Stop acquiring data on the specified DAQ
-
         Args:
             daq (int)       : the DAQ you are disabling
         """
@@ -472,7 +492,6 @@ class SD_DIG(Instrument):
     def writeRegisterByNumber(self, regNumber, varValue):
         """
         Write to a register of the AWG, by reffreing to the register number
-
         Args:
             regNumber (int) : number of the registry (0 to 16)
             varValue (int/double) : value to be written into the registry
@@ -483,7 +502,6 @@ class SD_DIG(Instrument):
 
     def daq_start_multiple(self, daq_mask, verbose=False):
         """ Start acquiring data or waiting for a trigger on the specified DAQs
-
         Args:
             daq_mask (int)  : the input DAQs you are enabling, composed as a bitmask
                               where the LSB is for DAQ_0, bit 1 is for DAQ_1 etc.
@@ -492,7 +510,6 @@ class SD_DIG(Instrument):
 
     def daq_trigger_multiple(self, daq_mask, verbose=False):
         """ Manually trigger the specified DAQs
-
         Args:
             daq_mask (int)  : the DAQs you are triggering, composed as a bitmask
                               where the LSB is for DAQ_0, bit 1 is for DAQ_1 etc.
@@ -541,7 +558,6 @@ class SD_DIG(Instrument):
     def set_digitizer_software(self, t_measure, cycles, sample_rate= 500e6, data_mode = DATA_MODE.FULL, channels = [1,2], Vmax = 2, fourchannel = False):
         """
         quick set of minumal settings to make it work.
-
         Args:
             t_measure (float) : time to measure in ns
             cycles (int) : number of cycles
@@ -568,7 +584,6 @@ class SD_DIG(Instrument):
     def set_digitizer_analog_trg(self, t_measure, cycles, sample_rate= 500e6, data_mode = DATA_MODE.FULL, channels = [1,2], Vmax = 2):
         """
         quick set of minumal settings to make it work.
-
         Args:
             t_measure (float) : time to measure in ns
             cycles (int) : number of cycles
@@ -592,10 +607,9 @@ class SD_DIG(Instrument):
             self.set_meas_time(t_measure)
             self.set_MAV_filter(16,1)
 
-    def set_digitizer_HVI(self, t_measure, cycles, sample_rate= 500e6, data_mode = DATA_MODE.FULL, channels = [1,2], Vmax = 2):
+    def set_digitizer_HVI(self, t_measure, cycles, sample_rate=500e6, data_mode=DATA_MODE.FULL, channels=[1,2], Vmax=2):
         """
-        quick set of minumal settings to make it work.
-
+        quick set of minimal settings to make it work.
         Args:
             t_measure (float) : time to measure in ns
             cycles (int) : number of cycles
@@ -617,8 +631,8 @@ class SD_DIG(Instrument):
 if __name__ == '__main__':
 #%%
           # load digitizer
-    digitizer1.close()
-    digitizer1 = SD_DIG("digitizer1", chassis = 0, slot = 5)
+    # digitizer1.close()
+    digitizer1 = SD_DIG("digitizer1", chassis = 0, slot = 6)
 
     # clear all ram (normally not needed, but just to sure)
     digitizer1.daq_flush(1)
@@ -626,44 +640,45 @@ if __name__ == '__main__':
     digitizer1.daq_flush(3)
     digitizer1.daq_flush(4)
 
-    digitizer1.set_aquisition_mode(MODES.AVERAGE)
+    # digitizer1.set_aquisition_mode(MODES.AVERAGE)
 
     #%%
     # simple example
-
+    digitizer1.set_digitizer_software(1e3, 10, sample_rate=500e6, data_mode=DATA_MODE.AVERAGE_TIME_AND_CYCLES, channels=[1,2], Vmax=0.25, fourchannel=False)
+    print(digitizer1.measure())
     ####################################
     #  settings (feel free to change)  #
-    ####################################
-    t_list = np.logspace(2.3, 2.7, 20)
-    res =[]
-    for t in t_list:
-        cycles = 1000
-        t_measure = t #e3 # ns
-        ####################################
-
-
-        # show some multiparameter properties
-        # print(digitizer1.measure.shapes)
-        # print(digitizer1.measure.setpoint_units)
-        # print(digitizer1.measure.setpoints)
-        # # measure the parameter
-        digitizer1.set_digitizer_software(t_measure, cycles, data_mode=DATA_MODE.FULL, channels = [1,2])
-        digitizer1.set_MAV_filter()
-        data = digitizer1.measure()
-        #    print(data)
-    #    plt.clf()
-        #    plt.plot(data[0][:,2], 'o-')
-        #    plt.plot(data[0][:,3], 'o-')
-    #    plt.plot(data[1], 'o-')
-        # print(data[0].shape, data[1].shape)
-
-        res.append(np.mean(data[1]))
-
-    #t_list = t_list-166
-    #res = np.array(res)/np.array(t_list)
-    plt.figure(2)
-    plt.clf()
-    plt.plot(t_list, res, 'o-')
+    # ####################################
+    # t_list = np.logspace(2.3, 2.7, 20)
+    # res =[]
+    # for t in t_list:
+    #     cycles = 1000
+    #     t_measure = t #e3 # ns
+    #     ####################################
+
+
+    #     # show some multiparameter properties
+    #     # print(digitizer1.measure.shapes)
+    #     # print(digitizer1.measure.setpoint_units)
+    #     # print(digitizer1.measure.setpoints)
+    #     # # measure the parameter
+    #     digitizer1.set_digitizer_software(t_measure, cycles, data_mode=DATA_MODE.FULL, channels = [1,2])
+    #     digitizer1.set_MAV_filter()
+    #     data = digitizer1.measure()
+    #     #    print(data)
+    # #    plt.clf()
+    #     #    plt.plot(data[0][:,2], 'o-')
+    #     #    plt.plot(data[0][:,3], 'o-')
+    # #    plt.plot(data[1], 'o-')
+    #     # print(data[0].shape, data[1].shape)
+
+    #     res.append(np.mean(data[1]))
+
+    # #t_list = t_list-166
+    # #res = np.array(res)/np.array(t_list)
+    # plt.figure(2)
+    # plt.clf()
+    # plt.plot(t_list, res, 'o-')
 
     #def fit_func(x, m, q):
     #    return x*m+q
@@ -672,4 +687,4 @@ if __name__ == '__main__':
     #param, var = scipy.optimize.curve_fit(fit_func, t_list, res)
     #plt.plot(np.linspace(0, max(t_list), 50), fit_func(np.linspace(0, max(t_list), 50), *param))
     #plt.xlabel('Integration time (ns)')
-    #plt.title('Intercept: %.2f' %param[1])
+    #plt.title('Intercept: %.2f' %param[1])
\ No newline at end of file
diff --git a/core_tools/drivers/M3102_firmware_loader.py b/core_tools/drivers/M3102_firmware_loader.py
new file mode 100644
index 00000000..b864d4a7
--- /dev/null
+++ b/core_tools/drivers/M3102_firmware_loader.py
@@ -0,0 +1,33 @@
+"""
+Standard firmware files for the digitizer of the V2 setup.
+
+CLEAN : normal image, revert to the normal AWG.
+AVG : special image with MAV and interator. A custom qcodes driver needs to be used for that.
+"""
+M3102A_CLEAN = "C:/V2_code/FPGA_Bitstreams/Digitizer_FW1.41/clean_4_41.sbp"
+M3102A_AVG = "C:/V2_code/FPGA_Bitstreams/Digitizer_FW1.41/averaging_firmware_1_41.sbp"
+
+from core_tools.drivers.M3102A import MODES
+
+def firmware_loader(dig, file):
+	"""
+	load a custom firmware image on board of the digitizer
+
+	Args:
+		dig <SD_dig (qcodes instrument) : digitzer object
+		file (string) : location where to find the new firmware
+	"""
+	err = dig.SD_AIN.FPGAload(file)
+	if file == M3102A_CLEAN:
+		dig.set_aquisition_mode(MODES.NORMAL)
+	else:
+		dig.set_aquisition_mode(MODES.AVERAGE)
+
+
+	if err == 0 :
+		print("Succesful upload of the firmware")
+	else:
+		print("upload failed, error code {} (see keysight manual for instructions)".format(err))
+
+if __name__ == '__main__':
+	firmware_loader(dig, M3102A_CLEAN)
\ No newline at end of file
diff --git a/core_tools/drivers/__pycache__/harware.cpython-37.pyc b/core_tools/drivers/__pycache__/harware.cpython-37.pyc
index 32c2c717096282368fea81d1b9cb50781245af55..c3e4adfe5a18a54939049a4880a82f3b6e1e8b51 100644
GIT binary patch
delta 568
zcmY+B%}X0W7{;A^ZnIgln@A*n1>L5}f|`<m2_{x-i&WZPq`eejY2(xcH5+DEMbQ61
z5As6k!J7xqCf6QH{R2FC*^`&{-dmxU&a-;y!v2_f-kFE@dFP^ZUQ(ZHT4G3k-x@!z
z)XQ+T+FIP$uh=bTlW(*f!EMiRcHF^D_$<VH`WWl8aT<yLVkZ5KPqM-wr;LUQ)6zf`
zB>c!WKXgSD`z#O#9s?%5Pt;hEP7{A-N>cE{ov!y#!dA8IE$($ZzH8eePnYVHUc@~e
zFo4S7R(;3PmRws=k%ANfo>D`zSe3Ze%uhg=1g3yApaB)2O5e5obYL4eno?YlzpF?n
zEE!E^laGYiA8xZ)&~%-;sVy~#HwOcg?ouVCDVG)#Mbh;h_L7eEF;=Fp`Z^uyV-zt?
zVlQxZj=mVPY?ZE!&t?NRBBBg^83-E3F)MVRWS-unUw`<&eLrs7n@&s4137%9C!8J^
z-r>E^Dzuo(J&z0V3Z^w6m=}N!Ap}Y5^gEqnby72*o-9EP13@qh!B4DpT~F-oa``58
ZlqQHWE5=*YLhS=T+7o!LGX2av`v*rgdvE{%

delta 582
zcmYk4&r2Io5XXJHn|0kEX0rjM@kioD(ky61kp`j&4FoAx55<#+3ns5gAg(gIY73rw
zsZeB&?V->^&l)@k{sBF;=kB2w|B1E->6_h47v6{2@67k@%zOK(`lV_#ElVE}&)@Rx
zweeM+X|x;rZw4K|eca*u9e&0K$0tWTh!SD=O}>)Y9`30(tbo7rlQ=*6qv#4dI0CT|
zz75(w54!z;dopAxF+X&)Nl7gwF)ry()G~!G?&x(^!FT#^_o)zKxgSK3rs&o1{VtFC
z5kK;Mn8nY=tX(0WO~?_Jhu@7|=2S)OWzugcq%YyB<*^k!u-1!HBuo=#2-GB4gk{1C
z-dV-DWZPs%8swQ+p(da)Gh<GeW8e<|SUr|@UB_;=Ym`J976>l>%vQBE5qb<NIA#y<
z!7fNp!z=q0YvP4cz(@PN+DshyA-BL<c%OS+ZjeQR8tEH^q;)Fuq;!@=Y&l!Te*@r0
zXL>s^v_x5~hCGTmz$Uq=>-1>oe5e-^lA-D%>WLMa0vc18X_|_ujE_PcmHc64ohoHQ
ni9lx%>o0;Zf>XM8AU{muW&U~YCFP{vd7L=`x~-b{1vdTxjq!Wn

diff --git a/core_tools/sweeps/__pycache__/__init__.cpython-37.pyc b/core_tools/sweeps/__pycache__/__init__.cpython-37.pyc
index 650a11821c64ae38743818bdb25ae9b01d2f65f5..29eb9bf6d3c33b76b1150636201cf6da20d80b84 100644
GIT binary patch
delta 45
zcmbQp*u}{0#LLUY00eui4^8B@5Hn7;iYYUSPtH$CjY-ZgN{uhc&(A5Im|zG12bK-D

delta 52
zcmeBToXE)S#LLUY00h63w@&1?khMy-imAv+iBHZ?NsUQKElbVGFUU>JD*+2kj5GuQ
De!URe

diff --git a/core_tools/sweeps/__pycache__/pulse_lib_sweep.cpython-37.pyc b/core_tools/sweeps/__pycache__/pulse_lib_sweep.cpython-37.pyc
index e6dbc34485b95182467b711fabed9cfe25d189c8..f93f2884ba64901205a20bd2845f59363058b6e0 100644
GIT binary patch
delta 521
zcmezA^2vqUiI<m)0SG33I=qoPmr2Yj*(#>YC_XtqB{e2FzbG}nBtJi=c=J@IgM56q
zI7?ECOXBkqb5k`LZ*5KyOk`w?o_tIwlQD6#f$%d%ff%4cMX?|v4n)LHRukRBm@xUV
zs3K$HWLB{NAZZ{hFPsXLoDamsEI@*Tk&mw^b@D<9$;}JJc$pb9CO66`u|<RQOH7_8
zuQ2($WENW*h?_n+U8;gHd-6w7;mMz+a)2rmq(y<`LW!iwH>4XmvVe*-8H+L}C&~!(
zf;n78z95Y`=tiED$r6GX3DUw;1PXy7h`|xEkx(m+%I1jY042@?aWMyw;9z87<YDJv
z1)2x4a+O@K5XhDyuvJV&nILxNWKVf%1CYqeS|FhbbwMsjju%9Tfe3jJAqyfbK}7WA
zDe_ZrImlN5=Ac~)IpQFL9s)7KK^#B_<xaL$oTHu$G7Ic2PypXz%TLNmO)e?21qo+>
a2#{}zawjP%vK4?>DwA#b#3$P+SpxuCsDWhw

delta 516
zcmez5^3#ReiI<m)0SKx(c5UR&Ws-GFwu-69Nr_L+Pf3kQNi9pw$uG!F%_{*5Z0=(^
z$j2AOS&~{@5}%ito2tnewK-NWk&!WS@_wOA#`sB!@|!(`k24BH0gWt*1`#nJB6hO8
z=pM$n$#+B*8RIAa6Ab_>6;1}q%?ILQ79hdF$j4Wdytz?KjhQiRa+Qn{(9pRus*^uR
zX0fG!3`w1wAXUMbIr$-n@Z>jAIpS$R!F?dz9AMonj6CcdtVL;)9p#cHUzBdt$N&m!
zG8UzSsVcTgg~W=?;vyFyw+Jl7Rpbj|XTk027lPOW(!f*%3YH>>ZGp0pP<!{u=7?tj
zCC&pes=Zl2d*9oG42GD_1hyDrJ5v=~1=w~>SG=qR>d=HbC3|wdye!b$z4B9VImJx@
z=9J9}IgHtp6%<8*q?zIz^+b@>U=M(T^%h%xQdVkmNs%o`7!*`RAa@sKPyVT>$e1^I
LJ)6vAb0up4l$V5D

diff --git a/core_tools/sweeps/exp_readout_runner.py b/core_tools/sweeps/exp_readout_runner.py
new file mode 100644
index 00000000..94a777ed
--- /dev/null
+++ b/core_tools/sweeps/exp_readout_runner.py
@@ -0,0 +1,145 @@
+from core_tools.utility.digitizer_param_conversions import IQ_to_scalar, down_sampler, Elzerman_param, get_phase_compentation_IQ_signal
+
+from core_tools.GUI.keysight_videomaps.data_getter.scan_generator_Keysight import construct_1D_scan_fast
+from core_tools.HVI.single_shot_exp.HVI_single_shot import load_HVI, set_and_compile_HVI, excute_HVI, HVI_ID
+from core_tools.sweeps.pulse_lib_sweep import spin_qubit_exp
+from core_tools.utility.mk_digitizer_param import get_digitizer_param
+from core_tools.utility.dig_utility import autoconfig_digitizer
+from core_tools.drivers.M3102_firmware_loader import M3102A_CLEAN, M3102A_AVG
+from core_tools.drivers.M3102A import DATA_MODE
+
+import qcodes as qc
+
+def measure_optimal_phase_IQ_sigal(SD = 'vSD2_P'):
+    '''
+    Measure the phase rotation that is needed to rotate the sigal of the SD into the I line of the IQ plane
+    
+    Args:
+        SD (str) : gates name to sweep (sigal generator)
+    Returns : 
+        phase (double) : phase rotation in the IQ plane
+    '''
+    station = qc.Station.default
+    #make scan around SD to make phase estimation
+    param = construct_1D_scan_fast(SD, 10, 400, 50000, False, station.pulse, station.dig, [1,2], 100e6)
+    return get_phase_compentation_IQ_signal(param)
+
+def run_readout_exp(name, segment, t_meas, n_rep, show_raw_traces, threshold, blib_down, phase):
+    '''
+    autoconfig utility for runing a readout experiment
+
+    Args:
+        name (str) : name of the measurement to be run
+        segment (segment_container) : segment to be played on the AWG
+        t_meas (double) : measurement time to set to the digitizer (will be removed when digitzer updates)
+        n_rep (double) : number times a experiment needs to be repeated
+        show_raw_traces (bool) : show raw traces
+        threshold (double) : threadhold for the detection
+        blib_down (bool) : direction of the blib (if true, downward blib expected).
+        phase (double) : phase rotation in the IQ plane for SD
+    Returns:
+        data (qCoDeS dataset) : returns the dataset of the measured data.
+    '''
+
+    station = qc.Station.default
+
+    data_mode = DATA_MODE.FULL
+    if n_rep == 1:
+        data_mode = DATA_MODE.AVERAGE_CYCLES
+    
+    dig_param = get_digitizer_param(station.dig, t_meas, n_rep, data_mode)
+    IQ_meas_param = IQ_to_scalar(dig_param, phase)
+    down_sampled_seq = down_sampler(IQ_meas_param, 0.3e6, None)
+    elzerman_det = Elzerman_param(down_sampled_seq, threshold, blib_down)
+    
+    if not isinstance(segment, list):
+        segment = [segment]
+    
+    my_seq = station.pulse.mk_sequence(segment)
+    
+    settings = dict()
+    settings['averaging'] = False
+
+    my_seq.add_HVI(HVI_ID, load_HVI, set_and_compile_HVI, excute_HVI, digitizer = station.dig, **settings)
+    my_seq.n_rep = n_rep
+    my_seq.neutralise = True
+
+    if show_raw_traces == True:
+        my_exp = spin_qubit_exp(name, my_seq, down_sampled_seq)
+    else:
+        my_exp = spin_qubit_exp(name, my_seq, elzerman_det)
+    data = my_exp.run()
+
+    elzerman_pulse = None
+    my_exp = None
+    my_seq = None
+    station.pulse.uploader.release_memory()
+
+    return data
+
+
+def run_PSB_exp(name, segment, t_meas, n_rep, raw_traces, phase):
+    '''
+    autoconfig utility for runing a readout experiment with PSB
+    (will use fpga averaging when possible)
+
+    Args:
+        name (str) : name of the measurement to be run
+        segment (segment_container) : segment to be played on the AWG
+        t_meas (double) : measurement time to set to the digitizer (will be removed when digitzer updates)
+        n_rep (double) : number times a experiment needs to be repeated
+        raw_traces (bool) : show raw traces
+        phase (double) : phase rotation in the IQ plane for SD
+    Returns:
+        data (qCoDeS dataset) : returns the dataset of the measured data.
+    '''
+    station = qc.Station.default
+    
+    if raw_traces == False:
+        autoconfig_digitizer(M3102A_AVG)
+        data_mode = DATA_MODE.AVERAGE_TIME_AND_CYCLES
+        if n_rep == 1:
+            data_mode = DATA_MODE.AVERAGE_CYCLES
+        station.dig.set_data_handling_mode(data_mode)
+    else:
+        autoconfig_digitizer(M3102A_CLEAN)
+        data_mode = DATA_MODE.FULL
+        station.dig.set_data_handling_mode(data_mode)
+        
+    
+
+    dig_param = get_digitizer_param(station.dig, t_meas, n_rep, data_mode)
+    IQ_meas_param = IQ_to_scalar(dig_param, phase)
+    
+    if raw_traces == True:
+        down_sampled_seq = down_sampler(IQ_meas_param, 5e6)
+
+    my_seq = station.pulse.mk_sequence([segment])
+    
+    settings = dict()
+    settings['averaging'] = True
+    if raw_traces == True:
+        settings['averaging'] = False
+
+
+    my_seq.add_HVI(HVI_ID, load_HVI, set_and_compile_HVI, excute_HVI, digitizer = station.dig, **settings)
+    my_seq.n_rep = n_rep
+    my_seq.neutralise = True
+
+    if raw_traces == True:
+        my_exp = spin_qubit_exp(name, my_seq, down_sampled_seq)
+    else:
+        my_exp = spin_qubit_exp(name, my_seq, IQ_meas_param)
+    data = my_exp.run()
+
+    elzerman_pulse = None
+    my_exp = None
+    my_seq = None
+    station.pulse.uploader.release_memory()
+
+    autoconfig_digitizer(M3102A_AVG)
+
+    return data
+
+
+
diff --git a/core_tools/utility/dig_utility.py b/core_tools/utility/dig_utility.py
index 693f7e3c..f608bd69 100644
--- a/core_tools/utility/dig_utility.py
+++ b/core_tools/utility/dig_utility.py
@@ -1,5 +1,5 @@
-from V2_software.drivers.M3102A.M3102A import SD_DIG, MODES, DATA_MODE
-from V2_software.drivers.M3102A.M3102_firmware_loader import firmware_loader, M3102A_CLEAN, M3102A_AVG
+from core_tools.drivers.M3102A import SD_DIG, MODES, DATA_MODE
+from core_tools.drivers.M3102_firmware_loader import firmware_loader, M3102A_CLEAN, M3102A_AVG
 import qcodes as qc
 
 def autoconfig_digitizer(firmware = M3102A_AVG):
@@ -12,7 +12,7 @@ def autoconfig_digitizer(firmware = M3102A_AVG):
 	t_measure = 1e4 #1ms (unit ns)
 	cycles = 1 # just measure once.
 
-	station.dig.set_digitizer_software(t_measure, cycles, data_mode=DATA_MODE.AVERAGE_TIME_AND_CYCLES, channels = [1,2])
+	station.dig.set_digitizer_software(t_measure, cycles, data_mode=DATA_MODE.AVERAGE_TIME_AND_CYCLES, channels = [1,2], fourchannel=True )
 
 	station.dig.daq_flush(1)
 	station.dig.daq_flush(2)
diff --git a/core_tools/utility/digitizer_param_conversions.py b/core_tools/utility/digitizer_param_conversions.py
new file mode 100644
index 00000000..e9584d73
--- /dev/null
+++ b/core_tools/utility/digitizer_param_conversions.py
@@ -0,0 +1,210 @@
+from qcodes import MultiParameter
+from scipy import signal
+import numpy as np
+import matplotlib.pyplot as plt
+import lmfit
+
+
+def lin_func(x, a, b):
+    return x*a + b
+
+
+#TODO move to better location at later point
+def get_phase_compentation_IQ_signal(param):
+    '''
+    Args:
+        param (Multiparameter) : parameter with a get method that returns an I and Q array of a signal.
+    Return:
+        rotation_angle (double) : angle at which to rotate the signal to project it to the I plane.
+    '''
+    data_in = param.get()
+    
+    model = lmfit.Model(lin_func)
+    
+    a = np.average(data_in[0])/np.average(data_in[1])
+    b = 0
+    param = model.make_params(a=a, b=b)
+    result = model.fit(data_in[1], param, x=data_in[0])
+    
+    angle = np.angle(1+1j*result.best_values['a'])
+    
+    # print(result.best_values['a'])
+    # print(angle)
+    # x= np.linspace(-120, 20, 100) 
+    # plt.plot(x, lin_func(x, result.best_values['a'], result.best_values['b']))
+    # plt.plot(data_in[0], data_in[1])
+    # new_data = data_in[0] +1j*data_in[1]
+    # new_data *= np.exp(1j*(-angle))
+    # plt.plot(np.real(new_data), np.imag(new_data))
+
+    # new_data = x +1j*lin_func(x, result.best_values['a'], result.best_values['b'])
+    # new_data *= np.exp(1j*(-angle))
+    # plt.plot(np.real(new_data), np.imag(new_data))
+    return angle
+
+class IQ_to_scalar(MultiParameter):
+    '''
+    parameter that converts IQ data of the digitizer into scalar data
+    NOTE : ONLY ONE IQ CHANNEL PAIR SUPPORTED ATM !!!
+    '''
+    def __init__(self, digitizer, phase_rotation):
+        '''
+        Args:
+            digitizer (MultiParameter) : instrument class of the digitizer
+            phase_rotation (double) : rotation in the IQ plane (will keep only I)
+        '''
+        self.dig = digitizer
+        self.phase_rotation = phase_rotation
+        self.sample_rate = digitizer.sample_rate
+
+        names = ('RF_readout_amplitude',)
+        super().__init__('IQ_to_scalar_convertor', names=names, shapes=(self.dig.shapes[0],),
+                         labels=(self.dig.labels[0],), units=(self.dig.units[0],),
+                         setpoints=(self.dig.setpoints[0],),
+                         setpoint_names=(self.dig.setpoint_names[0],),
+                         setpoint_labels=(self.dig.setpoint_labels[0],),
+                         setpoint_units=(self.dig.setpoint_units[0],))
+
+    def get_raw(self):
+        data_in = self.dig.get_raw()
+        data_out = (data_in[0] + 1j*data_in[1])*np.exp(1j*self.phase_rotation)
+
+        return (data_out.real, )
+
+
+class down_sampler(MultiParameter):
+    '''
+    Down sampler for a digitizer, given a certain bandwidth, take make blibs for example more clear.
+    '''
+    def __init__(self, digitizer, bandwidth, highpass = None):
+        '''
+        Args: 
+            digitizer (MultiParameter) : instrument class of the digitizer 
+            bandwidth (double) : wanted bandwidth (3db point of a butterworth filter)
+            highpass (double) : if defined, sets a highpass filter at a given freq
+        '''
+        self.dig = digitizer
+        self.bandwidth = bandwidth
+        self.highpass = highpass
+        self.sample_rate = digitizer.sample_rate
+        self.sample_drop_rate = int(self.dig.sample_rate/bandwidth/2)
+
+        shapes, setpoints = self.get_shape()
+        super().__init__('Elzerman_state_differentiator', names=digitizer.names, shapes=shapes,
+                         labels=digitizer.labels, units=digitizer.units, setpoints=setpoints,
+                         setpoint_names=digitizer.setpoint_names,
+                         setpoint_labels=digitizer.setpoint_labels,
+                         setpoint_units=digitizer.setpoint_units)
+
+    def get_raw(self):
+        data_out = tuple()
+        data_in = self.dig.get()
+
+        for i in range(len(data_in)):
+            filtered_data = np.empty(self.shapes[0])
+
+            if filtered_data.ndim == 2:
+                for j in range(len(filtered_data)):
+                    filtered_data[j,:] = filter_data(data_in[i][j], self.bandwidth, self.dig.sample_rate, 'lowpass')[::self.sample_drop_rate]
+                
+                    if self.highpass is not None:
+                        filtered_data[j,:] =  filter_data(filtered_data[j], self.highpass,  self.sample_rate/self.sample_drop_rate, 'highpass')
+            else:
+                filtered_data[:] = filter_data(data_in[i], self.bandwidth, self.dig.sample_rate, 'lowpass')[::self.sample_drop_rate]
+                
+                if self.highpass is not None:
+                    filtered_data[:] =  filter_data(filtered_data[:], self.highpass, self.sample_rate/self.sample_drop_rate, 'highpass')
+
+            data_out += (filtered_data,)
+
+        return tuple(data_out)
+
+    def get_shape(self):
+        shapes, setpoints = tuple(), tuple()
+
+        for i in range(len(self.dig.names)):
+            setpt = self.dig.setpoints[i]
+            shape = tuple()
+
+            if len(setpt) > 1:
+                setpt = (setpt[0], np.asarray(setpt[1]).flatten()[::self.sample_drop_rate], )
+                shape = (len(setpt[0]), len(setpt[1]))
+            else:
+                setpt = (np.asarray(setpt).flatten()[::self.sample_drop_rate], )
+                shape = setpt[0].shape
+
+            shapes += (shape, )
+            setpoints += (setpt, )
+
+        return shapes, setpoints
+
+    def update_shape(self):
+        self.shapes, self.setpoints = self.get_shape()
+
+class Elzerman_param(MultiParameter):
+    """
+    parameter that aims to detect blibs.
+    expected that the input data is already well filtered.
+    """
+    def __init__(self, digitizer, threshold, blib_down):
+        '''
+        Args:
+            digitizer (MultiParameter) :  parameter providing the data for the dip detection
+            threshold (double) : threadhold for the detection
+            blib_down (bool) : direction of the blib (if true, downward blib expected).
+        '''
+        self.dig = digitizer
+        self.threshold = threshold  
+        self.blib_down = blib_down
+
+        names = tuple()
+        shapes = tuple()
+        labels = tuple()
+        units = tuple()
+        setpoints = tuple()
+
+        for i in range(len(digitizer.names)):
+            names += ("readout_signal_ch{}".format(i), )
+            shapes += ((), )
+            labels += ('Spin probability' ,)
+            units += ('%', )
+            
+            setpoints += ((),)
+
+        super().__init__('Elzerman_state_differentiaor', names=names, shapes=shapes,
+                         labels=labels, units=units, setpoints=setpoints)
+
+    
+    def get_raw(self):
+        # expected format from the getter <tuple<np.ndarray[ndim=2,dtype=double]>>
+        data_in = self.dig.get()
+        data_out = []
+
+        for data in data_in:
+            if self.blib_down == True:
+                blib_pt = np.where(data<self.threshold)[0]
+            else :
+                blib_pt = np.where(data>self.threshold)[0]
+            
+            data_out.append(np.unique(blib_pt).size/data.shape[0])
+
+        return data_out
+
+def filter_data(data, cutoff, fs, pass_zero, order = 4):
+    '''
+    filter noise out of a dataset
+    Args:
+        data (np.ndarray) : data array to filter
+        cutoff (double) : cutoff in HZ (not that this is the last 0db point and not -3db)
+        fs (double) : sample rate of the signal
+        pass_zero (str) : 'lowpass' or 'highpass'
+    '''
+    b, a = signal.butter(order, cutoff/(fs/2), pass_zero)
+    return signal.filtfilt(b, a, data)
+
+
+if __name__ == '__main__':
+    a = np.zeros([1000])
+    a[100:400] = 1
+
+    filter_data(a, 10e3, 2e6, 'highpass')
\ No newline at end of file
diff --git a/core_tools/utility/powerpoint.py b/core_tools/utility/powerpoint.py
index 4761a3ee..324aacb2 100644
--- a/core_tools/utility/powerpoint.py
+++ b/core_tools/utility/powerpoint.py
@@ -309,7 +309,7 @@ try:
                 notes = '\n' + extranotes + '\n' + notes
             if gates is not None:
                 notes = 'gates: ' + str(gates.allvalues()) + '\n\n' + notes
-        if isinstance(notes, qcodes.DataSet):
+        if isinstance(notes, qcodes.data.data_set.DataSet):
             notes = reshape_metadata(notes, printformat='s', add_gates=True)
 
         if notes is not None:
diff --git a/example_T2Hahn.py b/example_T2Hahn.py
new file mode 100644
index 00000000..6544e84e
--- /dev/null
+++ b/example_T2Hahn.py
@@ -0,0 +1,48 @@
+from pulse_templates.utility.plotting import plot_seg
+from pulse_templates.demo_pulse_lib.virtual_awg import get_demo_lib
+from pulse_templates.utility.oper import add_block, add_ramp
+from pulse_templates.utility.plotting import plot_seg
+
+from pulse_templates.coherent_control.single_qubit_gates.standard_set import single_qubit_std_set
+from pulse_templates.coherent_control.single_qubit_gates.single_qubit_gates import single_qubit_gate_spec
+from pulse_templates.elzerman_pulses.basic_elzerman_pulse import elzerman_read
+from pulse_templates.coherent_control.single_qubit_gates.T2_meas import T2_hahn
+
+from pulse_lib.segments.utility.looping import linspace
+
+pulse = get_demo_lib('quad')
+
+INIT = pulse.mk_segment()
+MANIP = pulse.mk_segment()
+READ = pulse.mk_segment()
+
+# assume 1QD -- elzerman init
+t_init = 50e3
+gates = ('vP4',)
+p_0 = (0, )
+
+add_block(INIT, t_init, gates, p_0)
+
+
+
+# T2 hahn
+MANIP.vP4 += 20
+xpi2 = single_qubit_gate_spec('qubit4_MW', 1.1e8, 1000, MW_power=120, padding=2) #X
+xpi = single_qubit_gate_spec('qubit4_MW', 1.1e8, 2000, MW_power=120, padding=2) #X2
+
+ss_set = single_qubit_std_set()
+ss_set.X = xpi2
+ss_set.X2 = xpi
+
+times = linspace(100, 1e5, 100, axis=0, name='time', unit='ns')
+
+T2_hahn(MANIP, ss_set, times, 1e6)
+
+#
+p_read = (0, )
+t_read = 100e3
+
+elzerman_read(READ, gates, t_read, p_read, disable_trigger=False)
+
+# run exp()
+run_exp([INIT, MANIP, READ], nrep = 100, ..)
diff --git a/keysightSD1/__pycache__/__init__.cpython-37.pyc b/keysightSD1/__pycache__/__init__.cpython-37.pyc
index 6d004f27c47df9774e6f467424ec2855aaa75840..407f3c1af412b5f482b6cd8d7407260cfda814db 100644
GIT binary patch
delta 432
zcmbO|pJmE?7Ct9lUM>b8*kgStu10nvpBA&2Zn9NOnNfUleoAUga(+>2d`W(OPVweY
z=59X5sLc-r?HCztCMyVw1Idj7CX;i7<ArU3CUgREF$<93U}Rw|vIWXXPOcSHpDZUb
zQ_>D7+zXNi*#(k^z#==as+S`3#GQc>Q$Q*>kW>^ogKfAZ>Lct16rKf=huh#b*-)%g
z*b^u=4@KM)C~h&CRoq9|2PnP>Ma~D}DsewHcc6HY$K;FR@{GQdKZ*-aP8OG%EGH4c
zev8%3Gs4wn@<(3L$sfd7xK_grm?UAj`KCm45gW+PBJa&s-4l%1Kt3z7pL}7jxP$|c
z86^-B<mvA28Wiv2A08hZ>E~SJIQjoxaYm=j;`^p)L$rEr-u&{iilz-nD=6fOz=nWA
Ypa|s6BCz=&#~1m62*2s;`54~-0B&bxH2?qr

delta 454
zcmbO-pJn!Z7Ct9lUM>b8m_2z{T)F5*J}qWhlVq!yiky`A<ouM>n3UAA)SUc++|;}h
zu)t<N=59X5TbpkQ+A&Jn0yQ=QaWTjU4n`IbWMW|~vYl)wJbCg45f=VVkSq&Sw#W`B
zE;;#w@Bv2q$s0w)f#gCto5`%A)r>Ba8%0Hdq@l3M<kO-)!tOvVvrzQ9gXQAHLWRA6
z;`31Cyufn5!~%qUf#Qo$<b1()mWlhZc>u+WJSU$LmlyQ|GM9mru>lE)!;AdDh8RiM
zGDb}<kdPMk2TH95sewD+AEaXQC5h-FHjo>Nd^Q_*PcV|R2eBQ1M3i7ikf*!5Yf!w0
zr@KddaHOAek>litd&MN3P-KHVgIyuA&YK1HP1A;$=Dm5%%gZX7pfD}60})_zT|g{Q
WXcU311o^KB<oY82=_~jc-v9tb`Drr%

-- 
GitLab