From 70b1eb46e421739244db294218f6e02e9589fa55 Mon Sep 17 00:00:00 2001
From: Guenter Quast <guenter.quast@kit.edu>
Date: Sun, 7 Jul 2024 18:09:01 +0200
Subject: [PATCH] removed mimoCoRB interface from this release - now in repo
 redpitaya-daq

---
 README.md                   |   35 -
 config/spectrum_config.yaml |   90 ---
 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  |  107 ---
 read_npy.py                 |   23 -
 redP_mimoCoRB.py            |   99 ---
 redPdaq.py                  | 1332 -----------------------------------
 11 files changed, 1920 deletions(-)
 delete mode 100644 config/spectrum_config.yaml
 delete mode 100644 modules/filters.py
 delete mode 100644 modules/plot_histograms.py
 delete mode 100644 modules/plot_waveform.py
 delete mode 100644 modules/redPitaya_source.py
 delete mode 100644 modules/save_files.py
 delete mode 100644 modules/spectrum_filter.py
 delete mode 100755 read_npy.py
 delete mode 100755 redP_mimoCoRB.py
 delete mode 100755 redPdaq.py

diff --git a/README.md b/README.md
index 89a646d..8cf26a3 100644
--- a/README.md
+++ b/README.md
@@ -36,16 +36,9 @@ courses at the Faculty of Physics at Karlsruhe Institute of Technology.
 
   - *README.md*         this documentation 
   - *mchpa.py*          client program 
-  - *redPdaq.py*        client for data-acquisition using the mcpha server
   - *examples/*         recorded spectra
   - *examples/peakFitter.py* code to find and fit peaks in spectum data
-  - *read_npy.py*       a simple example to read waveforms saved in *.npy* format
-  - *redP_mimocorb.py*  runs *redPdaq* as a client of the buffer manager *mimoCoRB*
-  - *setup.yaml*        coniguration script defining the *mimoCoRB* application
-  - *modules/* and *config/* contain code and configuration files for the *redP_mimoCoRB* application
   - *mcpha.ui*          qt5 graphical user interface for *mcpha* application 
-  - *rpControl.ui*      qt5 tab for *redPdaq* application   
-  - *mcpha_daq.ui*      qt5 tab for oscilloscope with daq mode
   - *mcpha_gen.ui*      qt5 tab for generator 
   - *mcpha_osc.ui*      qt5 tab for oscilloscope
   - *mcpha_hst.ui*      qt5 tab for histogram display 
@@ -121,31 +114,6 @@ Note that spectra and waveforms are plotted with a very large number of channels
 the resolution of a computer display. It is therefore possible to use the looking-glass button
 of the *matplotlib*window to mark regions to zoom in for a detailed inspection of the data. 
 
-## Oscilloscope and data recorder
-
-The script *redPdaq.py* relies on the same server and FPGA image as the pulse-height analyzer,
-providing the same functionality as *mcpha.py*. In addition, however, there is a button
-"*Start DAQ*" in the oscilloscope tab to run the oscilloscope in data acquisition mode,
-i.e. continuously. A subset of the data is shown in the oscilloscope display, together with
-information on the trigger rate and the transferred data volume. A configurable user-defined
-function may also be called to analyse and store the recorded waveforms. 
-It is possible to transfer data over a one-Gbit network from the RedPitaya with a rate of 50 MB/s
-or about 500 waveforms/s.
-
-An examples of call-back functions callable from within redPdaq is provided with the package
-
-  - redP_consumer()            
-      calculates and displays statistics on trigger rate and data volume
-
-
-*redP_mimocorb.py* is a script containing code to be started from the command line and
-a function defined in the script, *redP_to_rb*, is called  as sub-process within the
-*mimiCoRB* buffer manager frame-work for more advanced data analysis tasks requiring
-multiple processes running in parallel. A *mimoCoRB* setup-file is also provided and can
-be started by running typing `redP_mimoCoRB.py setup.yaml` on the command line. Modules
-and configuration files for a pulse-height analysis of recorded signals are contained
-as exampless in the sub-directories *modules/* and *config/*, respectively.
-
 
 ## Installation
 
@@ -206,9 +174,6 @@ Then, on the client side:
     the *oscilloscpe* tab;
   - now click the tab *spectrum histogram 1*; adjust the amplitude threshold and time
     of exposure, then click the *Start* button and watch the spectrum building up;
-  - if running `redPdaq.py`, threre is a buttton "StartDQQ"; click it to cintinuously transfer
-    waveform data to the client computer. If a filename was specified, data is recorded to disk
-    in *.npy* format; note that any active spectrum rab is put in paused mode if DAQ is active.
   - when finished, use the *Save* button to save the spectrum to a file with a 
     meaningful name.
 
diff --git a/config/spectrum_config.yaml b/config/spectrum_config.yaml
deleted file mode 100644
index 2a6cfc1..0000000
--- a/config/spectrum_config.yaml
+++ /dev/null
@@ -1,90 +0,0 @@
-# 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 1000
-  analogue_offset: &analogue_offset 0
-  # special settings for RedPitaya
-  decimation_index: &decimation 4 # decimation 0:1, i:2^(i+1) # data decimation factor, 0 is 8 ns/sample
-  sample_time_ns: &sample_time_ns 256 #  for decimation 4
-  invert_channel1: &invert1 0 
-  invert_channel2: &invert2 0
-
-  trigger_level: &trigger_level 50
-  trigger_channel: &trigger_channel '1'
-  trigger_direction: &trigger_direction "rising" # or "falling"
-  pre_trigger_samples: &pre_trigger_samples 103 # 5%
-
-# dict for spectrum_filter, function find_peaks
-find_peaks:
-  # signal_characteristics: 256ns/sample, duration 50µs ( ~200 samples)
-  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: 200  # has to be positive and higher than avg. noise peaks to not cause havoc!
-  trigger_channel: *trigger_channel
-  peak_minimal_distance: 400  # minimal distance between two peaks in number of samples
-  peak_minimal_width: 100  # in number of samples
-  trigger_channel: *trigger_channel
-  trigger_position_tolerance: 20  # in number of samples
-
-# dict for RedPitaya redPoscdaq
-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" # or "auto"
-  # special settings for RedPitaya
-  decimation_index: *decimation
-  invert_channel1: *invert1
-  invert_channel2: *invert2
-  
-# 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
-    ch1_height:   [50., 3000.,   250, 20., "ph 1A", 0]
-    ch2_height:   [50., 3000.,   250, 20., "ph 1B", 0]
diff --git a/modules/filters.py b/modules/filters.py
deleted file mode 100644
index 7158cc6..0000000
--- a/modules/filters.py
+++ /dev/null
@@ -1,94 +0,0 @@
-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
deleted file mode 100644
index fce2408..0000000
--- a/modules/plot_histograms.py
+++ /dev/null
@@ -1,27 +0,0 @@
-"""
-**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
deleted file mode 100644
index 66d0c12..0000000
--- a/modules/plot_waveform.py
+++ /dev/null
@@ -1,33 +0,0 @@
-"""
-**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
deleted file mode 100644
index adabc04..0000000
--- a/modules/redPitaya_source.py
+++ /dev/null
@@ -1,54 +0,0 @@
-"""
-**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
deleted file mode 100644
index 0007447..0000000
--- a/modules/save_files.py
+++ /dev/null
@@ -1,26 +0,0 @@
-"""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
deleted file mode 100644
index aaa9962..0000000
--- a/modules/spectrum_filter.py
+++ /dev/null
@@ -1,107 +0,0 @@
-"""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']:
-    if trigger_channel not in ['1','2']:
-         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/read_npy.py b/read_npy.py
deleted file mode 100755
index 74fdc45..0000000
--- a/read_npy.py
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/usr/bin/env python3
-"""read file from redPoscdaq (in npy format) and display data
-"""
-
-from npy_append_array import NpyAppendArray
-import numpy as np
-import sys
-import matplotlib.pyplot as plt
-
-data = np.load(sys.argv[1], mmap_mode='r')
-print("data read sucessfully, shape = ", data.shape)
-
-n_samples = len(data[0,0])
-xplt = 0.5 + np.linspace(0, n_samples, num=n_samples, endpoint=True)
-fig = plt.figure("Oscillogram", figsize=(8,6))
-
-for d in data:
-    plt.plot(xplt, d[0], '-')
-    plt.plot(xplt, d[1], '-')
-    plt.xlabel("time bin")
-    plt.ylabel("Voltage")    
-    plt.show()
-                  
diff --git a/redP_mimoCoRB.py b/redP_mimoCoRB.py
deleted file mode 100755
index c165d73..0000000
--- a/redP_mimoCoRB.py
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/env python3
-"""
-**redP_mimoCoRB**: use mimoCoRB with the RedPitaya and redPoscdaq.py 
-
-Input data is provided as numpy-arry of shape (number_of_channels, number_of_samples)
-via callback of the __call__() function in class redP_mimoCoRB.
-
-This script depends on redPdaq.py and is started as a sup-process within the mimoCoRB
-framework. The detailed set-up of ring buffers and the associated funtions is specified
-in a configuration file, *setup.yaml*. The process suite is started by running this 
-script from the command line, possibly specifying the name of the configutation file
-as an argument.  
-
-As a demonstration, a configuration *setup.yaml* is contained in this package to 
-import waveforms from the RedPitaya, display a sub-set of the waveforms and perform
-a pulse-height analysis with updating results shown as histograms. 
-To run this example, connect the out1 of the RedPitaya to one or both of the inputs, 
-type "./redP_mimoCoRB.py* to run the example and use the graphical interface to connect 
-the RedPitaya to the network, start the pulse generator, and finally press the *StartDAQ" 
-button in the  oscilloscope tag to start data transfer to the *mimiCoRB* input buffer. 
-
-Stop data taking with the button "End run" in the *mimoCoRB* conotrol window to 
-cleanly shut down all processes. 
-
-Note that this script depends on additional code for the *mimoCoRB* peak finder
-in the sub-directory *modules/* and a corresponding configuration file in 
-subdirectory *config/*.
-"""
-
-import time
-import sys
-import redPdaq as rp
-from mimocorb.buffer_control import rbPut
-
-class redP_mimocorb():            
-    """ Interface for redPoscdaq.py to the daq rinbuffer mimoCoRB 
-    """    
-    def __init__(self, source_list=None, sink_list=None, observe_list=None, config_dict=None,
-                 **rb_info):
-        # initialize mimoCoRB interface 
-        self.rb_exporter = rbPut(config_dict=config_dict, sink_list=sink_list, **rb_info)
-        self.number_of_channels = len(self.rb_exporter.sink.dtype)
-        self.events_required = 1000 if "eventcount" not in config_dict else config_dict["eventcount"]
-
-        self.event_count = 0
-        self.active=True
-    def __call__(self, data):
-        """function called by redPoscdaq  
-        """
-        if (self.events_required == 0 or self.event_count < self.events_required) and self.active:
-             # deliver pulse data and no metadata
-             active = self.rb_exporter(data, None) # send data
-             self.event_count += 1
-        else:
-             active = self.rb_exporter(None, None) # send None when done
-             print("redPoscdaq exiting") 
-             sys.exit()
-           
-def redP_to_rb(source_list=None, sink_list=None, observe_list=None, config_dict=None, **rb_info):
-    """Main function, 
-      executed as a multiprocessing Process, to pass data from the RedPitaya to a mimoCoRB buffer
- 
-      :param config_dict: configuration dictionary
-        - events_required: number of events to be taken
-        - number_of_samples, sample_time_ns, pretrigger_samples and analogue_offset
-        - decimation index, invert flags, trigger mode and trigger direction for RedPitaya
-    """
-
-    # initialize mimocorb class
-    rb_source= redP_mimocorb(config_dict=config_dict, sink_list=sink_list, **rb_info)
-    #print("data source initialized")
-
-    # start oscilloscope in callback mode
-    #print("starting osci")
-    rp.run_rpControl(callback=rb_source, conf_dict=config_dict)
-
-if __name__ == "__main__":  # -------------------------------------- 
-#run mimoCoRB data acquisition suite
-  # the code below is idenical to the mimoCoRB script run_daq.py
-    import argparse
-    import sys, time
-    from mimocorb.buffer_control import run_mimoDAQ
-
-    # define command line arguments ...
-    parser = argparse.ArgumentParser(description=__doc__)
-    parser.add_argument('filename', nargs='?', default = "setup.yaml",
-                    help = "configuration file")
-    parser.add_argument('-v','--verbose', type=int, default=2,
-                    help="verbosity level (2)")
-    parser.add_argument('-d','--debug', action='store_true',
-                    help="switch on debug mode (False)")
-    # ... and parse command line input
-    args = parser.parse_args()
-
-    print("\n*==* script " + sys.argv[0] + " running \n")
-    daq = run_mimoDAQ(args.filename, verbose=args.verbose, debug=args.debug)
-    daq.setup()
-    daq.run()
-    print("\n*==* script " + sys.argv[0] + " finished " + time.asctime() + "\n")
diff --git a/redPdaq.py b/redPdaq.py
deleted file mode 100755
index 06a20dc..0000000
--- a/redPdaq.py
+++ /dev/null
@@ -1,1332 +0,0 @@
-#!/usr/bin/env python3
-"""redPoscdaq: fast data acquistion using the oscilloscope client of the MCPHA 
-    application running on a RedPitaya FPGA board
-
-    Contains a button to run the oscilloscope in daq mode, i.e. it is restarted 
-    continously. If defined, data is exported via calling a callback function. 
-    Optionally, triggered waveforms can be stored a numpy binary file 
-    (.npy format).
-
-    Code derived from mcpha.py by Pavel Demin  
-
-    This code is compatible with release 20240204 of the alpine linux image
-    https://github.com/pavel-demin/red-pitaya-notes/releases/tag/20240204
-"""
-
-script_name = 'redPdaq.py'
-
-# Communication with server process is achieved via command codes:
-#   command(code, number, data)  #    number typically is channel number
-#
-# code values
-# code  2:  reset/initialze oscilloscope
-# code  4:  set decimation factor
-# code 11:  read status
-# code 13:  set trigger source chan 1/2
-# code 14:  set trigger slope rise/fall
-# code 15:  set trigger mode norm/autoUi_HstDisplay, QWidget = loadUiType("mcpha_hst.ui")
-# code 16:  set trigger level in ADC counts
-# code 17:  set number of pre-trigger samples
-# code 18:  set number of total samples 
-# code 19:  start oscilloscope
-# code 20:  read oscilloscope data (two channels at once) 
-# code 21:  set pulse fall-time for generator in µs
-# code 22:  set pulse rise-time in ns 
-# code 25:  set pulse rate in kHz
-# code 26:  fixed frequency or poisson
-# code 27_  reset generator
-# code 28:  set bin for pulse distribution, as a histogram with 4096 channels, 0-500 mV
-# code 29:  start generator 
-# code 30:  stop generator 
-
-import argparse
-import os
-import sys
-import time
-import struct
-
-from functools import partial
-import numpy as np
-import matplotlib
-from matplotlib.figure import Figure
-from multiprocessing import Event
-
-# !!! for conditional import from npy_append_array !!!
-def import_npy_append_array():
-    global NpyAppendArray
-    from npy_append_array import NpyAppendArray
-
-from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
-from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
-
-if "PyQt5" in sys.modules:
-    from PyQt5.uic import loadUiType
-    from PyQt5.QtCore import Qt, QTimer, QEventLoop, QRegExp
-    from PyQt5.QtGui import QPalette, QColor, QRegExpValidator
-    from PyQt5.QtWidgets import QApplication, QMainWindow, QDialog, QFileDialog
-    from PyQt5.QtWidgets import QWidget, QLabel, QCheckBox, QComboBox
-    from PyQt5.QtNetwork import QAbstractSocket, QTcpSocket
-else:
-    from PySide2.QtUiTools import loadUiType
-    from PySide2.QtCore import Qt, QTimer, QEventLoop, QRegExp
-    from PySide2.QtGui import QPalette, QColor, QRegExpValidator
-    from PySide2.QtWidgets import QApplication, QMainWindow, QDialog, QFileDialog
-    from PySide2.QtWidgets import QWidget, QLabel, QCheckBox, QComboBox
-    from PySide2.QtNetwork import QAbstractSocket, QTcpSocket
-
-# define global graphics style
-import matplotlib.pyplot as plt 
-pref_style = "default"
-_style = pref_style if pref_style in plt.style.available else "default"
-plt.style.use(_style)
-    
-Ui_MCPHA, QMainWindow = loadUiType("rpControl.ui")
-Ui_LogDisplay, QWidget = loadUiType("mcpha_log.ui")
-Ui_HstDisplay, QWidget = loadUiType("mcpha_hst.ui")
-Ui_OscDisplay, QWidget = loadUiType("mcpha_daq.ui")
-Ui_GenDisplay, QWidget = loadUiType("mcpha_gen.ui")
-
-if sys.platform != "win32":
-    path = "."
-else:
-    path = os.path.expanduser("~")
-
-
-class rpControl(QMainWindow, Ui_MCPHA):
-    """ control Pavel Demin's MCHPA application on RedPitaya
-
-        Modes of operation:
-           - in oscilloscope mode to just display data
-
-           - in daq mode, i.e. read data at maximum rate, 
-               optionally recording data to disk or calling an external function
-    """
-
-    rates = {0:1, 1: 4, 2: 8, 3: 16, 4: 32, 5: 64, 6: 128, 7: 256}
-    def __init__(self, callback=None, conf_dict=None):
-        """
-           Args: 
-
-             callback: function receiving recorded waveforms
-             conf_dict: a configuration dictionary
-        """
-        super(rpControl, self).__init__()
-
-        plt.style.use("default") # set graphics style
-
-        self.callback_function = callback
-        # initialize parameters
-        self.confd = {} if conf_dict is None else conf_dict
-        self.parse_confd()
-        # get configuration from command line
-        if os.path.split(sys.argv[0])[1] == script_name:
-             self.interactive = True
-             self.parse_args()
-        else:
-             self.interactive = False            
-        self.callback = True if callback is not None else False
-         
-        # set physical units (for axis labels)
-        self.get_physical_units()
-        self.setupUi(self)
-        # initialize variables
-        self.idle = True
-        self.hst_waiting = [False for i in range(2)]
-        self.osc_ready = False
-        self.osc_waiting = False
-        self.hst_reset = 0 
-        self.state = 0
-        self.status = np.zeros(9, np.uint32)
-        self.timers = self.status[:4].view(np.uint64)
-        # create tabs
-        self.log = LogDisplay()
-        if self.interactive:
-            self.hst1 = HstDisplay(self, self.log, 0)
-            self.hst2 = HstDisplay(self, self.log, 1)
-        else:
-            self.hst1 = None
-            self.hst2 = None
-        self.osc_daq = OscDAQ(self, self.log)        
-        self.gen = GenDisplay(self, self.log)
-        self.tabindex_log = self.tabWidget.addTab(self.log, "Messages")
-        self.tabWidget.addTab(self.hst1, "Spectrum Channel 1")
-        self.tabWidget.addTab(self.hst2, "Spectrum Channel 2")
-        self.tabindex_osc = self.tabWidget.addTab(self.osc_daq, "Oscilloscope")
-        self.tabindex_gen = self.tabWidget.addTab(self.gen, "Pulse generator")
-        # configure controls
-        self.connectButton.clicked.connect(self.start)
-        self.neg1Check.toggled.connect(partial(self.set_negator, 0))
-        self.neg2Check.toggled.connect(partial(self.set_negator, 1))
-        self.rateValue.addItems(map(str, rpControl.rates.values()))
-        self.rateValue.setEditable(True)
-        self.rateValue.lineEdit().setReadOnly(True)
-        self.rateValue.lineEdit().setAlignment(Qt.AlignRight)
-        for i in range(self.rateValue.count()):
-            self.rateValue.setItemData(i, Qt.AlignRight, Qt.TextAlignmentRole)
-        self.rateValue.setCurrentIndex(1)
-        self.rateValue.currentIndexChanged.connect(self.set_rate)
-        rx = QRegExp(r"^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|rp-[0-9A-Fa-f]{6}\.local$")
-        self.addrValue.setValidator(QRegExpValidator(rx, self.addrValue))
-        # create TCP socket
-        self.socket = QTcpSocket(self)
-        self.socket.connected.connect(self.connected)
-        self.socket.error.connect(self.display_error)
-        # create event loop
-        self.loop = QEventLoop()
-        self.socket.readyRead.connect(self.loop.quit)
-        self.socket.error.connect(self.loop.quit)
-        # create timers 
-        self.startTimer = QTimer(self)  # timer for network connectin
-        self.startTimer.timeout.connect(self.start_timeout)
-        self.readTimer = QTimer(self)   # for readout 
-        # self.readTimer.timeout.connect(self.update_oscDisplay)
-        self.readTimer.timeout.connect(self.read_timeout)
-
-        # transfer command-line arguments to gui
-        self.osc_daq.levelValue.setValue(self.trigger_level)
-        self.osc_daq.autoButton.setChecked(self.trigger_mode)
-        self.osc_daq.ch2Button.setChecked(self.trigger_source)
-        self.osc_daq.fallingButton.setChecked(self.trigger_slope)
-        self.rateValue.setCurrentIndex(self.decimation_index)
-        self.neg1Check.setChecked(self.invert1)
-        self.neg2Check.setChecked(self.invert2)
-        
-        # automatically connect
-        if self.ip_address is not None:
-            self.addrValue.setText(self.ip_address)
-            self.start()
-        else:    
-            self.addrValue.setStyleSheet("color: darkorange")
-
-    def parse_confd(self):
-        # all relevant parameters are here
-        self.daq_mode = False # True
-        self.ip_address ='192.168.1.100' if "ip_address" not in self.confd \
-            else self.confd["ip_address"]
-        self.sample_size = 2048 if "number_of_samples" not in self.confd \
-            else self.confd["number_of_samples"]
-        self.pretrigger_fraction = 0.05 if "pre_trigger_samples" not in self.confd \
-            else self.confd["pre_trigger_samples"]/self.sample_size
-        self.trigger_mode = 0 if "trigger_mode" not in self.confd else \
-            0 if (self.confd["trigger_mode"] == "norm" or self.confd["trigger_mode"] == 0) else 1
-        self.trigger_source = 0  if "trigger_channel" not in self.confd \
-            else int(self.confd["trigger_channel"])-1
-        self.trigger_level = 500 if "trigger_level" not in self.confd else self.confd["trigger_level"]
-        self.trigger_slope = 0 if "trigger_direction" not in self.confd else \
-            0 if (self.confd["trigger_direction"]=="rising" or self.confd["trigger_direction"]=="0") else 1
-        self.decimation_index = 1 if "decimation_index" not in self.confd else \
-            self.confd["decimation_index"]
-        self.invert1 = 0 if "invert_channel1" not in self.confd else self.confd["invert_channel1"]
-        self.invert2 = 0 if "invert_channel2" not in self.confd else self.confd["invert_channel2"]
-        self.readInterval = 1000 # used to control update of oscilloscope display
-        # other parameters
-        self.filename = ''  # file name for data export, '' means disable
-
-    def parse_args(self):
-        parser = argparse.ArgumentParser(description=__doc__)
-        parser.add_argument('-c', '--connect_ip', type=str, default=self.ip_address,
-            help='connect IP address of RedPitaya')
-        # for oscilloscope display
-        parser.add_argument('-i', '--interval', type=int, default=self.readInterval,
-            help='interval for readout (in ms)')
-        # trigger mode
-        parser.add_argument('-t', '--trigger_level', type=int, default=self.trigger_level,
-            help='trigger level in ADC counts')
-        parser.add_argument('--trigger_slope', type=int, choices={0, 1},
-                            default=self.trigger_slope, help='trigger slope')
-        parser.add_argument('--trigger_source', type=int, choices={1, 2},
-                            default=self.trigger_source+1, help='trigger channel')
-        parser.add_argument('--trigger_mode', type=int, choices={0 , 1},
-                            default=self.trigger_mode, help='trigger mode')
-        parser.add_argument('-s', '--sample_size', type=int, default=self.sample_size,
-            help='size of waveform sample')
-        parser.add_argument('-p', '--pretrigger_fraction', type=float,
-                            default=self.pretrigger_fraction, help='pretrigger fraction')
-        parser.add_argument('-f', '--file', type=str, default='',
-                            help='file name')        
-        args = parser.parse_args()
-        # all relevant parameters are here
-        self.ip_address = args.connect_ip
-        self.sample_size = args.sample_size
-        self.pretrigger_fraction = args.pretrigger_fraction
-        self.readInterval = args.interval # used to control oscilloscope display
-        # 
-        self.trigger_level = args.trigger_level
-        self.trigger_source = args.trigger_source - 1
-        self.trigger_mode = args.trigger_mode
-        self.trigger_slope = args.trigger_slope
-        # other parameters
-        self.filename = args.file
-        if self.filename:
-            # data recording with npy_append_array()
-            import_npy_append_array()
-        
-    def get_physical_units(self):
-        """get physical units corresponding to ADC units and channel numbers
-        """
-        # Voltages: 4096 channels/Volt
-        self.adc_unit = 1000/4096
-        # time per sample at sampling rate of 125 MHz / decimation_factor
-        self.time_bin = 0.008
-    # !gq end
-        
-    def start(self):
-        self.socket.connectToHost(self.addrValue.text(), 1001) # connect to port 1001 (mcpha_server on RP) 
-        self.startTimer.start(1000) # shorter time-out
-        self.connectButton.setText("Disconnect")
-        self.connectButton.clicked.disconnect()
-        self.connectButton.clicked.connect(self.stop)
-
-    def stop(self):
-        self.osc_daq.stop()
-        self.gen.stop()
-        self.readTimer.stop()
-        self.startTimer.stop()
-        self.loop.quit()
-        self.socket.abort()
-        self.connectButton.setText("Connect")
-        self.connectButton.clicked.disconnect()
-        self.connectButton.clicked.connect(self.start)
-        self.addrValue.setStyleSheet("color: red")
-        self.log.print("IO stopped")
-        self.idle = True
-
-    def closeEvent(self, event):
-        self.stop()
-
-    def start_timeout(self):
-        self.log.print("error: connection timeout")
-        self.stop()
-
-    def display_error(self):
-        self.log.print("error: %s" % self.socket.errorString())
-        self.stop()
-
-    def connected(self):
-        # coming here when connection is established
-        self.startTimer.stop()
-        self.log.print("IO started")
-        self.addrValue.setStyleSheet("color: green")
-        self.tabWidget.setCurrentIndex(self.tabindex_osc)
-        # initialize variables for readout
-        self.idle = False
-        self.osc_waiting = False
-        self.daq_waiting = False
-        self.osc_reset = False
-        self.osc_start = False
-        self.state = 0
-        self.hst_reset = 0
-        self.hst_waiting = [False for i in range(2)]
-        self.set_rate(self.rateValue.currentIndex())
-        self.set_negator(0, self.neg1Check.isChecked())
-        self.set_negator(1, self.neg2Check.isChecked())
-        #
-        # finally, start readout timer calling update_oscData() read_timeout()
-        self.readTimer.start(self.readInterval)
-
-       #### start oscilloscope in DAQ mode
-#!        if self.interactive and self.daq_mode:
-        if self.daq_mode:
-            self.osc_daq() # __call__ method of osc_daq class 
-        
-    def command(self, code, number, value):
-        self.socket.write(struct.pack("<Q", code << 56 | number << 52 | (int(value) & 0xFFFFFFFFFFFFF)))
-
-    def read_data(self, data):
-        view = data.view(np.uint8)
-        size = view.size
-        while self.socket.state() == QAbstractSocket.ConnectedState and self.socket.bytesAvailable() < size:
-            self.loop.exec_()
-        if self.socket.bytesAvailable() < size:
-            return False
-        else:
-            view[:] = np.frombuffer(self.socket.read(size), np.uint8)
-            return True
-
-    def read_timeout(self):
-        """data transfer from RP,  triggered by QTimer readTimer
-        """
-        # send reset commands for histograms
-        if self.hst_reset & 1:
-            self.command(0, 0, 0)  # hst 1
-        if self.hst_reset & 2:
-            self.command(0, 1, 0)  # hst 2
-        if self.hst_reset & 4:
-            self.command(1, 0, 0)  # reset timer 1
-        if self.hst_reset & 8:
-            self.command(1, 1, 0)  # reset timer 2
-        self.hst_reset = 0
-        if self.osc_reset:
-            self.reset_osc()
-        if self.osc_start:
-            self.start_osc()
-        # read histogram and oscilloscope data and update displays if not in daq mode
-        if not self.daq_waiting: 
-            self.command(11, 0, 0)  # read status
-            if not self.read_data(self.status):
-                return
-            # spectrum ch1
-            if self.hst_waiting[0]:
-                self.command(12, 0, 0)  # code 12: read histogram
-                if self.read_data(self.hst1.buffer):
-                    self.hst1.update(self.timers[0], False)
-                else:
-                    return
-            # spectrum ch2
-            if self.hst_waiting[1]: 
-                self.command(12, 1, 0)
-                if self.read_data(self.hst2.buffer):
-                    self.hst2.update(self.timers[1], False)
-                else:
-                    return
-            # oscilloscope
-            if self.osc_waiting and not self.status[8] & 1:
-                self.command(20, 0, 0)  # read oscilloscope data 
-                if self.read_data(self.osc_daq.buffer):
-                    self.osc_daq.update_osci_display()
-                    self.mark_reset_osc()
-                    self.mark_start_osc()
-                else:
-                    self.log.print("failed to read oscilloscope data")
-                    return 1
-                
-
-    def run_oscDaq(self):
-        """continuous fast data transfer from RedPitaya via mcpha oscilloscope
-        """
-        # depends on
-        #   self.start_daq()
-        #   osc.process_data()
-        
-        # presently, not compatible with historgram mode, so pause
-        if self.hst_waiting[0]:
-            self.hst1.pause()
-        if self.hst_waiting[1]:
-            self.hst2.pause()
-        # initialize trigger mode etc. 
-        self.osc_daq.start_daq()
-        #
-        self.reset_osc()
-        self.start_osc()
-        while self.daq_waiting:
-            # check status
-            self.read_status()
-            if not self.read_data(self.status):
-                self.log.print("failed to read status")
-                return
-            if not self.status[8] & 1:
-                self.command(20, 0, 0)  # read oscilloscope data 
-                if self.read_data(self.osc_daq.buffer):
-                    self.timestamp = time.time()    
-                    self.reset_osc()
-                    self.osc_daq.process_data()
-                    if self.callback:
-                        self.callback_function(self.osc_daq.data)
-                    self.start_osc()
-                    self.osc_daq.deadT += time.time() - self.timestamp
-                else:
-                    self.log.print("failed to read oscilloscope data")
-                    return        
-
-    def reset_hst(self, number):
-        self.hst_reset |= 1 << number
-
-    def reset_timer(self, number):
-        self.hst_reset |= 4 << number
-
-    def set_pha_delay(self, number, value):
-        self.command(6, number, value)
-
-    def set_pha_thresholds(self, number, min, max):
-        self.command(7, number, min)
-        self.command(8, number, max)
-
-    def set_timer(self, number, value):
-        self.command(9, number, value)
-
-    def set_timer_mode(self, number, value):
-        self.command(10, number, value)
-        self.hst_waiting[number] = value
-            
-    def mark_start_osc(self):
-        self.osc_start = True
-        # self.osc_waiting = True
-        
-    def mark_reset_osc(self):
-        self.osc_reset = True
-
-    def reset_osc(self):        
-        self.command(2, 0, 0)  
-        self.osc_reset = False
-
-    def start_osc(self):        
-        self.command(19, 0, 0)    
-        self.osc_start = False
-
-    def read_status(self):
-        self.command(11, 0, 0) 
-            
-    def stop_osc(self):
-        self.reset_osc()
-        self.osc_waiting = False
-        self.daq_waiting = False
-
-    def set_rate(self, index):
-        # set RP decimation factor 
-        self.command(4, 0, rpControl.rates[index])
-
-    def set_negator(self, number, value):
-        self.command(5, number, value)
-
-    def set_trg_source(self, number):
-        self.command(13, number, 0)
-
-    def set_trg_slope(self, value):
-        self.command(14, 0, value)
-
-    def set_trg_mode(self, value):
-        self.command(15, 0, value)
-
-    def set_trg_level(self, value):
-        self.command(16, 0, value)
-
-    def set_osc_pre(self, value):
-        self.command(17, 0, value)
-
-    def set_osc_tot(self, value):
-        self.command(18, 0, value)
-
-    def set_gen_fall(self, value):
-        self.command(21, 0, value)
-
-    def set_gen_rise(self, value):
-        self.command(22, 0, value)
-
-    def set_gen_rate(self, value):
-        self.command(25, 0, value)
-
-    def set_gen_dist(self, value):
-        self.command(26, 0, value)
-
-    def reset_gen(self):
-        self.command(27, 0)
-
-    def set_gen_bin(self, value):
-        self.command(28, 0, value)
-
-    def start_gen(self):
-        self.command(29, 0, 1)
-
-    def stop_gen(self):
-        self.command(30, 0, 0)
-
-
-class LogDisplay(QWidget, Ui_LogDisplay):
-    def __init__(self):
-        super(LogDisplay, self).__init__()
-        self.setupUi(self)
-
-    def print(self, text):
-        self.logViewer.appendPlainText(text)
-
-class HstDisplay(QWidget, Ui_HstDisplay):
-    def __init__(self, rpControl, log, number):
-        super(HstDisplay, self).__init__()
-        self.setupUi(self)
-        # initialize variables
-        self.rpControl = rpControl
-        self.log = log
-        self.number = number
-        self.sum = 0
-        self.time = np.uint64([75e8, 0])
-        self.factor = 1
-        self.bins = 4096
-        self.min = 0
-        self.max = self.bins - 1 
-        self.buffer = np.zeros(self.bins, np.uint32)
-        if number == 0:
-            self.color = "#FFAA00"
-        else:
-            self.color = "#00CCCC"
-        # create figure
-        self.figure = Figure()
-        if sys.platform != "win32":
-            self.figure.set_facecolor("none")
-        # gq self.figure.subplots_adjust(left=0.18, bottom=0.08, right=0.98, top=0.95)
-        self.figure.subplots_adjust(left=0.10, bottom=0.08, right=0.98, top=0.92)
-        self.canvas = FigureCanvas(self.figure)
-        self.plotLayout.addWidget(self.canvas)
-        self.ax = self.figure.add_subplot(111)
-        self.ax.grid()
-        self.ax.set_ylabel("counts")
-        self.xunit = "[{:.3f} mV / channel]".format(1000/4096 * self.factor)
-        self.ax.set_xlabel("channel number " + self.xunit)
-#        self.ax.set_xlabel("channel number")
-        # !gq
-        self.ax_x2=self.ax.secondary_xaxis('top',
-                functions=(self.adc2mV, self.mV2adc))
-        self.ax_x2.set_xlabel("Voltage [mV]", color='grey')
-        # !gq end
-        x = np.arange(self.bins)
-        (self.curve,) = self.ax.plot(x, self.buffer, drawstyle="steps-mid", color=self.color)
-        self.roi = [0, self.max]
-        self.line = [None, None]
-        self.active = [False, False]
-        self.releaser = [None, None]
-        # marker lines for roi
-        self.line[0] = self.ax.axvline(self.min, picker=True, pickradius=5)
-        self.line[1] = self.ax.axvline(self.max, picker=True, pickradius=5)
-        # create navigation toolbar
-        self.toolbar = NavigationToolbar(self.canvas, None, False)
-        self.toolbar.layout().setSpacing(6)
-        # remove subplots action
-        actions = self.toolbar.actions()
-        self.toolbar.removeAction(actions[6])
-        self.toolbar.removeAction(actions[7])
-        self.logCheck = QCheckBox("log scale")
-        self.logCheck.setChecked(False)
-        self.binsLabel = QLabel("rebin factor")
-        self.binsValue = QComboBox()
-        self.binsValue.addItems(["1", "2", "4", "8"])
-        self.binsValue.setEditable(True)
-        self.binsValue.lineEdit().setReadOnly(True)
-        self.binsValue.lineEdit().setAlignment(Qt.AlignRight)
-        for i in range(self.binsValue.count()):
-            self.binsValue.setItemData(i, Qt.AlignRight, Qt.TextAlignmentRole)
-        self.toolbar.addSeparator()
-        self.toolbar.addWidget(self.logCheck)
-        self.toolbar.addSeparator()
-        self.toolbar.addWidget(self.binsLabel)
-        self.toolbar.addWidget(self.binsValue)
-        self.plotLayout.addWidget(self.toolbar)
-        # configure controls
-        actions[0].triggered.disconnect()
-        actions[0].triggered.connect(self.home)
-        self.logCheck.toggled.connect(self.set_scale)
-        self.binsValue.currentIndexChanged.connect(self.set_bins)
-        self.thrsCheck.toggled.connect(self.set_thresholds)
-        self.startButton.clicked.connect(self.start)
-        self.resetButton.clicked.connect(self.reset)
-        self.saveButton.clicked.connect(self.save)
-        self.loadButton.clicked.connect(self.load)
-        self.canvas.mpl_connect("motion_notify_event", self.on_motion)
-        self.canvas.mpl_connect("pick_event", self.on_pick)
-        # update controls
-        self.set_thresholds(self.thrsCheck.isChecked())
-        self.set_time(self.time[0])
-        self.set_scale(self.logCheck.isChecked())
-
-    # !gq helper functions to convert adc counts to physical units    
-    def adc2mV(self, c):
-        """convert adc-count to Voltage in mV
-        """
-        return c * self.rpControl.adc_unit * self.factor
-
-    def mV2adc(self, v):
-        """convert voltage in mV to adc-count 
-        """
-        return v / (self.rpControl.adc_unit * self.factor)
-    # !gq end helper
-        
-    def start(self):
-        if self.rpControl.idle:
-            return
-        self.set_thresholds(self.thrsCheck.isChecked())
-        self.set_enabled(False)
-        h = self.hoursValue.value()
-        m = self.minutesValue.value()
-        s = self.secondsValue.value()
-        value = (h * 3600000 + m * 60000 + s * 1000) * 125000
-        self.sum = 0
-        self.time[:] = [value, 0]
-        self.rpControl.reset_timer(self.number)
-        self.rpControl.set_pha_delay(self.number, 100)
-        self.rpControl.set_pha_thresholds(self.number, self.min, self.max)
-        self.rpControl.set_timer(self.number, value)
-#
-        self.resume()
-
-    def pause(self):
-        self.rpControl.set_timer_mode(self.number, 0)
-        self.startButton.setText("Resume")
-        self.startButton.clicked.disconnect()
-        self.startButton.clicked.connect(self.resume)
-        self.log.print("timer %d stopped" % (self.number + 1))
-
-    def resume(self):
-        self.rpControl.set_timer_mode(self.number, 1)
-        self.startButton.setText("Pause")
-        self.startButton.clicked.disconnect()
-        self.startButton.clicked.connect(self.pause)
-        self.log.print("timer %d started" % (self.number + 1))
-
-    def stop(self):
-        self.rpControl.set_timer_mode(self.number, 0)
-        self.set_enabled(True)
-        self.set_time(self.time[0])
-        self.startButton.setText("Start")
-        self.startButton.clicked.disconnect()
-        self.startButton.clicked.connect(self.start)
-        self.log.print("timer %d stopped" % (self.number + 1))
-
-    def reset(self):
-        if self.rpControl.idle:
-            return
-        self.stop()
-        self.rpControl.reset_hst(self.number)
-        self.rpControl.reset_timer(self.number)
-        self.totalValue.setText("%.2e" % 0)
-        self.instValue.setText("%.2e" % 0)
-        self.avgValue.setText("%.2e" % 0)
-        self.buffer[:] = np.zeros(self.bins, np.uint32)
-        self.update_plot()
-        self.update_roi()
-
-    def set_enabled(self, value):
-        if value:
-            self.set_thresholds(self.thrsCheck.isChecked())
-        else:
-            self.minValue.setEnabled(False)
-            self.maxValue.setEnabled(False)
-        self.thrsCheck.setEnabled(value)
-        self.hoursValue.setEnabled(value)
-        self.minutesValue.setEnabled(value)
-        self.secondsValue.setEnabled(value)
-
-    def home(self):
-        self.set_scale(self.logCheck.isChecked())
-
-    def set_xplot_range(self):
-        """set x-range for histogram plot
-        """
-        mn = self.min // self.factor
-        mx = self.max // self.factor
-        xsize = mx - mn
-        self.ax.set_xlim(mn - 0.02*xsize, mx*1.02)
-        self.ax.relim()
-        self.ax.autoscale_view(scalex=True, scaley=True)
-            
-    def set_scale(self, checked):
-        self.toolbar.home()
-        self.toolbar.update()
-        if checked:
-            self.ax.set_ylim(1, 1e10)
-            self.ax.set_yscale("log")
-        else:
-            self.ax.set_ylim(auto=True)
-            self.ax.set_yscale("linear")
-    # !gq size = self.bins // self.factor
-    #    self.ax.set_xlim(-0.05 * size, size * 1.05)
-        self.set_xplot_range()
-        self.canvas.draw()
-
-    def set_bins(self, value):
-        factor = 1 << value
-        self.factor = factor
-        bins = self.bins // self.factor
-        self.xunit = "[{:.3f} mV / channel]".format(1000/4096 * self.factor)
-        self.ax.set_xlabel("channel number " + self.xunit)
-        x = np.arange(bins)
-        y = self.buffer.reshape(-1, self.factor).sum(-1)
-        self.curve.set_xdata(x)
-        self.curve.set_ydata(y)
-        self.set_scale(self.logCheck.isChecked())
-    # gq update roi
-        self.roi[0] = self.min
-        self.roi[1] = self.max
-        self.update_roi()
-
-    def set_thresholds(self, checked):
-        self.minValue.setEnabled(checked)
-        self.maxValue.setEnabled(checked)
-        if checked:
-            self.min = self.minValue.value()
-            self.max = self.maxValue.value()
-        else:
-            self.min = 0
-            self.max = self.bins - 1
-
-        # !gq update xplot range and roi
-        self.set_xplot_range()
-        self.roi[0] = self.min
-        self.roi[1] = self.max
-        self.update_roi()
-
-    def set_time(self, value):
-        value = value // 125000
-        h, mod = divmod(value, 3600000)
-        m, mod = divmod(mod, 60000)
-        s = mod / 1000
-        self.hoursValue.setValue(int(h))
-        self.minutesValue.setValue(int(m))
-        self.secondsValue.setValue(s)
-
-    def update(self, value, sync):
-        self.update_rate(value)
-        if not sync:
-            self.update_time(value)
-        self.update_plot()
-        self.update_roi()
-
-    def update_rate(self, value):
-        sum = self.buffer.sum()
-        self.totalValue.setText("%.2e" % sum)
-        if value > self.time[1]:
-            rate = (sum - self.sum) / (value - self.time[1]) * 125e6
-            self.instValue.setText("%.2e" % rate)
-        if value > 0:
-            rate = sum / value * 125e6
-            self.avgValue.setText("%.2e" % rate)
-        self.sum = sum
-        self.time[1] = value
-
-    def update_time(self, value):
-        if value < self.time[0]:
-            self.set_time(self.time[0] - value)
-        else:
-            self.stop()
-
-    def update_plot(self):
-        y = self.buffer.reshape(-1, self.factor).sum(-1)
-        self.curve.set_ydata(y)
-        self.ax.relim()
-        self.ax.autoscale_view(scalex=False, scaley=True)
-        self.canvas.draw()
-
-    def update_roi(self):
-        y = self.buffer.reshape(-1, self.factor).sum(-1)
-        x0 = self.roi[0] // self.factor
-        x1 = self.roi[1] // self.factor
-        roi = y[x0 : x1 + 1]
-        y0 = roi[0]
-        y1 = roi[-1]
-        tot = roi.sum()
-        bkg = (x1 + 1 - x0) * (y0 + y1) / 2.0
-        self.roistartValue.setText("%d" % x0)
-        self.roiendValue.setText("%d" % x1)
-        self.roitotValue.setText("%.2e" % tot)
-        self.roibkgValue.setText("%.2e" % bkg)
-        self.line[0].set_xdata([x0, x0])
-        self.line[1].set_xdata([x1, x1])
-        self.canvas.draw_idle()
-
-    def on_motion(self, event):
-        if event.inaxes != self.ax:
-            return
-        x = int(event.xdata + 0.5)
-        if x < 0:
-            x = 0
-        if x >= self.bins // self.factor:
-            x = self.bins // self.factor - 1
-        y = self.curve.get_ydata(True)[x]
-        self.numberValue.setText("%d" % x)
-        self.entriesValue.setText("%d" % y)
-        delta = 40
-        if self.active[0]:
-            x0 = x * self.factor
-            if x0 > self.roi[1] - delta:
-                self.roi[0] = self.roi[1] - delta
-            else:
-                self.roi[0] = x0
-            self.update_roi()
-        if self.active[1]:
-            x1 = x * self.factor
-            if x1 < self.roi[0] + delta:
-                self.roi[1] = self.roi[0] + delta
-            else:
-                self.roi[1] = x1
-            self.update_roi()
-
-    def on_pick(self, event):
-        for i in range(2):
-            if event.artist == self.line[i]:
-                self.active[i] = True
-                self.releaser[i] = self.canvas.mpl_connect("button_release_event", partial(self.on_release, i))
-
-    def on_release(self, i, event):
-        self.active[i] = False
-        self.canvas.mpl_disconnect(self.releaser[i])
-
-    def save(self):
-        try:
-            dialog = QFileDialog(self, "Save hst file", path, "*.hst")
-            dialog.setDefaultSuffix("hst")
-            timestamp = time.strftime("%Y%m%d-%H%M%S")
-            name = "histogram-%s.hst" % timestamp
-            dialog.selectFile(name)
-            dialog.setAcceptMode(QFileDialog.AcceptSave)
-            if dialog.exec() == QDialog.Accepted:
-                name = dialog.selectedFiles()
-                np.savetxt(name[0],
-                           self.buffer,
-                           fmt="%u",
-                           header ='mcpha spectrum ' + timestamp + '\n counts per channel',
-                           newline=os.linesep)
-                self.log.print("histogram %d saved to file %s" % ((self.number + 1), name[0]))
-        except:
-            self.log.print("error: %s" % sys.exc_info()[1])
-
-    def load(self):
-        try:
-            dialog = QFileDialog(self, "Load hst file", path, "*.hst")
-            dialog.setDefaultSuffix("hst")
-            dialog.setAcceptMode(QFileDialog.AcceptOpen)
-            if dialog.exec() == QDialog.Accepted:
-                name = dialog.selectedFiles()
-                self.buffer[:] = np.loadtxt(name[0], np.uint32)
-                self.update_plot()
-        except:
-            self.log.print("error: %s" % sys.exc_info()[1])
-        
-
-class OscDAQ(QWidget, Ui_OscDisplay):
-    """Oscilloscope display with daq mode:
-       in daq_mode:
-         - data is read from the RedPitaya at maximum rate
-         - statistics is accumulated and displayed
-         - optinally, data is output to a file or sent to an external function
-    """
-    def __init__(self, rpControl, log):
-        super(OscDAQ, self).__init__()
-        self.setupUi(self)
-        # initialize variables
-        self.rpControl = rpControl
-        self.log = log
-        self.l_tot = self.rpControl.sample_size
-        self.pre = self.rpControl.pretrigger_fraction * self.l_tot
-        self.buffer = np.zeros(self.l_tot * 2, np.int16)
-        # same memory with different view of shape(2, samples_per_channel)
-        self.data = self.buffer.reshape(2, self.l_tot, order='F')
-        self.filename = rpControl.filename if self.rpControl.filename != '' else None
-        # create figure
-        self.figure = Figure()
-        if sys.platform != "win32":
-            self.figure.set_facecolor("none")
-        # gq self.figure.subplots_adjust(left=0.18, bottom=0.08, right=0.98, top=0.95)
-        self.figure.subplots_adjust(left=0.10, bottom=0.08, right=0.90, top=0.92)
-        self.canvas = FigureCanvas(self.figure)
-        self.plotLayout.addWidget(self.canvas)
-        self.ax = self.figure.add_subplot(111)
-        self.ax.grid()
-        self.ax.set_xlim(-0.02*self.l_tot, 1.02*self.l_tot)
-        self.ax.set_ylim(-4500, 4500)
-        self.xunit = "[{:d} ns / sample]".format(4*8)
-        self.ax.set_xlabel("sample number " + self.xunit)
-        self.yunit = "[{:.3f} mV]".format(1./4.096)
-        self.ax.set_ylabel("ADC units " + self.yunit)
-        # self.ax.set_xlabel("sample number")
-        # self.ax.set_ylabel("ADC units")
-        # gq
-        self.ax_x2=self.ax.secondary_xaxis('top', functions=(self.tbin2t, self.t2tbin))
-        self.ax_x2.set_xlabel("time [µs]", color='grey', size='x-large')
-        self.ax_y2=self.ax.secondary_yaxis('right',
-                    functions=(self.adc2mV, self.mV2adc))
-        self.ax_y2.set_ylabel("Voltage [mV]", color='grey', size='x-large')
-        # gq end
-        self.osctxt= self.ax.text(0.1, 0.96, ' ', transform=self.ax.transAxes,
-                                  color="darkblue", alpha=0.7)
-
-        x = np.arange(self.l_tot)
-        (self.curve2,) = self.ax.plot(x, self.buffer[1::2],
-                                      color="#00CCCC", label="chan 2")
-        (self.curve1,) = self.ax.plot(x, self.buffer[0::2],
-                                      color="#FFAA00", label="chan 1")
-        self.ax.legend(handles=[self.curve1, self.curve2])
-        self.line = [None, None]
-        self.line[0] = self.ax.axvline(self.pre, linestyle="dotted")
-        self.line[1] = self.ax.axhline(self.levelValue.value(), linestyle="dotted")
-        self.canvas.draw()
-        # create navigation toolbar
-        self.toolbar = NavigationToolbar(self.canvas, None, False)
-        self.toolbar.layout().setSpacing(6)
-        # remove subplots action
-        actions = self.toolbar.actions()
-        self.toolbar.removeAction(actions[6])
-        self.toolbar.removeAction(actions[7])
-        # configure colors
-        self.plotLayout.addWidget(self.toolbar)
-        palette = QPalette(self.ch1Label.palette())
-        palette.setColor(QPalette.Window, QColor("#FFAA00"))
-        palette.setColor(QPalette.WindowText, QColor("black"))
-        self.ch1Label.setAutoFillBackground(True)
-        self.ch1Label.setPalette(palette)
-        self.ch1Value.setAutoFillBackground(True)
-        self.ch1Value.setPalette(palette)
-        palette.setColor(QPalette.Window, QColor("#00CCCC"))
-        palette.setColor(QPalette.WindowText, QColor("black"))
-        self.ch2Label.setAutoFillBackground(True)
-        self.ch2Label.setPalette(palette)
-        self.ch2Value.setAutoFillBackground(True)
-        self.ch2Value.setPalette(palette)
-        # configure controls
-        self.autoButton.toggled.connect(self.rpControl.set_trg_mode)
-        self.ch2Button.toggled.connect(self.rpControl.set_trg_source)
-        self.fallingButton.toggled.connect(self.rpControl.set_trg_slope)
-        self.levelValue.valueChanged.connect(self.set_trg_level)
-        self.startButton.clicked.connect(self.start)
-        self.startDAQButton.clicked.connect(self.rpControl.run_oscDaq)
-        self.saveButton.clicked.connect(self.save)
-        self.loadButton.clicked.connect(self.load)
-        self.canvas.mpl_connect("motion_notify_event", self.on_motion)
-
-        self.rpControl.osc_ready = True
-        
-    def __call__(self):
-        # run in data acquisition mode
-        self.rpControl.run_oscDaq()
-             
-    # gq helper functions to convert acd channels and sampling time to physical units
-    def tbin2t(self, tbin):
-        """convert time bin to time in µs
-        """
-        dfi = self.rpControl.rateValue.currentIndex()  # current decimation factor index
-        df = 4 if dfi == -1 else rpControl.rates[dfi]  # decimation factor
-        return (tbin-self.pre)*self.rpControl.time_bin * df
- 
-    def t2tbin(self, t):
-        """convert time in µs to time bin 
-        """
-        dfi = self.rpControl.rateValue.currentIndex()  # current decimation factor index
-        df = 4 if dfi == -1 else rpControl.rates[dfi]  # decimation factor
-        return t/(self.rpControl.time_bin * df)+self.pre
-
-    def adc2mV(self, c):
-        """convert adc-count to Voltage in mV
-        """
-        return c * self.rpControl.adc_unit
-
-    def mV2adc(self, v):
-        """convert voltage in mV to adc-count 
-        """
-        return v / self.rpControl.adc_unit
-    # gq end
-    
-    def setup_trigger(self):
-        # extract trigger parameters from gui and send to server
-        self.trg_mode = int(self.autoButton.isChecked())
-        self.trg_source = int(self.ch2Button.isChecked())
-        self.trg_slope = int(self.fallingButton.isChecked())
-        self.trg_level = self.levelValue.value()
-        self.rpControl.set_trg_mode(self.trg_mode)
-        self.rpControl.set_trg_source(self.trg_source)
-        self.rpControl.set_trg_slope(self.trg_slope)
-        self.rpControl.set_trg_level(self.trg_level)
-        self.rpControl.set_osc_pre(self.pre)
-        self.rpControl.set_osc_tot(self.l_tot)
-
-    def set_gui4start(self):
-        self.startButton.setText("Start monitor")
-        self.startButton.setStyleSheet("")
-        self.startButton.clicked.disconnect()
-        self.startButton.clicked.connect(self.start)
-        self.startDAQButton.setEnabled(True)
-        if self.rpControl.hst1 is not None:
-            self.rpControl.hst1.startButton.setEnabled(True)
-        if self.rpControl.hst2 is not None:
-            self.rpControl.hst2.startButton.setEnabled(True)
-
-    def set_gui4stop(self):
-        # set start and stop buttons
-        self.startButton.setText("Stop")
-        self.startButton.setStyleSheet("color: red")
-        self.startButton.clicked.disconnect()
-        self.startButton.clicked.connect(self.stop)
-        self.startDAQButton.setEnabled(False)
-
-    def start(self):
-        """start oscilloscope display
-        """
-        if self.rpControl.idle:
-            return
-        #
-        self.setup_trigger()
-        self.set_gui4stop()
-        # 
-        self.rpControl.mark_reset_osc()
-        self.rpControl.mark_start_osc()
-        self.rpControl.osc_waiting = True
-        self.osctxt.set_text('')
-        self.log.print("oscilloscope started")
-
-    def start_daq(self):
-        """start oscilloscope in daq mode
-        """
-        if self.rpControl.idle:
-            return
-        # initialize daq statisics 
-        self.NTrig = 0
-        self.dN = 0
-        self.Nprev = self.NTrig
-        self.T0 = time.time()
-        self.deadT = 0.
-        self.Tprev = self.T0
-        self.dT = 0.
-        # set-up trigger and GUI Buttons
-        self.setup_trigger()
-        self.set_gui4stop()
-        # disable spectrum update
-        if self.rpControl.hst1 is not None:
-            self.rpControl.hst1.startButton.setEnabled(False)
-        if self.rpControl.hst2 is not None:
-            self.rpControl.hst2.startButton.setEnabled(False)
-        self.rpControl.daq_waiting = True
-        self.log.print("daq started")
-       
-    def stop(self):
-        self.rpControl.stop_osc()
-        self.set_gui4start()
-        self.log.print("oscilloscope stopped")
-
-    def process_data(self):
-        """ statistics recorded data:
-
-           - count number of Triggers and keep account of timing;
-           - graphical display of a subset of the data 
-        """
-        self.NTrig += 1
-        t = time.time()
-        dt = t - self.Tprev
-        self.Tprev = t
-        self.dT += dt
-        dead_time_fraction = self.deadT/dt
-        self.deadT = 0.
-        #
-        # do something with data (e.g. pass to sub-process via mpQueue)
-        # write to file:
-        if self.filename is not None:
-            with NpyAppendArray(self.filename) as npa:
-                npa.append( np.array([self.data]) )
-        #        
-        # output status and update scope display once per readInterval
-        if 1000*self.dT >= self.rpControl.readInterval:
-            dN = self.NTrig - self.Nprev
-            r = dN/self.dT
-            self.Nprev = self.NTrig
-            T_active = t - self.T0
-            self.dT = 0.
-            status_txt = "active: {:.1f}s  trigger rate: {:.2f} Hz,  data rate: {:.4g} MB/s".format(T_active, r, r*self.l_tot*4e-6)
-            # print(status_txt, end='\r')
-            self.osctxt.set_text(status_txt)
-            # update graph on display
-            self.curve1.set_ydata(self.data[0])
-            self.curve2.set_ydata(self.data[1])
-            self.xunit = "[{:d} ns / sample]".format(8*rpControl.rates[self.rpControl.rateValue.currentIndex()])
-            self.ax.set_xlabel("sample number " + self.xunit)
-            self.canvas.draw()
-
-    def update_osci_display(self):
-        self.curve1.set_ydata(self.buffer[0::2])
-        self.curve2.set_ydata(self.buffer[1::2])
-        self.xunit = "[{:d} ns / sample]".format(8*rpControl.rates[self.rpControl.rateValue.currentIndex()])
-        self.ax.set_xlabel("sample number " + self.xunit)
-        self.canvas.draw()
-
-    def set_trg_level(self, value):
-        self.line[1].set_ydata([value, value])
-        self.canvas.draw()
-        self.rpControl.set_trg_level(value)
-
-    def on_motion(self, event):
-        if event.inaxes != self.ax:
-            return
-        x = int(event.xdata + 0.5)
-        if x < 0:
-            x = 0
-        if x >= self.l_tot:
-            x = self.l_tot - 1
-        y1 = self.curve1.get_ydata(True)[x]
-        y2 = self.curve2.get_ydata(True)[x]
-        self.timeValue.setText("%d" % x)
-        self.ch1Value.setText("%d" % y1)
-        self.ch2Value.setText("%d" % y2)
-
-    def save(self):
-        try:
-            dialog = QFileDialog(self, "Save osc file", path, "*.osc")
-            dialog.setDefaultSuffix("osc")
-            name = "oscillogram-%s.osc" % time.strftime("%Y%m%d-%H%M%S")
-            dialog.selectFile(name)
-            dialog.setAcceptMode(QFileDialog.AcceptSave)
-            if dialog.exec() == QDialog.Accepted:
-                name = dialog.selectedFiles()
-                self.buffer.tofile(name[0])
-                self.log.print("histogram %d saved to file %s" % ((self.number + 1), name[0]))
-        except:
-            self.log.print("error: %s" % sys.exc_info()[1])
-
-    def load(self):
-        try:
-            dialog = QFileDialog(self, "Load osc file", path, "*.osc")
-            dialog.setDefaultSuffix("osc")
-            dialog.setAcceptMode(QFileDialog.AcceptOpen)
-            if dialog.exec() == QDialog.Accepted:
-                name = dialog.selectedFiles()
-                self.buffer[:] = np.fromfile(name[0], np.int16)
-                self.update()
-        except:
-            self.log.print("error: %s" % sys.exc_info()[1])
-
-                                   
-class GenDisplay(QWidget, Ui_GenDisplay):
-    def __init__(self, rpControl, log):
-        super(GenDisplay, self).__init__()
-        self.setupUi(self)
-        # initialize variables
-        self.rpControl = rpControl
-        self.log = log
-        self.bins = 4096
-        self.buffer = np.zeros(self.bins, np.uint32)
-        for i in range(16): # initialize with delta-functions
-           self.buffer[(i+1)*256-1] = 1
-        # create figure
-        self.figure = Figure()
-        if sys.platform != "win32":
-            self.figure.set_facecolor("none")
-        # !gq self.figure.subplots_adjust(left=0.18, bottom=0.08, right=0.98, top=0.95)
-        self.figure.subplots_adjust(left=0.08, bottom=0.08, right=0.98, top=0.92)
-        self.canvas = FigureCanvas(self.figure)
-        self.plotLayout.addWidget(self.canvas)
-        self.ax = self.figure.add_subplot(111)
-        self.ax.grid()
-        self.ax.set_ylabel("counts")
-        self.xunit = "[0.122 mV /channel]"
-        self.ax.set_xlabel("channel number " + self.xunit)
-        # self.ax.set_xlabel("channel number")
-        # !gq
-        self.ax_x2=self.ax.secondary_xaxis('top',
-                functions=(self.adc2mV, self.mV2adc))
-        self.ax_x2.set_xlabel("Voltage [mV]", color='grey')
-        x = np.arange(self.bins)
-        (self.curve,) = self.ax.plot(x, self.buffer, drawstyle="steps-mid", color="#FFAA00")
-        # create navigation toolbar
-        self.toolbar = NavigationToolbar(self.canvas, None, False)
-        self.toolbar.layout().setSpacing(6)
-        # remove subplots action
-        actions = self.toolbar.actions()
-        self.toolbar.removeAction(actions[6])
-        self.toolbar.removeAction(actions[7])
-        self.logCheck = QCheckBox("log scale")
-        self.logCheck.setChecked(False)
-        self.toolbar.addSeparator()
-        self.toolbar.addWidget(self.logCheck)
-        self.plotLayout.addWidget(self.toolbar)
-        # configure controls
-        actions[0].triggered.disconnect()
-        actions[0].triggered.connect(self.home)
-        self.logCheck.toggled.connect(self.set_scale)
-        self.startButton.clicked.connect(self.start)
-        self.loadButton.clicked.connect(self.load)
-
-    # gq
-    def adc2mV(self, c):
-        """convert adc-count to Voltage in mV 
-           !!! there is a factor of two betwenn channel definition here and in hstDisplay !
-        """
-        return c * self.rpControl.adc_unit/2
-
-    def mV2adc(self, v):
-        """convert voltage in mV to adc-count 
-        """
-        return v * self.rpControl.adc_unit*2
-    # gq end
-        
-    def start(self):
-        if self.rpControl.idle:
-            return
-        self.rpControl.set_gen_fall(self.fallValue.value())
-        self.rpControl.set_gen_rise(self.riseValue.value())
-        self.rpControl.set_gen_rate(self.rateValue.value() * 1000)
-        self.rpControl.set_gen_dist(self.poissonButton.isChecked())
-        for value in np.arange(self.bins, dtype=np.uint64) << 32 | self.buffer:
-            self.rpControl.set_gen_bin(value)
-        self.rpControl.start_gen()
-        self.startButton.setText("Stop")
-        self.startButton.clicked.disconnect()
-        self.startButton.clicked.connect(self.stop)
-        self.log.print("generator started")
-
-    def stop(self):
-        self.rpControl.stop_gen()
-        self.startButton.setText("Start")
-        self.startButton.clicked.disconnect()
-        self.startButton.clicked.connect(self.start)
-        self.log.print("generator stopped")
-
-    def home(self):
-        self.set_scale(self.logCheck.isChecked())
-
-    def set_scale(self, checked):
-        self.toolbar.home()
-        self.toolbar.update()
-        if checked:
-            self.ax.set_ylim(1, 1e10)
-            self.ax.set_yscale("log")
-        else:
-            self.ax.set_ylim(auto=True)
-            self.ax.set_yscale("linear")
-        self.ax.relim()
-        self.ax.autoscale_view(scalex=True, scaley=True)
-        self.canvas.draw()
-        
-    def load(self):
-        try:
-            dialog = QFileDialog(self, "Load gen file", path, "*.gen")
-            dialog.setDefaultSuffix("gen")
-            dialog.setAcceptMode(QFileDialog.AcceptOpen)
-            if dialog.exec() == QDialog.Accepted:
-                name = dialog.selectedFiles()
-                self.buffer[:] = np.loadtxt(name[0], np.uint32)
-                self.curve.set_ydata(self.buffer)
-                self.ax.relim()
-                self.ax.autoscale_view(scalex=False, scaley=True)
-                self.canvas.draw()
-        except:
-            self.log.print("error: %s" % sys.exc_info()[1])
-
-class redP_consumer():            
-    def __init__(self):
-        self.NTrig = 0
-        self.dN = 0
-        self.Nprev = self.NTrig
-        self.T0 = time.time()
-        self.Tprev = self.T0
-        self.dT = 0.
-
-    def data_sink(self, data):
-        """function called by redPoscdaq 
-           this simple version calculates statistics only
-        """
-        self.databuffer = data    
-        # analyze data
-        self.NTrig += 1
-        t = time.time()
-        dt = t - self.Tprev
-        self.Tprev = t
-        self.dT += dt
-        l_tot = len(self.databuffer[0])
-        
-        # output status and update scope display once per second
-        if self.dT >= 1.:
-            dN = self.NTrig - self.Nprev
-            r = dN/self.dT
-            self.Nprev = self.NTrig
-            T_active = t - self.T0
-            self.dT = 0.
-            status_txt = "active: {:.1f}s  trigger rate: {:.2f} Hz,  data rate: {:.4g} MB/s".format(T_active, r, r*l_tot*4e-6)
-            print(status_txt, end='\r')
-
-def run_rpControl(callback=None, conf_dict=None):
-    # start redPidaya GUI under Qt5 
-    app = QApplication(sys.argv)
-    dpi = app.primaryScreen().logicalDotsPerInch()
-    matplotlib.rcParams["figure.dpi"] = dpi
-    application = rpControl(callback=callback, conf_dict=conf_dict)
-    application.show()
-    sys.exit(app.exec_())
-
-
-if __name__ == '__main__': # --------------------------------------------
-#    run_rpControl()
-
-    data_processor = redP_consumer()
-    run_rpControl(callback=data_processor.data_sink)
-- 
GitLab