Beispiel #1
0
    def beam_pattern_cartesian_multi_freq(self, save_fig=False):
        '''
        Plot Array Response including several Frequencies and Azimuth Angle infomation
        '''
        title = '{}_{}'.format('Beampattern_CartersianMultiFreq',
                               self.beam_label)
        if save_fig:
            fig_name = self._set_fig_name(title)
        else:
            fig_name = None

        #frequency to plot
        freq_range = constants.get('freq_range_small')
        angle_range = np.degrees(constants.get('angle_range'))
        y_lim_lower = []

        plot = Plot()
        plot.append_axes()
        for freq in freq_range:
            response_freq = self.beam_pattern(freq)
            y_lim_lower.append(np.min(response_freq))
            plot.plot_vector(response_freq,
                             angle_range,
                             label='{} kHz'.format(freq / 1000))

        y_lim_lower = np.maximum(-70, np.min(y_lim_lower))

        plot.set_pipeline(title=title,
                          ylim=(y_lim_lower, 1),
                          xlim=(0, 180),
                          legend='lower right',
                          xlabel='Azimuth Angle[Deg]',
                          ylabel='Beampattern[dB]',
                          fig_name=fig_name)
    def steering_vector_2D_from_point(self, frequency, source, attn=True, ff=False):
        """ Creates a steering vector for a particular frequency and source

        Args:
            frequency
            source: location in cartesian coordinates
            attn: include attenuation factor if True
            ff:   uses far-field distance if true

        Return:
            A 2x1 ndarray containing the steering vector
        """
        X = np.array(source)
        if X.ndim == 1:
            X = source[:,np.newaxis]

        # normalize for far-field if requested
        if (ff):
            X -= self.center
            Xn = np.sqrt(np.sum(X**2, axis=0))
            X *= constants.get('ffdist')/Xn
            X += self.center

        D = distance(self.R, X)
        omega = 2 * np.pi * frequency

        if attn:
            # TO DO 1: This will mean slightly different absolute value for
            # every entry, even within the same steering vector. Perhaps a
            # better paradigm is far-field with phase carrier.
            return 1. / (4 * np.pi) / D * np.exp(-1j * omega * D / constants.get('c'))
        else:
            return np.exp(-1j * omega * D / constants.get('c'))
Beispiel #3
0
    def beam_pattern_heatmap(self, save_fig=False):
        '''
        Plot heatmap of Array Response including all Frequency and Azimuth Angle infomation
        '''
        title = '{}_{}'.format('Beampattern_Heatmap', self.beam_label)
        if save_fig:
            fig_name = self._set_fig_name(title)
        else:
            fig_name = None

        freq = np.linspace(8000, 1, num=800, endpoint=True)
        response_matrix = np.zeros(
            (len(freq), len(constants.get('angle_range'))))

        for index, f in enumerate(freq):
            response_matrix[index:] = self.beam_pattern(f)

        #plot
        xticks = np.arange(0, len(constants.get('angle_range')), 60)
        yticks = np.arange(0, 900, 100)
        xticklabels = np.arange(
            0, len(constants.get('angle_range')), 60, dtype=int) / 2.0
        yticklabels = np.linspace(8, 0, num=9, endpoint=True, dtype=int)

        plot = Plot()
        plot.append_axes()
        plot.plot_heatmap(response_matrix)
        plot.set_pipeline(title=title,
                          xticks=xticks,
                          yticks=yticks,
                          xticklabels=[int(tick) for tick in xticklabels],
                          yticklabels=yticklabels,
                          xlabel='Azimuth Angle[Deg]',
                          ylabel='Freq[kHz]',
                          fig_name=fig_name)
Beispiel #4
0
    def heatmap_beam_pattern(self, save_fig=True):
        '''
        beampattern heatmap only available for frequency domain beamforming
        case : draw beampatten for heatmap. 
               There will be as many subplots as beamforming numbers.
        '''
        if self._beamforming_cout == 0:
            raise ValueError(
                'No beamforming to analysis! Please append one firstly!')

        beamforming_dict = self._beamforming_list[0]
        if beamforming_dict.get('beam_domain') == 'time':
            raise ValueError(
                'Time domain beamforming donnot support heatmap plot!')

        print('This may take a few seconds. Please wating...')
        figsize = (10, 5) if self._beamforming_cout == 1 else (
            10, 3 * self._beamforming_cout)
        plot = Plot(figsize=figsize, subplot_rows=self._beamforming_cout)
        plot.append_axes_combo()

        xticks = np.arange(0, len(constants.get('angle_range')), 60)
        yticks = np.arange(0, 900, 100)
        xticklabels = np.arange(
            0, len(constants.get('angle_range')), 60, dtype=int) / 2.0
        yticklabels = np.linspace(8, 0, num=9, endpoint=True, dtype=int)

        freq_range = np.linspace(8000, 1, num=800, endpoint=True)
        for beam_index, beam in enumerate(self._beamforming_list):

            response_matrix = np.zeros(
                (len(freq_range), len(constants.get('angle_range'))))
            for freq_index, freq in enumerate(freq_range):
                response_matrix[freq_index:] = beam[
                    'beam_instance'].beam_pattern(freq)

            plot.plot_heatmap(response_matrix,
                              label_pad=-65,
                              subplot_index=beam_index)
            plot.set_pipeline(title=beam['beam_instance'].beam_label,
                              xticks=xticks,
                              yticks=yticks,
                              xticklabels=[int(tick) for tick in xticklabels],
                              yticklabels=yticklabels,
                              xlabel='Azimuth Angle[Deg]',
                              ylabel='Freq[kHz]',
                              wait_for_user=False,
                              subplot_index=beam_index)

        if self._beamforming_cout > 1:
            plot.suptitle(
                'Beampattern Heatmap Compare for Different Beamforming',
                hspace=0.5)

        if save_fig:
            fig_name = self.fig_name('Beampattern_Heatmap')
            plot.save_figure(fig_name)

        plot.wait_for_user()
	def filter(self):
		g = self.steer_vector()

		theta_array_0_90   = np.array_split(constants.get('angle_range'), 2)[0]
		theta_array_90_180 = np.array_split(constants.get('angle_range'), 2)[1]
		gamma_0_half_pi  = self.diffuse_noise_coherence(theta_array_0_90)
		gamma_half_pi_pi = self.diffuse_noise_coherence(theta_array_90_180)

		[t, _]           = jeig(gamma_0_half_pi, gamma_half_pi_pi)
		t     = t[:, 0: self.Q]

		filts = t.dot(pinv(t.T.dot(g).dot(g.T).dot(t))).dot(t.T).dot(g).dot(self.i_ell)
		return filts 
Beispiel #6
0
    def front_back_ratio(self):
        '''
        return the front_back_ratio in TDB
        '''
        theta_array_0_90 = np.array_split(constants.get('angle_range'), 2)[0]
        theta_array_90_180 = np.array_split(constants.get('angle_range'), 2)[1]

        filts = self.filter
        gamma_0_half_pi = self.diffuse_noise_coherence(theta_array_0_90)
        gamma_half_pi_pi = self.diffuse_noise_coherence(theta_array_90_180)
        front_back_ratio = (filts.T.dot(gamma_0_half_pi).dot(filts)) / (
            filts.T.dot(gamma_half_pi_pi).dot(filts))
        return dB(front_back_ratio, True)
    def get_rir(self, mic, visibility, Fs, t0=0., t_max=None):
        '''
        Compute the room impulse response between the source
        and the microphone whose position is given as an
        argument.
        '''

        # fractional delay length
        fdl = constants.get('frac_delay_length')
        fdl2 = (fdl - 1) // 2

        # compute the distance
        dist = self.distance(mic)
        time = dist / constants.get('c') + t0
        alpha = self.damping / (4. * np.pi * dist)

        # the number of samples needed
        if t_max is None:
            # we give a little bit of time to the sinc to decay anyway
            N = np.ceil((1.05 * time.max() - t0) * Fs)
        else:
            N = np.ceil((t_max - t0) * Fs)

        N += fdl

        t = np.arange(N) / float(Fs)
        ir = np.zeros(t.shape)

        try:
            # Try to use the Cython extension
            from build_rir import fast_rir_builder
            fast_rir_builder(ir, time, alpha, visibility, Fs, fdl)

        except ImportError:
            print(
                "Cython-extension build_rir unavailable. Falling back to pure python"
            )
            # fallback to pure Python implemenation
            from utilities import fractional_delay

            for i in range(time.shape[0]):
                if visibility[i] == 1:
                    time_ip = int(np.round(Fs * time[i]))
                    time_fp = (Fs * time[i]) - time_ip
                    ir[time_ip - fdl2:time_ip + fdl2 +
                       1] += alpha[i] * fractional_delay(time_fp)

        return ir
Beispiel #8
0
    def __init__(self, d_mic, pattern='Cardioid'):
        if pattern not in ['Quadrupole', 'Cardioid', 'Hypercardioid', 'Supercardioid']:
            raise ValueError('No this pattern in SecondOrder Beamforming!')

        super(SecondOrder, self).__init__(d_mic, M=3, phi=0)
        self.null_directions = constants.get('second_order_null_angle')[pattern]
        self.beam_label = '{}{}'.format(pattern, self.beam_label)
    def steering_vector_2D(self, frequency, phi, dist, attn=False):

        phi = np.array([phi]).reshape(phi.size)

        # Assume phi and dist are measured from the array's center
        X = dist * np.array([np.cos(phi), np.sin(phi)]) + self.center

        D = distance(self.R, X)
        omega = 2 * np.pi * frequency

        if attn:
            # TO DO 1: This will mean slightly different absolute value for
            # every entry, even within the same steering vector. Perhaps a
            # better paradigm is far-field with phase carrier.
            return 1. / (4 * np.pi) / D * np.exp(-1j * omega * D / constants.get('c'))
        else:
            return np.exp(-1j * omega * D / constants.get('c'))
Beispiel #10
0
    def __init__(self, d_mic, pattern='Diople'):

        if pattern not in ['Diople', 'Cardioid', 'Hypercardioid', 'Supercardioid']:
            raise ValueError('No this pattern in FirstOrder Beamforming!')
        super(FirstOrder, self).__init__(d_mic, M=2, phi=0)
        self.null_directions = constants.get('first_order_null_angle')[pattern]

        self.beam_label = '{}{}'.format(pattern, self.beam_label)
Beispiel #11
0
    def __frequency_fixed_noise_improvement_performance(
            self, wng_or_df, save_fig=True):
        '''
        Only for frequency domain or 
        beamforming performace for directivity and white noise gain
        case : draw directivity or white noise gain in only one figure

        Parameter
        ---------
        wng_or_df: string
            White Noise Gain or Directivity
        '''
        freq_range = constants.get('freq_range_large')

        plot = Plot(figsize=(8, 6))
        plot.append_axes()
        axis_limit_max = []
        axis_limit_min = []
        for beam_index, beam in enumerate(self._beamforming_list):

            gain_array = np.zeros_like(freq_range, dtype=np.float32)
            for freq_index, freq in enumerate(freq_range):
                if wng_or_df == 'White Noise Gain':
                    gain_array[freq_index] = beam[
                        'beam_instance'].white_noise_gain(freq)
                elif wng_or_df == 'Directivity':
                    gain_array[freq_index] = beam['beam_instance'].directivity(
                        freq)
                else:
                    raise ValueError('Please check DF or WNG are allowed')

            axis_limit_max.append(np.max(gain_array))
            axis_limit_min.append(np.min(gain_array))
            plot.plot_vector(gain_array,
                             freq_range,
                             label=beam['beam_instance'].beam_label)

        if save_fig:
            fig_name = self.fig_name(wng_or_df)
        else:
            fig_name = None

        beamforming_dict = self._beamforming_list[0]
        title = '{wng_or_df} for {domain}Domain {dtype}Beamforming'.format(
            wng_or_df=wng_or_df,
            domain=(beamforming_dict.get('beam_domain')).capitalize(),
            dtype=(beamforming_dict.get('beam_type')).capitalize())

        plot.set_pipeline(title=title,
                          xlim=(0, 8000),
                          ylim=(np.min(axis_limit_min) - 1,
                                np.max(axis_limit_max) + 1),
                          xlabel='Freq[Hz]',
                          ylabel='dB',
                          legend='lower right',
                          fig_name=fig_name)
    def unit_delay(self, theta):
        '''
        computing the delay between two successive microphones

        Parameters
        ----------
        theta: float
            A given azimuth angle

        '''
        return self.d_mic / constants.get('c') * np.cos(theta)
def comparePlot(signal1, signal2, Fs, fft_size=512, norm=False, equal=False, title1=None, title2=None):

    import matplotlib.pyplot as plt

    td_amp = np.maximum(np.abs(signal1).max(), np.abs(signal2).max())

    if norm:
        if equal:
            signal1 /= np.abs(signal1).max()
            signal2 /= np.abs(signal2).max()
        else:
            signal1 /= td_amp
            signal2 /= td_amp
        td_amp = 1.

    plt.subplot(2,2,1)
    plt.plot(np.arange(len(signal1))/float(Fs), signal1)
    plt.axis('tight')
    plt.ylim(-td_amp, td_amp)
    if title1 is not None:
        plt.title(title1)

    plt.subplot(2,2,2)
    plt.plot(np.arange(len(signal2))/float(Fs), signal2)
    plt.axis('tight')
    plt.ylim(-td_amp, td_amp)
    if title2 is not None:
        plt.title(title2)

    import stft
    import windows

    eps = constants.get('eps')

    F1 = stft.stft(signal1, fft_size, fft_size / 2, win=windows.hann(fft_size))
    F2 = stft.stft(signal2, fft_size, fft_size / 2, win=windows.hann(fft_size))

    # try a fancy way to set the scale to avoid having the spectrum
    # dominated by a few outliers
    p_min = 1
    p_max = 99.5
    all_vals = np.concatenate((dB(F1+eps), dB(F2+eps))).flatten()
    vmin, vmax = np.percentile(all_vals, [p_min, p_max])

    cmap = 'jet'
    interpolation='sinc'

    plt.subplot(2,2,3)
    stft.spectroplot(F1.T, fft_size, fft_size / 2, Fs, vmin=vmin, vmax=vmax,
            cmap=plt.get_cmap(cmap), interpolation=interpolation)

    plt.subplot(2,2,4)
    stft.spectroplot(F2.T, fft_size, fft_size / 2, Fs, vmin=vmin, vmax=vmax, 
            cmap=plt.get_cmap(cmap), interpolation=interpolation)
Beispiel #14
0
    def beam_pattern(self):
        """
        绘制波束形成的波束图
        """
        filts = self.filter
        performace = []
        for theta in constants.get('angle_range'):
            g_tmp = self.steer_vector(theta)
            performace_theta = filts.T.dot(g_tmp).dot(g_tmp.T).dot(filts)
            performace.append(performace_theta)

        return dB(np.array(performace), True)
    def _set_fig_name(self, title):
        '''
        if save the fig, the directory and fig name should set

        Parameters
        ----------
        titile: string
            fig title name
        '''
        pic_path = os.path.join(constants.get('pic_path'), self.beam_type)
        make_directory_if_not_exists(pic_path)
        fig_name = os.path.join(pic_path, '{}.png'.format(title))
        return fig_name
    def response(self, phi_list, frequency):

        i_freq = np.argmin(np.abs(self.frequencies - frequency))

        if self.weights is None and self.filters is not None:
            self.weightsFromFilters()
        elif self.weights is None and self.filters is None:
            raise NameError('Beamforming weights or filters need to be computed first.')

        # For the moment assume that we are in 2D
        bfresp = np.dot(H(self.weights[:,i_freq]), self.steering_vector_2D(
            self.frequencies[i_freq], phi_list, constants.get('ffdist')))

        return self.frequencies[i_freq], bfresp
    def plot_beam_response(self):

        if self.weights is None and self.filters is not None:
            self.weightsFromFilters()
        elif self.weights is None and self.filters is None:
            raise NameError('Beamforming weights or filters need to be computed first.')

        phi = np.linspace(-np.pi, np.pi-np.pi/180, 360)
        freq = self.frequencies

        resp = np.zeros((freq.shape[0], phi.shape[0]), dtype=complex)

        for i,f in enumerate(freq):
            # For the moment assume that we are in 2D
            resp[i,:] = np.dot(H(self.weights[:,i]), self.steering_vector_2D(
                f, phi, constants.get('ffdist')))

        H_abs = np.abs(resp)**2
        H_abs /= H_abs.max()
        H_abs = 10*np.log10(H_abs)

        p_min = 0
        p_max = 100
        vmin, vmax = np.percentile(H_abs.flatten(), [p_min, p_max])

        import matplotlib.pyplot as plt

        plt.imshow(H_abs, 
                   aspect='auto', 
                   origin='lower', 
                   interpolation='sinc',
                   vmax=vmax, vmin=vmin)

        plt.xlabel('Angle [rad]')
        xticks = [-np.pi, -np.pi/2, 0, np.pi/2, np.pi]
        for i,p in enumerate(xticks):
            xticks[i] = np.argmin(np.abs(p - phi))
        xticklabels = ['$-\pi$', '$-\pi/2$', '0', '$\pi/2$', '$\pi$']
        plt.setp(plt.gca(), 'xticks', xticks)
        plt.setp(plt.gca(), 'xticklabels', xticklabels)

        plt.ylabel('Freq [kHz]')
        yticks = np.zeros(4)
        f_0 = np.floor(self.Fs/8000.)
        for i in np.arange(1,5):
            yticks[i-1] = np.argmin(np.abs(freq - 1000.*i*f_0))
        #yticks = np.array(plt.getp(plt.gca(), 'yticks'), dtype=np.int)
        plt.setp(plt.gca(), 'yticks', yticks)
        plt.setp(plt.gca(), 'yticklabels', np.arange(1,5)*f_0)
Beispiel #18
0
    def beam_pattern_polar(self, f, save_fig=False):

        title = '{}_{}_f_{}'.format('Beampattern_Polar', self.beam_label, f)
        if save_fig:
            fig_name = self._set_fig_name(title)
        else:
            fig_name = None

        response = self.beam_pattern(f)
        #expand to pi to 2*pi, when plot polar

        sweep_angle = np.concatenate((constants.get('angle_range'),
                                      (constants.get('angle_range') + np.pi)),
                                     axis=0)
        response = np.concatenate((response, np.fliplr([response])[0]), axis=0)

        plot = Plot()
        plot.append_axes(polar=True)
        plot.plot_vector(response, sweep_angle)
        plot.set_pipeline(xlim=(0, 2 * np.pi),
                          ylim=(-50, np.max(response) + 1),
                          title=title,
                          xlabel='Azimuth Angle[Deg]',
                          fig_name=fig_name)
    def farFieldWeights(self, phi):
        '''
        This method computes weight for a far field at infinity
        
        phi: direction of beam
        '''

        u = unit_vec2D(phi)
        proj = np.dot(u.T, self.R - self.center)[0]

        # normalize the first arriving signal to ensure a causal filter
        proj -= proj.max()

        self.weights = np.exp(2j * np.pi * 
        self.frequencies[:, np.newaxis] * proj / constants.get('c')).T
Beispiel #20
0
    def beam_pattern_cartesian(self, save_fig=False):

        title = '{}_{}'.format('Beampattern_Cartesian', self.beam_label)
        if save_fig:
            fig_name = self._set_fig_name(title)
        else:
            fig_name = None

        response = self.beam_pattern()

        plot = Plot()
        plot.append_axes()
        plot.plot_vector(response, np.degrees(constants.get('angle_range')))
        plot.set_pipeline(xlim=(0, 180),
                          ylim=(np.min(response) - 1, np.max(response) + 1),
                          title=title,
                          xlabel='Azimuth Angle[Deg]',
                          ylabel='Beampattern[dB]',
                          fig_name=fig_name)
Beispiel #21
0
    def fig_name(self, fig_type):
        '''
        fig name : fig_type combine all the beamformings' label

        Parameter
        ---------
        fig_type: string
            the fig type, such as beampattern polar or cartesian
        '''
        title = ''
        # combine all beamforming instance label together
        for beam in self._beamforming_list:
            title = '{}_{}'.format(title, beam['beam_instance'].beam_label)
        title = '{}{}'.format(fig_type, title)

        pic_path = os.path.join(constants.get('pic_path'),
                                'beamforming_compare')
        make_directory_if_not_exists(pic_path)

        return os.path.join(pic_path, '{}.png'.format(title))
Beispiel #22
0
    def _array_gain_freq_response(self, wng_or_df, plot=True, save_fig=False):
        '''
        plot array gain agaist freq

        Parameters
        ----------
        wng_or_df: string
            Directivity: draw directivity agaist frequency
            White_Noise_Gain: draw white_noise_gain agaist frequency
        '''
        freq_range = constants.get('freq_range_large')
        array_gain = np.zeros_like(freq_range, dtype=np.float64)

        for freq_index, freq in enumerate(freq_range):
            if wng_or_df == 'White_Noise_Gain':
                array_gain[freq_index] = self.white_noise_gain(freq)
            elif wng_or_df == 'Directivity':
                array_gain[freq_index] = self.directivity(freq)

        if plot:
            title = '{}_{}'.format(wng_or_df, self.beam_label)
            if save_fig:
                fig_name = self._set_fig_name(title)
            else:
                fig_name = None
            # title    = '{}_{}'.format(wng_or_df, self.title_tmp)
            # fig_name = os.path.join(self.pic_path ,'{}.png'.format(title))
            plot = Plot()
            plot.append_axes()
            plot.plot_vector(array_gain, freq_range)
            plot.set_pipeline(
                title=title,
                xlabel='Freq[Hz]',
                ylabel=wng_or_df,
                xlim=[0, np.max(freq_range)],
                ylim=[np.min(array_gain) - 5,
                      np.max(array_gain) + 5],
                fig_name=fig_name)
            return None

        return array_gain
Beispiel #23
0
    def diffuse_noise_coherence(self, f, alpha=None):
        '''
        Compute the spherically isotropic(diffuse) noise field coherence matrix
        
        Parameters
        ----------
        f: float
            frequency

        alpha: float 
            diffuse factor, if the value is zero it reprensents an full isotropic duffuse sound
        '''

        [m_mat, n_mat] = np.meshgrid(np.arange(self.M), np.arange(self.M))
        mat = 2 * f * (n_mat - m_mat) * (self.d_mic / constants.get('c'))
        noise_coherence = np.sinc(mat)

        if alpha:
            noise_coherence = (
                1 - alpha) * noise_coherence + alpha * np.identity(self.M)

        return noise_coherence
def highpass(signal, Fs, fc=None, plot=False):
    '''
    Filter out the really low frequencies, default is below 50Hz
    '''

    if fc is None:
        fc = constants.get('fc_hp')

    # have some predefined parameters
    rp = 5  # minimum ripple in dB in pass-band
    rs = 60   # minimum attenuation in dB in stop-band
    n = 4    # order of the filter
    type = 'butter'

    # normalized cut-off frequency
    wc = 2. * fc / Fs

    # design the filter
    from scipy.signal import iirfilter, lfilter, freqz
    b, a = iirfilter(n, Wn=wc, rp=rp, rs=rs, btype='highpass', ftype=type)

    # plot frequency response of filter if requested
    if (plot):
        import matplotlib.pyplot as plt
        w, h = freqz(b, a)

        plt.figure()
        plt.title('Digital filter frequency response')
        plt.plot(w, 20 * np.log10(np.abs(h)))
        plt.title('Digital filter frequency response')
        plt.ylabel('Amplitude Response [dB]')
        plt.xlabel('Frequency (rad/sample)')
        plt.grid()

    # apply the filter
    signal = lfilter(b, a, signal.copy())

    return signal
Beispiel #25
0
    def diffuse_noise_coherence(self, theta_list=constants.get('angle_range')):
        '''
        The equivalent form of diffuse noise coherence in the time domain

        Parameter
        ---------
        theta_list : array
        default, 0-pi
        '''
        g_phi = []
        for theta in theta_list:
            g = self.steer_vector(theta)
            x = g.dot(g.T)
            g_phi.append(g.dot(g.T))

        g_phi = np.array(g_phi)
        int_g = np.zeros((g_phi.shape[1], g_phi.shape[2]))
        d_theta = theta_list[1] - theta_list[0]
        for id1 in np.arange(g_phi.shape[1]):
            for id2 in np.arange(g_phi.shape[2]):
                int_g[id1, id2] = np.sum(g_phi[:, id1, id2] *
                                         np.sin(theta_list) * d_theta)

        return int_g / 2
Beispiel #26
0
    def steer_vector(self, f, theta=None):
        '''
        Computing steer vector[导向矢量]
        
        Parameters
        ----------
        f: float 
          frequency for computing

        theta : angle list or array
        
        return: steervector when theta is list or array; single exp function when theta is a single value 
        '''
        if theta is None:
            theta = constants.get('angle_range')
            # theta  = self.phi

        if isinstance(theta, list):
            theta = np.asarray(theta)

        delay = np.outer(self.unit_delay(theta), np.arange(self.M))
        steer_vector = np.exp(-2j * np.pi * f * delay)

        return steer_vector.T
Beispiel #27
0
    def beam_pattern(self, polar, save_fig):
        '''
        plot either beamforming or beamforming group's beampattern
        Case1 -- if the beamforming is a time domain beamforming, either ploar or catersian
                all the beam instance are plotted in the same figure, because it's frequency invariant.
        
        Case2-- if the beamforming is a frequency domain beamforming, then if polar, it will be plotted in 4 figures, 
                every figure represents a single frequency.
                if catesian, the figure number is equal to the beamforming instance number. One figure represents 
                a beamforming, the different frequency plotted in this same figure.
        
        Parameters
        ----------
        polar: bool True or False
        save_fig: bool True of False
        '''
        if self._beamforming_cout == 0:
            raise ValueError(
                'No beamforming to analysis! Please append one firstly!')

        beamforming_dict = self._beamforming_list[0]
        coordinate = 'Polar' if polar else 'Cartesian'
        title = '{coordinate} Beampattern for {domain}Domain {dtype}Beamforming'.format(
            coordinate=coordinate,
            domain=(beamforming_dict.get('beam_domain')).capitalize(),
            dtype=(beamforming_dict.get('beam_type')).capitalize())

        if polar:
            sweep_angle = np.concatenate(
                (constants.get('angle_range'),
                 (constants.get('angle_range') + np.pi)),
                axis=0)
            xlim = (0, 2 * np.pi)
        else:
            sweep_angle = np.degrees(constants.get('angle_range'))
            xlim = (0, 180)

        if beamforming_dict.get('beam_domain') == 'time':
            plot = Plot(figsize=(6, 6)) if polar else Plot(figsize=(8, 5))
            plot.append_axes_combo(polar=polar)

            axis_limit_max = 0
            axis_limit_min = 0

            for beam_index, beam in enumerate(self._beamforming_list):
                response = beam['beam_instance'].beam_pattern()
                axis_limit_max = np.maximum(axis_limit_max, np.max(response))
                axis_limit_min = np.minimum(axis_limit_min, np.min(response))
                if polar:
                    response = np.concatenate(
                        (response, np.fliplr([response])[0]), axis=0)
                plot.plot_vector(response,
                                 sweep_angle,
                                 label=beam['beam_instance'].beam_label)
            plot.set_pipeline(xlim=xlim,
                              ylim=(axis_limit_min - 1, axis_limit_max + 1),
                              title=title,
                              xlabel='Azimuth Angle[Deg]',
                              wait_for_user=False)
            plot.set_legend(loc=(0.7, 0.01), fontsize=6)

        elif beamforming_dict.get('beam_domain') == 'frequency':
            if polar:
                mini_freq_range = constants.get('freq_range_mini')
                plot = Plot(figsize=(8, 8), subplot_cols=2, subplot_rows=2)
                plot.append_axes_combo(polar=True)

                # axis_limit_max = 0
                # axis_limit_min = 0
                for beam_index, beam in enumerate(self._beamforming_list):
                    for freq_index, freq in enumerate(mini_freq_range):
                        # get response
                        response = beam['beam_instance'].beam_pattern(freq)
                        # axis_limit_max = np.maximum(axis_limit_max, np.max(response))
                        # axis_limit_min = np.minimum(axis_limit_min, np.min(response))
                        response = np.concatenate(
                            (response, np.fliplr([response])[0]), axis=0)
                        # plot
                        plot.plot_vector(
                            response,
                            sweep_angle,
                            label=beam['beam_instance'].beam_label,
                            subplot_index=freq_index)
                        plot.set_pipeline(
                            xlim=(0, 2 * np.pi),
                            ylim=(-50, 1),
                            title='',
                            xlabel='Azimuth Angle[Deg]_{}Hz'.format(freq),
                            wait_for_user=False,
                            subplot_index=freq_index)
                plot.set_legend(loc=(0.5, 0.01), fontsize=6, subplot_index=3)
                plot.suptitle(title)

            else:
                plot = Plot(figsize=(8, 4 * self._beamforming_cout),
                            subplot_rows=self._beamforming_cout)
                plot.append_axes_combo()
                axis_limit_max = 0
                axis_limit_min = 0
                for beam_index, beam in enumerate(self._beamforming_list):
                    for freq in constants.get('freq_range_small'):
                        response = beam['beam_instance'].beam_pattern(freq)
                        axis_limit_max = np.maximum(axis_limit_max,
                                                    np.max(response))
                        axis_limit_min = np.minimum(axis_limit_min,
                                                    np.min(response))
                        plot.plot_vector(response,
                                         np.degrees(
                                             constants.get('angle_range')),
                                         label='{} kHz'.format(freq / 1000),
                                         subplot_index=beam_index)
                        plot.set_pipeline(
                            title=beam['beam_instance'].beam_label,
                            xlim=xlim,
                            ylim=(axis_limit_min - 1, axis_limit_max + 1),
                            xlabel='Azimuth Angle[Deg]',
                            ylabel='Beampatten[dB]',
                            wait_for_user=False,
                            subplot_index=beam_index)
                plot.set_legend(subplot_index=self._beamforming_cout - 1)

                if self._beamforming_cout > 1:
                    plot.suptitle(title, 0.5)

        if save_fig:
            fig_name = self.fig_name(
                'Beampattern_{coordinate}'.format(coordinate=coordinate))
            plot.save_figure(fig_name)

        plot.wait_for_user()
    def rakeOneForcingFilters(self, sources, interferers, R_n, epsilon=5e-3):
        '''
        Compute the time-domain filters of a beamformer with unit response
        towards multiple sources.
        '''

        dist_mat = distance(self.R, sources.images)
        s_time = dist_mat / constants.get('c')
        s_dmp = 1./(4*np.pi*dist_mat)

        dist_mat = distance(self.R, interferers.images)
        i_time = dist_mat / constants.get('c')
        i_dmp = 1./(4*np.pi*dist_mat)

        # compute offset needed for decay of sinc by epsilon
        offset = np.maximum(s_dmp.max(), i_dmp.max())/(np.pi*self.Fs*epsilon)
        t_min = np.minimum(s_time.min(), i_time.min())
        t_max = np.maximum(s_time.max(), i_time.max())

        # adjust timing
        s_time -= t_min - offset
        i_time -= t_min - offset
        Lh = np.ceil((t_max - t_min + 2*offset)*float(self.Fs))

        # the channel matrix
        K = sources.images.shape[1]
        Lg = self.Lg
        off = (Lg - Lh)/2
        L = self.Lg + Lh - 1

        H = np.zeros((Lg*self.M, 2*L))
        As = np.zeros((Lg*self.M, K))

        for r in np.arange(self.M):

            # build constraint matrix
            hs = u.lowPassDirac(s_time[r,:,np.newaxis], s_dmp[r,:,np.newaxis], self.Fs, Lh)[:,::-1]
            As[r*Lg+off:r*Lg+Lh+off,:] = hs.T

            # build interferer RIR matrix
            hx = u.lowPassDirac(s_time[r,:,np.newaxis], s_dmp[r,:,np.newaxis], self.Fs, Lh).sum(axis=0)
            H[r*Lg:(r+1)*Lg,:L] = u.convmtx(hx, Lg).T

            # build interferer RIR matrix
            hq = u.lowPassDirac(i_time[r,:,np.newaxis], i_dmp[r,:,np.newaxis], self.Fs, Lh).sum(axis=0)
            H[r*Lg:(r+1)*Lg,L:] = u.convmtx(hq, Lg).T

        ones = np.ones((K,1))

        # We first assume the sample are uncorrelated
        K_x = np.dot(H[:,:L], H[:,:L].T)
        K_nq = np.dot(H[:,L:], H[:,L:].T) + R_n

        # Compute the TD filters
        K_nq_inv = np.linalg.inv(K_x+K_nq)
        C = np.dot(K_nq_inv, As)
        B = np.linalg.inv(np.dot(As.T, C))
        g_val = np.dot(C, np.dot(B, ones))
        self.filters = g_val.reshape((self.M,Lg))

        # compute and return SNR
        A = np.dot(g_val.T, H[:,:L])
        num = np.dot(A, A.T)
        denom =  np.dot(np.dot(g_val.T, K_nq), g_val)

        return num/denom
Beispiel #29
0
 def microphone_array(self, *, d_mic, M):
     phi = self.sound_field._desired_signal_dict['phi']
     self.sound_field._uniform_array = FrequencyDomainULABase(d_mic, M, phi)
     self.sound_field.Ts = self.sound_field._uniform_array.d_mic / constants.get('c')
def build_rir_matrix(mics, sources, Lg, Fs, epsilon=5e-3, unit_damping=False):
    '''
    A function to build the channel matrix for many sources and microphones

    Parameters
    ----------
    mics: ndarray
        a dim-by-M ndarray where each column is the position of a microphone
    sources: list of pyroomacoustics.SoundSource
        list of sound sources for which we want to build the matrix
    Lg: int
        the length of the beamforming filters
    Fs: int
        the sampling frequency
    epsilon: float, optional
        minimum decay of the sinc before truncation. Defaults to epsilon=5e-3
    unit_damping: bool, optional
        determines if the wall damping parameters are used or not. Default to false.

    Returns
    -------
    the function returns the RIR matrix H =

    ::

         --------------------
         | H_{11} H_{12} ...
         | ...
         |
         --------------------

    where H_{ij} is channel matrix between microphone i and source j.
    H is of type (M*Lg)x((Lg+Lh-1)*S) where Lh is the channel length (determined by epsilon),
    and M, S are the number of microphones, sources, respectively.
    '''

    from .beamforming import distance
    from .utilities import low_pass_dirac, convmtx

    # set the boundaries of RIR filter for given epsilon
    d_min = np.inf
    d_max = 0.
    dmp_max = 0.
    for s in range(len(sources)):
        dist_mat = distance(mics, sources[s].images)
        if unit_damping is True:
            dmp_max = np.maximum((1. / (4 * np.pi * dist_mat)).max(), dmp_max)
        else:
            dmp_max = np.maximum((sources[s].damping[np.newaxis, :] /
                                  (4 * np.pi * dist_mat)).max(), dmp_max)
        d_min = np.minimum(dist_mat.min(), d_min)
        d_max = np.maximum(dist_mat.max(), d_max)

    t_max = d_max / constants.get('c')
    t_min = d_min / constants.get('c')

    offset = dmp_max / (np.pi * Fs * epsilon)

    # RIR length
    Lh = int((t_max - t_min + 2 * offset) * float(Fs))

    # build the channel matrix
    L = Lg + Lh - 1
    H = np.zeros((Lg * mics.shape[1], len(sources) * L))

    for s in range(len(sources)):
        for r in np.arange(mics.shape[1]):

            dist = sources[s].distance(mics[:, r])
            time = dist / constants.get('c') - t_min + offset
            if unit_damping == True:
                dmp = 1. / (4 * np.pi * dist)
            else:
                dmp = sources[s].damping / (4 * np.pi * dist)

            h = low_pass_dirac(time[:, np.newaxis], dmp[:, np.newaxis], Fs,
                               Lh).sum(axis=0)
            H[r * Lg:(r + 1) * Lg, s * L:(s + 1) * L] = convmtx(h, Lg).T

    return H