def LS_Filter_Toeplitz(ref, srv, nlag, return_filter=False): '''Least squares clutter removal for passive radar. Computes filter taps by assuming that the autocorrelation matrix of the reference channel signal is Hermitian and Toeplitz. Faster than the direct matrix inversion method, but inaccurate if the assumptions are violated (i.e. if the input signal is not wide sense stationary.) Parameters: ref: reference signal srv: surveillance signal nlag: filter length in samples return_filter: (bool) option to return filter taps as well as cleaned signal Returns: y: surveillance signal with clutter removed w: (optional) least squares filter taps ''' rs = np.roll(ref, -10) # compute the first column of the autocorelation matrix of ref c = xcorr(rs, rs, 0, nlag) # compute the cross-correlation of ref and srv r = xcorr(srv, rs, 0, nlag) # solve the Toeplitz least squares problem w = solve_toeplitz(c, r) if return_filter: return srv - np.convolve(rs, w, mode='same'), w else: return srv - np.convolve(rs, w, mode='same')
def LS_Filter_Toeplitz(refChannel, srvChannel, filterLen, peek=10, return_filter=False): '''Block east squares adaptive filter. Computes filter coefficients using scipy's solve_toeplitz function. This assumes the autocorrelation matrix of refChannel is Hermitian and Toeplitz (i.e. wide the reference signal is wide sense stationary). Faster than the direct matrix inversion method but inaccurate if the assumptions are violated. Parameters: refChannel: Array containing the reference channel signal srvChannel: Array containing the surveillance channel signal filterLen: Length of the least squares filter (in samples) peek: Number of noncausal filter taps. Set to zero for a causal filter. If nonzero, clutter estimates can depend on future values of the reference signal (this helps sometimes) return_filter: Boolean indicating whether to return the filter taps Returns: srvChannelFiltered: Surveillance channel signal with clutter removed filterTaps: (optional) least squares filter taps ''' if refChannel.shape != srvChannel.shape: raise ValueError(f'''Input vectors must have the same length - got {refChannel.shape} and {srvChannel.shape}''') # shift reference channel because for some reason the filtering works # better when you allow the clutter filter to be noncausal refChannelShift = np.roll(refChannel, -1 * peek) # compute the first column of the autocorelation matrix of ref autocorrRef = xcorr(refChannelShift, refChannelShift, 0, filterLen + peek - 1) # compute the cross-correlation of ref and srv xcorrSrvRef = xcorr(srvChannel, refChannelShift, 0, filterLen + peek - 1) # solve the Toeplitz least squares problem filterTaps = solve_toeplitz(autocorrRef, xcorrSrvRef) # compute clutter signal and remove from surveillance Channel clutter = np.convolve(refChannelShift, filterTaps, mode='full') clutter = clutter[0:srvChannel.shape[0]] srvChannelFiltered = srvChannel - clutter if return_filter: return srvChannelFiltered, filterTaps else: return srvChannelFiltered
def direct_xambg(refChannel, srvChannel, rangeBins, freqBins, sampleRate): ''' Direct Cross-Ambiguity Fuction (time domain method) Args: refChannel: reference channel data srvChannel: surveillance channel data rangeBins: number of range bins to compute freqBins: number of doppler bins to compute sampleRate: input sample rate in Hz Returns: ndarray of dimensions (nf, nlag+1, 1) containing the cross-ambiguity surface. third dimension added for easy stacking in dask. ''' if refChannel.shape != srvChannel.shape: raise ValueError('Input vectors must have the same length') # calculate the coherent processing interval in seconds CPI = refChannel.shape[0] / sampleRate # pre-allocate space for the result xambg = np.zeros((freqBins, rangeBins + 1, 1), dtype=np.complex64) # loop over frequency bins for i in range(freqBins): # get Doppler shift for the current bin df = (i - 0.5 * freqBins) / CPI # create a frequency shifted copy of the reference signal ref_shifted = frequency_shift(refChannel, df, sampleRate) # correlate surveillance and shifted reference signals xambg[i, :, 0] = xcorr(ref_shifted, srvChannel, rangeBins, 0) return xambg
def signal_preview(config): inputFile = h5py.File(config['input_file'], 'r') if config['interleaved_input_channels']: data = inputFile[config['interleaved_data_path']][0:config['input_chunk_length']] data_IQ = deinterleave_IQ(data) ref = data_IQ[0::2] srv = data_IQ[1::2] offset = find_channel_offset(ref,srv,4,50000) else: # get the first few seconds of data and use it to estimate the # offset between the channels ref = inputFile[config['input_ref_path']][0:config['input_chunk_length']] srv = inputFile[config['input_srv_path']][0:config['input_chunk_length']] ref = deinterleave_IQ(ref) srv = deinterleave_IQ(srv) offset = find_channel_offset(ref,srv,4,50000) plt.figure() plt.psd(ref, NFFT=8192, Fs=config['input_sample_rate'], Fc=config['input_center_freq']) plt.psd(srv, NFFT=8192, Fs=config['input_sample_rate'], Fc=config['input_center_freq']) plt.legend(['Reference channel', 'Surveillance channel']) plt.title("Spectrum of Input Data") plt.show() reff = frequency_shift(ref, config['offset_freq'], config['input_sample_rate']) srvv = frequency_shift(srv, config['offset_freq'], config['input_sample_rate']) reff = resample(reff, config['resamp_up'], config['resamp_dn']) srvv = resample(srvv, config['resamp_up'], config['resamp_dn']) plt.figure() plt.psd(reff, NFFT=2048, Fs=config['channel_bandwidth'], Fc=config['channel_freq']) plt.psd(srvv, NFFT=2048, Fs=config['channel_bandwidth'], Fc=config['channel_freq']) plt.legend(['Reference channel', 'Surveillance channel']) plt.title("Channel Spectrum") plt.show() fig, ax = plt.subplots() xx = np.arange(-2000, 2001) xc = np.abs(xcorr(ref, srv, 2000, 2000)) plt.plot(xx, xc) plt.title("Cross-correlation between channels") plt.text(0.1, 0.9, f"Offset of {offset} samples between channels", transform=ax.transAxes) plt.show()
def direct_xambg(ref, srv, fs, fd, df, nlag): '''Create a range-doppler map from two signals input parameters: ref, srv: input signals fs: input sample rate (samples/sec) fd: number of Doppler bins to compute df: doppler resolution (Hz) nlag: maximum lag between signals (samples) ''' if ref.shape != srv.shape: raise ValueError('Input vectors must have the same length') omega_d = 2 * np.pi * np.arange(-1 * fd, fd - df, df) / fs nf = omega_d.shape[0] t = np.arange(ref.shape[0]) xambg = np.zeros((nf, nlag + 1, 1), dtype=np.complex64) for k in range(nf): srv_shift = np.exp(1j * omega_d[k] * t) * srv xambg[k, :, 0] = xcorr(ref, srv_shift, 0, nlag) return xambg