def estimate_DOA(self):
        #print("[ INFO ] Python DSP: Estimating DOA")

        iq_samples = self.module_receiver.iq_samples[:, 0:self.DOA_sample_size]
        # Calculating spatial correlation matrix
        R = de.corr_matrix_estimate(iq_samples.T, imp="fast")

        if self.en_DOA_FB_avg:
            R = de.forward_backward_avg(R)

        M = np.size(iq_samples, 0)

        if self.DOA_ant_alignment == "UCA":
            self.DOA_theta = np.linspace(0, 360, 361)
            #scanning_vectors = de.gen_uca_scanning_vectors(M, self.DOA_inter_elem_space, self.DOA_theta)
            x = self.DOA_inter_elem_space * np.cos(
                2 * np.pi / M * np.arange(M))
            y = self.DOA_inter_elem_space * np.sin(
                -2 * np.pi / M * np.arange(M))  # For this specific array only
            scanning_vectors = de.gen_scanning_vectors(M, x, y, self.DOA_theta)

            # DOA estimation
            if self.en_DOA_Bartlett:
                self.DOA_Bartlett_res = de.DOA_Bartlett(R, scanning_vectors)
            if self.en_DOA_Capon:
                self.DOA_Capon_res = de.DOA_Capon(R, scanning_vectors)
            if self.en_DOA_MEM:
                self.DOA_MEM_res = de.DOA_MEM(R,
                                              scanning_vectors,
                                              column_select=0)
            if self.en_DOA_MUSIC:
                self.DOA_MUSIC_res = de.DOA_MUSIC(R,
                                                  scanning_vectors,
                                                  signal_dimension=1)

        elif self.DOA_ant_alignment == "ULA":
            self.DOA_theta = np.linspace(-90, 90, 181)
            x = np.zeros(M)
            y = np.arange(M) * self.DOA_inter_elem_space
            scanning_vectors = de.gen_scanning_vectors(M, x, y, self.DOA_theta)

            # DOA estimation
            if self.en_DOA_Bartlett:
                self.DOA_Bartlett_res = de.DOA_Bartlett(R, scanning_vectors)
            if self.en_DOA_Capon:
                self.DOA_Capon_res = de.DOA_Capon(R, scanning_vectors)
            if self.en_DOA_MEM:
                self.DOA_MEM_res = de.DOA_MEM(R,
                                              scanning_vectors,
                                              column_select=0)
            if self.en_DOA_MUSIC:
                self.DOA_MUSIC_res = de.DOA_MUSIC(R,
                                                  scanning_vectors,
                                                  signal_dimension=1)

        print(self.DOA_MUSIC_res)
    def DOA_demo(self):
        self.logger.debug("-> Running simulation <-")
        
        soi_theta = self.horizontalSlider_source_DOA.value()
        
        M = self.spinBox_noa.value() # Number of antenna elements
        N = 2**self.spinBox_sample_size.value() 
        r = self.doubleSpinBox_UCA_r.value()
        d = self.doubleSpinBox_ULA_d.value()
        K = 1 + self.spinBox_multipath_components.value()
        alphas = [1.0]
        thetas = [soi_theta]
        phases = [0]
        try:
            multipath_alphas_str= self.lineEdit_multipath_amplitudes.text().split(',')
            if self.checkBox_multipath_random_angles.isChecked():
                multipath_angles_str = ""
                for k in range(K-1):
                    theta_rnd=np.random.uniform(0,360)
                    multipath_angles_str += "{:3.1f},".format(theta_rnd)

            if self.checkBox_multipath_random_phases.isChecked():                    
                multipath_phases_str = ""
                for k in range(K-1):
                    phase_rnd=np.random.uniform(0,360)
                    multipath_phases_str += "{:3.1f},".format(phase_rnd)

                self.lineEdit_multipath_phases.setText(multipath_phases_str[:len(multipath_phases_str)-1])
            
            multipath_angles_str= self.lineEdit_multipath_angles.text().split(',')
            multipath_phases_str= self.lineEdit_multipath_phases.text().split(',')
            
            # Add multipath parameters
            for k in range(K-1):
                alphas.append(float(multipath_alphas_str[k]))
                thetas.append(float(multipath_angles_str[k]))
                phases.append(float(multipath_phases_str[k]))
                logging.debug("k: {:d}, alpha:{:f} phi:{:f} theta:{:f}".format(k,alphas[k+1],phases[k+1], thetas[k+1]))
            self.label_status.setText("<span style=\" font-size:8pt; font-weight:600; color:#01df01;\" >Running simulation</span>")
        except:
            K=1
            alphas = [1.0]
            phases = [0]
            thetas = [soi_theta]            
            self.label_status.setText("<span style=\" font-size:8pt; font-weight:600; color:#ff0000;\" >Improper multipath parameters</span>")

        alphas = 10**(np.array(alphas)/10) * np.exp(1j*np.deg2rad(np.array(phases)))
        
        noise_pow = 10**(-1*self.spinBox_snr_dB.value()/10)
        
        # Generate the signal of interest        
        soi = np.random.normal(0,1,N) +1j* np.random.normal(0,1,N)
        
        # Generate multichannel uncorrelated noise
        noise = np.random.normal(0, np.sqrt(noise_pow), (M,N) ) +1j* np.random.normal(0, np.sqrt(noise_pow), (M,N) )
          
        """ SNR debug display  
        pn = np.average(np.abs(noise**2))
        ps = np.average(np.abs(soi**2))
        logging.info("SNR: {:.2f}".format(10*np.log10(ps/pn)))
        """
        
        self.axes_DOA.clear()
        legend=[]
            
        if self.checkBox_en_UCA.checkState():
            #---------------- U C A-------------------
            
            A = np.zeros((M, K), dtype=complex)
            
            for k in range(K):
                A[:,k] = np.exp(1j*2*np.pi*r*np.cos(np.radians(thetas[k]-np.arange(0,M,1)*(360)/M))) # UCA
            
            soi_matrix  = (np.outer( soi, np.inner(A, alphas))).T                 
            
            # Create received signal
            rec_signal = soi_matrix + noise
            
            # Calulcate cross-correlation matrix
            R = de.corr_matrix_estimate(rec_signal.T, imp="fast")
            
            #R = forward_backward_avg(R)
            
            # Generate array alignment vector            
            array_alignment = np.arange(0, M, 1) * d
            scanning_vectors = de.gen_uca_scanning_vectors(M, r, self.thetas)
            
            
            # DOA estimation
            alias_highlight = False # Track thaht aliase regions are already shown
            if self.checkBox_en_Bartlett.checkState():
                Bartlett = de.DOA_Bartlett(R, scanning_vectors)  
                de.DOA_plot(Bartlett, self.thetas, log_scale_min = -50, axes=self.axes_DOA)
                legend.append("UCA - Bartlett")        
                self.label_Bartlett_UCA_res.setText("{:.1f}".format(np.argmax(Bartlett)))
            else:
                self.label_Bartlett_UCA_res.setText("-")
            
            if self.checkBox_en_Capon.checkState():
                Capon = de.DOA_Capon(R, scanning_vectors)
                de.DOA_plot(Capon, self.thetas, log_scale_min = -50, axes=self.axes_DOA)
                legend.append("UCA - Capon")
                self.label_Capon_UCA_res.setText("{:.1f}".format(np.argmax(Capon)))
            else:
                self.label_Capon_UCA_res.setText("-")
    
            if self.checkBox_en_MEM.checkState():
                MEM = de.DOA_MEM(R, scanning_vectors,  column_select = 0)
                de.DOA_plot(MEM, self.thetas, log_scale_min = -50, axes=self.axes_DOA)
                legend.append("MEM")
                self.label_MEM_UCA_res.setText("{:.1f}".format(np.argmax(MEM)))
            else:
                self.label_MEM_UCA_res.setText("-")
    
            if self.checkBox_en_MUSIC.checkState():
                MUSIC = de.DOA_MUSIC(R, scanning_vectors, signal_dimension = 1)
                de.DOA_plot(MUSIC, self.thetas, log_scale_min = -50, axes=self.axes_DOA)
                legend.append("MUSIC")
                self.label_MUSIC_UCA_res.setText("{:.1f}".format(np.argmax(MUSIC)))
            else:
                self.label_MUSIC_UCA_res.setText("-")

        
        if self.checkBox_en_ULA.checkState():
            #---------------- U L A-------------------            
            # Prepare Array-response matrix
            A = np.zeros((M, K), dtype=complex)
            
            for k in range(K):
                A[:,k] = np.exp(np.arange(0,M,1)*1j*2*np.pi*d*np.cos(np.deg2rad(thetas[k])))                
            
            soi_matrix  = (np.outer( soi, np.inner(A, alphas))).T                 
            
            # Create received signal
            rec_signal = soi_matrix + noise
            
            ## R matrix calculation
            R = de.corr_matrix_estimate(rec_signal.T, imp="fast")
            
            if self.checkBox_en_FBavg.isChecked():
                R = de.forward_backward_avg(R)
            
            # Generate array alignment vector            
            array_alignment = np.arange(0, M, 1) * d
            scanning_vectors = de.gen_ula_scanning_vectors(array_alignment, self.thetas)
                        
            # DOA estimation
            alias_highlight = True # Track thaht aliase regions are already shown
            if self.checkBox_en_Bartlett.checkState():
                Bartlett = de.DOA_Bartlett(R, scanning_vectors)    
                de.DOA_plot(Bartlett, self.thetas, log_scale_min = -50, axes=self.axes_DOA, alias_highlight=alias_highlight, d=d)                
                legend.append("ULA - Bartlett")
                alias_highlight = False
                self.label_Bartlett_ULA_res.setText("{:.1f}".format(np.argmax(Bartlett[0:180])))
            else:
                self.label_Bartlett_ULA_res.setText("-")
            
            if self.checkBox_en_Capon.checkState():
                Capon  = de.DOA_Capon(R, scanning_vectors)
                de.DOA_plot(Capon, self.thetas, log_scale_min = -50, axes=self.axes_DOA, alias_highlight=alias_highlight, d=d)
                legend.append("ULA - Capon")
                alias_highlight = False
                self.label_Capon_ULA_res.setText("{:.1f}".format(np.argmax(Capon[0:180])))
            else:
                self.label_Capon_ULA_res.setText("-")

    
            if self.checkBox_en_MEM.checkState():
                MEM = de.DOA_MEM(R, scanning_vectors,  column_select = 0)
                de.DOA_plot(MEM, self.thetas, log_scale_min = -50, axes=self.axes_DOA, alias_highlight=alias_highlight, d=d)
                legend.append("ULA - MEM")
                alias_highlight = False
                self.label_MEM_ULA_res.setText("{:.1f}".format(np.argmax(MEM[0:180])))
            else:
                self.label_MEM_ULA_res.setText("-")
    
            if self.checkBox_en_MUSIC.checkState():
                MUSIC = de.DOA_MUSIC(R, scanning_vectors, signal_dimension = 1)
                de.DOA_plot(MUSIC, self.thetas, log_scale_min = -50, axes=self.axes_DOA, alias_highlight=alias_highlight, d=d)
                legend.append("ULA - MUSIC")
                alias_highlight = False
                self.label_MUSIC_ULA_res.setText("{:.1f}".format(np.argmax(MUSIC[0:180])))
            else:
                self.label_MUSIC_ULA_res.setText("-")

            
        self.axes_DOA.legend(legend)        
        self.canvas_DOA.draw()