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
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
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
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)
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)
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
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
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
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
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()
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
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]])
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