From 659f32a1a8fc88ef419970241d699917969f6bae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnter=20Quast?= <guenter.quast@online.de>
Date: Fri, 7 Jun 2024 17:25:56 +0200
Subject: [PATCH] added components of mimoCoRB

---
 config/spectrum_config.yaml                   |  80 +++++++++++++
 modules/#redPitaya_source.py#                 |  64 +++++++++++
 modules/__pycache__/filters.cpython-310.pyc   | Bin 0 -> 2728 bytes
 modules/__pycache__/filters.cpython-311.pyc   | Bin 0 -> 5594 bytes
 .../plot_histograms.cpython-310.pyc           | Bin 0 -> 976 bytes
 .../plot_histograms.cpython-311.pyc           | Bin 0 -> 1418 bytes
 .../__pycache__/plot_waveform.cpython-310.pyc | Bin 0 -> 1175 bytes
 .../__pycache__/plot_waveform.cpython-311.pyc | Bin 0 -> 1616 bytes
 .../__pycache__/save_files.cpython-310.pyc    | Bin 0 -> 982 bytes
 .../__pycache__/save_files.cpython-311.pyc    | Bin 0 -> 1493 bytes
 .../spectrum_filter.cpython-310.pyc           | Bin 0 -> 3736 bytes
 .../spectrum_filter.cpython-311.pyc           | Bin 0 -> 5498 bytes
 modules/filters.py                            |  94 ++++++++++++++++
 modules/plot_histograms.py                    |  27 +++++
 modules/plot_waveform.py                      |  33 ++++++
 modules/redPitaya_source.py                   |  54 +++++++++
 modules/save_files.py                         |  26 +++++
 modules/spectrum_filter.py                    | 106 ++++++++++++++++++
 redP_mimoCoRB.py                              |  51 +++++++++
 setup.yaml                                    |  86 ++++++++++++++
 20 files changed, 621 insertions(+)
 create mode 100644 config/spectrum_config.yaml
 create mode 100644 modules/#redPitaya_source.py#
 create mode 100644 modules/__pycache__/filters.cpython-310.pyc
 create mode 100644 modules/__pycache__/filters.cpython-311.pyc
 create mode 100644 modules/__pycache__/plot_histograms.cpython-310.pyc
 create mode 100644 modules/__pycache__/plot_histograms.cpython-311.pyc
 create mode 100644 modules/__pycache__/plot_waveform.cpython-310.pyc
 create mode 100644 modules/__pycache__/plot_waveform.cpython-311.pyc
 create mode 100644 modules/__pycache__/save_files.cpython-310.pyc
 create mode 100644 modules/__pycache__/save_files.cpython-311.pyc
 create mode 100644 modules/__pycache__/spectrum_filter.cpython-310.pyc
 create mode 100644 modules/__pycache__/spectrum_filter.cpython-311.pyc
 create mode 100644 modules/filters.py
 create mode 100644 modules/plot_histograms.py
 create mode 100644 modules/plot_waveform.py
 create mode 100644 modules/redPitaya_source.py
 create mode 100644 modules/save_files.py
 create mode 100644 modules/spectrum_filter.py
 create mode 100644 redP_mimoCoRB.py
 create mode 100644 setup.yaml

diff --git a/config/spectrum_config.yaml b/config/spectrum_config.yaml
new file mode 100644
index 0000000..433f84e
--- /dev/null
+++ b/config/spectrum_config.yaml
@@ -0,0 +1,80 @@
+# Dict with uid's as key and a nested dict with configuration variables
+general:
+  runtime: 600  # desired runtime in seconds
+  runevents: &number_of_events 100000
+  number_of_samples: &number_of_samples 2048
+  analogue_offset: &analogue_offset 0
+  sample_time_ns: &sample_time_ns 32
+
+  trigger_level: &trigger_level 50
+  trigger_channel: &trigger_channel '1'
+  trigger_direction: &trigger_direction 'rising'
+  pre_trigger_samples: &pre_trigger_samples 103 # 5%
+
+find_peaks:
+  sample_time_ns: *sample_time_ns
+  analogue_offset: *analogue_offset
+  number_of_samples: *number_of_samples
+  pre_trigger_samples: *pre_trigger_samples
+  peak_minimal_prominence: 50  # has to be positive and higher than avg. noise peaks to not cause havoc!
+  trigger_channel: *trigger_channel
+  peak_minimal_distance: 10  # minimal distance between two peaks in number of samples
+  peak_minimal_width: 7  # in number of samples
+  trigger_channel: *trigger_channel
+  trigger_position_tolerance: 7  # in number of samples
+
+# dict for RedPitaya redPoscidaq
+redP_to_rb:
+  ip_address: '192.168.0.103'
+  eventcount: *number_of_events
+  sample_time_ns: *sample_time_ns
+  number_of_samples: *number_of_samples
+  pre_trigger_samples: *pre_trigger_samples
+  trigger_channel: *trigger_channel
+  trigger_level: *trigger_level
+  trigger_mode: 'norm'
+
+# Dict for simul_source.py
+simul_source:
+  sample_time_ns: *sample_time_ns
+  number_of_samples: *number_of_samples
+  pre_trigger_samples: *pre_trigger_samples
+  analogue_offset: *analogue_offset
+  eventcount: *number_of_events
+  sleeptime: 0.03
+  random: true
+
+# Dict for push_simul
+push_simul:
+  sample_time_ns: *sample_time_ns
+  number_of_samples: *number_of_samples
+  pre_trigger_samples: *pre_trigger_samples
+  analogue_offset: *analogue_offset
+  eventcount: *number_of_events
+  sleeptime: 0.03
+  random: true
+
+save_to_txt:
+  filename: "spectrum"
+
+save_parquet:
+  filename: "spectrum"
+
+plot_waveform:
+  title: "Muon waveform"
+  min_sleeptime: 0.5                # time to wait between graphics updates
+  number_of_samples: *number_of_samples
+  sample_time_ns: *sample_time_ns
+  analogue_offset: *analogue_offset # analogue offset in V
+  pre_trigger_samples: *pre_trigger_samples
+  channel_range: 4096                # channel range in mV
+  trigger_channel: *trigger_channel  # Channel name in the PicoScope. Valid values are 'A', 'B', 'C' or 'D'  
+  trigger_level: *trigger_level  # value in mV, take account of analogue_offset, which is added to input voltage !
+
+plot_histograms:
+  title: "on-line histograms"
+  # define histograms 
+  histograms:  
+    #  name        min   max   nbins  ymax   name   lin/log
+    chA_height:   [50., 3000.,   250, 5.9, "ph 1A", 0]
+    chB_height:   [50., 3000.,   250, 5.9, "ph 1B", 0]
diff --git a/modules/#redPitaya_source.py# b/modules/#redPitaya_source.py#
new file mode 100644
index 0000000..423ea6f
--- /dev/null
+++ b/modules/#redPitaya_source.py#
@@ -0,0 +1,64 @@
+"""
+**redPitaya_source**: mimoCoRB source compatible to redPoscdaq
+
+Input data is provided as numpy-arry of shape (number_of_channels, number_of_samples).
+"""
+
+from mimocorb.buffer_control import rbImport
+import numpy as np
+import sys, time
+from mutiprocessing import Event
+
+def simul_source(source_list=None, sink_list=None, observe_list=None, config_dict=None, **rb_info):
+    """
+    Generate simulated data and pass data to buffer
+
+    The class mimocorb.buffer_control/rbImport is used to interface to the
+    newBuffer and Writer classes of the package mimoCoRB.mimo_buffer
+
+    This example may serve as a template for other data sources
+
+    :param config_dict: configuration dictionary
+
+      - events_required: number of events to be simulated or 0 for infinite
+      - sleeptime: (mean) time between events
+      - random: random time between events according to a Poission process
+      - number_of_samples, sample_time_ns, pretrigger_samples and analogue_offset
+        describe the waveform data to be generated (as for oscilloscope setup)
+
+    Internal parameters of the simulated physics process (the decay of a muon)
+    are (presently) not exposed to user.
+    """
+
+    global databuffer
+    data_ready = Event()
+    data_ready.clear()
+
+    run_rpControl(callback=rp_data)
+
+    def rp_data(data):
+        while data_ready.is_set():
+            time.sleep(0.01)
+        databuffer = data    
+        data_ready.set()
+
+    events_required = 1000 if "eventcount" not in config_dict else config_dict["eventcount"]
+
+    def yield_data():
+        """generate simulated data, called by instance of class mimoCoRB.rbImport"""
+
+        event_count = 0
+        while events_required == 0 or event_count < events_required:
+            data_ready.wait()
+            # deliver pulse data and no metadata
+            yield (databuffer, None)
+            data_ready.clear()
+            event_count += 1
+            
+    datasource = rbImport(config_dict=config_dict, sink_list=sink_list, ufunc=yield_data, **rb_info)
+    # possibly check consistency of provided dtype with simulation !
+
+    # TODO: Change to logger!
+    # print("** simul_source ** started, config_dict: \n", config_dict)
+    # print("?> sample interval: {:02.1f}ns".format(osci.time_interval_ns.value))
+    datasource()
diff --git a/modules/__pycache__/filters.cpython-310.pyc b/modules/__pycache__/filters.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9aef4eb7f1388cf9ab8704995f557ad269652889
GIT binary patch
literal 2728
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

literal 0
HcmV?d00001

diff --git a/modules/__pycache__/filters.cpython-311.pyc b/modules/__pycache__/filters.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cb6d48f582732eccf3eeb668b632970fc3d21180
GIT binary patch
literal 5594
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

literal 0
HcmV?d00001

diff --git a/modules/__pycache__/plot_histograms.cpython-310.pyc b/modules/__pycache__/plot_histograms.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cf4bec83dff02352931ad37bd2a6f2832be03571
GIT binary patch
literal 976
zcmZWo&2AGh5MJ+3vfU&tr$RaTgh-?|N2Ca$hBk<bc2hN}1i7r7*h$>(t`~bZQlfBb
z&%6Sy#HH`SQ~1iMFTeo_iP<ESsvYgjc;@qtW<J}ZQt=FsZ$Ey~7nWiC%!B(<gkTp`
zU4o%T&!C8!A?l%pH+v>{E3_iJXQL(N^c?0~6{vlUAhS*#TDU~LB6Df++?2a)jk?S{
z^R7w^os}utGOp26;{x^qvIe;d^{&=wiI%U;3u}?B<R~>e3$1R<K6$lT5{fh!bCrsb
zB$2As8u%{46Cye3hwP=Q;-M4~?q|axlQ>g69^r^bVo#jB-MRZASmE9+3~Bf`NI!Xq
zX{USM9E}EczGEc?@DAt)P=&#y#@v{rxjDCnHaF(>ck~r~apum2k#o~As^)C!VUTtl
z@|a=DRT7dZ7Q_F|=RHIGXawk*=UuO6@fLL&34!S`kCQBI;DLyTe3VI&auGxELGFp1
zF8BJ<gchp>T~Z>G0~Un9o?Zh=&X>!w=qn~CE1<Nj7UX4gQT78K4}~t~yZ~D_hAcs~
z-~08kh*<q|MpRlK@w9%z=rK>pl+-1o$zrt~4U#dbM}lS{Q}qWYx06iu#)Di1N~@gd
z&7y~79%(lSBEsV!n7N&S<Oz&BbC0L#Si}vik$1b9w}ZB4ue$FKIvBntK5Flu9=34P
z-^YHti;r4Ozl-0sPjIITx#nTpZ{gO**4}CNpzT*}AEdgWi$OreAPBUjrb?Sa>8<?6
z420}Ix-k--jG?`Vr1=CP?`taq4*zu=lZZhfCy_i(wVjZ3tld6QEC;kr<Y=Oc6DAd$
slXk+hOr;B2@0s#q_}5GF-XMt7GcdN{S}rOh7kQRvxuxf56C(T1U&;a-a{vGU

literal 0
HcmV?d00001

diff --git a/modules/__pycache__/plot_histograms.cpython-311.pyc b/modules/__pycache__/plot_histograms.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..888023e5d33dc72af599714231aa19140b3b82eb
GIT binary patch
literal 1418
zcmZWn&1)M+6rb50NvoADHJ1qY(xHZ?5+u6#14JmOmYNC@DcB0MYGGMvR?^!0mDybf
zsVb=8gHNT0Kzgew#c^;B{u6rWA0P!m#a`P>ZUU1YeCo_fa%<(y%$u3_HS>P&y<A<D
z0i$33_=kKV0`M2VEJ&IkF0&AT7l42|Adm<Ktix~@>H^zELo`tx!I>^nj|f1E>_-<t
z2s&ln=WNZ0*{zbu5v<2Fi9`<sE~SL|#7RsO_QT(B*-3))WzyjO%0foq+TsXqzR_dy
zn-U1Z7bNDtx^N=#ljP$3|DDJramxO69{!DMP8l-iY|UsMYi0QbBJ2UaGRfZ8ayIK2
zwx@J-*R$JHHQj7BkFRg|P^G%sFtktI3~o`|#0|gI(kS*_-D+b~H|-sJ_w#$#4$53F
zzvX|Zyn?|8%eo*^J1Df<O*H=|-*^=`j+c4HUI6d93IOy$ANGa5*g`t!qu=0<@OxId
ze&m!{p0hP0UR9Qn(Cf;WQPnbZOT$EW9YgJ6yY*j6JQH?*FEN&j&U2Y(D2H+Kj><5>
zy5;y@9ye{PrMG>mdb(|~;7z`(v^%q{P>y^i8YEoXr%g>U7^Xp-0d+7tuGkG%qla@v
zVirx|VS*TKD7w|MgBVAPVd!#1cqSNM6>}Zi)N)UK)%9|1-OKH2<gxClT{TBF;)JKW
zX4C1YInyS-p}D!Gn%;A|L25}xc~9{kb90Ohz`G<!ef0BpXXT6j@81lQ-||_T9|jL5
zC^2~OL;0vY2*)LJ0fD3_rm9<t(v$X@RCgFeJ?U}R>)2KvUqSipZDw@^@AkH9UzYbU
zn+5!+T72@bgbS4-u2gIIQK?X=;V-JYc(2A}g@@Hj375Vq?L4WKtCb9@Fv_HYn4%E7
zsVITycHKa*-C&LPUel(H_j-@=#tLLp^?1gH-UvjW;m*_LKekj;V}T+L)h#bTj_P#+
zsiC?WR|H6<?ZY5;s8N^IHi#JezU#dLG{w9rN9#(uKLhzQ`Z2rmMs@#YY$`$sr-A^{
z6mS*=Ffow%n1J{o922lLyg4V})^IVoM`|D+C6AwtKza<)Ly(?EC77Ioo9s3~E62Sz
z;@VJLo1plhbe#Q(K7aOS`p%p5oip*ZbaCr-VwBz(r#D9O#t3bW(dH0sE=-%x4^B2-
t=6}tfH7?di>s#aXtr6NDqwOKu4sC|=&IlF9s5o5AX$+tb*=r#R{{jfgepdhh

literal 0
HcmV?d00001

diff --git a/modules/__pycache__/plot_waveform.cpython-310.pyc b/modules/__pycache__/plot_waveform.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..e4803edcf405c5ab825674ee3fc3f085ebe0cf1e
GIT binary patch
literal 1175
zcmZ8g&2Aev5GMCWTCXIhhrs<g@hT&>js=9Gh!U%4&{#4o1qgH@s3o<cy}R6!TpKc!
zlXLDX)CPLVd-N%I?WK8v9tsrb?8*t;4wf^V9}d5r8LD0{MnJ#*{ulq#N9d1Qc}#6k
zp2ITlfpN4z9CI(j3*1O=;eqrsKMxiG-uc2sD8joI5AHEo_Ib!#@A0B7BHq6A^mDPt
zBjH`dcO8K*x*UIv?(vuC7GlA(2euxM@Ai3zckjJhzp?geFZX86>OZ-88y_B)nX-q6
zLsCmCi<DfmE3r~KH)N$%PL|bbB{Zo_?aO7ZUaIpK$2%)cRAIHsj;BkZjnG#@pD@cv
z{1N^UO+H}PsngP3v<v5#qclx|+W9~&kRhy}U>O3;qAl9ut+(}8fkfNjH~b5J7jDB_
zR99t&`W`#Q2*m{G403;lOL%>A0~t`t8s!7_xCtJXOtYNGqO9zY0PvMeE6uD_1>rKO
zCDWT-EFnitJ*bs76GM_}R<31YNTU<0$WjoqR@ViA(~Y;eqdu3*f|^W-(#jl;)*Pbk
zwGaiVN&qR$=UBt?G81r1E@)w%kz~z^LS$64A{Ea_sfE=tO@*erNJCh`VX;i5m7r?1
zGQvJ&BZLc+XbH&dTKump44SDlQ6`aD23(bZU)ZW_4o|cPmG8G)$EZptf@acKw+9`+
z+-<vR*>rcOb#}$0b=6#3FR3h6%C*YOLZ3e%-GO9(zZk4lE(SkW%-BIH?ciMSGilj|
z4Yc58vpdR@a?J+0;#DThpc$1<(|25MT<<@YeMoES**WOOSodJ#B1&@x^`<w`41T?Y
z#@<9{8@pD;kT@KFJgA}IqnmH%Kfam~_(tS(dh+J=n2aVTWHOzT)A49BCqGQj$!rd`
z(d+4COvZ1=FW<~xO(*?e0@Q!v+LUsYQ0jcMG0s!QJ+0qDqV)3k(THn!`N+jNv$Zjk
zOXpWmxldpV292gJEVV4G3rc3!E?P1pYKIG$POn@W9+81zbYXT;8QWCz5$iVp{oSr+
e-@{8l<||+Uih>AtafD+(_M<Pp!3UrP|NINagkClP

literal 0
HcmV?d00001

diff --git a/modules/__pycache__/plot_waveform.cpython-311.pyc b/modules/__pycache__/plot_waveform.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..13b544f98c384cbdc54a7eff10c19b584be60ad5
GIT binary patch
literal 1616
zcmZ7$J!>OJbXHn@cxBlfgeXo@3~_N-*j8hoL4<(Mmhr(^GPW)pYL?ONNLqWpGBdKV
zqJRP}T!j<|mo6p*-yOcfWqv@4l+q!H*)}P1oxmi8tGwB@%-L^e-kW*z-n`G*SJ|wF
z0Dk`cKl7hBLVwB47}7V3tGt5HOJt%NG8HpsD>Wsgv04mh+>SeHO;w_^L@g0RG_jW)
zUt+3fpnVQA0+7p^iDRXfqG>a^ACp{Wg3g4QqOrZu8_7G(NPHitvNt*Anaa#~iSF9y
z0RJvWL7{~-=x4`D=EUjLc>N(YH8XPtew~ND$&)k4%sI>mZtj}AZ^4Z1B01-#I?uIy
z-nTuG&lj<z!g8DVkQ~s4#~h9u%yV$P-DpsT+gzSGmgB8?JC7G5u5Ng)V4l5LsZ+)&
zJD_Zx2!gfu(uMLHCCJEY^+-BMRQFe#O+&qLP!gGcnZc_fpqEJ8<{zOh>MGq>H{MV!
z)Kz~~eo(%HnspOrAbAcm0$ERpB}TSDRl@+=VDUBxY)XH^K)`oP`hF9U777=A!ia+{
z*Kdm=hHx5Iv(1RGJQtgmAt_-;ku1gwVU~1Z37ZyilM%mV861auEIeGNn76z`7lU?q
z8rqP0j^*myrqmaf1F8~(v^b>H#cdy)r2JNv6UVnHSaGQC@_B5ui0e{YXT)vNdF(SP
znAL1jrbm(-6W4@EY_Hj-y4PrMDsFDW*reQG7I-FF^j=yBl-Og~<A!D1fIT1lr=snL
z%4H^7X)YOLxYuR|)oqK5U;_GJKUz+D^|0pAE)ykBmstx^tgc&b!wZtWEui{b)>#$@
z<Lk$TmgmsIH*La2p=pW24mGzeL5@g)QPU4s3y$HpNWt;UwoQ2<Y@%^rF8W77_6~>1
z_aP@=1k^+SeuAcEe){axy7>IJZ~NJ=Vf@jIXm8_E)p{G>Zys;<!tV++P>|Mjhd_z+
zPI?!z<wGxY(%VO(<+(+Ct!VE*2B~Xhp|eume6ovStm3W8`m?7cyjotz<w_NAl~&7D
zyiwV~yH((=KCP5Xxb(cV_N@A(Ql3-G0CUqpO4m)#(DfkBk9ZLCc<_;YREEduiyyod
zCLc+lIYdaNZPkN#8<H)9l+L;YdP5HqKC@gAs6G*`AYCV%%7Z{9ta%Wm;5BmS-ymV{
zwYhkM*feM}`CqwCe;K9rEA|C!<)h<&0~*B@MH$6niaJ6PrIC{9Y4W>76TR@eMDzW-
z;}SjSkH;v9l<A&!Jauw7K)E5x^-*q=%qrOtx{EMUCr>)>;xqmD%%wWfE1l$jWG@c?
z%sqUUd-yv3HhuBnZDx>L8s?S;+R{K>9;(ZIb$Kjmyx2codR6?T__}^EJD7bmoP9J<
fSBC0JUtI}h`r6t+T_39J{qY#3koqZD3$yS)yRg6Q

literal 0
HcmV?d00001

diff --git a/modules/__pycache__/save_files.cpython-310.pyc b/modules/__pycache__/save_files.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..d2288e065bf1d008f36656f6533fcbd3773e07f4
GIT binary patch
literal 982
zcmZ`%%}?7f6t~l)P16G1G=a9m<ZBNs$4O{nt461xL5paSre3l%c4;7qi|tml>ZD0*
z+HHTq4j}EqVgHb?J8|ZQG-=N%f?arupWm0?$M1zNEqMs&clv4Yy@1dkqx?@LP~O0i
zUw~26Lljdh!9AScR?mXFkQ7q8XQxij!HBL<n>t@{uLx3+mOv^om%7K6*kol|X4a8+
zUSasCO7T;4f!EL(#t1wf*p_JJe3@41(uH+a$gRu9ORY|R>b2RoT~5adBQl(@U=Sxv
z62-|d%qSR*PPY9v8E`>ps6rBFM4c)EZZZnR$(SkQOGD*(gplqqmLy#y6fB7$8P7<R
zgi?YCZ__yC5f}Xz{qbPHL=f>z37&XY00wH+EnOA;K=JOWGNpAb*Y*~Lj3u&7;4Ky5
zcHn$~bNw8cf}!SUUZD7=HMf7^ANaE~cg_$Dj5_GDU<y<uAB%_uNi3DFK>a~{7|=LU
zm#B_~1z9eO!erKE8E40fxGL{UCIBGwN|+Vu60{a)1FkJO5iVGAcJtaF@|5`}<4`L9
zFjoFPqkFLmr=c$x9p%z`8jXgbzracQ_sG8(O`$)t2*m?{xe95o&BRZzhs6pA56&-4
zm@sI-6!706Q8w(ZF|iD)uI9;a7x&l%)C_xMd~Oh=VVniQ%<V)X9;pp7bN8ld$g>Ti
zv3Io!Q8##f_OknayF=h>klpsy!A_GjT3e*m?vmYRqtzwv+WVx_1zTgM-D;BN$L8CE
z?smIXw_8B<6<rDf%A+9A1v!=4;!>|Y@L%NnSd3$)ziTuLQwHyyk%%*;?NO+P+U<vu
z8Hct*aX8VX2@^8rnRb$+F>LJ~id@R^a9=H!u!$kNGy~embzEG-p6wM~=NVqbRc!zL
E2mXQtjsO4v

literal 0
HcmV?d00001

diff --git a/modules/__pycache__/save_files.cpython-311.pyc b/modules/__pycache__/save_files.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8438319ec401b1a914fec694630618c82f38c344
GIT binary patch
literal 1493
zcmb_b&2Jk;6rb50f9%>xTM$^`G*S+csaju>1_cC4Y9k^wb`-k^`Le8c*6}9ZU3YdI
z8WSWV710yoP^lLV7D8#G6sgDl2|e1CC8Cw+g;Q^qa?6P~yD>BcZoHj&Z|2RL_kQoq
z+eb4qIs*FD{j&MJiqIc&Go|$T;&c`x^bj#rM+`Hijq5m)N?ie|wpEwZiJPb=Fk&-|
z+{5)G$OOogoo2~hMf%}PLAJ(Hwz3mNl)6Di@;AxGJK3p@;koG&9RiADm@)N0T-0C`
zMM%T_nW+`i2D8)k>yqg#a|pPOBW&{I5VAjp8PmD1?3)poQbls+XM5jm_^jKuX<+Wz
zM$>8A0Tn)NnH~eB+|%_!g*JUonJG-_cvRdK6pm@f<afHZkY}zbbe&RKYdHaRV~5;s
zJK)UssMR)u0G4pqbzI-_dE<Jc+icq0uzXK&zpcNLc*}Vu%<_gI{Mub1Lx-26-DVs}
zYAkY|nG{Kwvh=4P0DFkUUqwZb+yOdJ8U9H*Aiv-r@HYts9V8CH?iglF@XV-+j#L>o
z6Zl<j*+$z5M3@0oO=sI+jwOyz9&=gGqa;s(2(`fRc4ND&-w13DWs|qKY_l*0fE};t
zhf1&)NPJKfptDa3E#I{Zce-XE3fqn-Y}stn5$3*G;5O?-t81>+X_<w%d_m#w2f5za
z2Y~W@gl|Cg`DJh{dn>pKtdD-z-Wh6JgR9Rz1OemuLM;1hCnVWl`?h$m*pI$bFh~D?
z3As!Z$|T>0%fI5a%=snQhuH}IdBgCxLG_k2o5G(PYM&2wpM4Di#`Eo1{sTV0B7LO|
z!!;exFnZ~##hs2=p}q9xzG(U03O&R6t9QZb3}5Rl*FIaXQW&drqf)$mt3+4JMOv=Z
z=tgO^T%(^>wrI5my472ia*39{DBZkWTd$P!qzsh56s8P=`IcdXYOo)KiXViz|LEVS
zL*o49+e8^kpq}a4a6i%EjweFWF-0p(H_X762O%-}_FkCUvw7h7UYKa_bfJ$l=|E;0
zmF_J4QQQz8^Lfz877tzmo2VG$iK1dML6YeRXZpJQPLS4*z7w=I{I@YlD_EPLi!6I_
z8=dNiWbgN0sB=Sg?u2Ol(*5^;<PX03Ew}I@xA0hfl74#SNoJH=9Oo8C`r?QzjmgrG
zEKN<V2fKsCM=L+CJZ?OlAI;ww&)*o4<uO?vlI6%|sNWos;+PbN(=kaQ@*YHs^8XX%
Cw0dj+

literal 0
HcmV?d00001

diff --git a/modules/__pycache__/spectrum_filter.cpython-310.pyc b/modules/__pycache__/spectrum_filter.cpython-310.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..2654ba2c30ba1ff2f15a2e882c28db1786368ca7
GIT binary patch
literal 3736
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

literal 0
HcmV?d00001

diff --git a/modules/__pycache__/spectrum_filter.cpython-311.pyc b/modules/__pycache__/spectrum_filter.cpython-311.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..cb8923dcc4ebbf21c4b01e74dd7460d7a7ed07df
GIT binary patch
literal 5498
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
F{10oL37r4{

literal 0
HcmV?d00001

diff --git a/modules/filters.py b/modules/filters.py
new file mode 100644
index 0000000..7158cc6
--- /dev/null
+++ b/modules/filters.py
@@ -0,0 +1,94 @@
+from mimocorb import mimo_buffer as bm
+from scipy import signal
+import numpy as np
+from numpy.lib import recfunctions as rfn
+import sys
+import os
+
+
+def normed_pulse(ch_input, position, prominence, analogue_offset):
+    # > Compensate for analogue offset
+    ch_data = ch_input - analogue_offset
+    # > Find pulse area
+    #       rel_height is not good because of the quantized nature of the picoscope data
+    #       so we have to "hack" a little bit to always cut 10mV above the analogue offset
+    width_data = signal.peak_widths(ch_data, [int(position)], rel_height=(ch_data[int(position)] - 10) / prominence)
+    left_ips, right_ips = width_data[2], width_data[3]
+    # Crop pulse area and normalize
+    pulse_data = ch_data[int(np.floor(left_ips)) : int(np.ceil(right_ips))]
+    pulse_int = sum(pulse_data)
+    pulse_data *= 1 / pulse_int
+    return pulse_data, int(np.floor(left_ips)), pulse_int
+
+
+def correlate_pulses(data_pulse, reference_pulse):
+    correlation = signal.correlate(data_pulse, reference_pulse, mode="same")
+    shift_array = signal.correlation_lags(data_pulse.size, reference_pulse.size, mode="same")
+    shift = shift_array[np.argmax(correlation)]
+    return shift
+
+
+def tag_peaks(input_data, prominence, distance, width):
+    peaks = {}
+    peaks_prop = {}
+    for key in input_data.dtype.names:
+        peaks[key], peaks_prop[key] = signal.find_peaks(
+            input_data[key], prominence=prominence, distance=distance, width=width
+        )
+    return peaks, peaks_prop
+
+
+def correlate_peaks(peaks, tolerance):
+    m_dtype = []
+    for key in peaks.keys():
+        m_dtype.append((key, np.int32))
+    next_peak = {}
+    for key, data in peaks.items():
+        if len(data) > 0:
+            next_peak[key] = data[0]
+    correlation_list = []
+    while len(next_peak) > 0:
+        minimum = min(next_peak.values())
+        line = []
+        for key, data in peaks.items():
+            if key in next_peak:
+                if abs(next_peak[key] - minimum) < tolerance:
+                    idx = data.tolist().index(next_peak[key])
+                    line.append(idx)
+                    if len(data) > idx + 1:
+                        next_peak[key] = data[idx + 1]
+                    else:
+                        del next_peak[key]
+                else:
+                    line.append(-1)
+            else:
+                line.append(-1)
+        correlation_list.append(line)
+    array = np.zeros(len(correlation_list), dtype=m_dtype)
+    for idx, line in enumerate(correlation_list):
+        array[idx] = tuple(line)
+    return array
+
+
+def match_signature(peak_matrix, signature):
+    if len(signature) > len(peak_matrix):
+        return False
+    # Boolean array with found peaks
+    input_peaks = rfn.structured_to_unstructured(peak_matrix) >= 0
+    must_have_peak = np.array(signature, dtype=np.str0) == "+"
+    must_not_have_peak = np.array(signature, dtype=np.str0) == "-"
+    match = True
+    # Check the signature for each peak (1st peak with 1st signature, 2nd peak with 2nd signature, ...)
+    for idx in range(len(signature)):
+        # Is everywhere a peak, where the signature expects one -> Material_conditial(A, B): (not A) OR B
+        first = (~must_have_peak[idx]) | input_peaks[idx]
+        # Is everywhere no peak, where the signature expects no peak -> NAND(A, B): not (A and B)
+        second = ~(must_not_have_peak[idx] & input_peaks[idx])
+        match = match & (np.all(first) & np.all(second))
+    return match
+
+
+if __name__ == "__main__":
+    print("Script: " + os.path.basename(sys.argv[0]))
+    print("Python: ", sys.version, "\n".ljust(22, "-"))
+    print("THIS IS A MODULE AND NOT MEANT FOR STANDALONE EXECUTION")
diff --git a/modules/plot_histograms.py b/modules/plot_histograms.py
new file mode 100644
index 0000000..fce2408
--- /dev/null
+++ b/modules/plot_histograms.py
@@ -0,0 +1,27 @@
+"""
+**plot_histograms**: histogram variable(s) from buffer using mimoCoRB.histogram_buffer 
+"""
+import sys
+import os
+from mimocorb.histogram_buffer import histogram_buffer
+import matplotlib
+
+# select matplotlib frontend if needed
+matplotlib.use("TkAgg")
+
+
+def plot_histograms(source_list=None, sink_list=None, observe_list=None, config_dict=None, **rb_info):
+    """
+    Online display of histogram(s) of variable(s) from mimiCoRB buffer
+
+    :param input: configuration dictionary
+
+    """
+    histbuf = histogram_buffer(source_list, sink_list, observe_list, config_dict, **rb_info)
+    histbuf()
+
+
+if __name__ == "__main__":
+    print("Script: " + os.path.basename(sys.argv[0]))
+    print("Python: ", sys.version, "\n".ljust(22, "-"))
+    print("THIS IS A MODULE AND NOT MEANT FOR STANDALONE EXECUTION")
diff --git a/modules/plot_waveform.py b/modules/plot_waveform.py
new file mode 100644
index 0000000..66d0c12
--- /dev/null
+++ b/modules/plot_waveform.py
@@ -0,0 +1,33 @@
+"""
+**plot**: plotting waveforms from buffer using mimoCoRB.buffer_control.OberserverData 
+"""
+
+import sys
+import os
+from mimocorb.plot_buffer import plot_buffer
+import matplotlib
+
+# select matplotlib frontend if needed
+matplotlib.use("TkAgg")
+
+
+def plot_waveform(source_list=None, sink_list=None, observe_list=None, config_dict=None, **rb_info):
+    """
+    Plot waveform data from mimiCoRB buffer
+
+    :param input: configuration dictionary
+
+      - plot_title: graphics title to be shown on graph
+      - min_sleeptime: time between updates
+      - sample_time_ns, channel_range, pretrigger_samples and analogue_offset
+        describe the waveform data as for oscilloscope setup
+    """
+
+    pltbuf = plot_buffer(source_list, sink_list, observe_list, config_dict, **rb_info)
+    pltbuf()
+
+
+if __name__ == "__main__":
+    print("Script: " + os.path.basename(sys.argv[0]))
+    print("Python: ", sys.version, "\n".ljust(22, "-"))
+    print("THIS IS A MODULE AND NOT MEANT FOR STANDALONE EXECUTION")
diff --git a/modules/redPitaya_source.py b/modules/redPitaya_source.py
new file mode 100644
index 0000000..adabc04
--- /dev/null
+++ b/modules/redPitaya_source.py
@@ -0,0 +1,54 @@
+"""
+**simul_source**: a simple template for a mimoCoRB source to 
+enter simulated wave form data in a mimoCoRB buffer.
+
+Input data is provided as numpy-arry of shape (number_of_channels, number_of_samples).
+"""
+
+from mimocorb.buffer_control import rbImport
+import numpy as np
+import sys, time
+from pulseSimulator import pulseSimulator
+
+def simul_source(source_list=None, sink_list=None, observe_list=None, config_dict=None, **rb_info):
+    """
+    Generate simulated data and pass data to buffer
+
+    The class mimocorb.buffer_control/rbImport is used to interface to the
+    newBuffer and Writer classes of the package mimoCoRB.mimo_buffer
+
+    This example may serve as a template for other data sources
+
+    :param config_dict: configuration dictionary
+
+      - events_required: number of events to be simulated or 0 for infinite
+      - sleeptime: (mean) time between events
+      - random: random time between events according to a Poission process
+      - number_of_samples, sample_time_ns, pretrigger_samples and analogue_offset
+        describe the waveform data to be generated (as for oscilloscope setup)
+
+    Internal parameters of the simulated physics process (the decay of a muon)
+    are (presently) not exposed to user.
+    """
+
+    events_required = 1000 if "eventcount" not in config_dict else config_dict["eventcount"]
+
+    def yield_data():
+        """generate simulated data, called by instance of class mimoCoRB.rbImport"""
+
+        event_count = 0
+        while events_required == 0 or event_count < events_required:
+            pulse = dataSource(number_of_channels)
+            # deliver pulse data and no metadata
+            yield (pulse, None)
+            event_count += 1
+
+    dataSource = pulseSimulator(config_dict)
+    simulsource = rbImport(config_dict=config_dict, sink_list=sink_list, ufunc=yield_data, **rb_info)
+    number_of_channels = len(simulsource.sink.dtype)
+    # possibly check consistency of provided dtype with simulation !
+
+    # TODO: Change to logger!
+    # print("** simul_source ** started, config_dict: \n", config_dict)
+    # print("?> sample interval: {:02.1f}ns".format(osci.time_interval_ns.value))
+    simulsource()
diff --git a/modules/save_files.py b/modules/save_files.py
new file mode 100644
index 0000000..0007447
--- /dev/null
+++ b/modules/save_files.py
@@ -0,0 +1,26 @@
+"""Module save_files to handle file I/O for data in txt and parquet format
+
+   This module relies on classes in mimocorb.buffer_control
+"""
+
+import sys
+import os
+from mimocorb.buffer_control import rb_toTxtfile, rb_toParquetfile
+
+
+# def save_to_txt(source_dict):
+def save_to_txt(source_list=None, sink_list=None, observe_list=None, config_dict=None, **rb_info):
+    sv = rb_toTxtfile(source_list=source_list, config_dict=config_dict, **rb_info)
+    sv()
+    # print("\n ** save_to_txt: end seen")
+
+
+def save_parquet(source_list=None, sink_list=None, observe_list=None, config_dict=None, **rb_info):
+    sv = rb_toParquetfile(source_list=source_list, config_dict=config_dict, **rb_info)
+    sv()
+
+
+if __name__ == "__main__":
+    print("Script: " + os.path.basename(sys.argv[0]))
+    print("Python: ", sys.version, "\n".ljust(22, "-"))
+    print("THIS IS A MODULE AND NOT MEANT FOR STANDALONE EXECUTION")
diff --git a/modules/spectrum_filter.py b/modules/spectrum_filter.py
new file mode 100644
index 0000000..fc80228
--- /dev/null
+++ b/modules/spectrum_filter.py
@@ -0,0 +1,106 @@
+"""Module **pulse_filter** 
+
+This (rather complex) module filters waveform data to search for valid signal pulses. 
+The code first validates the trigger pulse, identifies coincidences of signals in 
+different layers (indiating the passage of a cosmic ray particle, a muon) and finally
+searches for double-pulse signatures indicating that a muon was stopped in or near 
+a detection layer where the resulting decay-electron produced a delayed pulse. 
+The time difference between the initial and the delayed pulses is the individual 
+lifetime of the muon.
+
+The decay time and the properties of the signal pulses (height, integral and 
+postition in time) are written to a buffer; the raw wave forms are optionally
+also written to another buffer. 
+
+The callable functions *find_peaks()* and *calulate_decay_time()* depend on the 
+buffer manager *mimoCoRB* and provide the filter functionality described above. 
+These functions support multiple sinks to be configured for output. 
+
+The relevant configuration parameters can be found in the section *find_peaks:* 
+and *calculate_decay_time:* in the configuration file. 
+
+""" 
+
+from mimocorb.buffer_control import rbTransfer
+import numpy as np
+import pandas as pd
+import os, sys
+
+from filters import *
+
+def find_peaks(source_list=None, sink_list=None, observe_list=None, config_dict=None, **rb_info):
+    """filter client for mimoCoRB: Find valid signal pulses in waveform data
+
+       Input: 
+
+        - wave form data from source buffer defined in source_list
+
+       Returns: 
+  
+         - None if filter not passed
+         - list of list(s) of pulse parameters, written to sinks defined in sink_list
+
+    """
+    
+    if config_dict is None:
+        raise ValueError("ERROR! Wrong configuration passed (in lifetime_modules: calculate_decay_time)!!")
+
+    # Load configuration
+    sample_time_ns = config_dict["sample_time_ns"]
+    analogue_offset = config_dict["analogue_offset"]*1000
+    peak_minimal_prominence = config_dict["peak_minimal_prominence"]
+    peak_minimal_distance = config_dict["peak_minimal_distance"]
+    peak_minimal_width = config_dict["peak_minimal_width"]
+    pre_trigger_samples = config_dict["pre_trigger_samples"]
+    trigger_channel = config_dict["trigger_channel"]
+    if trigger_channel not in ['A','B','C','D']:
+         trigger_channel = None    
+    trigger_position_tolerance = config_dict["trigger_position_tolerance"]
+
+    pulse_par_dtype = sink_list[-1]['dtype']
+
+    
+    def tag_pulses(input_data):   
+        """find all valid pulses 
+
+        This function to be called by instance of class mimoCoRB.rbTransfer
+
+           Args:  input data as structured ndarray
+    
+           Returns: list of parameterized pulses
+        """
+
+        # Find all the peaks and store them in a dictionary
+        peaks, peaks_prop = tag_peaks(input_data, peak_minimal_prominence, peak_minimal_distance, peak_minimal_width)
+
+        # identify trigger channel, validate trigger pulse and get time of trigger pulse
+        if trigger_channel is not None:
+           trigger_peaks = peaks['ch' + trigger_channel]
+           if len(trigger_peaks) == 0:
+               return None
+           reference_position = trigger_peaks[np.argmin(np.abs(trigger_peaks - pre_trigger_samples))]
+        else: # external or no trigger: set to nominal position 
+           reference_position = pre_trigger_samples
+
+        peak_data= np.zeros( (1,), dtype=pulse_par_dtype)
+        for key in peaks.keys():
+            for position, height, left_ips, right_ips in zip(
+                    peaks[key], peaks_prop[key]['prominences'],
+                    peaks_prop[key]['left_ips'], peaks_prop[key]['right_ips']):
+                if np.abs(reference_position - position) < trigger_position_tolerance:
+                    peak_data[0][key+'_position'] = position
+                    peak_data[0][key+'_height'] = input_data[key][position] - analogue_offset #height
+                    left = int(np.floor(left_ips))
+                    right = int(np.ceil(right_ips))
+                    peak_data[0][key+'_integral'] = \
+                    sum(input_data[key][left:right] - analogue_offset) * sample_time_ns * 1e-9/50/5
+        return [peak_data]    
+        
+    p_filter = rbTransfer(source_list=source_list, sink_list=sink_list, config_dict=config_dict,
+                        ufunc=tag_pulses, **rb_info)
+    p_filter()
+
+if __name__ == "__main__":
+    print("Script: " + os.path.basename(sys.argv[0]))
+    print("Python: ", sys.version, "\n".ljust(22, '-'))
+    print("THIS IS A MODULE AND NOT MEANT FOR STANDALONE EXECUTION")
diff --git a/redP_mimoCoRB.py b/redP_mimoCoRB.py
new file mode 100644
index 0000000..6882767
--- /dev/null
+++ b/redP_mimoCoRB.py
@@ -0,0 +1,51 @@
+"""
+**redP_mimoCoRB**: a simple template to use mimoCoRB with the RedPitaya and redPoscdaq.py 
+
+Input data is provided as numpy-arry of shape (number_of_channels, number_of_samples).
+"""
+
+import time
+import sys
+
+import redPoscdaq as rp
+
+
+def redP_to_rb(source_list=None, sink_list=None, observe_list=None, config_dict=None, **rb_info):
+    """
+    Get data from RedPitaya and pass data to buffer
+
+    The class mimocorb.buffer_control/rbImport is used to interface to the
+    newBuffer and Writer classes of the package mimoCoRB.mimo_buffer
+
+    This example may serve as a template for other data sources
+
+    :param config_dict: configuration dictionary
+
+      - events_required: number of events to be simulated or 0 for infinite
+      - sleeptime: (mean) time between events
+      - random: random time between events according to a Poission process
+      - number_of_samples, sample_time_ns, pretrigger_samples and analogue_offset
+        describe the waveform data to be generated (as for oscilloscope setup)
+
+    Internal parameters of the simulated physics process (the decay of a muon)
+    are (presently) not exposed to user.
+    """
+
+    # initialize mimocorb class inside redPoscidaq
+    datasource= rp.redP_mimocorb(config_dict=config_dict, sink_list=sink_list, **rb_info)
+    #print("data source initialized")
+
+    # start oscilloscope
+    #print("starting osci")
+    rp.run_rpControl(callback=datasource.data_sink, conf_dict=config_dict)
+    
+
+#daq = run_mimoDAQ('redP_mimoCoRB.yaml')
+#daq.setup()
+#RB_1 = daq.ringbuffers['RB_1']
+#sink_dict = RB_1.new_writer()
+#datasource= rp.redP_mimocorb(config_dict={}, sink_list=[sink_dict], RB_1='write')
+#print("data source initialized")
+#rp.run_rpControl(callback=datasource.data_sink)
+#print("starting DAQ")
+#daq.run()
diff --git a/setup.yaml b/setup.yaml
new file mode 100644
index 0000000..a079387
--- /dev/null
+++ b/setup.yaml
@@ -0,0 +1,86 @@
+# Configuration for recording two channels with mimoCoRB
+#  -----------------------------------------------------
+#
+# configure two buffers:
+#  - RB_1 for raw waveforms
+#  - RB_2 for derived pulse parameters
+#  data from RB_2, the result buffer, are saved to a file in csv (text) format.
+#
+#  - data from RB_1 are also passed to an obsever process driving a real-time display
+#  - data from RB_2 are passed to a Reader process driving a real-time histogram display
+#
+# Notes:
+# 
+#    1. additional config files controlling the user functions are
+#       located in the subdirectory config/
+#    2. necessary application-specific user code is located
+#       in the subdirectory modules/
+#
+# ----------------------------------------------------------------------------
+#
+
+RingBuffer:
+  # define ring buffers
+  - RB_1:
+      # raw input data buffer (from picoScope, file or simulation)
+      number_of_slots: 16
+      channel_per_slot: 2048
+      data_type:
+          1: ['chA', "float32"]
+          2: ['chB', "float32"]
+  - RB_2:
+      # buffer with correct signature double pulse parameters
+      number_of_slots: 16
+      channel_per_slot: 1
+      data_type:
+      data_type:
+          1: ['chA_height', "float32"]
+          2: ['chA_position', "int32"]
+          3: ['chA_integral', "float32"]
+          4: ['chB_height', "float32"]
+          5: ['chB_position', "int32"]
+          6: ['chB_integral', "float32"]
+
+Functions:
+  # define functions and ringbuffer assignment
+
+  - Fkt_main:
+      config_file: "config/spectrum_config.yaml"
+
+  - Fkt_1:
+       ##  for simulation with rbPut
+       file_name: "redP_mimoCoRB"
+       fkt_name: "redP_to_rb"
+       num_process: 1
+       RB_assign:
+           RB_1: "write"
+
+  - Fkt_2:
+       file_name: "modules/spectrum_filter"
+       fkt_name: "find_peaks"
+       num_process: 2
+       RB_assign:
+           RB_1: "read"
+           RB_2: "write"
+
+  - Fkt_3:
+      file_name: "modules/save_files"
+      fkt_name: "save_to_txt"
+      num_process: 1
+      RB_assign:
+           RB_2: "read"
+
+# --- the following functions are optioal 	   
+
+  - Fkt_4:
+      file_name: "modules/plot_waveform"
+      fkt_name: "plot_waveform"
+      num_process: 1
+      RB_assign:
+           RB_1: "observe"
+  - Fkt_5:
+      file_name: "modules/plot_histograms"
+      fkt_name: "plot_histograms"
+      num_process: 1
+      RB_assign:
+           RB_2: "read"  # pulse parameters
-- 
GitLab