From b1ec523aaa33a0203198aaa98d2d126c811f7426 Mon Sep 17 00:00:00 2001 From: JohannesDittloff Date: Fri, 9 May 2025 10:39:47 +0200 Subject: [PATCH] add f sweep --- .../__pycache__/measurement.cpython-311.pyc | Bin 8808 -> 10934 bytes .../test_measurement.cpython-311.pyc | Bin 11668 -> 11755 bytes prsctrl/devices/lock_in/__init__.py | 4 +- .../__pycache__/__init__.cpython-311.pyc | Bin 1661 -> 1659 bytes .../lock_in/__pycache__/base.cpython-311.pyc | Bin 1736 -> 1700 bytes prsctrl/devices/lock_in/base.py | 9 +- .../impl/__pycache__/dummy.cpython-311.pyc | Bin 8156 -> 8064 bytes .../impl/__pycache__/sr830.cpython-311.pyc | Bin 23053 -> 24099 bytes prsctrl/devices/lock_in/impl/dummy.py | 9 +- prsctrl/devices/lock_in/impl/model7260.py | 4 +- prsctrl/devices/lock_in/impl/sr830.py | 57 ++- .../impl/__pycache__/dummy.cpython-311.pyc | Bin 1892 -> 1910 bytes prsctrl/devices/monochromator/impl/dummy.py | 2 +- prsctrl/measurement.py | 277 ++++++------ prsctrl/prsctrl_interactive.py | 235 ++++++---- prsctrl/test_measurement.py | 8 +- {utility => prsctrl/tests}/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 212 bytes .../sweep_frequency.cpython-311.pyc | Bin 0 -> 4419 bytes prsctrl/tests/sweep_frequency.py | 65 +++ .../data_collector.cpython-311.pyc | Bin 9583 -> 9585 bytes .../__pycache__/prsdata.cpython-311.pyc | Bin 15571 -> 26657 bytes prsctrl/utility/data_collector.py | 4 +- prsctrl/utility/prsdata.py | 420 ++++++++++++------ utility/__pycache__/__init__.cpython-310.pyc | Bin 190 -> 0 bytes utility/__pycache__/__init__.cpython-311.pyc | Bin 206 -> 0 bytes utility/testing.py | 17 - 27 files changed, 711 insertions(+), 400 deletions(-) rename {utility => prsctrl/tests}/__init__.py (100%) create mode 100644 prsctrl/tests/__pycache__/__init__.cpython-311.pyc create mode 100644 prsctrl/tests/__pycache__/sweep_frequency.cpython-311.pyc create mode 100644 prsctrl/tests/sweep_frequency.py delete mode 100644 utility/__pycache__/__init__.cpython-310.pyc delete mode 100644 utility/__pycache__/__init__.cpython-311.pyc delete mode 100644 utility/testing.py diff --git a/prsctrl/__pycache__/measurement.cpython-311.pyc b/prsctrl/__pycache__/measurement.cpython-311.pyc index e82a8e42c68e99eec0cf2a151771a02b5e5c3946..3944542e1ccba91ce2c071f5f2f06fe722d354ed 100644 GIT binary patch literal 10934 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 diff --git a/prsctrl/__pycache__/test_measurement.cpython-311.pyc b/prsctrl/__pycache__/test_measurement.cpython-311.pyc index fabeff80947966bcc24759d56e7ba4016eb5a8af..8de3b3eba792067bf304e4f5fbadc103e17680b7 100644 GIT binary patch delta 2168 zcmaJ>TW}Lq7(QonPnss1WOLozlU@idh;pw5gi;U~pk;VB` zk`!csqoa4Wyc;TfDU&ZrOYpywn&+EP5T`&{P26-!^Uo*=PoxGJX87hRXnH*iQg7Oo9)=n#CA#szpG9VDOg#c=5A*!e&Qgd0- zqIK0znhfRx`CuOALwP12?Ng)m6I#h6sssZAL=$=m2dtZo0knZ;_5;H4GJar;tIQHx z2;n2O4$UhfOKNY#salUGoR9P=(WVLQ%09|%YBrZcx8S?AF)O=j2;7RDCa*F|wgazY z`NnN4sU(YN5n9o9(L##0RQRSb#Kw0vemZ2 z+J>*N9?p)Gb9&KXIPflxW7((WL=q+1$UXkYQm=S&z>f#eep)(J7Y3R2)oqCY!6o^Z{xL-V@{NMv zBtSs&kNZ>F$~+23K~`}RjKcEy!hcvTuzXWI2UeyQ0)Nh#*>-uOmZmEou0pLy9r?z?zDfQ652_3^N;O!8Q6_~aNU7#&A;P*0O3VE!2b%* z<9~e(@G4Guzh~*6EKPp}?+A4Sg^KSZobV0Dvc%8`7uh#P`uY+=cRCXnGTnX2!9ij5 ztdDVnzaM7t6~Fxjyr1Ix{%<`ZEhJG`l}G&-bvmGJG8e zLqR7Z!HcS7>dldiprx`BC3`bMFFqBTfpz$C$PLrj9*)9&xFx*b&`G)JLJ8;a#jqFU zXbQ|MD&m5$s2WPdQ|U!zLJ-nIfd*}&FiGJeg-aCPqtHR2pTb!RNeUSXBNR3gNYkYa z^z;OJFdN};AQIJoO)OieYZf1l?6Cae^_MhCx4Hxf$Vz)jkEbIaTqxOrD<;QU7o0m} zCHJ=)sPrWO3`ZH6Z@%d%@-6a?BSrp*+Tlb({em1pBq?+56VL$ z^7+w)p=>dpC8=-2-3*!bFm23VRNKq^2|&Cu@$J+_$e*E&D8#Sopuq%*L)9@q6Xh9` z%r_Nln~K`z1#PpeZJz(B-k`7>BS$xY;s$*Orzp0VI?Rfn&8iL#KV0L3(^wz#z=OCh z_FD{-?-uGQY@TWlLe6rEYGX6`wKskxG3mSX-R98}LezJNgxa(XZYFlEP`6q;bsKhD zR7JHNpr{>aE0IFJN~Kn!RX(VYkP1RozmScB1d-qqB=|`s_(32s<4v&R21ed9_s%`{ zv3uwGx1slkET32`CP3Epf^cf}L(2|mH19hzGB(_w9-ci2w;8uj7y-z`Jdrbfhh?=#2_7FksdW51LBZzno~JsLT1!B&ET(12hCx{R18n(0hkKmfceP& z2-;hK35Z;iM%9uv(A3&OZo-UA5Cy>sdTP)GMs_}Tv+y|g(K;nJ>b&FtNQD8}QA_=ut*Kx!i0w9h=!GKtAwV6sb$1}KlSf++C|prOt9BAqF%oHYMdHw!>f21U z6r@5+0TzM#MprUw8Zr`)lPE1ZsiRd z6+6lOBED!9gis-zV9};YuD%YfPUGeZcgt-Zex`TgU#uRdePt%xirZ~|HbOdryBuO{ zTYX5?p*ZADv|VwKeVKE)fRR)Jr|Ug{lpgJpR*)^>ePyJ?yUDxoVaB3;7+?y zw`-EaGc6)Ma|8zpK7r8KxVm{0D?j ze5ostZP{o=r~v9-UdCluQ$|mt~EWF}Dd^NJ#O!S)RP`wJj%|1G(dpsd+$;CBOOU=IHs z@cswmCO#x3+jGPv2Is9O#}kQBc{r6BMrYEa^2$M{G1u~$@FvZoP*{=G@G&*1mmh5fic6l@+Lp7Z)t`s{dC=BUh~)JRr7i7$s{ z#Hid!qmzq529X-M+#ADkJe?esW!xE#!aj_`{S5=up2Cm9{$p1t3ufnaaao?%pB{~; zGxIEAXXFAUZKhD9Fh${Q3hz)jOd&xbN#PWQ423L(O$0Jgygm}bgAvK}kf>Yfk&4ww zmu*3>^EE600wvb7z~OHqA6)vw2lS=|0L;6YlDOu9P!@MSlFMSxA*zOYB{r#+=)y!CL?JipbWloyoq!K612HVZLO>E?F43uN0fks_o3{b`@J|3Av@B+Wl8oZ>nF diff --git a/prsctrl/devices/lock_in/__init__.py b/prsctrl/devices/lock_in/__init__.py index 7a5aeed..da58514 100644 --- a/prsctrl/devices/lock_in/__init__.py +++ b/prsctrl/devices/lock_in/__init__.py @@ -1,4 +1,4 @@ -from .base import Lock_In_Amp +from .base import LockInAmp TYPENAME_DUMMY = "Dummy" TYPENAME_SR830 = "SR830" @@ -16,7 +16,7 @@ def list_devices() -> dict[str,list[str]]: pass return devices -def connect_device(type_name: str, device_name: str) -> Lock_In_Amp: +def connect_device(type_name: str, device_name: str) -> LockInAmp: if type_name == TYPENAME_DUMMY: return DummyLockInAmp() elif type_name == TYPENAME_SR830: diff --git a/prsctrl/devices/lock_in/__pycache__/__init__.cpython-311.pyc b/prsctrl/devices/lock_in/__pycache__/__init__.cpython-311.pyc index 9becb0247942a6f5e7a1e474e1b163566fde3b57..b8587058f327351e005f25564a8c86ad6e245b99 100644 GIT binary patch delta 71 zcmey%^P7ivIWI340}yPTD3cLAk++kb(qM7}3O8!QyNf;%q>1_Eh#1##D|pmJ|*Q^EiR>oGlDM(;`GT+ z&i2fk9Kg7aF?aHR#sJ3R$^J}w?2=INHl`YOUMQQJIZd4(!gkCp&?`zUPAw4xy0b_W zMDPQNTdd_7naLT+Oq1gn6&bxJZ)H}iw<<_1O3YONOIRsH=qN<$CbCWo-un#(fseqg{P PCNO^liGIN%1$HL@ep`Wf delta 572 zcmZ3&dxDpDIWI340}$Mem(Dmlk#~0e3?OGZLn=cQV+unQQz~-`X9{x*%QB$oYLGfG zh++YXbEUAhFrtaGg2mY|#Myx2jH&FY9H|^>Op-8T7}A(h*wM_^WQgJfD&}ZmKr@;v zg(;XpllvC8PkwTCyk}ngWGBXTj5(7(G6qa`V$$Q3hKrXo)o}8|dB2#_6a-;B$J_$F zqSWHl5<#FFibO#KKaiO0$EeJhJb5Lvnq5$8Noi4DF_!`q=t2OTA6=4}n_66wm|LKu zP?n!ll9-+v3zzhpT)-k`z*Ur*m=a%>m{XcsC5CDc$VU(#nHktbTH-!1fkZ`s><>(n6Iewj-(wAARGw_WW@|3T2vm+qOi=y^ L68(Zj3LIhphcJoZ diff --git a/prsctrl/devices/lock_in/base.py b/prsctrl/devices/lock_in/base.py index 5814f6f..13a7dc2 100644 --- a/prsctrl/devices/lock_in/base.py +++ b/prsctrl/devices/lock_in/base.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from typing import Callable -class Lock_In_Amp(ABC): +class LockInAmp(ABC): @abstractmethod def test_connection(self) -> None: """ @@ -19,16 +19,15 @@ class Lock_In_Amp(ABC): pass @abstractmethod - def read_value(self) -> tuple[float, float]: + def read_value(self, which:str) -> float: """ Read a single value - Returns - ------- - [timestamp, voltage] + :param which: X, Y, R, Theta """ pass + @abstractmethod def __str__(self): pass \ No newline at end of file diff --git a/prsctrl/devices/lock_in/impl/__pycache__/dummy.cpython-311.pyc b/prsctrl/devices/lock_in/impl/__pycache__/dummy.cpython-311.pyc index c8ed839a7b9ad871bbca64f60087cc50ce8d3588..5d882767d27bbfdd4a89a988668889d82a34b4e6 100644 GIT binary patch delta 934 zcmZ|MOH30{6adh<{Q)fl{r`U;1*T{PYCwd55I;yLN(cle22*)p5o~b^1*5TGYSiGu zdR^#Fjfq4PU37yBG%j@QPScpUG%H&kn|D1cJ>mmV)C+XT zx4ntMytr6aF_@a1J(SF^MIn-Z?l$dUnw&<~un5!TK-RKI{-Xpt(F(!rRMoVKRa3Qs z8FPZHV0}<##k{1O`A84* zlU^1eeJn`&S&bZE%`BAb0XfL(DOE$uh_p-tEmKSJHi{c5u464MOfMZ`P1IG-wv!E4 zgk_K$M+B+0@Clx2;X~n3S(B=)s9j3TOeXMdxev1romYv_rYIIdQYj*jyNI-k2x3wn zRw-f3yF=J*@Z(Rt8=CQ&J4~ZKh~lbY6ngQEp$pp4TDcCLxLG*>`!KGzpht{CHy#!@ zsH41UgR(EGVlY&y)mC@HL7b?53zX8PQYo*S$6}&P3r*6m1-WF1SfI7?F-D}F8f2s;CuTR z%wwD5rY_CDlYf^V_ZnRq36xA}j^EhEXWs9%kxQ delta 1058 zcmZ|NTSyd97y#h;*WJ~1clLg9FS=WKTURTyLPAp{cDDot#P+vsm#*o z0+RhvJw=y?!iR*Gz=(S6A&Lroh%M?#s25S{Ir@)C6x1;Dopb*GKj+LG=4|*=SbiYO zxe{O6Nnih&DLJXrH^mPPbnhDOp1M>8rBly6mU2{&tD|bHK^{G(iViOuUu0s>6MKh~L3u{IzYvKAFuC#Hb7400cp@X%flXakr zbz&atLN}X-9@dRs)`LFQ8^wHXd|1Hdqn|CnK-7=xu#jIXfJGcxC0>iSDduffbAJu@ zOSoT%>oLexMOemF#Ta5sE=ngMTG}oJE7ObcLIa1>Gua{4=0hM3XXVmxo5j!f9n4KB z@kj2Ibmxx|`JEnmn`f-x^;9WaTdW5IiT+sNc+X%m7Krx++EUqj_I9*{164s)M$zDlE6tniS37P-uSM_Z-aE?X9A)oKp*d6NT~3HT!iRa@W-aC(;WpD_E2KQ9-pLiw`_5I6+o-1Ei?S{SwZt9Da^76sfP)=> zn=z?0<%`m9a?+oDdR5^Kt`a}n$ON;P#b^FODtPzzO|pMOU9AfIi_*Wn+O@Qt`SG8; IV&=tv0R*86A^-pY diff --git a/prsctrl/devices/lock_in/impl/__pycache__/sr830.cpython-311.pyc b/prsctrl/devices/lock_in/impl/__pycache__/sr830.cpython-311.pyc index 67af417639c327354bcd5e0575e3d14a64d2550c..82576151305d883e1c060e03ceb020b469b91125 100644 GIT binary patch delta 3707 zcma)8eN0=|6@S-$HU=AvjUfgMJRk&2`2I>s)AE@#G(egFP2;9@9qxm;F$UddNI|C1 zh)twlNqSqsnzlpf+K^ON>sj50LW!fKAx9*&0AW2nK zy?XC=&pG#=^X@(8ymRj}Z;@9&AZh1PQ&R+J&%U2`#JuBlnw>m*x^%MID-02uB%7!~ zP8||y@|@0-_vql!F8}EdOIq|e0PV&K5rhRn7F>i1E)69v zEp4URIpPwjI48Jt!0CX~Q$27=Gzri^+jt?#pBWZt>RAGxN|Q}P8fu;sU1pjN6Vj;# z&_b;=V_4&|QX6m?G!xK9jdCW;&4RgEpuY+9H&Hu`*n!IiE}P~6mqT*_b3x_+nSbhBKPpuiU;;EQ6q1?Fs~rGO=%UIyx0 zs1vwSS`JtSvaKL1fy7WQ62>s*zz9%fd-@{O?k23USI1*Qm?Mqp}a zCvAeMwR9(KhQ1E^?an^)m}w37 zwQn|1F{n8sysY6;VZjbv?^Jr1f5PMQQfYKNFg708E`4pOl6uCKfOJ@vM*Z*(NvsR> zr}TRV`tNIzoVvJ(6t69?72OE~tP;SfWvNK_0mMbLrQixAiJx&eXN~|lE5wZHi{eYk zVPhdY*R76lUf+ri0G>UkvvGzx;{b5so%$)J`;TBv0|30UpqE?-@q!#eR`?szi^)z6 zOKh9pTX+`%P@cU-!D9h6)$?Jan=G3_W*? zq38JKwkBl$G*V;1+}+QEc2&<Q^9nyM`qHXh0hCD2hxgB=|!l$nJ4jI_CBHFr%65q0$f= z1vMC>@`QI#PLbByn!zEjFCa6w;u{_NA&a>`V3L*&r?V*AYhuT|4@r4_!$8IO?6}Qmp~{uXI5BE7t(XWhW2IgT3PGWvermh zd$g>5aeK_18#Z(-cV4S#yIRo}spyDSbSxeU<sl=*o`mTGGN( zTF;S(`J1f+q>~%k&P$)boT==-{dd&YR2?248iM!?%K^9FJvQp~2b6nwq^&6hJ*?Kf zIc{mcSff~i8pRwif^@A>@pV}J#f{={oD2)oVvw-%ptz*xwGJEa$jsiVqxzusb+B|1 zt*PyHJt71(-_c3>5jr9ia>JC58#+5`rJxiTAM?o%`p2sL)Weu(vO>B^KeUmQz3d}c z0ehQ&(&_BOLd>w#GhCU|!0;Bpst`;7aqWoL&(K$iKQJF<>F!$`Mm+%!#7zC4*ZkhK zb8qUs;1VSQvmvic{I#9;%&eve7Tc;ok~_)bV4_z`c6-Fy5w&)NO&u|_WnPRK(-MO* zQ~Jq0PwrV1uh}cF+AEhmE0&d>h`l{(Z;zNdq9#@2$4}OnIzk`r@{#28B(9@@$uXH% z?ang3k6XilvaQ^*GlP3}x01i`v%BvlkA%M3?bfE_9#}KNRfJ#jL*31pw`id-its7F z(0u@OH9hV6%OIG_;*s51#251P%xd+s=#K>+%*zdF_T>}u3@_Y&0TS}^eubRot$l}y zo4?w(l`QZpeFaucZ!Z+_jZE)lZ}M;Y9-p}%zvDtUgzyLgMHohS6hS>md`OKVFa)gr zY#Lz(0V^;&fuI%$tfTA&1S|uIlj9Yn&LaE*;T*yR0c^8hBAJLYQtu)B9zk`<$4Fg5 zP*dg#Qh!Id%o_%_z!o1IaFD0?V*_uOnl^`x#qi8_u4+VG-I8`yfbP3s3hlaIPnyiS zZn7#ME|;u`=gSlHrHo7cf3zp)@4=$b!v|)`OkD4SyQ$Y7Cu6tWX|QHu;BPn}GRiWTF8LqZ{`iS z?2DF&AvbD(^A?}gq;<22lhMqoVFT``QfIT*K%F=a5o4F|WS z#zpEK45*uDZr*m#o?-qwuirbu-dmrPd2Rz-;eWM|58W(ee1Z( ZB-A&;-$%d!080sax&Nl<`zUoo{|7`K%cB4Q delta 2775 zcma)-drX_x6~Mpy`GGN*haCqnU_M9!*gVW51V{)k6A0yP9&M~rhjKq+8Zh+Qq=k79 zr9^{n$w+V2R$UdLW}{8Ws#IUIrc)<%4`uECNP*BLv-Z$d<&S9ERG|90Zjv% zmTEzZqESE{t)|gni>7+e^fX3}kz=Q#1Yt;|v9oHoff~RbN8^C;G#;2h6M%^{ks6PQ zZX-3#fNG*UfIA@E41s0{+<8o(JKrSkB$@;x$)F{JmO@iNvrr2#6>OL_pWzY=JBq{+jX(lj>W&wB6UBKPowHv(l&^@4yX47owa=@4a#$4!gp|?SAgWgVS zXd$dHk2>7>v&n^!kAHcz(2v{o{ol&sl;wqMQKst zL#7`=s9y^>8Ts**`ePYWCYL-wrGQtKP7I7p$kN!5O3&Jr z774o(zhj~2RrgEo#pqSDecfzdjn7+;&s)*uF{8kzYm<36{W-G8z1Ef})(Ii3i`O|# z{9|h_xuASu)f4`=G*i};rm?M~L9(esY7|2HK78~c#>QAkGwh@C^E^drCvPiW$wJ6G z{DsWdMb;C^RGDQb?z3Y=Q(xSPe8nv%d7WV`Qco1 z@D~d!_uPpjLKp#qa5}e}8YDco_ipfiNa?)O5igP;e^!?S;#qO(1pnO8#%~tcpPB=w zyBYwSM+r`*z+OZt8l~?erV(cVHWm9G${c}Z1_RbJqz?>_%AUb7pFa?a9U2}9$jsv( z85@^*OL3V4uNFVXpA^Ee!cNOAbHSRqVAbqcH#_F^x06zDCFQRr<*z0ct|t}F#VQ{Z z?<2&+!zIn+KHpz@oxH)x-g5CPLD|2z2Mhhfvh70uw5)}Q%D>8>J{NF8n=kk$Jh|Ml zQwJ4cK`=+^W7CAl6CvW_1LYa25IMq6m)Da3|G2y+@e=x62DGzxfrA>p=-i>?Rb0}L zqx|;=?Z(JrbsL-KXXt#58>^3!M8#jdUque^ck15K;qJGIArl{f{XbrRfYkEK^%u!? z-siMxoiG)D8tk<+{)*GgTP)FHnDEzXGx+i=Vjyly7lg~2CAhAa@yd?0@2Y*pLE;x5kwg2)9CkNadcVij0navRZA6h{7!=;*IS ztKt6BPyLwJ9&*m;rH7hJ4oongUmB2n6Qkpkc&qun1LLxkTU9I#4@qM_SsL^X_^e%KZ#<-tN5=z`>;MjXxS=s!ySa~w8aoV)xH~cda6;nv z4;q_hZpXxh1x<7VSt#A;|70@1T=81hqG2Vi@T$CGs#-Nwt(&Um8s-{q8xq2T3Vod$ z{Udzu7}HkL>sO7=b)$17)){#Wi9SqjTdcP%jx~$p3b|5zWz@B5X;`;3eAI=%bxYG+ z!$RglSLC;QzUAAZ{x`_D8DySsQF@w2NGbar6ktj`IK&>qN?`eHF_C+Z{TvOr^;sU` z2I5lyzkm1;S>x5skC7*p`DTwQ;WHenL|`^{h2L+jG=F0_Z)|%UV$ZkLlNJ8gwp8L( zzG^$EB0*)ktD2B`{tMS7(#gHuesYHYvHNMV@%3``Booe$_hiJW*(H3$=H0;G=$W2z zp`jP?IN}K6Da0UR2oZV9UX+d_d0k zh_?mE%{I0U>K`CJLPXa88c0hMy&Al-fURne|pp4`b+M+nxc_b)EV#( zi=rk!ENnxkeA%ZZMuVo6goTG_Rf?WCNz66_b6~<-fRH9~;Mfg*^T}2bKL7ds$$Am? qGF}<}H+g%%J;AyneC^NY`W^h5ld2V$|jU diff --git a/prsctrl/devices/lock_in/impl/dummy.py b/prsctrl/devices/lock_in/impl/dummy.py index 1ef62d1..6e572ac 100644 --- a/prsctrl/devices/lock_in/impl/dummy.py +++ b/prsctrl/devices/lock_in/impl/dummy.py @@ -1,9 +1,9 @@ -from ..base import Lock_In_Amp +from ..base import LockInAmp from typing import Callable from time import time as now import numpy as np -class DummyLockInAmp(Lock_In_Amp): +class DummyLockInAmp(LockInAmp): def __init__(self): super().__init__() @@ -42,9 +42,8 @@ class DummyLockInAmp(Lock_In_Amp): def check_overloads(self) -> bool | str: return False - def read_value(self): - """Read the value of R""" - return float(self.query("OUTP? 3")) + def read_value(self, which: str): + return -1.0 def reset(self): pass diff --git a/prsctrl/devices/lock_in/impl/model7260.py b/prsctrl/devices/lock_in/impl/model7260.py index 5bd74b2..ce648cd 100644 --- a/prsctrl/devices/lock_in/impl/model7260.py +++ b/prsctrl/devices/lock_in/impl/model7260.py @@ -1,7 +1,7 @@ import pyvisa # import pkg_resources -from ..base import Lock_In_Amp +from ..base import LockInAmp from prsctrl.util.visa import enumerate_devices import logging @@ -9,7 +9,7 @@ log = logging.getLogger(__name__) import numpy as np -class Model7260(Lock_In_Amp): +class Model7260(LockInAmp): """ Wrapper class for the Model 7260 DSP Lock-In controlled via pyvisa """ diff --git a/prsctrl/devices/lock_in/impl/sr830.py b/prsctrl/devices/lock_in/impl/sr830.py index 8da29e3..d1e446b 100644 --- a/prsctrl/devices/lock_in/impl/sr830.py +++ b/prsctrl/devices/lock_in/impl/sr830.py @@ -4,13 +4,13 @@ import struct # for converting bytes to float import numpy as np -from ..base import Lock_In_Amp +from ..base import LockInAmp from prsctrl.utility.visa import enumerate_devices import logging log = logging.getLogger(__name__) -class SR830(Lock_In_Amp): +class SR830(LockInAmp): """ Wrapper class for the SR830 controlled via pyvisa """ @@ -152,10 +152,12 @@ class SR830(Lock_In_Amp): return "Output" return False - def measureTODO(self): pass - def read_value(self): - """Read the value of R""" - return float(self.query("OUTP? 3")) + OUTP = ["X", "Y", "R", "theta"] + def read_value(self, which: str): + if which not in self.OUTP: + raise ValueError(f"Invalid output: {which}. Must be one of {self.OUTP}") + outp = self.OUTP.index(which) + 1 + return float(self.query(f"OUTP? {outp}")) def reset(self): self.instr.write("*RST") @@ -230,14 +232,16 @@ class SR830(Lock_In_Amp): ofsl = int(self.query("OFSL?")) return SR830.OFSL[ofsl] - def get_wait_time_s(self): + def get_wait_time_s(self, time_const: float|None=None, filter_slope: int|None=None): """ Get the wait time required to reach 99% of the final value. + :param time_const: If not passed, the value will be read from the device + :param filter_slop: If not passed, the value will be read from the device See Manual 3-21 :return: """ - time_const = self.get_time_constant_s() - filter_slope = self.get_filter_slope() + if not time_const: time_const = self.get_time_constant_s() + if not filter_slope: filter_slope = self.get_filter_slope() if filter_slope == 6: return 5 * time_const elif filter_slope == 12: return 7 * time_const elif filter_slope == 18: return 9 * time_const @@ -327,22 +331,22 @@ class SR830(Lock_In_Amp): """ Get the data from the buffer. - :return: np.ndarray - Returns a numpy of shape (<1 if one channel, 2 if both channels>, n_points) + :return: np.ndarray | tuple[np.ndarray, np.ndarray] """ if self._buffer_length is None: raise RuntimeError(f"Buffer not set up, call buffer_setup() first.") self.run("PAUS") take_n_points = min(self.buffer_get_n_points(), self._buffer_length) # there might be more points stored then was required - if CH1 and CH2: - data = np.empty((2, take_n_points), dtype=float) - elif CH1 or CH2: - data = np.empty((1, take_n_points), dtype=float) - else: + # if CH1 and CH2: + # data = (np.empty((2, take_n_points), dtype=float) for _ in range(2)) + # elif CH1 or CH2: + # data = np.empty((1, take_n_points), dtype=float) + if (not CH1) and (not CH2): raise ValueError("Either CH1 or CH2 must be set True.") + data = [] if CH1: - data[ 0, :] = self._buffer_get_data(1, 0, take_n_points)[:] + data.append(self._buffer_get_data(1, 0, take_n_points)) if CH2: - data[-1, :] = self._buffer_get_data(2, 0, take_n_points)[:] + data.append(self._buffer_get_data(2, 0, take_n_points)) return data def _buffer_get_data_slow(self, CH=1, start=0, n_points=None): @@ -402,3 +406,20 @@ class SR830(Lock_In_Amp): def __str__(self): return "SR830" + +def set_measurement_params(lockin: SR830, p: dict={}, **kwargs): + params = p | kwargs + key_to_setter = { + "time_constant_s": lockin.set_time_constant_s, + "filter_slope": lockin.set_filter_slope, + "sync_filter": lockin.set_sync_filter, + "reserve": lockin.set_reserve, + "sensitivity_volt": lockin.set_sensitivity_volt, + "frequency_Hz": lockin.set_frequency_Hz, + "reference": lockin.set_reference, + "reference_trigger": lockin.set_reference_trigger, + } + for k, v in params.items(): + if k not in key_to_setter.keys(): + raise KeyError(f"Invalid parameter {k}") + key_to_setter[k](v) diff --git a/prsctrl/devices/monochromator/impl/__pycache__/dummy.cpython-311.pyc b/prsctrl/devices/monochromator/impl/__pycache__/dummy.cpython-311.pyc index 3686fd1ca14192be559e2a06270fe5a534f00279..874cdf2df44778c2c141c349b3266c85c79ae0f8 100644 GIT binary patch delta 125 zcmaFD_l=KtIWI340}#xS-Ozn)5r!zTEzRN7k!sJ(_H2EWw z2zPO6i9$)fLUnmg?PNigI7wxo&IX3N9NhgJT^tt#O)qkoUEwghz+tv|GRtEo0F2um Ar~m)} diff --git a/prsctrl/devices/monochromator/impl/dummy.py b/prsctrl/devices/monochromator/impl/dummy.py index ee5caf4..b756e36 100644 --- a/prsctrl/devices/monochromator/impl/dummy.py +++ b/prsctrl/devices/monochromator/impl/dummy.py @@ -13,7 +13,7 @@ class DummyMonochromator(Monochromator): self.wavelength_nm = -1 def set_wavelength_nm(self, wavelength_nm): - log.info("Dummy-Monochromator set to {wl} nm") + log.info(f"Dummy-Monochromator set to {wavelength_nm} nm") self.wavelength_nm = wavelength_nm def get_wavelength_nm(self): diff --git a/prsctrl/measurement.py b/prsctrl/measurement.py index d929ae0..526a827 100644 --- a/prsctrl/measurement.py +++ b/prsctrl/measurement.py @@ -4,133 +4,125 @@ Created on Fri Jan 24 15:18:31 2025 @author: Matthias Quintern """ -from .measurement_device.base import VoltageMeasurementDevice -from .led_control_device.base import LedControlDevice -from .led_script import LedScript -from .utility.prsdata import DataCollector +from .devices.lock_in.base import LockInAmp +from .devices.shutter.base import Shutter +from .devices.monochromator import Monochromator +from .utility.prsdata import PrsData import time import datetime from queue import Queue +import numpy as np import logging log = logging.getLogger(__name__) -def measure( - vm_dev: VoltageMeasurementDevice, - led_dev: LedControlDevice, - led_script: LedScript, - data: DataCollector, - delta_t: float=0.1, - flush_after:int|None=None, - use_buffer=False, - max_measurements: int=None, - stop_on_script_end: bool=False, - verbose: bool=False, - command_queue: None|Queue=None, - data_queue: None|Queue=None, - add_measurement_info_to_metadata=True +def get_wavelengths_values(wl_range: tuple | list): + """ + :param wl_range: + if tuple, return list(range(*wl_range)) + if list, return copy of wl_range + """ + if isinstance(wl_range, tuple): + wavelengths = list(range(*wl_range)) + elif isinstance(wl_range, list) or isinstance(wl_range, np.ndarray): + wavelengths = wl_range.copy() + else: + raise ValueError(f"Invalid type for 'wavelengths_nm': {type(wl_range)}") + return wavelengths + + +def measure_spectrum( + monochromator: Monochromator, + lockin: LockInAmp, + shutter: Shutter, + data: PrsData, + measurement_params:dict, + aux_DC="Aux In 4", + command_queue: None | Queue = None, + data_queue: None | Queue = None, + add_measurement_info_to_metadata=True ): - """ - Perform a measurement - Parameters - ---------- - vm_dev : VoltageMeasurementDevice - DESCRIPTION. - led_dev : LedControlDevice - DESCRIPTION. - led_script : LedScript - DESCRIPTION. - data : DataCollector - DESCRIPTION. - delta_t : float, optional - Target interval between measurements and led updates. The default is 0.1. - flush_after : int|None, optional - If int, flush values to disk after . The default is None. - use_buffer : TYPE, optional - If True, use the buffer measurement mode. The default is False. - max_measurements : int, optional - Number of measurements to perform before returning. - Note: If use_buffer=True, a few more than max_measurements might be performed - The default is None. - stop_on_script_end : bool, optional - Stop when the script end is reached. - verbose : bool, optional - If True, print some messages. The default is False. - command_queue : None|Connection, optional - A queue to receive to commands from. - Commands may be: - "stop" -> stops the measurement - ("led_script", ) a new led script to use - The default is None. - data_queue : None|Queue, optional - A queue to put data in. The default is None. - add_measurement_info_to_metadata : bool, optional - If True, add measurement info to the metadata: - time, measurement_interval, measurement_use_buffer, measurement_voltage_device, measurement_led_device - The default is True. - Returns - ------- - None. + import pyvisa + def run_lockin_cmd(cmd, n_try=2): + com_success = n_try + e = None + while com_success > 0: + try: + return cmd() + except pyvisa.VisaIOError as e: + # TODO: retry if status bit is set + lockin.try_recover_from_communication_error(e) + com_success -= 1 + raise e + + default_measurement_params = { + "measurement_time_s": 30, + "sample_rate_Hz": 512, + "wait_time_s": 1, + "wavelengths_nm": (390, 720, 1), + } + measurement_params = default_measurement_params | measurement_params + wait_time_s = measurement_params["wait_time_s"] + sample_rate_Hz = measurement_params["sample_rate_Hz"] + measurement_time_s = measurement_params["measurement_time_s"] + n_bins = sample_rate_Hz * measurement_time_s + wavelengths = get_wavelengths_values(measurement_params["wavelengths_nm"]) + print(wavelengths) + + timeout_s = 3 * measurement_time_s + timeout_interval = 0.5 - """ get_time = lambda: datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S") if add_measurement_info_to_metadata: - data.metadata["measurement_interval"] = str(delta_t) + " s" - data.metadata["measurement_use_buffer"] = str(use_buffer) - data.metadata["measurement_voltage_measurement_device"] = str(vm_dev) - data.metadata["measurement_led_control_device"] = str(led_dev) - led_name = led_dev.get_led_name() - if led_name: data.metadata["measurement_led_lamp"] = led_name + data.metadata["device_lock-in"] = str(lockin) + data.metadata["device_monochromator"] = str(monochromator) data.metadata["measurement_time_start"] = get_time() # write metadata to disk data.write_metadata() - vm_dev.reset(True) - if use_buffer: - vm_dev.buffer_measure(delta_t, verbose=True) - # allow 0 instead of None - if max_measurements == 0: max_measurements = None - if flush_after == 0: flush_after = None + print(wait_time_s) + data.metadata["messages"] = [] + try: - i = 0 - led_val = led_script.start() - try: - led_dev.set_level(led_val) - except Exception as e: - log.error(f"Error setting led to {led_val:03}%: {e}") - raise e - t_iter_start = time.time() - while True: - # using while True and if, to be able to log the stop reason - if max_measurements is not None and i >= max_measurements: - log.info(f"Reached maximum number of measurements ({i}{max_measurements}), stopping measurement") - break - # 1) read value(s) - if use_buffer: - try: - values = vm_dev.buffer_read_new_values() - except ValueError as e: - # print(f"Error in buffer measurement {i}:", e) - values = [] - else: - values = [vm_dev.read_value()] - # print(values) - # 2) process value(s) - for (tval, vval) in values: - if i == 0: - t0 = tval - tval -= t0 - current_data = (i, tval, vval, led_val) - data.add_data(*current_data) - # 3) write data - if verbose: print(f"n = {i:6d}, t = {tval: .2f} s, U = {vval: .5f} V, LED = {led_val:03}%" + " "*10, end='\r') - if flush_after is not None and (i+1) % flush_after == 0: - data.flush(verbose=verbose) - # if a queue was given, put the data - if data_queue is not None: - data_queue.put(current_data) - i += 1 + shutter.open() + for i_wl, wl in enumerate(wavelengths): + log.info(f"Measuring at lambda={wl} nm") + run_lockin_cmd(lambda: lockin.buffer_setup(CH1="R", CH2=aux_DC, length=n_bins, sample_rate=sample_rate_Hz)) + + data_queue.put(("set_wavelength", wl)) + monochromator.set_wavelength_nm(wl) + data_queue.put(("wait_stable", )) + # wait the wait time + time.sleep(wait_time_s) + overload = run_lockin_cmd(lambda: lockin.check_overloads()) + if overload: + msg = f"Overload of {overload} at {wl} nm" + log.warning(msg) + data.metadata["messages"].append(msg) + theta = [] + measure_phase = lambda: theta.append(run_lockin_cmd(lambda: lockin.read_value("theta"))) + data_queue.put(("measuring", )) + measure_phase() + run_lockin_cmd(lambda: lockin.buffer_start_fill()) + # check if its done + t = timeout_s + while t > 0: + t -= timeout_interval + time.sleep(timeout_interval) + measure_phase() + if run_lockin_cmd(lambda: lockin.buffer_is_done()): + break + if t < 0: raise RuntimeError("Timed out waiting for buffer measurement to finish") + # done + dR_raw, R_raw = run_lockin_cmd(lambda: lockin.buffer_get_data(CH1=True, CH2=True)) + data[wl] = {} + data[wl]["dR_raw"] = dR_raw * 2 * np.sqrt(2) # convert RMS to Peak-Peak + data[wl]["R_raw"] = R_raw + data[wl]["theta_raw"] = np.array(theta) + spec_data = data.get_spectrum_data([wl]) + data.write_partial_file(wl) + data_queue.put(("data", spec_data)) # if a pipe was given, check for messages if command_queue is not None and command_queue.qsize() > 0: @@ -138,49 +130,56 @@ def measure( if recv == "stop": log.info(f"Received 'stop', stopping measurement") break - elif type(recv) == tuple and recv[0] == "led_script": - log.info(f"Received 'led_script', replacing script") - led_script = recv[1] elif type(recv) == tuple and recv[0] == "metadata": log.info(f"Received 'metadata', updating metadata") data.metadata |= recv[1] data.write_metadata() else: log.error(f"Received invalid message: '{recv}'") - - # 4) sleep - # subtract the execution time from the sleep time for a more - # accurate frequency - dt_sleep = delta_t - (time.time() - t_iter_start) - if dt_sleep > 0: - # print(f"Sleeping for {dt_sleep}") - time.sleep(dt_sleep) - t_iter_start = time.time() - # 5) update LED - if stop_on_script_end and led_script.is_done(t_iter_start): - log.info("Reached led script end, stopping measurement") - break - new_led_val = led_script.get_state(t_iter_start) - if new_led_val != led_val: - try: - led_dev.set_level(new_led_val) - led_val = new_led_val - except Exception as e: - log.error(f"Error setting led to {new_led_val:03}%: {e}") - raise e - except KeyboardInterrupt: log.info("Keyboard interrupt, stopping measurement") except Exception as e: log.critical(f"Unexpected error, stopping measurement. Error: {e}") if command_queue is not None: command_queue.put(("exception", e)) + raise e + if add_measurement_info_to_metadata: data.metadata["measurement_time_stop"] = get_time() # Write again after having updated the stop time data.write_metadata() - data.flush() - led_dev.off() + data.write_full_file() - - + +def set_offsets_laser_only( + lockin: LockInAmp, + shutter: Shutter, + wait_time_s, + R=True, + phase=True, + data_queue: None | Queue = None, + ): + """ + Set the R offset from the signal when only the laser is on. + This signal should be stray laser light and laser induced PL + :param phase: If True, use the Auto-Phase function to offset the phase + :param R: If True, use the Auto-Offset function to offset R + :return: Offset as percentage of the full scale R, Phase offset in degrees + """ + log.info("Setting offset when the lamp is off.") + shutter.close() + if data_queue: + data_queue.put(("wait_stable", )) + time.sleep(wait_time_s + 10) + # TODO: generalize for other lock-ins + if data_queue: + data_queue.put(("set_offsets", )) + lockin.run("AOFF 3") # auto offset R + # R must come before phase, because after auto-phase the signal needs to stabilize again + if R: + R_offset_fs = float(lockin.query("OEXP? 3").split(",")[0]) # returns R offset and expand + if phase: + lockin.run("APHS") + phase_offset_deg = float(lockin.query("PHAS? 3")) # returns R offset and expand + if data_queue: data_queue.put(("offsets", R_offset_fs, phase_offset_deg)) + return R_offset_fs, phase_offset_deg diff --git a/prsctrl/prsctrl_interactive.py b/prsctrl/prsctrl_interactive.py index a1c6f03..730ee97 100644 --- a/prsctrl/prsctrl_interactive.py +++ b/prsctrl/prsctrl_interactive.py @@ -2,10 +2,13 @@ run this before using this library: ipython -i prctrl_interactive.py """ +from toolz import frequencies + version = "0.1" import numpy as np import matplotlib.pyplot as plt +import time from datetime import datetime as dtime from os import path, makedirs @@ -30,13 +33,14 @@ from .devices import lock_in as mod_lock_in from .devices import lamp as mod_lamp from .devices import monochromator as mod_monochromator # import base classes -from .devices.lock_in import Lock_In_Amp +from .devices.lock_in import LockInAmp from .devices.shutter import Shutter from .devices.lamp import Lamp from .devices.monochromator import Monochromator -# from .measurement import measure as _measure -from .utility.data_collector import PrsDataCollector +from .devices.lock_in.impl.sr830 import set_measurement_params +from .measurement import measure_spectrum as _measure_spectrum, set_offsets_laser_only, get_wavelengths_values +from .utility.prsdata import PrsData, plot_spectrum from .utility.config_file import ConfigFile from .utility.device_select import select_device_interactive, connect_device_from_config_or_interactive from .update_funcs import Monitor @@ -64,12 +68,11 @@ test = False # DEVICES # global variable for the instrument/client returned by pyvisa/bleak -lockin: Lock_In_Amp|None = None +lockin: LockInAmp | None = None shutter: Shutter|None = None lamp: Lamp|None = None mcm: Monochromator|None = None -data_collector = PrsDataCollector(data_path=settings["datadir"], data_name="interactive", dirname="interactive_test", add_number_if_dir_exists=True) t0 = 0 data = None md = None @@ -78,97 +81,179 @@ from .test_measurement import _measure_both_sim def measure_both_sim(**kwargs): return _measure_both_sim(mcm, lockin, shutter, **kwargs) -def monitor(script: str|int=0, interval: float|None=None, metadata:dict={}, flush_after: int|None=None, use_buffer: bool|None=None, max_measurements=None, stop_on_script_end: bool=False, max_points_shown=None): - """ - Monitor the voltage with matplotlib. - - Opens a matplotlib window and takes measurements depending on settings["interval"] - - Waits for the user to press a key - - If use_buffer=False, uses python's time.sleep() for waiting the interval, which is not very precise. - With use_buffer=True, the timing of the voltage data readings will be very precise, however, - the led updates may deviate up to . - - The data is automatically saved to "_