def advanced_menu(self):
        """
        Show a config dialog for advanced options, ie. gate width,
        interval for the rate measurement, options for writing pulse file
        and the write_daq_status option.

        :returns: None
        """
        # get the actual channels from the DAQ card
        self.daq.put("DC")

        # wait explicitly until the channels get loaded
        self.logger.info("loading channel information...")
        time.sleep(1)

        # show dialog
        dialog = AdvancedDialog(get_setting("gate_width"),
                                get_setting("time_window"),
                                get_setting("write_daq_status"))

        if dialog.exec_() == 1:
            # update time window
            time_window = float(dialog.get_widget_value("time_window"))

            if time_window < 0.01 or time_window > 10000.:
                self.logger.warning("Time window too small or too big, " +
                                    "resetting to 5 s.")
                time_window = 5.0

            update_setting("time_window", time_window)

            # update write_daq_status
            write_daq_status = dialog.get_widget_value("write_daq_status")
            update_setting("write_daq_status", write_daq_status)

            # update gate width
            gate_width = int(dialog.get_widget_value("gate_width"))
            update_setting("gate_width", gate_width)

            # transform gate width for daq msg
            gate_width = bin(gate_width // 10).replace('0b', '').zfill(16)
            gate_width_03 = format(int(gate_width[0:8], 2), 'x').zfill(2)
            gate_width_02 = format(int(gate_width[8:16], 2), 'x').zfill(2)

            # set gate widths
            self.daq.put("WC 03 %s" % gate_width_03)
            self.daq.put("WC 02 %s" % gate_width_02)

            # adjust the update interval
            self.widget_updater.start(time_window * 1000)

            self.logger.debug("Writing gate width WC 02 %s WC 03 %s" %
                              (gate_width_02, gate_width_03))
            self.logger.debug("Setting time window to %.2f " % time_window)
            self.logger.debug("Switching write_daq_status option to %s" %
                              write_daq_status)

        self.daq.put("DC")
    def threshold_menu(self):
        """
        Shows thresholds dialog.

        :returns: None
        """
        # get the actual thresholds from the DAQ card
        self.daq.put('TL')

        # wait explicitly until the thresholds get loaded
        self.logger.info("loading threshold information..")
        time.sleep(1.5)

        # get thresholds from settings
        thresholds = [get_setting("threshold_ch%d" % i, 300) for i in range(4)]

        # show dialog
        dialog = ThresholdDialog(thresholds)

        if dialog.exec_() == 1:
            commands = []

            # update thresholds config
            for ch in range(4):
                val = dialog.get_widget_value("threshold_ch_%d" % ch)
                update_setting("threshold_ch%d" % ch, val)
                commands.append("TL %d %s" % (ch, val))

            # apply new thresholds to daq card
            for cmd in commands:
                self.daq.put(cmd)
                self.logger.info("Set threshold of channel %s to %s" %
                                 (cmd.split()[1], cmd.split()[2]))

        self.daq.put('TL')
    def get_thresholds_from_msg(self, msg):
        """
        Explicitly scan message for threshold information.

        Return True if found, False otherwise.

        :param msg: daq message
        :type msg: str
        :returns: bool
        """
        if msg.startswith('TL') and len(msg) > 9:
            msg = msg.split('=')
            update_setting("threshold_ch0", int(msg[1][:-2]))
            update_setting("threshold_ch1", int(msg[2][:-2]))
            update_setting("threshold_ch2", int(msg[3][:-2]))
            update_setting("threshold_ch3", int(msg[4]))
            self.logger.debug(
                "Got Thresholds %d %d %d %d" %
                tuple([get_setting("threshold_ch%d" % i) for i in range(4)]))
            return True
        else:
            return False
    def process_incoming(self):
        """
        This functions gets everything out of the daq.

        Handles all the messages currently in the daq
        and passes the results to the corresponding widgets.

        :returns: None
        """
        while self.daq.data_available():
            try:
                msg = self.daq.get(0)
            except DAQIOError:
                self.logger.debug("Queue empty!")
                return None

            # make daq msg public for child widgets
            self.last_daq_msg = msg

            # Check contents of message and do what it says
            self.get_widget("daq").update()

            gps_widget = self.get_widget("gps")

            # try to extract GPS information if widget is active and enabled
            if gps_widget.active() and gps_widget.isEnabled():
                gps_widget.update()
                continue

            status_widget = self.get_widget("status")

            # update status widget if active
            if status_widget.isVisible() and status_widget.active():
                status_widget.update()

            decay_widget = self.get_widget("decay")

            # update previous coincidence config on decay widget if active
            if msg.startswith('DC') and len(msg) > 2 and decay_widget.active():
                try:
                    split_msg = msg.split(" ")
                    t_03 = split_msg[4].split("=")[1]
                    t_02 = split_msg[3].split("=")[1]
                    decay_widget.set_previous_coincidence_times(t_03, t_02)
                except Exception:
                    self.logger.debug('Wrong DC command.')
                continue

            # check for threshold information
            if self.get_thresholds_from_msg(msg):
                continue

            # check for channel configuration
            if self.get_channels_from_msg(msg):
                continue

            # ignore status messages
            if msg.startswith('ST') or len(msg) < 50:
                continue

            # calculate rate
            if self.get_widget("rate").calculate():
                continue

            # extract pulses if needed
            if (get_setting("write_pulses") or self.is_widget_active("pulse")
                    or self.is_widget_active("decay")
                    or self.is_widget_active("velocity")):
                self.pulses = self.pulse_extractor.extract(msg)

            # trigger calculation on all pulse widgets
            self.calculate_pulses()
    def get_channels_from_msg(self, msg):
        """
        Explicitly scan message for channel information.

        Return True if found, False otherwise.

        DC gives:

        DC C0=23 C1=71 C2=0A C3=00

        Which has the meaning:

        MM - 00 -> 8bits for channel enable/disable, coincidence and veto

        +---------------------------------------------------------------------+
        |                              bits                                   |
        +====+====+===========+===========+========+========+========+========+
        |7   |6   |5          |4          |3       |2       |1       |0       |
        +----+----+-----------+-----------+--------+--------+--------+--------+
        |veto|veto|coincidence|coincidence|channel3|channel2|channel1|channel0|
        +----+----+-----------+-----------+--------+--------+--------+--------+

        +-----------------+
        |Set bits for veto|
        +=================+
        |00 - ch0 is veto |
        +-----------------+
        |01 - ch1 is veto |
        +-----------------+
        |10 - ch2 is veto |
        +-----------------+
        |11 - ch3 is veto |
        +-----------------+

        +------------------------+
        |Set bits for coincidence|
        +========================+
        |00 - singles            |
        +------------------------+
        |01 - twofold            |
        +------------------------+
        |10 - threefold          |
        +------------------------+
        |11 - fourfold           |
        +------------------------+

        :param msg: daq message
        :type msg: str
        :returns: bool
        """
        if msg.startswith('DC ') and len(msg) > 25:
            msg = msg.split(' ')

            coincidence_time = msg[4].split('=')[1] + msg[3].split('=')[1]
            msg = bin(int(msg[1][3:], 16))[2:].zfill(8)
            veto_config = msg[0:2]
            coincidence_config = msg[2:4]
            channel_config = msg[4:8]

            update_setting("gate_width", int(coincidence_time, 16) * 10)

            # set default veto config
            for i in range(4):
                if i == 0:
                    update_setting("veto", True)
                else:
                    update_setting("veto_ch%d" % (i - 1), False)

            # update channel config
            for i in range(4):
                update_setting("active_ch%d" % i, channel_config[3 - i] == '1')

            # update coincidence config
            for i, seq in enumerate(['00', '01', '10', '11']):
                update_setting("coincidence%d" % i, coincidence_config == seq)

            # update veto config
            for i, seq in enumerate(['00', '01', '10', '11']):
                if veto_config == seq:
                    if i == 0:
                        update_setting("veto", False)
                    else:
                        update_setting("veto_ch%d" % (i - 1), True)

            self.logger.debug('gate width timew indow %d ns' %
                              get_setting("gate_width"))
            self.logger.debug(
                "Got channel configurations: %d %d %d %d" %
                tuple([get_setting("active_ch%d" % i) for i in range(4)]))
            self.logger.debug(
                "Got coincidence configurations: %d %d %d %d" %
                tuple([get_setting("coincidence%d" % i) for i in range(4)]))
            self.logger.debug(
                "Got veto configurations: %d %d %d %d" %
                tuple([get_setting("veto")] +
                      [get_setting("veto_ch%d" % i) for i in range(3)]))

            return True
        else:
            return False
    def config_menu(self):
        """
        Show the channel config dialog.

        :returns: None
        """
        # get the actual channels from the DAQ card
        self.daq.put("DC")

        # wait explicitly until the channels get loaded
        self.logger.info("loading channel information...")
        time.sleep(1)

        # get current config values
        channel_config = [get_setting("active_ch%d" % i) for i in range(4)]
        coincidence_config = [
            get_setting("coincidence%d" % i) for i in range(4)
        ]
        veto = get_setting("veto")
        veto_config = [get_setting("veto_ch%d" % i) for i in range(3)]

        # show dialog
        dialog = ConfigDialog(channel_config, coincidence_config, veto,
                              veto_config)

        if dialog.exec_() == 1:

            # get and update channel and coincidence config
            for i in range(4):
                channel_config[i] = dialog.get_widget_value(
                    "channel_checkbox_%d" % i)
                coincidence_config[i] = dialog.get_widget_value(
                    "coincidence_checkbox_%d" % i)

                update_setting("active_ch%d" % i, channel_config[i])
                update_setting("coincidence%d" % i, coincidence_config[i])

            # get and update veto state
            veto = dialog.get_widget_value("veto_checkbox")
            update_setting("veto", veto)

            # get and update veto channel config
            for i in range(3):
                veto_config[i] = dialog.get_widget_value("veto_checkbox_%d" %
                                                         i)

                update_setting("veto_ch%d" % i, veto_config[i])

            # build daq message to apply the new config to the card
            tmp_msg = ""

            if veto:
                if veto_config[0]:
                    tmp_msg += "01"
                elif veto_config[1]:
                    tmp_msg += "10"
                elif veto_config[2]:
                    tmp_msg += "11"
                else:
                    tmp_msg += "00"
            else:
                tmp_msg += "00"

            coincidence_set = False

            # singles, twofold, threefold, fourfold
            for i, coincidence in enumerate(["00", "01", "10", "11"]):
                if coincidence_config[i]:
                    tmp_msg += coincidence
                    coincidence_set = True

            if not coincidence_set:
                tmp_msg += "00"

            # now calculate the correct expression for the first
            # four bits
            self.logger.debug("The first four bits are set to %s" % tmp_msg)
            msg = "WC 00 %s" % hex(int(''.join(tmp_msg), 2))[-1].capitalize()

            channel_set = False
            enable = ['0', '0', '0', '0']

            for i, active in enumerate(reversed(channel_config)):
                if active:
                    enable[i] = '1'
                    channel_set = True

            if channel_set:
                msg += hex(int(''.join(enable), 2))[-1].capitalize()
            else:
                msg += '0'

            # send the message to the daq card
            self.daq.put(msg)

            self.logger.info("The following message was sent to DAQ: %s" % msg)

            for i in range(4):
                self.logger.debug("channel%d selected %s" %
                                  (i, channel_config[i]))

            for i, name in enumerate(
                ["singles", "twofold", "threefold", "fourfold"]):
                self.logger.debug("coincidence %s %s" %
                                  (name, coincidence_config[i]))

        self.daq.put("DC")