Beispiel #1
0
def main():
    args = example_utils.ExampleArgumentParser().parse_args()
    example_utils.config_logging(args)

    if args.socket_addr:
        raise Exception("Socket is not supported")
    elif args.spi:
        raise Exception("SPI is not supported")
    else:
        port = args.serial_port or example_utils.autodetect_serial_port()
        client = RegClient(port)

    client.connect()

    pin = RegClient._XM112_LED_PIN

    client._write_gpio(pin, 1)
    val = client._read_gpio(pin)
    print(val)

    for _ in range(3):
        sleep(0.1)
        client._write_gpio(pin, 0)  # on
        sleep(0.1)
        client._write_gpio(pin, 1)  # off

    client.disconnect()
Beispiel #2
0
class Radar():
    def __init__(self, radar_queue, interrupt_queue):
        # Setup for collecting data from radar
        self.args = example_utils.ExampleArgumentParser().parse_args()
        example_utils.config_logging(self.args)
        if self.args.socket_addr:
            self.client = JSONClient(self.args.socket_addr)
        else:
            port = self.args.serial_port or example_utils.autodetect_serial_port(
            )
            self.client = RegClient(port)

        self.client.squeeze = False
        self.config = configs.IQServiceConfig()
        self.config.sensor = self.args.sensors

        self.config.range_interval = [0.2, 0.6]  # Measurement interval
        self.config.sweep_rate = 1  # Frequency for collecting data
        self.config.gain = 1  # Gain between 0 and 1.
        self.time = 10  # Duration for a set amount of sequences
        self.seq = self.config.sweep_rate * self.time

        self.info = self.client.setup_session(
            self.config)  # Setup acconeer radar session
        self.num_points = self.info[
            "data_length"]  # Amount of data points per sampel

        #### Det här kanske inte ska vara i den här klassen #####
        # Vector for radar values from tracked data
        self.peak_vector = np.zeros((1, self.seq), dtype=np.csingle)
        self.data_idx = 0  # Inedex for peak vector used for filtering

        self.radar_queue = radar_queue
        self.interrupt_queue = interrupt_queue
        self.timeout = time.time() + self.time
        b = 0

    # Loop which collects data from the radar, tracks the maximum peak and filters it for further signal processing. The final filtered data is put into a queue.
    def get_data(self):
        self.client.start_streaming()  # Starts Acconeers streaming server
        while True:
            self.info, self.data = self.client.get_next()
            print("Getting data")
            if self.interrupt_queue.empty() == False or time.time(
            ) >= self.timeout:  # Interrupt from bluetooth
                # self.interrupt_queue.get()
                print('Breaking loop')
                break
        self.client.disconnect()
Beispiel #3
0
def main():
    args = example_utils.ExampleArgumentParser().parse_args()
    example_utils.config_logging(args)

    if args.socket_addr:
        print("Using detectors is only supported with the XM112 module")
        sys.exit()
    else:
        port = args.serial_port or example_utils.autodetect_serial_port()
        client = RegClient(port)

    config = configs.DistancePeakDetectorConfig()
    config.sensor = args.sensors
    config.range_interval = [0.1, 0.7]
    config.sweep_rate = 60
    config.gain = 0.5

    client.setup_session(config)

    pg_updater = PGUpdater(config)
    pg_process = PGProcess(pg_updater)
    pg_process.start()

    client.start_streaming()

    interrupt_handler = example_utils.ExampleInterruptHandler()
    print("Press Ctrl-C to end session")

    while not interrupt_handler.got_signal:
        info, data = client.get_next()

        try:
            pg_process.put_data(data)
        except PGProccessDiedException:
            break

    print("Disconnecting...")
    pg_process.close()
    client.disconnect()
Beispiel #4
0
class Radar(threading.Thread):
    def __init__(self):
        # Setup radar data
        self.args = example_utils.ExampleArgumentParser().parse_args()
        example_utils.config_logging(self.args)
        if self.args.socket_addr:
            self.client = JSONClient(self.args.socket_addr)
        else:
            port = self.args.serial_port or example_utils.autodetect_serial_port(
            )
            self.client = RegClient(port)

        self.client.squeeze = False
        self.config = configs.IQServiceConfig()
        self.config.sensor = self.args.sensors
        self.config.range_interval = [0.2, 0.6]
        self.config.sweep_rate = 1
        self.config.gain = 1
        self.time = 5
        self.seq = self.config.sweep_rate * self.time

        self.info = self.client.setup_session(self.config)
        self.num_points = self.info["data_length"]
        self.matrix = np.zeros((self.seq, self.num_points), dtype=np.csingle)
        self.matrix_copy = np.zeros((self.seq, self.num_points),
                                    dtype=np.csingle)
        self.matrix_idx = 0
        super(Radar, self).__init__()

    def run(self):
        # Metod för att hämta data från radarn
        self.client.start_streaming()
        for i in range(self.seq * 2):
            self.get_data()
            self.tracker()
            self.filter_HeartRate()
            self.filter_RespRate()
            self.matrix_idx += 1
            if self.matrix_idx >= self.seq:
                self.matrix_idx = 0
        self.client.disconnect()

    def get_data(self):
        # Spara fil sker inte senare. Hämtar data från radarn och sparar det i en matris.

        # for i in range(0, self.seq):
        if self.matrix_idx < self.seq:
            self.info, self.data = self.client.get_next()
            self.matrix[self.matrix_idx][:] = self.data[:]
            print("Seq number {}".format(self.matrix_idx))
            te.sleep(1)

        #filename = "test.csv"
        #np.savetxt(filename, self.matrix)

    def filter_HeartRate(self):
        pass

    def filter_RespRate(self):
        pass

    def tracker(self):
        self.matrix_copy = self.matrix
        # matrix_row =
        #tracker_max = np.argmax[np.abs(self.matrix_copy)]

        # print(tracker_max)
        pass
Beispiel #5
0
class Radar(threading.Thread):
    def __init__(self, HR_filter_queue,
                 go):  # Lägg till RR_filter_queue som inputargument
        self.go = go
        # Setup for collecting data from radar
        self.args = example_utils.ExampleArgumentParser().parse_args()
        example_utils.config_logging(self.args)
        if self.args.socket_addr:
            self.client = JSONClient(self.args.socket_addr)
            # Test för att se vilken port som används av radarn
            print("RADAR Port = " + self.args.socket_addr)
        else:
            port = self.args.serial_port or example_utils.autodetect_serial_port(
            )
            self.client = RegClient(port)

        self.client.squeeze = False
        self.config = configs.IQServiceConfig()
        self.config.sensor = self.args.sensors

        self.config.range_interval = [0.2, 0.6]  # Measurement interval
        self.config.sweep_rate = 20  # Frequency for collecting data
        self.config.gain = 1  # Gain between 0 and 1.
        self.time = 1  # Duration for a set amount of sequences
        self.seq = self.config.sweep_rate * self.time  # Amount of sequences during a set time and sweep freq

        self.info = self.client.setup_session(
            self.config)  # Setup acconeer radar session
        self.num_points = self.info[
            "data_length"]  # Amount of data points per sampel
        self.data_getting = 0  # Inedex for printing still getting data once a second

        self.HR_filter_queue = HR_filter_queue
        # self.a = a
        # self.RR_filter_queue = RR_filter_queue
        # Initiation for tracking method
        self.N_avg = 10  # How manny peaks averaged over when calculating peaks_filtered
        self.I_peaks = np.zeros(self.N_avg)
        self.locs = np.zeros(self.N_avg)
        self.I_peaks_filtered = np.zeros(self.N_avg)
        self.tracked_distance = np.zeros(self.N_avg)
        self.tracked_amplitude = np.zeros(self.N_avg)
        self.tracked_phase = np.zeros(self.N_avg)
        self.threshold = 0  # variable for finding peaks above threshold
        self.data_idx = 0  # Index in vector for tracking. Puts new tracked peak into tracking vector
        # converts index to real length
        self.real_dist = np.linspace(self.config.range_interval[0],
                                     self.config.range_interval[1],
                                     num=self.num_points)
        self.counter = 0  # Used only for if statement only for first iteration and not when data_idx goes back to zero

        super(Radar, self).__init__()  # Inherit threading vitals

    # Loop which collects data from the radar, tracks the maximum peak and filters it for further signal processing. The final filtered data is put into a queue.
    def run(self):

        self.client.start_streaming()  # Starts Acconeers streaming server
        # static variable impported from bluetooth_app class (In final version)
        while self.go:
            # for i in range(self.seq*2):
            data = self.get_data()
            tracked_data = self.tracking(data)
            print("Tracked distance: ", tracked_data)
            # self.filter_HeartRate()
            # self.filter_RespRate()
            self.data_getting += 1
            if self.data_getting % self.config.sweep_rate == 0:
                print("Still getting data")
                self.HR_filter_queue.put(2)
            # Resets matrix index to zero for printing getting data.
            if self.data_getting >= self.config.sweep_rate:
                self.data_getting = 0
        print("End of getting data from radar")

        self.client.disconnect()

    # Method to collect data from the streaming server
    def get_data(self):
        # self.data should be accessable from all other methods
        info, data = self.client.get_next()
        #print("How long the data is ", data)
        return np.array(data)

    # Filter for heart rate using the last X sampels according to data_idx. Saves data to queue
    # def filter_HeartRate(self):
    #     # HR_peak_vector = copy.copy(self.peak_vector)
    #     # for i in range(5):
    #     #     HR_peak_vector[0][i] = 0
    #     # # self.HR_filter_queue.put(HR_peak_vector)
    #     pass

    # Filter for Respitory rate. Saves data to queue

    # def filter_RespRate(self):
    #     # RR_peak_vector = copy.copy(self.peak_vector)
    #     # for i in range(5):
    #     #     RR_peak_vector[0][i] = 0
    #     # self.RR_filter_queue.put(RR_peak_vector)
    #     pass

    # Tracks the maximum peak from collected data which is filtered for further signal processing
    def tracking(self, data):
        data = np.transpose(data)
        # self.data = data      # Removed because using local data variable. Easier to understand how data travells in class
        # print("Length of data input ", str(len(data)))
        if self.data_idx == 0 and self.counter == 0:  # things that only happens first time
            I = np.argmax(np.abs(data))
            self.I_peaks[:] = I
            self.I_peaks_filtered[0] = self.I_peaks[0]
            self.tracked_distance[0] = self.real_dist[int(
                self.I_peaks_filtered[0])]
            self.tracked_amplitude[0] = np.abs(data[int(
                self.I_peaks_filtered[0])])
            self.tracked_phase[0] = np.angle(data[int(
                self.I_peaks_filtered[0])])

        # After first seq continous tracking
        else:
            self.locs, _ = signal.find_peaks(
                np.abs(data))  # find local maximas in data
            # removes local maxima if under threshhold
            self.locs = [
                x for x in self.locs if (np.abs(data[x]) > self.threshold)
            ]
            difference = np.subtract(self.locs,
                                     self.I_peaks_filtered[self.data_idx])
            print("locks: ", self.locs)
            print("Last I_peaks_filtered: ",
                  self.I_peaks_filtered[self.data_idx])
            print("difference: ", difference)
            abs = np.abs(difference)
            argmin = np.argmin(abs)
            Index_in_locks = argmin  # index of closest peak in locs

            # Index_in_locks = np.argmin(np.abs(self.locks - self.I_peaks_filtered[self.data_idx - 1]))       # difference between current peak index and last peak index

            if len(self.locs) == 0:  # if no peak is found
                self.I_peaks[self.data_idx] = self.I_peaks[self.data_idx - 1]
                print("Last peak value. Not updated.")
            else:
                I = self.locs[int(Index_in_locks)]
                self.I_peaks[self.data_idx] = I

            print("I_peaks: ", self.I_peaks)

            # if self.counter == 0:  # Questions about this part.
            #     self.i_avg_start = 0        # this will be 0 as long as counter == 0
            #     if self.data_idx == self.N_avg - 1:  # change dist to nmbr of sequences later
            #         self.counter = 1
            # else:
            # self.i_avg_start = self.data_idx - (self.N_avg - 1)

            self.I_peaks_filtered[self.data_idx] = np.round(
                np.mean(self.I_peaks))  # mean value of N_avg latest peaks

            # determines threshold
            self.threshold = np.abs(data[int(
                self.I_peaks_filtered[self.data_idx])]) * 0.5

            self.tracked_distance[self.data_idx] = self.real_dist[int(
                self.I_peaks_filtered[self.data_idx])]
            self.tracked_amplitude[self.data_idx] = np.abs(data[int(
                self.I_peaks_filtered[self.data_idx])])
            self.tracked_phase[self.data_idx] = np.angle(data[int(
                self.I_peaks_filtered[self.data_idx])])

        # print("I_peaks_filtered: ", self.I_peaks_filtered)

        self.data_idx += 1
        if self.data_idx == self.N_avg:
            self.data_idx = 0
        return self.tracked_distance[self.data_idx - 1]
Beispiel #6
0
class GUI(QMainWindow):
    sig_scan = pyqtSignal(object)
    use_cl = False
    data = None
    client = None
    sweep_count = -1
    acc_file = os.path.join(os.path.dirname(__file__), "acc.png")
    last_file = os.path.join(os.path.dirname(__file__), "last_config.npy")
    sweep_buffer = 500
    env_plot_max_y = 0
    cl_supported = False

    def __init__(self):
        super().__init__()

        self.init_labels()
        self.init_textboxes()
        self.init_buttons()
        self.init_dropdowns()
        self.init_sublayouts()
        self.start_up()

        self.main_widget = QWidget()
        self.main_layout = QtWidgets.QGridLayout(self.main_widget)
        self.main_layout.sizeConstraint = QtWidgets.QLayout.SetDefaultConstraint

        self.main_layout.addLayout(self.panel_sublayout, 0, 1)
        self.main_layout.setColumnStretch(0, 1)

        self.canvas = self.init_graphs()
        self.main_layout.addWidget(self.canvas, 0, 0)

        self.main_widget.setLayout(self.main_layout)
        self.setCentralWidget(self.main_widget)

        self.setGeometry(50, 50, 1200, 700)
        self.setWindowTitle("Acconeer Exploration GUI")
        self.show()

        self.radar = data_processing.DataProcessing()

    def init_labels(self):
        text = {
            "sensor":       "Sensor",
            "server":       "Host address",
            "scan":         "Scan",
            "gain":         "Gain",
            "frequency":    "Sample frequency",
            "sweeps":       "Number of sweeps",
            "sweep_buffer": "Sweep buffer",
            "start_range":  "Start (m)",
            "end_range":    "Stop (m)",
            "clutter":      "Background settings",
            "clutter_file": "",
            "interface":    "Interface",
            "power_bins":   "Power bins",
        }

        self.labels = {}
        for key in text:
            self.labels[key] = QLabel(self)
        for key, val in self.labels.items():
            val.setText(text[key])

        self.labels["power_bins"].setVisible(False)

    def init_textboxes(self):
        text = {
            "sensor":       "1",
            "host":         "192.168.1.100",
            "frequency":    "10",
            "sweeps":       "-1",
            "gain":         "0.4",
            "start_range":  "0.18",
            "end_range":    "0.72",
            "sweep_buffer": "100",
            "power_bins":   "6",
        }
        self.textboxes = {}
        for key in text:
            self.textboxes[key] = QLineEdit(self)
            self.textboxes[key].setText(text[key])

        self.textboxes["power_bins"].setVisible(False)

    def init_graphs(self, mode="Select service"):
        axes = {
            "Select service": [None, None],
            "IQ": [None, None],
            "Envelope": [None, None],
            "Power bin": [None, None],
            "Presence detection": [prd, prd.PresenceDetectionProcessor],
            "Breathing": [br, br.BreathingProcessor],
            "Phase tracking": [pht, pht.PhaseTrackingProcessor],
            "Sleep breathing": [sb, sb.PresenceDetectionProcessor],
            "Obstacle detection": [od, od.ObstacleDetectionProcessor],
        }

        self.external = axes[mode][1]
        canvas = None

        self.textboxes["power_bins"].setVisible(False)
        self.labels["power_bins"].setVisible(False)
        self.cl_supported = False
        if "IQ" in self.mode.currentText() or "Envelope" in self.mode.currentText():
            self.cl_supported = True
        else:
            self.load_clutter_file(force_unload=True)
        self.buttons["create_cl"].setEnabled(self.cl_supported)
        self.buttons["load_cl"].setEnabled(self.cl_supported)

        self.current_canvas = mode

        font = QFont()
        font.setPixelSize(12)
        ax_color = (0, 0, 0)
        ax = ("bottom", "left")

        if mode == "Select service":
            canvas = QLabel()
            pixmap = QPixmap(self.acc_file)
            canvas.setPixmap(pixmap)
            return canvas

        pg.setConfigOption("background", "#f0f0f0")
        pg.setConfigOption("foreground", "k")
        pg.setConfigOption("leftButtonPan", False)
        pg.setConfigOptions(antialias=True)
        canvas = pg.GraphicsLayoutWidget()

        if self.external:
            self.service_widget = axes[mode][0].PGUpdater(self.update_sensor_config())
            self.service_widget.setup(canvas)
            self.external = axes[mode][1]
            return canvas
        elif "power" in mode.lower():
            self.power_plot_window = canvas.addPlot(title="Power bin")
            self.power_plot_window.showGrid(x=True, y=True)
            for i in ax:
                self.power_plot_window.getAxis(i).tickFont = font
                self.power_plot_window.getAxis(i).setPen(ax_color)
            pen = pg.mkPen(example_utils.color_cycler(0), width=2)
            self.power_plot = pg.BarGraphItem(x=np.arange(1, 7),
                                              height=np.linspace(0, 6, num=6),
                                              width=.5,
                                              pen=pen,
                                              name="Power bins")
            self.power_plot_window.setLabel("left", "Amplitude")
            self.power_plot_window.setLabel("bottom", "Power bin range (mm)")
            self.power_plot_window.setYRange(0, 10)
            self.power_plot_window.setXRange(0.5, 6.5)
            self.power_plot_window.addItem(self.power_plot)
            self.textboxes["power_bins"].setVisible(True)
            self.labels["power_bins"].setVisible(True)
        else:
            self.envelope_plot_window = canvas.addPlot(title="Envelope")
            self.envelope_plot_window.showGrid(x=True, y=True)
            self.envelope_plot_window.addLegend()
            for i in ax:
                self.envelope_plot_window.getAxis(i).tickFont = font
                self.envelope_plot_window.getAxis(i).setPen(ax_color)

            pen = example_utils.pg_pen_cycler()
            self.envelope_plot = self.envelope_plot_window.plot(range(10),
                                                                np.zeros(10),
                                                                pen=pen,
                                                                name="Envelope")
            self.envelope_plot_window.setYRange(0, 1)
            pen = pg.mkPen(0.2, width=2, style=QtCore.Qt.DotLine)
            self.clutter_plot = self.envelope_plot_window.plot(range(10),
                                                               np.zeros(10),
                                                               pen=pen,
                                                               name="Background")
            self.clutter_plot.setZValue(2)

            self.snr_text = pg.TextItem(text="", color=(1, 1, 1), anchor=(0, 1))
            self.snr_text.setZValue(3)
            self.envelope_plot_window.addItem(self.snr_text)
            self.envelope_plot_window.setLabel("left", "Amplitude")
            self.envelope_plot_window.setLabel("bottom", "Distance (mm)")

            canvas.nextRow()

            if mode.lower() == "iq":
                self.iq_plot_window = canvas.addPlot(title="Phase")
                self.iq_plot_window.showGrid(x=True, y=True)
                self.iq_plot_window.addLegend()
                for i in ax:
                    self.iq_plot_window.getAxis(i).tickFont = font
                    self.iq_plot_window.getAxis(i).setPen(ax_color)
                pen = example_utils.pg_pen_cycler()
                self.iq_plot = self.iq_plot_window.plot(range(10),
                                                        np.arange(10)*0,
                                                        pen=pen,
                                                        name="IQ Phase")
                self.iq_plot_window.setLabel("left", "Normalized phase")
                self.iq_plot_window.setLabel("bottom", "Distance (mm)")
                canvas.nextRow()
            self.hist_plot_image = canvas.addPlot()
            self.hist_plot = pg.ImageItem(titel="History")
            colormap = plt.get_cmap("viridis")
            colormap._init()
            lut = (colormap._lut * 255).view(np.ndarray)
            self.hist_plot.setLookupTable(lut)
            pen = example_utils.pg_pen_cycler(1)
            self.hist_plot_peak = self.hist_plot_image.plot(range(10),
                                                            np.zeros(10),
                                                            pen=pen)
            self.hist_plot_image.addItem(self.hist_plot)
            self.hist_plot_image.setLabel("left", "Distance (mm)")
            self.hist_plot_image.setLabel("bottom", "Time (Sweep number)")
            for i in ax:
                    self.hist_plot_image.getAxis(i).tickFont = font
                    self.hist_plot_image.getAxis(i).setPen(ax_color)

        return canvas

    def init_dropdowns(self):
        self.mode = QComboBox(self)
        self.mode.addItem("Select service")
        self.mode.addItem("IQ")
        self.mode.addItem("Envelope")
        self.mode.addItem("Power bin")
        self.mode.addItem("Phase tracking")
        self.mode.addItem("Presence detection")
        self.mode.addItem("Breathing")
        self.mode.addItem("Sleep breathing")
        self.mode.addItem("Obstacle detection")
        self.mode.move(50, 250)

        self.mode_to_param = {
            "Select service": "",
            "IQ": "iq_data",
            "Envelope": "envelope_data",
            "Power bin": "power_bin",
            "Breathing": "iq_data",
            "Phase tracking": "iq_data",
            "Presence detection": "iq_data",
            "Sleep breathing": "iq_data",
            "Obstacle detection": "iq_data",
        }

        self.mode_to_config = {
            "Select service": ["", ""],
            "IQ": [configs.IQServiceConfig(), "internal"],
            "Envelope": [configs.EnvelopeServiceConfig(), "internal"],
            "Power bin": [configs.PowerBinServiceConfig(), "internal_power"],
            "Breathing": [br.get_base_config(), "external"],
            "Phase tracking": [pht.get_base_config(), "external"],
            "Presence detection": [prd.get_base_config(), "external"],
            "Sleep breathing": [sb.get_base_config(), "external"],
            "Obstacle detection": [od.get_base_config(), "external"]
        }

        self.mode.currentIndexChanged.connect(self.update_canvas)

        self.interface = QComboBox(self)
        self.interface.addItem("Socket")
        self.interface.addItem("Serial")
        self.interface.currentIndexChanged.connect(self.update_interface)

        self.ports = QComboBox(self)
        self.ports.addItem("Scan ports")
        self.ports.activated.connect(self.update_ports)
        self.update_ports()

    def update_ports(self):
        if "scan" not in self.ports.currentText().lower():
            return

        port_infos = serial.tools.list_ports.comports()
        ports = [port_info[0] for port_info in port_infos]

        self.ports.clear()
        self.ports.addItem("Scan ports")
        self.ports.addItems(ports)

    def init_buttons(self):
        self.buttons = {
            "start":        QtWidgets.QPushButton("Start", self),
            "connect":      QtWidgets.QPushButton("Connect", self),
            "stop":         QtWidgets.QPushButton("Stop", self),
            "create_cl":    QtWidgets.QPushButton("Scan Background", self),
            "load_cl":      QtWidgets.QPushButton("Load Background", self),
            "load_scan":    QtWidgets.QPushButton("Load Scan", self),
            "save_scan":    QtWidgets.QPushButton("Save Scan", self),
        }

        button_funcs = {
            "start": self.start_scan,
            "connect": self.connect_to_server,
            "stop": self.stop_scan,
            "create_cl": lambda: self.start_scan(create_cl=True),
            "load_cl": self.load_clutter_file,
            "load_scan": self.load_scan,
            "save_scan": lambda: self.save_scan(self.data),
        }

        button_enabled = {
            "start": False,
            "connect": True,
            "stop": True,
            "create_cl": False,
            "load_cl": True,
            "load_scan": True,
            "save_scan": False,
        }

        for key in button_funcs:
            self.buttons[key].clicked.connect(button_funcs[key])
            self.buttons[key].setEnabled(button_enabled[key])

    def init_sublayouts(self):
        # Panel sublayout
        self.panel_sublayout = QtWidgets.QHBoxLayout()
        panel_sublayout_inner = QtWidgets.QVBoxLayout()

        # Server sublayout
        server_sublayout_grid = QtWidgets.QGridLayout()
        server_sublayout_grid.addWidget(self.labels["server"], 0, 0)
        server_sublayout_grid.addWidget(self.labels["interface"], 0, 1)
        server_sublayout_grid.addWidget(self.ports, 1, 0)
        server_sublayout_grid.addWidget(self.textboxes["host"], 1, 0)
        server_sublayout_grid.addWidget(self.interface, 1, 1)
        server_sublayout_grid.addWidget(self.mode, 2, 0)
        server_sublayout_grid.addWidget(self.buttons["connect"], 2, 1)

        # Controls sublayout
        control_sublayout_grid = QtWidgets.QGridLayout()
        control_sublayout_grid.addWidget(self.labels["scan"], 0, 0)
        control_sublayout_grid.addWidget(self.buttons["start"], 1, 0)
        control_sublayout_grid.addWidget(self.buttons["stop"], 1, 1)
        control_sublayout_grid.addWidget(self.buttons["save_scan"], 2, 0)
        control_sublayout_grid.addWidget(self.buttons["load_scan"], 2, 1)
        control_sublayout_grid.addWidget(self.labels["clutter"], 3, 0)
        control_sublayout_grid.addWidget(self.buttons["create_cl"], 4, 0)
        control_sublayout_grid.addWidget(self.buttons["load_cl"], 4, 1)
        control_sublayout_grid.addWidget(self.labels["clutter_file"], 5, 0, 1, 2)

        # Settings sublayout
        settings_sublayout_grid = QtWidgets.QGridLayout()
        settings_sublayout_grid.addWidget(self.labels["sensor"], 0, 0)
        settings_sublayout_grid.addWidget(self.textboxes["sensor"], 0, 1)
        settings_sublayout_grid.addWidget(self.labels["start_range"], 1, 0)
        settings_sublayout_grid.addWidget(self.labels["end_range"], 1, 1)
        settings_sublayout_grid.addWidget(self.textboxes["start_range"], 2, 0)
        settings_sublayout_grid.addWidget(self.textboxes["end_range"], 2, 1)
        settings_sublayout_grid.addWidget(self.labels["frequency"], 3, 0)
        settings_sublayout_grid.addWidget(self.textboxes["frequency"], 3, 1)
        settings_sublayout_grid.addWidget(self.labels["gain"], 4, 0)
        settings_sublayout_grid.addWidget(self.textboxes["gain"], 4, 1)
        settings_sublayout_grid.addWidget(self.labels["sweeps"], 5, 0)
        settings_sublayout_grid.addWidget(self.textboxes["sweeps"], 5, 1)
        settings_sublayout_grid.addWidget(self.labels["sweep_buffer"], 6, 0)
        settings_sublayout_grid.addWidget(self.textboxes["sweep_buffer"], 6, 1)
        settings_sublayout_grid.addWidget(self.labels["power_bins"], 7, 0)
        settings_sublayout_grid.addWidget(self.textboxes["power_bins"], 7, 1)

        panel_sublayout_inner.addStretch(10)
        panel_sublayout_inner.addLayout(server_sublayout_grid)
        panel_sublayout_inner.addStretch(2)
        panel_sublayout_inner.addLayout(control_sublayout_grid)
        panel_sublayout_inner.addStretch(4)
        panel_sublayout_inner.addLayout(settings_sublayout_grid)
        panel_sublayout_inner.addStretch(20)
        self.panel_sublayout.addStretch(5)
        self.panel_sublayout.addLayout(panel_sublayout_inner)
        self.panel_sublayout.addStretch(10)

    def update_canvas(self, force_update=False):
        mode = self.mode.currentText()

        if force_update or self.current_canvas not in mode:
            self.main_layout.removeWidget(self.canvas)
            self.canvas.setParent(None)
            self.canvas.deleteLater()
            self.canvas = None
            self.canvas = self.init_graphs(mode)
            self.main_layout.addWidget(self.canvas, 0, 0)

        self.update_sensor_config()

    def update_interface(self):
        if self.buttons["connect"].text() == "Disconnect":
            self.connect_to_server()

        if "serial" in self.interface.currentText().lower():
            self.ports.show()
            self.textboxes["host"].hide()
            self.labels["server"].setText("Serial port")
        else:
            self.ports.hide()
            self.textboxes["host"].show()
            self.labels["server"].setText("Host address")

    def error_message(self, error):
        em = QtWidgets.QErrorMessage(self.main_widget)
        em.setWindowTitle("Error")
        em.showMessage(error)

    def start_scan(self, create_cl=False, from_file=False):
        if "Select" in self.mode.currentText():
            self.error_message("Please select a service")
            return

        if self.external:
            self.update_canvas(force_update=True)

        data_source = "stream"
        if from_file:
            data_source = "file"
        sweep_buffer = 500
        try:
            sweep_buffer = int(self.textboxes["sweep_buffer"].text())
        except Exception:
            self.error_message("Sweep buffer needs to be a positive integer\n")
            self.textboxes["sweep_buffer"].setText("500")

        params = {
            "sensor_config": self.update_sensor_config(),
            "use_clutter": self.use_cl,
            "create_clutter": create_cl,
            "data_source": data_source,
            "data_type": self.mode_to_param[self.mode.currentText()],
            "service_type": self.mode.currentText(),
            "sweep_buffer": sweep_buffer,
        }

        self.threaded_scan = Threaded_Scan(params, parent=self)
        self.threaded_scan.sig_scan.connect(self.thread_receive)
        self.sig_scan.connect(self.threaded_scan.receive)

        self.buttons["start"].setEnabled(False)
        self.buttons["load_scan"].setEnabled(False)
        self.buttons["save_scan"].setEnabled(False)
        self.buttons["create_cl"].setEnabled(False)
        self.buttons["load_cl"].setEnabled(False)
        self.mode.setEnabled(False)
        self.interface.setEnabled(False)

        self.threaded_scan.start()

    def stop_scan(self):
        self.sig_scan.emit("stop")
        self.buttons["load_scan"].setEnabled(True)
        if self.cl_supported:
            self.buttons["load_cl"].setEnabled(True)
        self.mode.setEnabled(True)
        self.interface.setEnabled(True)

    def connect_to_server(self):
        if self.buttons["connect"].text() == "Connect":
            max_num = 4
            if "Select service" in self.current_canvas:
                self.mode.setCurrentIndex(2)

            if self.interface.currentText().lower() == "socket":
                self.client = JSONClient(self.textboxes["host"].text())
            else:
                port = self.ports.currentText()
                if "scan" in port.lower():
                    self.error_message("Please select port first!")
                    return
                self.client = RegClient(port)
                max_num = 1

            conf = self.update_sensor_config()
            sensor = 1
            connection_success = False
            error = None
            while sensor <= max_num:
                conf.sensor = sensor
                try:
                    self.client.setup_session(conf)
                    self.client.start_streaming()
                    self.client.stop_streaming()
                    connection_success = True
                    self.textboxes["sensor"].setText("{:d}".format(sensor))
                    break
                except Exception as e:
                    sensor += 1
                    error = e
            if connection_success:
                self.buttons["start"].setEnabled(True)
                self.buttons["create_cl"].setEnabled(True)
                self.buttons["stop"].setEnabled(True)
            else:
                self.error_message("Could not connect to sever!\n{}".format(error))
                return

            self.buttons["connect"].setText("Disconnect")
            self.buttons["connect"].setStyleSheet("QPushButton {color: red}")
        else:
            self.buttons["connect"].setText("Connect")
            self.buttons["connect"].setStyleSheet("QPushButton {color: black}")
            self.sig_scan.emit("stop")
            self.buttons["start"].setEnabled(False)
            self.buttons["create_cl"].setEnabled(False)
            if self.cl_supported:
                self.buttons["load_cl"].setEnabled(True)

            try:
                self.client.stop_streaming()
            except Exception:
                pass

            try:
                self.client.disconnect()
            except Exception:
                pass

    def update_sensor_config(self):
        conf, service = self.mode_to_config[self.mode.currentText()]

        if not conf:
            return None

        external = ("internal" not in service.lower())

        conf.sensor = int(self.textboxes["sensor"].text())
        if external:
            color = "grey"
            self.textboxes["start_range"].setText(str(conf.range_interval[0]))
            self.textboxes["end_range"].setText(str(conf.range_interval[1]))
            self.textboxes["gain"].setText(str(conf.gain))
            self.textboxes["frequency"].setText(str(conf.sweep_rate))
            self.sweep_count = -1
        else:
            self.check_values(conf.mode)
            color = "white"
            conf.range_interval = [
                    float(self.textboxes["start_range"].text()),
                    float(self.textboxes["end_range"].text()),
            ]
            conf.sweep_rate = int(self.textboxes["frequency"].text())
            conf.gain = float(self.textboxes["gain"].text())
            self.sweep_count = int(self.textboxes["sweeps"].text())
            conf.bin_count = int(self.textboxes["power_bins"].text())

        lock = {
            "start_range": True,
            "end_range": True,
            "frequency": True,
            "gain": True,
            "sweeps": True,
        }

        for key in lock:
            if "sensor" not in key and "host" not in key:
                self.textboxes[key].setReadOnly(external)
                style_sheet = "QLineEdit {{background-color: {}}}".format(color)
                self.textboxes[key].setStyleSheet(style_sheet)

        return conf

    def check_values(self, mode):
        errors = []
        if not self.textboxes["frequency"].text().isdigit():
                errors.append("Frequency must be an integer and not less than 0!\n")
                self.textboxes["frequency"].setText("10")

        if not self.textboxes["sensor"].text().isdigit():
                errors.append("Sensor must be an integer between 1 and 4!\n")
                self.textboxes["sensor"].setText("0")
        else:
            sensor = int(self.textboxes["sensor"].text())
            sensor, e = self.check_limit(sensor, self.textboxes["sensor"], 1, 4)
            if e:
                errors.append("Sensor must be an integer between 1 and 4!\n")

        sweeps = self.is_float(self.textboxes["sweeps"].text(), is_positive=False)
        if sweeps == -1:
            pass
        elif sweeps >= 1:
            if not self.textboxes["sweeps"].text().isdigit():
                errors.append("Sweeps must be a -1 or an int larger than 0!\n")
                self.textboxes["sensor"].setText("-1")
        else:
            errors.append("Sweeps must be -1 or an int larger than 0!\n")
            self.textboxes["sweeps"].setText("-1")

        gain = self.is_float(self.textboxes["gain"].text())
        gain, e = self.check_limit(gain, self.textboxes["gain"], 0, 1, set_to=0.7)
        if e:
            errors.append("Gain must be between 0 and 1!\n")

        start = self.is_float(self.textboxes["start_range"].text())
        start, e = self.check_limit(start, self.textboxes["start_range"], 0.06, 6.94)
        if e:
            errors.append("Start range must be between 0.06m and 6.94m!\n")

        end = self.is_float(self.textboxes["end_range"].text())
        end, e = self.check_limit(end, self.textboxes["end_range"], 0.12, 7)
        if e:
            errors.append("End range must be between 0.12m and 7.0m!\n")

        r = end - start
        if r < 0:
            errors.append("Range must not be less than 0!\n")
            self.textboxes["end_range"].setText(str(start + 0.06))
            end = start + 0.06
            r = end - start
        if "envelope" in mode.lower() and r > 0.96:
            errors.append("Envelope range must be less than 0.96m!\n")
            self.textboxes["end_range"].setText(str(start + 0.96))
            end = start + 0.96
            r = end - start
        if "iq" in mode.lower() and r > 0.72:
            errors.append("IQ range must be less than 0.72m!\n")
            end = start + 0.72
            r = end - start

        if len(errors):
            self.error_message("".join(errors))

    def is_float(self, val, is_positive=True):
        try:
            f = float(val)
            if is_positive and f <= 0:
                raise ValueError("Not positive")
            return f
        except Exception:
            return False

    def check_limit(self, val, field, start, end, set_to=None):
        out_of_range = False
        if isinstance(val, bool):
            val = start
            out_of_range = True
        if val < start:
            val = start
            out_of_range = True
        if val > end:
            val = end
            out_of_range = True
        if out_of_range:
            if set_to:
                val = set_to
            field.setText(str(val))
        return val, out_of_range

    def load_clutter_file(self, force_unload=False):
        if "unload" in self.buttons["load_cl"].text().lower() or force_unload:
            self.use_cl = None
            self.labels["clutter_file"].setText("")
            self.buttons["load_cl"].setText("Load Background")
            self.buttons["load_cl"].setStyleSheet("QPushButton {color: black}")
        else:
            options = QtWidgets.QFileDialog.Options()
            options |= QtWidgets.QFileDialog.DontUseNativeDialog
            fn, _ = QtWidgets.QFileDialog.getOpenFileName(
                    self,
                    "Load background file",
                    "",
                    "All Files (*);;NumPy data Files (*.npy)",
                    options=options
                    )

            if fn:
                self.use_cl = fn
                self.labels["clutter_file"].setText("Background: {}".format(ntpath.basename(fn)))
                self.buttons["load_cl"].setText("Unload background")
                self.buttons["load_cl"].setStyleSheet("QPushButton {color: red}")

    def load_scan(self):
        options = QtWidgets.QFileDialog.Options()
        options |= QtWidgets.QFileDialog.DontUseNativeDialog
        filename, _ = QtWidgets.QFileDialog.getOpenFileName(
                self,
                "Load scan",
                "",
                "NumPy data files (*.npy)",
                options=options
                )

        if filename:
            try:
                self.data = np.load(filename)
                mode = self.data[0]["service_type"]
                index = self.mode.findText(mode, QtCore.Qt.MatchFixedString)
                if index >= 0:
                    self.mode.setCurrentIndex(index)
                    self.start_scan(from_file=True)
            except Exception as e:
                self.error_message("{}".format(e))

    def save_scan(self, data, clutter=False):
        if "sleep" in self.mode.currentText().lower():
            if int(self.textboxes["sweep_buffer"].text()) < 1000:
                self.error_message("Please set sweep buffer to >= 1000")
                return
        options = QtWidgets.QFileDialog.Options()
        options |= QtWidgets.QFileDialog.DontUseNativeDialog

        title = "Save scan"
        if clutter:
            title = "Save background"
        filename, _ = QtWidgets.QFileDialog.getSaveFileName(
                self, title, "", "NumPy data files (*.npy)", options=options)

        if filename:
            np.save(filename, data)
            if clutter:
                self.use_cl = filename
                label_text = "Background: {}".format(ntpath.basename(filename))
                self.labels["clutter_file"].setText(label_text)
                self.buttons["load_cl"].setText("Unload background")
                self.buttons["load_cl"].setStyleSheet("QPushButton {color: red}")

    def thread_receive(self, message_type, message, data=None):
        if "error" in message_type:
            self.error_message("{}".format(message))
            if "client" in message_type:
                if self.buttons["connect"].text() == "Disconnect":
                    self.connect_to_server()
                self.buttons["start"].setEnabled(False)
                self.buttons["create_cl"].setEnabled(False)
                self.mode.setEnabled(True)
                self.interface.setEnabled(True)
        elif message_type == "clutter_data":
            self.save_scan(data, clutter=True)
        elif message_type == "scan_data":
            self.data = data
            self.buttons["save_scan"].setEnabled(True)
        elif message_type == "scan_done":
            if "Disconnect" in self.buttons["connect"].text():
                self.buttons["start"].setEnabled(True)
                self.buttons["load_scan"].setEnabled(True)
                if self.cl_supported:
                    self.buttons["create_cl"].setEnabled(True)
                    self.buttons["load_cl"].setEnabled(True)
                self.mode.setEnabled(True)
        elif "update_plots" in message_type:
            if data:
                self.update_plots(data)
        elif "update_power_plots" in message_type:
            if data:
                self.update_power_plots(data)
        elif "update_external_plots" in message_type:
            if data:
                self.update_external_plots(data)
        else:
            print(message_type, message, data)

    def update_plots(self, data):
        mode = self.mode.currentText()
        update_ylims = False
        xstart = data["x_mm"][0]
        xend = data["x_mm"][-1]
        xdim = data["hist_env"].shape[0]
        if not data["sweep"]:
            self.env_plot_max_y = 0
            update_ylims = True
            self.envelope_plot_window.setXRange(xstart, xend)
            self.snr_text.setPos(xstart, 0)

            if mode == "IQ":
                self.iq_plot_window.setXRange(xstart, xend)
                self.iq_plot_window.setYRange(-1.1, 1.1)

            xax = self.hist_plot_image.getAxis("left")
            x = np.round(np.arange(0, xdim+xdim/9, xdim/9))
            labels = np.round(np.arange(xstart, xend+(xend-xstart)/9,
                              (xend-xstart)/9))
            ticks = [list(zip(x, labels))]
            xax.setTicks(ticks)

        snr = "SNR@peak: N/A"
        if data["snr"] and np.isfinite(data["snr"]):
            snr = "SNR@peak: %.1fdB" % data["snr"]

        self.snr_text.setText(snr, color=(1, 1, 1))

        max_val = max(np.max(data["env_clutter"]+data["env_ampl"]), np.max(data["env_clutter"]))
        peak_line = np.flip((data["hist_plot"]-xstart)/(xend - xstart)*xdim, axis=0)

        self.envelope_plot.setData(data["x_mm"], data["env_ampl"] + data["env_clutter"])
        self.clutter_plot.setData(data["x_mm"], data["env_clutter"])

        ymax_level = min(1.5*np.max(np.max(data["hist_env"])), self.env_plot_max_y)

        self.hist_plot.updateImage(data["hist_env"].T, levels=(0, ymax_level))
        self.hist_plot_peak.setData(peak_line)
        self.hist_plot_peak.setZValue(2)

        if mode == "IQ":
            self.iq_plot.setData(data["x_mm"], data["phase"])

        if max_val > self.env_plot_max_y:
            self.env_plot_max_y = 1.2 * max_val
            update_ylims = True

        if update_ylims:
            self.envelope_plot_window.setYRange(0, self.env_plot_max_y)

        if self.sweep_buffer > data["sweep"]:
            self.hist_plot_image.setYRange(0, xdim)

    def update_power_plots(self, data):
        update_ylims = False
        xstart = data["x_mm"][0]
        xend = data["x_mm"][-1]
        update_ylims = False
        if not data["sweep"]:
            bin_num = int(self.textboxes["power_bins"].text())
            bin_width = (xend - xstart)/(bin_num + 1)
            self.env_plot_max_y = 0
            update_ylims = True
            self.power_plot_window.setXRange(xstart, xend)
            self.power_plot.setOpts(x=data["x_mm"], width=bin_width)
            self.power_plot_window.setXRange(xstart - bin_width / 2,
                                             xend + bin_width / 2)
        self.power_plot.setOpts(height=data["iq_data"])

        if max(data["iq_data"]) > self.env_plot_max_y:
            self.env_plot_max_y = 1.2 * max(data["iq_data"])
            update_ylims = True

        if update_ylims:
            self.power_plot_window.setYRange(0, self.env_plot_max_y)

    def update_external_plots(self, data):
        self.service_widget.update(data)

    def start_up(self):
        if os.path.isfile(self.last_file):
            try:
                last = np.load(self.last_file)
                self.update_settings(last.item()["sensor_config"], last.item())
            except Exception as e:
                print("Could not load settings from last session\n{}".format(e))

    def update_settings(self, sensor_config, last_config=None):
        try:
            self.textboxes["gain"].setText("{:.1f}".format(sensor_config.gain))
            self.textboxes["frequency"].setText(str(sensor_config.sweep_rate))
            self.textboxes["start_range"].setText("{:.2f}".format(sensor_config.range_interval[0]))
            self.textboxes["end_range"].setText("{:.2f}".format(sensor_config.range_interval[1]))
            self.textboxes["sweep_buffer"].setText(last_config["sweep_buffer"])
            self.textboxes["sensor"].setText("{:d}".format(sensor_config.sensor[0]))
            self.interface.setCurrentIndex(last_config["interface"])
            self.ports.setCurrentIndex(last_config["port"])
        except Exception as e:
            print("Warning, could not restore last session\n{}".format(e))

        if last_config:
            self.textboxes["host"].setText(last_config["host"])
            self.sweep_count = last_config["sweep_count"]

    def closeEvent(self, event=None):
        if "select" not in str(self.mode.currentText()).lower():
            last_config = {
                "sensor_config": self.update_sensor_config(),
                "sweep_count": self.sweep_count,
                "host": self.textboxes["host"].text(),
                "sweep_buffer": self.textboxes["sweep_buffer"].text(),
                "interface": self.interface.currentIndex(),
                "port": self.ports.currentIndex(),
                }

            np.save(self.last_file, last_config)

        try:
            self.client.disconnect()
        except Exception:
            pass

        self.close()
class Radar(threading.Thread):
    def __init__(self, q):
        # Setup radar
        self.args = example_utils.ExampleArgumentParser().parse_args()
        example_utils.config_logging(self.args)
        if self.args.socket_addr:
            self.client = JSONClient(self.args.socket_addr)
        else:
            port = self.args.serial_port or example_utils.autodetect_serial_port()
            self.client = RegClient(port)

        self.client.squeeze = False
        self.config = configs.IQServiceConfig()
        self.config.sensor = self.args.sensors
        self.config.range_interval = [0.2, 0.6]  # Intervall för mätningar
        self.config.sweep_rate = 1  # Frekvensen
        self.config.gain = 1  # Gain mellan 0 och 1, vi använder 1
        self.time = 10  # Hur lång mätningen ska vara
        # totalt antal sekvenser som krävs för att få en specifik tid
        self.seq = self.config.sweep_rate * self.time

        self.info = self.client.setup_session(self.config)
        self.num_points = self.info["data_length"]  # Antalet mätpunkter per sekvens
        # print(self.num_points)
        # Matris med nollor som fylls med rardardata
        self.matrix = np.zeros((self.seq, self.num_points), dtype=np.csingle)
        self.matrix_copy = np.zeros((self.seq, self.num_points),
                                    dtype=np.csingle)  # En copy på ovanstående
        self.temp_vector = np.zeros((0, self.num_points), dtype=np.csingle)
        self.matrix_idx = 0  # Index för påfyllning av matris
        super(Radar, self).__init__()
        # En First In First Out (FIFO) kö där mätdata sparas och kan hämtas ut för signalbehandling
        self.q = q

    def run(self):
        # Metod för att hämta data från radarn
        self.client.start_streaming()  # Startar streaming tjänsten (Som ytterligare en egen tråd?)
        for i in range(self.seq*2):
            self.get_data()  # Hämta data metoden
            self.tracker()
            self.filter_HeartRate()
            self.filter_RespRate()
            self.matrix_idx += 1
            if self.matrix_idx >= self.seq:
                self.matrix_idx = 0
        self.client.disconnect()

    def get_data(self):
        # Spara fil sker inte senare. Hämtar data från radarn och sparar det i en matris.

        # for i in range(0, self.seq):
        if self.matrix_idx < self.seq:  # När index är under totala sekvenser fylls matrisen och datan sparas i kön.
            self.info, self.data = self.client.get_next()
            self.temp_vector = np.abs(self.data)

            #self.matrix[self.matrix_idx][:] = self.data[:]
            #print("Seq number {}".format(self.matrix_idx))
            self.q.put(self.data)
            # print(self.q.get())
        #filename = "test.csv"
        #np.savetxt(filename, self.matrix)

    def filter_HeartRate(self):
        pass

    def filter_RespRate(self):
        pass

    def tracker(self):
        if self.matrix_idx == 0:
            self.amplitude = np.abs(self.data)
            self.peak = np.argmax(self.amplitude)
        else:

            # print(tracker_max)
            pass