diff --git a/classical_doa/algorithm/broadband/cssm.py b/classical_doa/algorithm/broadband/cssm.py index 300feab..7486abc 100644 --- a/classical_doa/algorithm/broadband/cssm.py +++ b/classical_doa/algorithm/broadband/cssm.py @@ -10,14 +10,15 @@ def cssm(received_data, num_signal, array_position, fs, angle_grids, fre_ref, """Coherent Signal Subspace Method (CSSM) for wideband DOA estimation. Args: - received_data : 阵列接受信号 - num_signal : 信号个数 - array_position : 阵元位置, 应该是numpy array的形式, 行向量列向量均可 - fs: 采样频率 - angle_grids : 空间谱的网格点, 应该是numpy array的形式 - fre_ref: 参考频点 - pre_estimate: 角度预估计值 - unit : 角度的单位制, `rad`代表弧度制, `deg`代表角度制. Defaults to + received_data : Array received signals + num_signal : Number of signals + array_position : Position of array elements. It should be a numpy array + fs: sampling frequency + angle_grids : Angle grids corresponding to spatial spectrum. It should + be a numpy array. + fre_ref: reference frequency + pre_estimate: pre-estimated angles + unit : Unit of angle, 'rad' for radians, 'deg' for degrees. Defaults to 'deg'. References: @@ -34,24 +35,27 @@ def cssm(received_data, num_signal, array_position, fs, angle_grids, fre_ref, pre_estimate = pre_estimate.reshape(1, -1) array_position = array_position.reshape(-1, 1) - # 频域下的阵列接收信号 + # Divide the received signal into multiple frequency points signal_fre_bins = np.fft.fft(received_data, axis=1) fre_bins = np.fft.fftfreq(num_snapshots, 1 / fs) - # 计算参考频点下,预估计角度对应的流型矩阵 + # Calculate the manifold matrix corresponding to the pre-estimated angles at + # the reference frequency point matrix_a_ref = np.exp(-1j * 2 * np.pi * fre_ref / C *\ array_position @ pre_estimate) for i, fre in enumerate(fre_bins): - # 每个频点下,角度预估值对应的流型矩阵 + # Manifold matrix corresponding to the pre-estimated angles at + # each frequency point matrix_a_f = np.exp(-1j * 2 * np.pi * fre / C *\ array_position @ np.sin(pre_estimate)) matrix_q = matrix_a_f @ matrix_a_ref.transpose().conj() - # 对matrix_q进行奇异值分解 + # Perform singular value decomposition on matrix_q matrix_u, _, matrix_vh = np.linalg.svd(matrix_q) - # RSS法构造最佳聚焦矩阵 + # Construct the optimal focusing matrix using the RSS method matrix_t_f = matrix_vh.transpose().conj() @ matrix_u.transpose().conj() - # 将每个频点对应的接收信号聚焦到参考频点 + # Focus the received signals at each frequency point to the reference + # frequency point signal_fre_bins[:, i] = matrix_t_f @ signal_fre_bins[:, i] spectrum = music(received_data=signal_fre_bins, num_signal=num_signal, diff --git a/classical_doa/algorithm/broadband/imusic.py b/classical_doa/algorithm/broadband/imusic.py index 60ba373..bcd7561 100644 --- a/classical_doa/algorithm/broadband/imusic.py +++ b/classical_doa/algorithm/broadband/imusic.py @@ -9,13 +9,15 @@ def imusic(received_data, num_signal, array_position, fs, angle_grids, """Incoherent MUSIC estimator for wideband DOA estimation. Args: - received_data : 阵列接受信号 - num_signal : 信号个数 - array_position : 阵元位置, 应该是numpy array的形式, 行向量列向量均可 - fs: 采样频率 - angle_grids : 空间谱的网格点, 应该是numpy array的形式 - num_groups: FFT的组数, 每一组都独立做FFT - unit : 角度的单位制, `rad`代表弧度制, `deg`代表角度制. Defaults to + received_data : Array received signals + num_signal : Number of signals + array_position : Position of array elements. It should be a numpy array + fs: sampling frequency + angle_grids : Angle grids corresponding to spatial spectrum. It should + be a numpy array. + num_groups: Divide sampling points into serveral groups, and do FFT + separately in each group + unit : Unit of angle, 'rad' for radians, 'deg' for degrees. Defaults to 'deg'. References: @@ -27,7 +29,7 @@ def imusic(received_data, num_signal, array_position, fs, angle_grids, signal_fre_bins, fre_bins = divide_into_fre_bins(received_data, num_groups, fs) - # 对每一个频点运行MUSIC算法 + # MUSIC algorithm in every frequency point spectrum_fre_bins = np.zeros((signal_fre_bins.shape[1], angle_grids.size)) for i, fre in enumerate(fre_bins): spectrum_fre_bins[i, :] = music(received_data=signal_fre_bins[:, i, :], @@ -37,7 +39,6 @@ def imusic(received_data, num_signal, array_position, fs, angle_grids, angle_grids=angle_grids, unit=unit) - # 取平均 spectrum = np.mean(spectrum_fre_bins, axis=0) return np.squeeze(spectrum) diff --git a/classical_doa/algorithm/broadband/tops.py b/classical_doa/algorithm/broadband/tops.py index e57f489..546b469 100644 --- a/classical_doa/algorithm/broadband/tops.py +++ b/classical_doa/algorithm/broadband/tops.py @@ -15,15 +15,16 @@ def tops(received_data, num_signal, array_position, fs, num_groups, angle_grids, DOA estimation. Args: - received_data : 阵列接受信号 - num_signal : 信号个数 - array_position : 阵元位置, 应该是numpy array的形式, 行向量列向量均可 - fs: 采样频率 - num_groups: FFT的组数, 每一组都独立做FFT - angle_grids : 空间谱的网格点, 应该是numpy array的形式 - fre_ref: 参考频点 - unit : 角度的单位制, `rad`代表弧度制, `deg`代表角度制. Defaults to - 'deg'. + received_data: received signals from the array. + num_signal: Number of signals. + array_position: Array element positions, should be a numpy array + fs: Sampling frequency. + num_groups: Number of groups for FFT, each group performs an + independent FFT. + angle_grids: Grid points of spatial spectrum, should be a numpy array. + fre_ref: Reference frequency point. + unit: Unit of angle measurement, 'rad' for radians, 'deg' for degrees. + Defaults to 'deg'. References: Yoon, Yeo-Sun, L.M. Kaplan, and J.H. McClellan. “TOPS: New DOA Estimator @@ -39,7 +40,7 @@ def tops(received_data, num_signal, array_position, fs, num_groups, angle_grids, signal_fre_bins, fre_bins = divide_into_fre_bins(received_data, num_groups, fs) - # index of reference frequency if FFT output + # index of reference frequency in FFT output ref_index = int(fre_ref / (fs / fre_bins.size)) # get signal space of reference frequency signal_space_ref = get_signal_space(signal_fre_bins[:, ref_index, :], @@ -50,33 +51,34 @@ def tops(received_data, num_signal, array_position, fs, num_groups, angle_grids, matrix_d = np.empty((num_signal, 0), dtype=np.complex_) for j, fre in enumerate(fre_bins): - # 计算当前频点对应的噪声子空间 + # calculate noise subspace for the current frequency point noise_space_f = get_noise_space(signal_fre_bins[:, j, :], num_signal) - # 构造变换矩阵 + # construct transformation matrix matrix_phi = np.exp(-1j * 2 * np.pi * (fre - fre_ref) / C * array_position * np.sin(grid)) matrix_phi = np.diag(np.squeeze(matrix_phi)) - # 使用变换矩阵将参考频点的信号子空间变换到当前频点 + # transform the signal subspace of the reference frequency to the + # current frequency using the transformation matrix matrix_u = matrix_phi @ signal_space_ref - # 构造投影矩阵,减小矩阵U中的误差 + # construct projection matrix to reduce errors in matrix U matrix_a_f = np.exp(-1j * 2 * np.pi * fre / C * array_position * np.sin(grid)) matrix_p = np.eye(num_antennas) -\ 1 / (matrix_a_f.transpose().conj() @ matrix_a_f) *\ matrix_a_f @ matrix_a_f.transpose().conj() - # 使用投影矩阵对矩阵U进行投影 + # project matrix U using the projection matrix matrix_u = matrix_p @ matrix_u matrix_d = np.concatenate((matrix_d, matrix_u.T.conj() @ noise_space_f), axis=1) - # 使用矩阵D中的最小特征值构造空间谱 + # construct spatial spectrum using the minimum eigenvalue of matrix D _, s, _ = np.linalg.svd(matrix_d) spectrum[i] = 1 / min(s) diff --git a/classical_doa/algorithm/esprit.py b/classical_doa/algorithm/esprit.py index ac521be..6fbbf08 100644 --- a/classical_doa/algorithm/esprit.py +++ b/classical_doa/algorithm/esprit.py @@ -10,12 +10,12 @@ def esprit(received_data, num_signal, array_position, signal_fre, unit="deg"): the reference paper. Args: - received_data : 阵列接受信号 - num_signal : 信号个数 - array_position : 阵元位置, 应该是numpy array的形式, 行向量列向量均可 - signal_fre: 信号频率 - unit : 返回的估计角度的单位制, `rad`代表弧度制, `deg`代表角度制. - Defaults to 'deg'. + received_data : Array received signals + num_signal : Number of signals + array_position : Position of array elements. It should be a numpy array + signal_fre: Signal frequency + unit : Unit of angle, 'rad' for radians, 'deg' for degrees. Defaults to + 'deg'. Reference: Roy, R., and T. Kailath. “ESPRIT-Estimation of Signal Parameters via @@ -28,7 +28,8 @@ def esprit(received_data, num_signal, array_position, signal_fre, unit="deg"): # get signal space of two sub array. Each sub array consists of M-1 antennas matrix_e_x = signal_space[:-1, :] matrix_e_y = signal_space[1:, :] - # 两个子阵列中对应阵元之间的固定间距确保了旋转不变性 + # the fixed distance of corresponding elements in two sub-array ensures + # the rotational invariance sub_array_spacing = array_position[1] - array_position[0] matrix_c = np.hstack((matrix_e_x, matrix_e_y)).transpose().conj() @\ @@ -36,7 +37,7 @@ def esprit(received_data, num_signal, array_position, signal_fre, unit="deg"): # get eigenvectors eigenvalues, eigenvectors = np.linalg.eig(matrix_c) - sorted_index = np.argsort(np.abs(eigenvalues))[::-1] # 由大到小排序的索引 + sorted_index = np.argsort(np.abs(eigenvalues))[::-1] # descending order matrix_e = eigenvectors[:, sorted_index[:2 * num_signal]] # take the upper right and lower right sub matrix diff --git a/classical_doa/algorithm/music.py b/classical_doa/algorithm/music.py index 9d4943a..83b0e51 100644 --- a/classical_doa/algorithm/music.py +++ b/classical_doa/algorithm/music.py @@ -10,45 +10,49 @@ def music(received_data, num_signal, array_position, signal_fre, angle_grids, """1D MUSIC Args: - received_data : 阵列接受信号 - num_signal : 信号个数 - array_position : 阵元位置, 应该是numpy array的形式, 行向量列向量均可 - signal_fre: 信号频率 - angle_grids : 空间谱的网格点, 应该是numpy array的形式 - unit : 角度的单位制, `rad`代表弧度制, `deg`代表角度制. Defaults to + received_data : Array received signals + num_signal : Number of signals + array_position : Position of array elements. It should be a numpy array + signal_fre: Signal frequency + angle_grids : Angle grids corresponding to spatial spectrum. It should + be a numpy array. + unit : Unit of angle, 'rad' for radians, 'deg' for degrees. Defaults to 'deg'. """ noise_space = get_noise_space(received_data, num_signal) - # 变为列向量用于后续矩阵计算 + # Reshape to column vector for matrix calculations array_position = np.reshape(array_position, (-1, 1)) if unit == "deg": angle_grids = angle_grids / 180 * np.pi angle_grids = np.reshape(angle_grids, (1, -1)) - # 计算所有网格点信号入射时的流型矩阵 + # Calculate the manifold matrix when there are incident signal in all + # grid points tau_all_grids = 1 / C * array_position @ np.sin(angle_grids) manifold_all_grids = np.exp(-1j * 2 * np.pi * signal_fre * tau_all_grids) v = noise_space.transpose().conj() @ manifold_all_grids - # 矩阵v的每一列对应一个入射信号, 对每一列求二范数的平方 + # Each column of matrix v corresponds to an incident signal, calculate the + # square of the 2-norm for each column spectrum = 1 / np.linalg.norm(v, axis=0) ** 2 return np.squeeze(spectrum) + def root_music(received_data, num_signal, array_position, signal_fre, unit="deg"): """Root-MUSIC Args: - received_data : 阵列接受信号 - num_signal : 信号个数 - array_position : 阵元位置, 应该是numpy array的形式, 行向量列向量均可 - signal_fre: 信号频率 - unit : 角度的单位制, `rad`代表弧度制, `deg`代表角度制. Defaults to - 'deg'. + received_data : Array of received signals + num_signal : Number of signals + array_position : Position of array elements. It should be a numpy array + signal_fre: Signal frequency + unit: The unit of the angle, `rad` represents radian, `deg` represents + degree. Defaults to 'deg'. References: Rao, B.D., and K.V.S. Hari. “Performance Analysis of Root-Music.” @@ -60,9 +64,14 @@ def root_music(received_data, num_signal, array_position, signal_fre, num_antennas = array_position.size antenna_spacing = array_position[1] - array_position[0] - # 由于numpy提供的多项式求解函数需要以多项式的系数作为输入,而系数的提取非常 - # 复杂, 此处直接搬用doatools中rootMMUSIC的实现代码 - # 也可以使用sympy库求解多项式方程,但是计算量会更大 + # Since the polynomial solving function provided by numpy requires the + # coefficients of the polynomial as input, and extracting the coefficients + # is very complex, so the implementation code of rootMMUSIC in doatools is + # directly used here. + + # Alternatively, the sympy library can be used to solve polynomial + # equations, but it will be more computationally expensive. + # Compute the coefficients for the polynomial. matrix_c = noise_space @ noise_space.transpose().conj() coeff = np.zeros((num_antennas - 1,), dtype=np.complex_) @@ -72,8 +81,8 @@ def root_music(received_data, num_signal, array_position, signal_fre, # Find the roots of the polynomial. z = np.roots(coeff) - # 为了防止同时取到一对共轭根, 只取单位圆内的根 - # find k roots inside and closest to the unit circle + # To avoid simultaneously obtaining a pair of complex conjugate roots, only + # take roots inside the unit circle roots_inside_unit_circle = np.extract(np.abs(z) <= 1, z) sorted_index = np.argsort(np.abs(np.abs(roots_inside_unit_circle) - 1)) chosen_roots = roots_inside_unit_circle[sorted_index[:num_signal]] diff --git a/classical_doa/algorithm/sparse.py b/classical_doa/algorithm/sparse.py index 4d6f074..9f9e9e8 100644 --- a/classical_doa/algorithm/sparse.py +++ b/classical_doa/algorithm/sparse.py @@ -8,12 +8,13 @@ def omp(received_data, num_signal, array_position, signal_fre, angle_grids, """OMP based sparse representation algorithms for DOA estimation Args: - received_data : 阵列接受信号 - num_signal : 信号个数 - array_position : 阵元位置, 应该是numpy array的形式, 行向量列向量均可 - signal_fre: 信号频率 - angle_grids : 空间谱的网格点, 应该是numpy array的形式 - unit : 角度的单位制, `rad`代表弧度制, `deg`代表角度制. Defaults to + received_data : Array received signals + num_signal : Number of signals + array_position : Position of array elements. It should be a numpy array + signal_fre: Signal frequency + angle_grids : Angle grids corresponding to spatial spectrum. It should + be a numpy array. + unit : Unit of angle, 'rad' for radians, 'deg' for degrees. Defaults to 'deg'. Reference: diff --git a/classical_doa/algorithm/utils.py b/classical_doa/algorithm/utils.py index 5d874a9..2b5da51 100644 --- a/classical_doa/algorithm/utils.py +++ b/classical_doa/algorithm/utils.py @@ -9,7 +9,7 @@ def get_noise_space(received_data, num_signal): (received_data @ received_data.transpose().conj()) eigenvalues, eigenvectors = np.linalg.eig(corvariance_matrix) - sorted_index = np.argsort(np.abs(eigenvalues)) # 由小到大排序的索引 + sorted_index = np.argsort(np.abs(eigenvalues)) # ascending order noise_space = eigenvectors[:, sorted_index[:-num_signal]] return noise_space @@ -23,7 +23,7 @@ def get_signal_space(received_data, num_signal): (received_data @ received_data.transpose().conj()) eigenvalues, eigenvectors = np.linalg.eig(corvariance_matrix) - sorted_index = np.argsort(np.abs(eigenvalues)) # 由小到大排序的索引 + sorted_index = np.argsort(np.abs(eigenvalues)) # ascending order noise_space = eigenvectors[:, sorted_index[-num_signal:]] return noise_space @@ -39,21 +39,22 @@ def divide_into_fre_bins(received_data, num_groups, fs): fs : sampling frequency Returns: - `signal_fre_bins`: 一个m*n*l维的矩阵, 其中m为阵元数, n为FFT的点数, - l为组数 - `fre_bins`: 每一个FFT点对应的频点 + `signal_fre_bins`: a m*n*l tensor, in which m equals to number of + antennas, n is equals to point of FFT, l is the number of groups + `fre_bins`: corresponding freqeuncy of each point in FFT output """ num_snapshots = received_data.shape[1] - n_each_group = num_snapshots // num_groups # 每一组包含的采样点数 + # number of sampling points in each group + n_each_group = num_snapshots // num_groups if n_each_group < 128: - n_fft = 128 # 如果点数太少, 做FFT时补零 + n_fft = 128 # zero padding when sampling points is not enough else: n_fft = n_each_group signal_fre_bins = np.zeros((received_data.shape[0], n_fft, num_groups), dtype=np.complex_) - # 每一组独立做FFT + # do FTT separately in each group for group_i in range(num_groups): signal_fre_bins[:, :, group_i] = np.fft.fft( received_data[:, group_i * n_each_group:(group_i+1) * n_each_group], diff --git a/classical_doa/arrays.py b/classical_doa/arrays.py index 9ca9bb4..d9c1388 100644 --- a/classical_doa/arrays.py +++ b/classical_doa/arrays.py @@ -2,7 +2,7 @@ import numpy as np -C = 3e8 # 波速 +C = 3e8 # wave speed class Array(ABC): @@ -24,40 +24,27 @@ def set_rng(self, rng): """ self._rng = rng - def steering_vector(self, fre, azimuth, elevation=None, unit="deg"): - """计算某一入射角度对应的导向矢量 - - Args: - fre (float): 入射信号的频率 - azimuth (float): 入射信号的方位角 - elevation (float): 入射信号的俯仰角. Defaults to None, 如果 - `elevation` 为 None 只考虑一维信号. - unit (str): {'rad', 'deg'}, 入射角的角度制,'rad'代表弧度, - 'deg'代表角度,默认为'deg' - - Returns: - steering_vector (ndarray): 导向矢量 - """ - raise NotImplementedError() - def received_signal(self, signal, snr, angle_incidence, amp=None, broadband=False, unit="deg"): """Generate array received signal based on array signal model - 如果`broadband`为True, 生成宽带信号的仿真. + If `broadband` is set to True, generate array received signal based on + broadband signal's model. Args: - signal: Signal 类实例化的对象 - snr: 信噪比 - angle_incidence: 入射角度. 如果只考虑方位角, `angle_incidence`是 - 一个1xN维矩阵; 如果考虑二维, `angle_incidence`是一个2xN维矩阵, - 其中第一行为方位角, 第二行为俯仰角. - amp: 每个信号的幅度, 1d numpy array - broadband: 是否生成宽带接受信号 - unit: 角度的单位制, `rad`代表弧度制, `deg`代表角度制. Defaults to - 'deg'. + signal: An instance of the `Signal` class + snr: Signal-to-noise ratio + angle_incidence: Incidence angle. If only azimuth is considered, + `angle_incidence` is a 1xN dimensional matrix; if two dimensions + are considered, `angle_incidence` is a 2xN dimensional matrix, + where the first row is the azimuth and the second row is the + elevation angle. + amp: The amplitude of each signal, 1d numpy array + broadband: Whether to generate broadband received signals + unit: The unit of the angle, `rad` represents radian, + `deg` represents degree. Defaults to 'deg'. """ - # 将角度转换为弧度 + # Convert the angle from degree to radians if unit == 'deg': angle_incidence = angle_incidence / 180 * np.pi @@ -87,24 +74,26 @@ def _gen_broadband(self, signal, snr, angle_incidence, amp): class UniformLinearArray(Array): def __init__(self, m: int, dd: float, rng=None): - """均匀线阵 + """Uniform linear array. Args: m (int): number of antenna elements dd (float): distance between adjacent antennas rng (np.random.Generator): random generator used to generator random """ - # 阵元位置应该是一个Mx1维矩阵,用于后续计算导向矢量 + # array position should be a 2d Mx1 numpy array super().__init__(np.arange(m).reshape(-1, 1) * dd, rng) def _gen_narrowband(self, signal, snr, angle_incidence, amp): - """ULA时, angle_incidence应该是一个对应方位角的行向量""" + """We only consider azimuth when use ULA, so `angle_incidence` should + be a 1xN array. + """ azimuth = angle_incidence.reshape(1, -1) num_signal = azimuth.shape[1] - # 计算时延矩阵 + # calculate the time delay matrix matrix_tau = 1 / C * self._element_position @ np.sin(azimuth) - # 计算流形矩阵 + # calcualte the manifold matrix manifold_matrix = np.exp(-1j * 2 * np.pi * signal.frequency * matrix_tau) diff --git a/classical_doa/plot.py b/classical_doa/plot.py index bd47b90..57a45fc 100644 --- a/classical_doa/plot.py +++ b/classical_doa/plot.py @@ -3,21 +3,22 @@ from scipy.signal import find_peaks -def plot_spatial_specturm(spectrum, ground_truth, angle_grids, +def plot_spatial_spectrum(spectrum, ground_truth, angle_grids, peak_threshold=0.5, x_label="Angle", - y_label="Spetrum"): - """绘制空间谱 + y_label="Spectrum"): + """Plot spatial spectrum Args: - spectrum: 算法估计的空间谱 - ground_truth: 真是入射角度 - angle_grids: 空间谱对应的角度网格点 - peak_threshold: 用于寻找峰值的阈值(相对于最大值的比例) - x_label: x轴标度 - y_label: y轴标度 + spectrum: Spatial spectrum estimated by the algorithm + ground_truth: True incident angles + angle_grids: Angle grids corresponding to the spatial spectrum + peak_threshold: Threshold (relative to the maximum value) used to find + peaks + x_label: x-axis label + y_label: y-axis label """ spectrum = spectrum / np.max(spectrum) - # find peaks and peaks' height + # find peaks and peak heights peaks_idx, heights = find_peaks(spectrum, height=np.max(spectrum) * peak_threshold) angles = angle_grids[peaks_idx] @@ -59,16 +60,18 @@ def plot_spatial_specturm(spectrum, ground_truth, angle_grids, def plot_estimated_value(estimates, ground_truth, ticks_min=-90, ticks_max=90, - x_label="Angle", y_label="Spetrum"): - """展示估计的角度值 + x_label="Angle", y_label="Spectrum"): + """Display estimated angle values Args: - estimates: 角度估计值 - ground_truth: 真实入射角 - ticks_min (int, optional): x轴刻度的最小值. Defaults to -90. - ticks_max (int, optional): x轴刻度的最大值. Defaults to 90. - x_label (str, optional): x轴标度. Defaults to "Angle". - y_label (str, optional): y轴标度. Defaults to "Spetrum". + estimates: Angle estimates + ground_truth: True incident angles + ticks_min (int, optional): Minimum value for x-axis ticks. + Defaults to -90. + ticks_max (int, optional): Maximum value for x-axis ticks. + Defaults to 90. + x_label (str, optional): x-axis label. Defaults to "Angle". + y_label (str, optional): y-axis label. Defaults to "Spetrum". """ fig = plt.figure() ax = fig.add_subplot(1, 1, 1) diff --git a/classical_doa/signals.py b/classical_doa/signals.py index 38417a6..b37b1ba 100644 --- a/classical_doa/signals.py +++ b/classical_doa/signals.py @@ -4,9 +4,10 @@ class Signal(ABC): - """所有信号的基类 + """Base class for all signal classes - 继承此基类的信号必须要实现gen()方法, 用来产生仿真的采样信号 + Signals that inherit from this base class must implement the gen() method to + generate simulated sampled signals. """ def __init__(self, nsamples, fs, rng=None): self._nsamples = nsamples @@ -27,25 +28,27 @@ def set_rng(self, rng): """Setting random number generator Args: - rng (np.random.Generator): random generator used to generator random + rng (np.random.Generator): random generator used to generate random + numbers """ self._rng = rng @abstractmethod def gen(self, n, amp=None): - """产生采样后的信号 + """Generate sampled signals Args: - n (int): 信号的数目 - amp (np.array): 信号的幅度(n元素的1d array), 用来定义不同信号的不同 - 幅度, 默认为等幅信号 + n (int): Number of signals + amp (np.array): Amplitude of the signals (1D array of size n), used + to define different amplitudes for different signals. + By default it will generate equal amplitude signal. Returns: - signal (np.array): 采用后的信号 + signal (np.array): Sampled signals """ self.n = n - # 默认生成等幅信号 + # Default to generate signals with equal amplitudes if amp is None: self.amp = np.diag(np.ones(n)) else: @@ -54,13 +57,15 @@ def gen(self, n, amp=None): class ComplexStochasticSignal(Signal): def __init__(self, nsamples, fre, fs, rng=None): - """随机复信号(随机相位信号的复数形式) + """Complex stochastic signal (complex exponential form of random phase + signal) Args: - nsamples (int): 采样点数 - fre (float): 信号的频率 - fs (float): 采样频率 - rng (np.random.Generator): random generator used to generator random + nsamples (int): Number of sampling points + fre (float): Signal frequency + fs (float): Sampling frequency + rng (np.random.Generator): Random generator used to generate random + numbers """ super().__init__(nsamples, fs, rng) @@ -68,13 +73,13 @@ def __init__(self, nsamples, fre, fs, rng=None): @property def frequency(self): - """frequency of signal (narrowband)""" + """Frequency of the signal (narrowband)""" return self._fre def gen(self, n, amp=None): super().gen(n, amp) - # 产生复包络 + # Generate complex envelope envelope = self.amp @ (np.sqrt(1 / 2) *\ (self._rng.standard_normal(size=(self.n, self._nsamples)) +\ 1j * self._rng.standard_normal(size=(self.n, self._nsamples)))) @@ -90,25 +95,25 @@ def __init__(self, nsamples, fs, f0, f1, t1=None, rng=None): """Chirp signal Args: - nsamples (int): number of sampling points - f0 (np.array): start frequency at time 0. An 1d array of size n - f1 (np.array): frequency at time t1. An 1d array of size n - t1 (np.array): time at which f1 is specified. An 1d array of size n - fs (int | float): sampling frequency + nsamples (int): Number of sampling points + f0 (np.array): Start frequency at time 0. An 1D array of size n + f1 (np.array): Frequency at time t1. An 1D array of size n + t1 (np.array): Time at which f1 is specified. An 1D array of size n + fs (int | float): Sampling frequency """ super().__init__(nsamples, fs, rng) self._f0 = f0 if t1 is None: t1 = np.full(f0.shape, nsamples / fs) - self._k = (f1 - f0) / t1 # rate of frequency change + self._k = (f1 - f0) / t1 # Rate of frequency change def gen(self, n, amp=None): super().gen(n, amp) signal = np.zeros((self.n, self._nsamples), dtype=np.complex_) - # generate signal one by one + # Generate signal one by one for i in range(self.n): sampling_time = np.arange(self._nsamples) * 1 / self._fs signal[i, :] = np.exp(1j * 2 * np.pi * (self._f0[i] * sampling_time diff --git a/examples/narrowband_ula.ipynb b/examples/narrowband_ula.ipynb index f531403..6f39d7d 100644 --- a/examples/narrowband_ula.ipynb +++ b/examples/narrowband_ula.ipynb @@ -117,7 +117,7 @@ "metadata": {}, "outputs": [], "source": [ - "from classical_doa.plot import plot_estimated_value, plot_spatial_specturm" + "from classical_doa.plot import plot_estimated_value, plot_spatial_spectrum" ] }, { @@ -144,7 +144,7 @@ " unit=\"deg\")\n", "\n", "# 绘制空间谱\n", - "plot_spatial_specturm(spectrum=music_spectrum, angle_grids=search_grids,\n", + "plot_spatial_spectrum(spectrum=music_spectrum, angle_grids=search_grids,\n", " ground_truth=angle_incidence)" ] }, @@ -232,7 +232,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.5" + "version": "3.11.6" } }, "nbformat": 4,