Exemplo n.º 1
0
    def display_radMUSIC(self,
                         frequency,
                         npoints=360,
                         signals=1,
                         shw=True,
                         block_run=False):
        """Display a polar plot of estimated DOA using the MUSIC algorithm

        Arguments:
            frequency (float): The frequency to use for the MUSIC calculation.
            npoints (int): The total number of points around the circle at which to evaluate.
            signals (int): The numbers of signals to locate.
            shw (bool): Show the plot? If False, will return the data that was to be plotted.
            block_run (bool): Pause execution of the file while the figure is open? Set to True for running in the command-line.
        """

        X = 2 * np.pi * np.arange(start=0, stop=npoints) / npoints
        self.dataFFT = fft_pack.rfft(self.data,
                                     axis=0,
                                     n=2 * self.data.shape[0])
        pos = fft_pack.rfftfreq(2 * self.data.shape[0]) * self.sample_rate
        actidx = np.argmin(abs(pos - frequency))
        cors, _ = self._MUSIC1D_((pos[actidx], actidx), X, numsignals=signals)

        if shw:
            plt.figure()
            ax = plt.subplot(111, projection='polar')
            ax.plot(X, cors)
            ax.set_title("Estimated Acoustic Source Direction")
            plt.show(block=block_run)
        else:
            return cors
Exemplo n.º 2
0
def _shift(trace, shift):
    """Shift trace by given time and correct starttime => interpolation"""
    msg = ('interpolate trace %s with starttime %s to shift by %.6fs '
           '(Fourier method)')
    log.debug(msg, trace.id, trace.stats.starttime, shift)
    nfft = next_fast_len(len(trace))
    spec = rfft(trace.data, nfft)
    freq = rfftfreq(nfft, trace.stats.delta)
    spec *= np.exp(-2j * np.pi * freq * shift)
    trace.data = irfft(spec, nfft)[:len(trace)]
    trace.stats.starttime -= shift
    return trace
Exemplo n.º 3
0
    def display_radMUSICchunked(self,
                                frequency,
                                npoints=360,
                                signals=1,
                                shw=True,
                                block_run=False,
                                chunks=10):
        """Display a polar plot of estimated DOA using the MUSIC algorithm

        Arguments:
            frequency (float): The frequency to use for the MUSIC calculation.
            npoints (int): The total number of points around the circle at which to evaluate.
            signals (int): The numbers of signals to locate.
            shw (bool): Show the plot? If False, will return the data that was to be plotted.
            block_run (bool): Pause execution of the file while the figure is open? Set to True for running in the command-line.
        """

        X = 2 * np.pi * np.arange(start=0, stop=npoints) / npoints

        tRxx = np.zeros((self.mics.shape[0], self.mics.shape[0]),
                        dtype='complex128')
        indices = [
            int(x) for x in np.linspace(
                0, self.data.shape[0], num=chunks + 1, endpoint=True)
        ]
        for mark in np.arange(len(indices) - 1):
            dcr = self.data[indices[mark]:indices[mark + 1], :]
            print(dcr.shape)
            ft = fft_pack.rfft(dcr, axis=0, n=2 * self.data.shape[0])
            pos = fft_pack.rfftfreq(2 * self.data.shape[0]) * self.sample_rate
            actidx = np.argmin(abs(pos - frequency))
            tRxx += np.dot(ft[actidx:actidx + 1].T,
                           np.conj(ft[actidx:actidx + 1]))
        tRxx /= chunks
        print(tRxx)

        # self.dataFFT = fft_pack.rfft(self.data, axis=0, n=2*self.data.shape[0])

        cors, _ = self._MUSIC1D_((pos[actidx], actidx),
                                 X,
                                 numsignals=signals,
                                 SI=tRxx)

        if shw:
            plt.figure()
            ax = plt.subplot(111, projection='polar')
            ax.plot(X, cors)
            ax.set_title("Estimated Acoustic Source Direction")
            plt.show(block=block_run)
        else:
            return cors
Exemplo n.º 4
0
def rfftfreq(n_samples, sampling_rate):
    """
    Returns the positive discrete frequencies in the range `:math:[0, f_s/2]`
    for which the FFT of a real valued time-signal with n_samples is
    calculated. If the number of samples `:math:N` is even the number of
    frequency bins will be `:math:2N+1`, if `:math:N` is odd, the number of
    bins will be `:math:(N+1)/2`.

    Parameters
    ----------
    n_samples : int
        The number of samples in the signal
    sampling_rate : int
        The sampling rate of the signal

    Returns
    -------
    frequencies : array, double
        The positive discrete frequencies for which the FFT is calculated.
    """
    return fft_lib.rfftfreq(n_samples, d=1/sampling_rate)
Exemplo n.º 5
0
def rfftfreq(n_samples, sampling_rate):
    """
    Returns the positive discrete frequencies in the range
    :math:`[0, \\text{sampling\_rate}/2]` for which the FFT of a real valued
    time-signal with `n_samples` is calculated. If the number of samples
    :math:`N` is even the number of frequency bins will be :math:`2/N+1`, if
    :math:`N` is odd, the number of bins will be :math:`(N+1)/2`.

    Parameters
    ----------
    n_samples : int
        The number of samples in the signal
    sampling_rate : int
        The sampling rate of the signal

    Returns
    -------
    frequencies : array, double
        The positive discrete frequencies in Hz for which the FFT is
        calculated.
    """  # noqa: W605 (ignore \_ which is valid only in LaTex)
    return fft_lib.rfftfreq(n_samples, d=1/sampling_rate)
Exemplo n.º 6
0
    def generate_white_noise(self):
        """Generate a white noise with the relevant power spectrum.

        Returns
        -------
        white_noise : np.ndarray
        """
        # Compute the k grid
        d = self.Lbox / self.dimensions / (2 * np.pi)
        all_k = [fft.fftfreq(self.dimensions, d=d)] * (self.Ndim - 1) + [
            fft.rfftfreq(self.dimensions, d=d)
        ]

        self.kgrid = kgrid = np.array(np.meshgrid(*all_k, indexing="ij"))
        self.knorm = knorm = np.sqrt(np.sum(kgrid**2, axis=0))

        # Compute Pk
        Pk = np.zeros_like(knorm)
        mask = knorm > 0
        Pk[mask] = self.Pk(knorm[mask] * 2)

        # Compute white noise (in Fourier space)
        mu = np.random.standard_normal([self.dimensions] * self.Ndim)
        muk = fft.rfftn(mu)
        deltak = muk * np.sqrt(Pk)

        # Compute field in real space
        white_noise = fft.irfftn(deltak)

        # Normalize variance
        deltak_smoothed = deltak * self.filter.W(knorm)
        field = fft.irfftn(deltak_smoothed)
        std = field.std()

        self.white_noise_fft = deltak * self.sigma8 / std
        self.white_noise = white_noise * self.sigma8 / std

        return self.white_noise
Exemplo n.º 7
0
    def AF_MUSIC(self,
                 focusing_freq=-1,
                 npoints=1000,
                 signals=1,
                 shw=True,
                 block_run=True,
                 chunks=10):
        """Display a polar plot of estimated DOA using the MUSIC algorithm

        Arguments:
            focusing_freq (float): The frequency (in Hz) at which to perform the calculation. If <0, will default to 0.9*(spatial Nyquist frequency)
            npoints (int): The total number of points around the circle at which to evaluate.
            signals (int): The numbers of signals to locate.
            shw (bool): Show the plot? If False, will return the data that was to be plotted.
            block_run (bool): Pause execution of the file while the figure is open? Set to True for running in the command-line.
            chunks (int): How many sections to split the data up into. Will split up the data and average the result over the split sections
        """
        omegas = np.linspace(0, 2 * np.pi, npoints, endpoint=False)
        rest = np.zeros_like(omegas)

        if focusing_freq < 0:
            focusing_freq = self.spatial_nyquist_freq * 0.9

        # First generate Rxxs to get to T_autos
        # Tauto will go in here
        Tauto = np.zeros((self.mics.shape[0], self.mics.shape[0],
                          self.data.shape[0] // 2 + 1),
                         dtype="complex128")
        # Rxx will go in here
        Rxx = np.zeros((self.mics.shape[0], self.mics.shape[0],
                        self.data.shape[0] // 2 + 1),
                       dtype="complex128")
        # Ufi will go in here
        Ufi = np.zeros((self.mics.shape[0], self.mics.shape[0],
                        self.data.shape[0] // 2 + 1),
                       dtype="complex128")
        # Ryy will go in here
        Ryy = np.zeros((self.mics.shape[0], self.mics.shape[0],
                        self.data.shape[0] // 2 + 1),
                       dtype="complex128")

        # Split the data up into "chunks" sections
        indices = [
            int(x) for x in np.linspace(
                0, self.data.shape[0], num=chunks + 1, endpoint=True)
        ]

        # Calculate Rxx
        for mark in np.arange(len(indices) - 1):
            dcr = self.data[indices[mark]:indices[mark + 1], :]
            for chnl in np.arange(dcr.shape[1]):
                dcr[:, chnl] *= np.blackman(dcr.shape[0])

            # dft is RFFT of current data chunk
            dft = fft_pack.rfft(dcr, axis=0, n=self.data.shape[0]).T
            dft.shape = (dft.shape[0], 1, dft.shape[1])
            # print(dft.shape, self.data.shape, Tauto.shape)

            Rxx += np.einsum("jin,iln->jln", dft,
                             np.conj(np.transpose(dft, (1, 0, 2)))) / chunks

        # The frequencies for Tauto and DFT. They all have the same length so this is fine to do outside the loop
        pos = fft_pack.rfftfreq(self.data.shape[0]) * self.sample_rate

        # focusing_freq_index is the index along dft and Tauto to find f_0
        focusing_freq_index = np.argmin(np.abs(pos - focusing_freq))

        eig_f0, v_f0 = np.linalg.eigh(Rxx[:, :, focusing_freq_index])
        Uf0 = v_f0[:, np.argsort(np.abs(eig_f0))[::-1]]

        # Calculate Tautos
        for indx, fi in enumerate(pos):
            eig_fi, v_fi = np.linalg.eigh(Rxx[:, :, indx])
            Ufi[:, :, indx] = v_fi[:, np.argsort(np.abs(eig_fi))[::-1]]
            Tauto[:, :, indx] = dot(Uf0, np.conj(Ufi[:, :, indx].T)) / np.sqrt(
                pos.shape[0])

        # Calculate Ryy
        chunks = 1.0
        indices = [
            int(x) for x in np.linspace(
                0, self.data.shape[0], num=chunks + 1, endpoint=True)
        ]
        for mark in np.arange(len(indices) - 1):
            dcr = self.data[indices[mark]:indices[mark + 1], :]
            for chnl in np.arange(dcr.shape[1]):
                dcr[:, chnl] *= np.blackman(dcr.shape[0])

            # dft is RFFT of current data chunk
            dft = fft_pack.rfft(dcr, axis=0, n=self.data.shape[0]).T
            dft.shape = (dft.shape[0], 1, dft.shape[1])
            # print(dft.shape, self.data.shape, Tauto.shape)

            Yi = np.einsum("abc,bdc->adc", Tauto, dft)
            Ryy += np.einsum("jin,iln->jln", Yi,
                             np.conj(np.transpose(Yi, (1, 0, 2)))) / chunks

        Rcoh = np.sum(Ryy, axis=-1) / (self.data.shape[0] // 2 + 1)

        rest, _ = self._MUSIC1D_((focusing_freq, focusing_freq_index),
                                 omegas,
                                 SI=Rcoh)

        if shw:
            plt.figure()
            ax = plt.subplot(111, projection='polar')
            ax.plot(omegas, rest)
            ax.set_title("Estimated Acoustic Source Direction")
            plt.show(block=block_run)
        else:
            return rest
Exemplo n.º 8
0
    def AF_MUSIC2D(self,
                   focusing_freq=-1,
                   signals=1,
                   xrange=(-50, 50),
                   yrange=(-50, 50),
                   xstep=False,
                   ystep=False,
                   colormap="gist_heat",
                   shw=True,
                   block_run=True,
                   no_fig=False,
                   chunks=10):
        """Displays a heatmap for visual inspection of AF-MUSIC-based location estimation.

        Generates a grid of provided dimension/resolution, and evaluates the AF-MUSIC algorithm at
        each point on the grid.

        Arguments:
            focusing_freq (float): The frequency (in Hz) at which to perform the calculation. If <0, will default to 0.9*(spatial Nyquist frequency)
            signals (int): The number of signals to locate.
            xrange (float, float): The lower and upper bound in the x-direction.
            yrange (float, float): The lower and upper bound in the y-direction.
            xstep (float): If given, determines the size of the steps in the x-direction. Otherwise defaults to 1000 steps.
            ystep (float): If given, determines the size of the steps in the y-direction. Otherwise defaults to 1000 steps.
            colormap (str): The colour map for the heatmap. See https://matplotlib.org/examples/color/colormaps_reference.html
            shw (bool): If False, return the axis object rather than display.
            block_run (bool): Pause execution of the file while the figure is open? Set to True for running in the command-line.
            no_fig (bool): If True, return the heatmap grid rather than plot it.

        Returns:
            np.array: Returns EITHER the current (filled) heatmap domain if no_fig == True, OR a handle to the displayed figure.
        """
        raise NotImplementedError

        self.dataFFT = fft_pack.rfft(self.data, axis=0).T
        self.numbins = self.dataFFT.shape[1]
        pos = fft_pack.rfftfreq(self.data.shape[0]) * self.sample_rate

        if focusing_freq < 0:
            focusing_freq = self.spatial_nyquist_freq * 0.45
            # focusing_freq = pos[np.argmax(self.dataFFT[0, :])]
        else:
            focusing_freq = 1000

        idxs = np.array(np.arange(pos.shape[0]))

        refidx = np.argmin(abs(pos - focusing_freq))
        Rcoh = np.zeros((self.dataFFT.shape[0], self.dataFFT.shape[0]),
                        dtype='complex128')
        ul, self.Uf0 = la.eigh(
            dot(self.dataFFT[:, refidx:refidx + 1],
                self.dataFFT[:, refidx:refidx + 1].conj().T) /
            self.dataFFT.shape[0])
        ul = ul.real
        self.Uf0 = self.Uf0[:, argsort(abs(ul))[::-1]]

        pool = Pool(processes=7)
        res = pool.map(self._UfitoRyy_, idxs)
        pool.close()
        for r in res:
            Rcoh += r

        if xstep and ystep:
            xdom = np.linspace(start=xrange[0],
                               stop=xrange[1],
                               num=int((xrange[1] - xrange[0]) // xstep))
            ydom = np.linspace(start=yrange[0],
                               stop=yrange[1],
                               num=int((yrange[1] - yrange[0]) // ystep))
        else:
            xdom = np.linspace(start=xrange[0], stop=xrange[1], num=1000)
            ydom = np.linspace(start=yrange[0], stop=yrange[1], num=1000)
        self._hm_domain_ = np.zeros((len(ydom), len(xdom)))

        xdom, ydom = np.meshgrid(xdom, ydom)
        self._hm_domain_ = self._MUSIC2D_((pos[refidx], refidx),
                                          xdom,
                                          ydom,
                                          numsignals=signals,
                                          SI=Rcoh)

        if no_fig:
            return self._hm_domain_

        f = plt.figure()
        plt.imshow(self._hm_domain_,
                   cmap=colormap,
                   interpolation='none',
                   origin='lower',
                   extent=[xrange[0], xrange[1], yrange[0], yrange[1]])
        plt.colorbar()
        plt.xlabel("Horiz. Dist. from Center of Array [m]")
        plt.ylabel("Vert. Dist. from Center of Array [m]")

        if shw:
            plt.show(block=block_run)
            return
        else:
            return f
Exemplo n.º 9
0
    def display_MUSICheatmapchunked(self,
                                    frequency,
                                    signals=1,
                                    xrange=(-50, 50),
                                    yrange=(-50, 50),
                                    xstep=False,
                                    ystep=False,
                                    colormap="gist_heat",
                                    shw=True,
                                    block_run=False,
                                    no_fig=False,
                                    title="",
                                    chunks=10):
        """Displays a heatmap for visual inspection of MUSIC-based location estimation.

        Generates a grid of provided dimension/resolution, and evaluates the MUSIC algorithm at the given frequency at
        each point on the grid. Vectorised for fast execution.

        Arguments:
            frequency (float): The frequency (in Hz) at which to evaluate the MUSIC algorithm.
            xrange (float, float): The lower and upper bound in the x-direction.
            yrange (float, float): The lower and upper bound in the y-direction.
            xstep (float): If given, determines the size of the steps in the x-direction. Otherwise defaults to 1000 steps.
            ystep (float): If given, determines the size of the steps in the y-direction. Otherwise defaults to 1000 steps.
            colormap (str): The colour map for the heatmap. See https://matplotlib.org/examples/color/colormaps_reference.html
            shw (bool): If False, return the axis object rather than display.
            block_run (bool): Pause execution of the file while the figure is open? Set to True for running in the command-line.
            no_fig (bool): If True, return the heatmap grid rather than plot it.

        Returns:
            np.array: Returns EITHER the current (filled) heatmap domain if no_fig == True, OR a handle to the displayed figure.
        """
        if (xstep and ystep):
            xdom = np.linspace(xrange[0],
                               xrange[1],
                               num=(xrange[1] - xrange[0]) // xstep)
            ydom = np.linspace(yrange[0],
                               yrange[1],
                               num=(yrange[1] - yrange[0]) // ystep)
        else:
            xdom = np.linspace(start=xrange[0], stop=xrange[1], num=1000)
            ydom = np.linspace(start=yrange[0], stop=yrange[1], num=1000)

        self._hm_domain_ = np.zeros((len(ydom), len(xdom)))
        self.dataFFT = fft_pack.rfft(self.data,
                                     axis=0,
                                     n=2 * self.data.shape[0])
        pos = fft_pack.rfftfreq(2 * self.data.shape[0]) * self.sample_rate
        actidx = np.argmin(abs(pos - frequency))
        ff = (pos[actidx], actidx)
        xdom, ydom = np.meshgrid(xdom, ydom)

        tRxx = np.zeros((self.mics.shape[0], self.mics.shape[0]),
                        dtype='complex128')
        indices = [
            int(x) for x in np.linspace(
                0, self.data.shape[0], num=chunks + 1, endpoint=True)
        ]
        for mark in np.arange(len(indices) - 1):
            dcr = self.data[indices[mark]:indices[mark + 1], :]
            print(dcr.shape)
            ft = fft_pack.rfft(dcr, axis=0, n=2 * self.data.shape[0])
            pos = fft_pack.rfftfreq(2 * self.data.shape[0]) * self.sample_rate
            actidx = np.argmin(abs(pos - frequency))
            tRxx += np.dot(ft[actidx:actidx + 1].T,
                           np.conj(ft[actidx:actidx + 1]))
        tRxx /= chunks

        self._hm_domain_ = self._MUSIC2D_(ff,
                                          xdom,
                                          ydom,
                                          numsignals=signals,
                                          SI=tRxx)

        if no_fig:
            return self._hm_domain_

        f = plt.figure()
        plt.imshow(self._hm_domain_,
                   cmap=colormap,
                   interpolation='none',
                   origin='lower',
                   extent=[xrange[0], xrange[1], yrange[0], yrange[1]])
        plt.colorbar()
        plt.xlabel("Horiz. Dist. from Center of Array [m]")
        plt.ylabel("Vert. Dist. from Center of Array [m]")

        if shw:
            plt.show(block=block_run)
            # f.savefig(title, bbox_inches='tight')
            # plt.close(f)
            return
        else:
            return f
Exemplo n.º 10
0
    def estimate_DOA_path(self,
                          method,
                          path=lambda x:
                          (np.cos(2 * np.pi * x), np.sin(2 * np.pi * x)),
                          array_GPS=False,
                          npoints=2500,
                          map_zoom=20,
                          map_scale=2,
                          freq=False,
                          AF_freqs=(False, False)):
        """Gives an estimate of the source DOA along the `path` provided, otherwise along the unit circle if `path` is not present. 

        Parameters
        ----------
        method : str 
            One of; "GCC", "MUSIC", or "AF-MUSIC". The method to use for DOA estimation.
        path : str/function 
            A filepath to a saved Google Earth path (in .kmz form), else a function f: [0,1]->R^2 to act as a parametrisation of the path at which to evaluate the DOA estimator.
        npoints : int 
            The number of points along the path to sample.
        array_GPS : ()
            !! REQUIRES CONVERSION TO PYPROJ !!
        map_zoom : int
            Zoom level of GEarth imagery. See motionless documentation for more details.
        map_scale : int
            Map scale of GEarth imagery. See motionless documentation for more details. 
        freq : float
            The frequency at which to evaluate the *narrowband* MUSIC algorithm, if using.
        AF_freqs : (float, float)
            A lower and upper limit on the frequncies at which to eveluate the AF-MUSIC algorithm, if using.
        """
        pathstr = False
        if isinstance(path, str):
            assert array_GPS
            pathstr = path
            path = self._get_path(path, array_GPS)
        else:
            assert callable(path)

        dom = np.array(path(np.linspace(0, 1, npoints)))

        if method.upper() == "GCC":
            eval_dom = self._objective_(dom[0, :], dom[1, :])
        elif method.upper() == "MUSIC":
            assert freq, "Frequency must be provided for MUSIC calculation"
            pos = fft_pack.rfftfreq(2 * self.data.shape[0]) * self.sample_rate
            actidx = np.argmin(abs(pos - freq))
            self.dataFFT = fft_pack.rfft(self.data,
                                         axis=0,
                                         n=2 * self.data.shape[0])
            eval_dom = self._MUSIC2D_((pos[actidx], actidx), dom[0:1, :].T,
                                      dom[1:, :].T).flatten()
        elif method.upper() == "AF-MUSIC" or method.upper() == "AF_MUSIC":
            self.dataFFT = fft_pack.rfft(self.data,
                                         axis=0,
                                         n=2 * self.data.shape[0])
            eval_dom = self._AF_MUSIC_subset(dom[0:1, :].T,
                                             dom[1:, :].T,
                                             freqs=AF_freqs).flatten()
        else:
            print("Method not recognised. Defaulting to GCC.")
            eval_dom = self._objective_(dom[0, :], dom[1, :])

        maxidx = np.argmax(eval_dom)
        x_max = dom[0, maxidx]
        y_max = dom[1, maxidx]
        theta = np.arctan2(y_max, x_max) * 180 / np.pi

        plt.figure(1)
        if pathstr:
            plt.subplot(121)
        else:
            pass

        p = plt.scatter(dom[0, :], dom[1, :], c=eval_dom)
        for m in np.arange(self.mics.shape[0]):
            plt.scatter(self.mics[m, 0], self.mics[m, 1], marker='x')
        plt.xlim([np.min(dom[0, :]), np.max(dom[0, :])])
        plt.ylim([np.min(dom[1, :]), np.max(dom[1, :])])
        plt.title(
            r"{} DOA Estimate; Max at $({:.2f}, {:.2f})$, $\theta={:.1f}^\circ$"
            .format(pathstr if pathstr else "", x_max, y_max, theta))
        plt.xlabel(r"x [m] East/West")
        plt.ylabel(r"y [m] North/South")
        plt.colorbar(p)

        if pathstr:
            lat, lon = _inv_proj(dom[:, maxidx:maxidx + 1].T, array_GPS)
            # print(lat, lon, '\n', array_GPS)
            with open("./data/apikey", 'r') as f_ap:
                key = f_ap.readline()
            dmap = DecoratedMap(maptype='satellite',
                                key=key,
                                zoom=map_zoom,
                                scale=map_scale)
            dmap.add_marker(
                LatLonMarker(lat=array_GPS[1], lon=array_GPS[0], label='A'))
            dmap.add_marker(LatLonMarker(lat=lat[0], lon=lon[0], label='B'))
            response = requests.get(dmap.generate_url())
            with open("{}.png".format(pathstr), 'wb') as outfile:
                outfile.write(response.content)
            im = mpimg.imread("{}.png".format(pathstr))
            plt.subplot(122)
            plt.imshow(im)
            plt.xticks([])
            plt.yticks([])
            plt.title("{} Satellite Imagery".format(pathstr))
            plt.xlabel("A: Array\nB: Bird")

        plt.show()
Exemplo n.º 11
0
    def _AF_MUSIC_subset(self,
                         xdom,
                         ydom,
                         focusing_freq=-1,
                         npoints=1000,
                         signals=1,
                         shw=True,
                         block_run=True,
                         chunks=10,
                         freqs=(False, False)):
        """Display a polar plot of estimated DOA using the MUSIC algorithm

        Arguments:
            focusing_freq (float): The frequency (in Hz) at which to perform the calculation. If <0, will default to 0.9*(spatial Nyquist frequency)
            npoints (int): The total number of points around the circle at which to evaluate.
            signals (int): The numbers of signals to locate.
            shw (bool): Show the plot? If False, will return the data that was to be plotted.
            block_run (bool): Pause execution of the file while the figure is open? Set to True for running in the command-line.
            chunks (int): How many sections to split the data up into. Will split up the data and average the result over the split sections
        """
        # print("beggining subset routine")
        if focusing_freq < 0:
            if freqs[0]:
                if freqs[1]:
                    focusing_freq = (freqs[0] + freqs[1]) / 2.0
                else:
                    focusing_freq = (self.sample_rate + freqs[0]) / 2.0
            else:
                if freqs[1]:
                    focusing_freq = freqs[1] / 2.0
                else:
                    focusing_freq = self.sample_rate / 4.0
        # print(focusing_freq, freqs)
        # Split the data up into "chunks" sections
        indices = [
            int(x) for x in np.linspace(
                0, self.data.shape[0], num=int(chunks + 1), endpoint=True)
        ]

        # The frequencies for Tauto and DFT. They all have the same length so this is fine to do outside the loop
        pos = fft_pack.rfftfreq(self.data.shape[0]) * self.sample_rate

        if freqs[0]:
            pos = pos[pos >= freqs[0]]
            LHSl = self.data.shape[0] // 2 + 1 - len(pos)
        else:
            LHSl = 0

        if freqs[1]:
            pos = pos[pos <= freqs[1]]
            RHSl = len(pos) + LHSl
        else:
            RHSl = len(pos)

        # print("found lhs1 and rhs1")

        # Rxx will go in here
        Rxx = np.zeros((self.mics.shape[0], self.mics.shape[0],
                        self.data.shape[0] // 2 + 1),
                       dtype="complex128")

        # Calculate Rxx
        for mark in np.arange(len(indices) - 1):
            dcr = self.data[indices[mark]:indices[mark + 1], :]
            for chnl in np.arange(dcr.shape[1]):
                dcr[:, chnl] *= np.blackman(dcr.shape[0])

            # dft is RFFT of current data chunk
            dft = fft_pack.rfft(dcr, axis=0, n=self.data.shape[0]).T
            dft.shape = (dft.shape[0], 1, dft.shape[1])
            # print(dft.shape, self.data.shape, Tauto.shape)

            Rxx += np.einsum("jin,iln->jln", dft,
                             np.conj(np.transpose(dft, (1, 0, 2)))) / chunks
        # print("calculated Rxx")

        # focusing_freq_index is the index along dft and Tauto to find f_0
        focusing_freq_index = np.argmin(abs(pos - focusing_freq)) + LHSl

        eig_f0, v_f0 = np.linalg.eigh(Rxx[:, :, focusing_freq_index])
        Uf0 = v_f0[:, argsort(abs(eig_f0))[::-1]]
        # Calculate Tautos
        # Tauto will go in here
        Tauto = np.zeros((self.mics.shape[0], self.mics.shape[0], len(pos)),
                         dtype="complex128")
        # Ufi will go in here
        Ufi = np.zeros((self.mics.shape[0], self.mics.shape[0],
                        self.data.shape[0] // 2 + 1),
                       dtype="complex128")
        for indx, fi in enumerate(pos):
            eig_fi, v_fi = np.linalg.eigh(Rxx[:, :, indx + LHSl])
            Ufi[:, :, indx] = v_fi[:, argsort(abs(eig_fi))[::-1]]
            Tauto[:, :, indx] = dot(Uf0, np.conj(Ufi[:, :, indx].T)) / sqrt(
                pos.shape[0])

        # Calculate Ryy
        # Ryy will go in here
        Ryy = np.zeros((self.mics.shape[0], self.mics.shape[0], len(pos)),
                       dtype="complex128")

        # chunks=1.0
        indices = [
            int(x) for x in np.linspace(
                0, self.data.shape[0], num=int(chunks + 1), endpoint=True)
        ]
        for mark in np.arange(len(indices) - 1):
            dcr = self.data[indices[mark]:indices[mark + 1], :]
            for chnl in np.arange(dcr.shape[1]):
                dcr[:, chnl] *= np.blackman(dcr.shape[0])

            # dft is RFFT of current data chunk
            dft = fft_pack.rfft(dcr, axis=0, n=self.data.shape[0]).T
            # print(dft.shape)
            dft = dft[:, LHSl:RHSl]
            dft.shape = (dft.shape[0], 1, dft.shape[1])
            # print(dft.shape, Tauto.shape, LHSl, RHSl)

            Yi = np.einsum("abc,bdc->adc", Tauto, dft)
            Ryy += np.einsum("jin,iln->jln", Yi,
                             np.conj(np.transpose(Yi, (1, 0, 2)))) / chunks

        Rcoh = np.sum(Ryy, axis=-1) / (len(pos))

        rest = self._MUSIC2D_((focusing_freq, focusing_freq_index),
                              xdom,
                              ydom,
                              SI=Rcoh)

        return rest
Exemplo n.º 12
0
    def estimate_DOA_heatmap(self,
                             method,
                             xrange=(-50, 50),
                             yrange=(-50, 50),
                             xstep=False,
                             ystep=False,
                             colormap="bone",
                             shw=True,
                             block_run=True,
                             no_fig=False,
                             freq=False,
                             signals=1,
                             AF_freqs=(False, False),
                             f_0=-1,
                             array_GPS=False,
                             save_GIS=False):
        """Displays a heatmap for visual inspection of correlation-based location estimation.

        Generates a grid of provided dimension/resolution, and evaluates the selected DOA-estimation at each point on the grid.
        Vectorised for fast execution.

        Parameters
        ----------
        method : str 
            One of; "GCC", "MUSIC" or "AF-MUSIC". The method to be used in heatmap generation.
        xrange : (float, float) 
            The lower and upper bound in the x-direction.
        yrange : (float, float)
            The lower and upper bound in the y-direction.
        xstep : float 
            If given, determines the size of the steps in the x-direction. Otherwise defaults to 1000 steps.
        ystep : float 
            If given, determines the size of the steps in the y-direction. Otherwise defaults to 1000 steps.
        colormap : str 
            The colour map for the heatmap. See https://matplotlib.org/examples/color/colormaps_reference.html
        shw : bool
            If False, return the axis object rather than display.
        block_run : bool 
            Pause execution of the file while the figure is open? Set to True for running in the command-line.
        no_fig : bool 
            If True, return the heatmap grid rather than plot it.
        freq : float 
            Frequency, in Hz, at which to calculate the MUSIC spectrum.
        signals : int 
            The number of signals to be localised. Only relevant for MUSIC-based methods.
        AF_freqs : (float, float) 
            Lower and upper bounds on the frequencies (in Hz) at which to evaluate the AF-MUSIC algorithm.
        f_0 : float
            The reference frequency at which to calculate AF-MUSIC. Default of -1 uses the the midway point between AF_freqs.
        array_GPS : (float, float)
            False, or tuple of GPS lat/long.
        save_GIS : bool 
            Save the image as a tif wth a corresponding .tif.points file for use in GIS software? Requires array_GPS

        Returns
        -------
        np.array 
            Returns EITHER the current (filled) heatmap domain if no_fig == True, OR a handle to the displayed figure.
        """

        if (xstep and ystep):
            xdom = np.linspace(start=xrange[0],
                               stop=xrange[1],
                               num=int((xrange[1] - xrange[0]) // xstep))
            ydom = np.linspace(start=yrange[0],
                               stop=yrange[1],
                               num=int((yrange[1] - yrange[0]) // ystep))
        else:
            xdom = np.linspace(start=xrange[0], stop=xrange[1], num=1000)
            ydom = np.linspace(start=yrange[0], stop=yrange[1], num=1000)
        self._hm_domain_ = np.zeros((len(ydom), len(xdom)))

        xdom, ydom = np.meshgrid(xdom, ydom)

        self._hm_corners_ = np.array([[[xrange[0], yrange[1]],
                                       [xrange[1], yrange[1]]],
                                      [[xrange[0], yrange[0]],
                                       [xrange[1], yrange[0]]]])

        if method.upper() == "AF-MUSIC" or method.upper() == "AF_MUSIC":
            self.dataFFT = fft_pack.rfft(self.data,
                                         axis=0,
                                         n=2 * self.data.shape[0])
            self._hm_domain_ = self._AF_MUSIC_subset(xdom,
                                                     ydom,
                                                     freqs=AF_freqs,
                                                     focusing_freq=f_0)
        elif method.upper() == "MUSIC":
            assert freq, "Frequency must be provided for MUSIC calculation"
            pos = fft_pack.rfftfreq(2 * self.data.shape[0]) * self.sample_rate
            actidx = np.argmin(abs(pos - freq))
            self.dataFFT = fft_pack.rfft(self.data,
                                         axis=0,
                                         n=2 * self.data.shape[0])
            self._hm_domain_ = self._MUSIC2D_((pos[actidx], actidx),
                                              xdom,
                                              ydom,
                                              numsignals=signals)
        elif method.upper() == "GCC":
            self._hm_domain_ = self._objective_(xdom, ydom)
        else:
            print("Method not recognised. Defaulting to GCC.")
            self._hm_domain_ = self._objective_(xdom, ydom)

        if no_fig:
            return self._hm_domain_

        plt.imshow(self._hm_domain_,
                   cmap=colormap,
                   interpolation='none',
                   origin='lower',
                   extent=[xrange[0], xrange[1], yrange[0], yrange[1]])
        plt.colorbar()
        plt.xlabel("Horiz. Dist. from Center of Array [m]")
        plt.ylabel("Vert. Dist. from Center of Array [m]")
        plt.title("{}-based Source Location Estimate".format(method))

        if shw:
            plt.show(block=block_run)
            return
        else:
            return plt.imshow(
                self._hm_domain_,
                cmap=colormap,
                interpolation='none',
                origin='lower',
                extent=[xrange[0], xrange[1], yrange[0], yrange[1]])
Exemplo n.º 13
0
def drift_plot(fit, title=None, dt=0.1, dx=130, lf=-np.inf, hf=np.inf,
               log=False, cmap='magma', xc='b', yc='r'):
    '''
    Plotting utility to show drift curves nicely

    Parameters
    ----------
    fit : pandas DataFrame
        Assumes that it has attributes x0 and y0
    title : str (optional)
        Title of plot
    dt : float (optional)
        Sampling rate of data in seconds
    dx : pixel size (optional)
        Pixel size in nm
    lf : float (optional)
        Low frequency cutoff for fourier plot
    hf : float (optional)
        High frequency cutoff for fourier plot
    log : bool (optional)
        Take logarithm of FFT data before displaying
    cmap : string or matplotlib.colors.cmap instance
        Color map for scatter plot
    xc : string or `color` instance
        Color for x data
    yc : string or `color` instance
        Color for y data

    Returns
    -------
    fig : figure object
        The figure
    axs : tuple of axes objects
        In the following order, Real axis, FFT axis, Scatter axis
    '''
    # set up plot
    fig = plt.figure()
    fig.set_size_inches(8, 4)
    axreal = plt.subplot(221)
    axfft = plt.subplot(223)
    axscatter = plt.subplot(122)
    # label it
    if title is not None:
        fig.suptitle(title, y=1.02, fontweight='bold')
    # detrend mean
    ybar = fit.y0 - fit.y0.mean()
    ybar *= dx
    xbar = fit.x0 - fit.x0.mean()
    xbar *= dx
    # Plot Real space
    t = np.arange(len(fit)) * dt
    axreal.plot(t, xbar, xc, label=r"$x_0$")
    axreal.plot(t, ybar, yc, label=r"$y_0$")
    axreal.set_xlabel('Time (s)')
    axreal.set_ylabel('Displacement (nm)')
    # add legend to real axis
    axreal.legend(loc='best')
    # calc FFTs
    Y = rfftn(ybar)
    X = rfftn(xbar)
    # calc FFT freq
    k = rfftfreq(len(fit), dt)
    # limit FFT display range
    kg = np.logical_and(k > lf, k < hf)
    # Plot FFT
    if log:
        axfft.semilogy(k[kg], abs(X[kg]), xc)
        axfft.semilogy(k[kg], abs(Y[kg]), yc)
    else:
        axfft.plot(k[kg], abs(X[kg]), xc)
        axfft.plot(k[kg], abs(Y[kg]), yc)
    axfft.set_xlabel('Frequency (Hz)')
    # Plot scatter
    axscatter.scatter(xbar, ybar, c=t, cmap=cmap)
    axscatter.set_xlabel('x')
    axscatter.set_ylabel('y')
    # make sure the scatter plot is square
    lims = axreal.get_ylim()
    axscatter.set_ylim(lims)
    axscatter.set_xlim(lims)
    # tight layout
    axs = (axreal, axfft, axscatter)
    fig.tight_layout()
    # return fig, axs to user for further manipulation and/or saving if wanted
    return fig, axs