Skip to content
Snippets Groups Projects
Commit f8dc995f authored by lu.zhao@synchrotron-soleil.fr's avatar lu.zhao@synchrotron-soleil.fr
Browse files

add code comments to class PhaseNoiseGenerator

parent 5f29cbb1
Branches
No related tags found
1 merge request!37Feature feedback iq damper0
......@@ -2,32 +2,32 @@ import numpy as np
class PhaseNoiseGenerator:
"""
用于生成相位噪声序列的示例类。
可以根据需求定义不同类型的噪声模型:
- 白噪声
- 1/f 噪声
- 由给定PSD(Phase Noise Spectrum)生成时域噪声
- 等等
Example class for generating phase noise sequences.
Different types of noise models can be defined as needed:
- White noise
- 1/f noise
- Time-domain noise generated from a given PSD (Phase Noise Spectrum)
- Etc.
Parameters
----------
fs : float
采样频率 (若需要离线一次性生成整条噪声序列)
Sampling frequency (if generating the entire noise sequence offline at once)
n_samples : int
需要一次性生成多少点的噪声 (也可不固定)
Number of noise points to generate at once (can also be non-fixed)
noise_amplitude : float
噪声幅度因子(可理解为标准差或用于标定噪声大小)
Noise amplitude factor (can be understood as the standard deviation or used to calibrate noise magnitude)
model : str
噪声模型类型,可自定义 "white", "1/f", "custom_psd"
Type of noise model, customizable as "white", "1/f", "custom_psd", etc.
custom_psd : array-like, optional
用户自定义相位噪声谱密度(若需要)
User-defined phase noise power spectral density (if needed)
freq_vector : array-like, optional
对应 custom_psd 的频率坐标
Frequency coordinates corresponding to custom_psd
Usage
-----
1. init PhaseNoiseGenerator(fs, n_samples, noise_amplitude, ...)
2. 在每一次跟踪/每一圈调用 generate_noise() 获取噪声值(或序列)
1. Initialize PhaseNoiseGenerator(fs, n_samples, noise_amplitude, ...)
2. Call generate_noise() during each tracking step/turn to get noise values (or sequences)
"""
def __init__(self,
......@@ -44,9 +44,9 @@ class PhaseNoiseGenerator:
self.custom_psd = custom_psd
self.freq_vector = freq_vector
# 如果要一次性离线生成,可以在这里生成整条时间序列
# if generated_noise offline, generate it here
self.generated_noise = None
self.index = 0 # 用于记录当前取到哪一个采样点
self.index = 0 # record the current sample point
if model == "white":
self._generate_white_noise()
......@@ -55,11 +55,11 @@ class PhaseNoiseGenerator:
elif model == "custom_psd" and (custom_psd is not None):
self._generate_custom_psd_noise()
else:
# 默认白噪声
# default to white noise if model is not recognized
self._generate_white_noise()
def _generate_white_noise(self):
""" 简单白噪声生成 """
""" wite noise generation """
self.generated_noise = np.random.normal(
loc=0.0,
scale=self.noise_amplitude,
......@@ -67,63 +67,68 @@ class PhaseNoiseGenerator:
)
def _generate_1overf_noise(self):
""" 1/f 噪声生成(示例:通过频域滤波实现) """
# 频域产生白噪声
""" 1/f noise generation by filter in frequement domain """
# white noise generation in frequency domain
white = np.fft.rfft(
np.random.normal(0.0, 1.0, self.n_samples)
)
freqs = np.fft.rfftfreq(self.n_samples, 1.0/self.fs)
# 对应 ~ 1/f 滤波
# ~ 1/f filtering
H = 1.0 / (freqs + 1e-6)
colored = white * H
# 逆变换回时域
# transform back to time domain
out = np.fft.irfft(colored, n=self.n_samples)
# 调整幅度
# adjust amplitude to user specified level
out = out / np.std(out) * self.noise_amplitude
self.generated_noise = out
def _generate_custom_psd_noise(self):
"""
根据用户给定的相位噪声谱密度 custom_psd 生成时域噪声(示例)。
这里假设 custom_psd freq_vector 二者长度相同,
并且 freq_vector[0] 对应 DCfreq_vector[-1] 对应最高频。
according to the user-defined phase noise power spectral density custom_psd to generate time-domain noise (example).
Here, it is assumed that custom_psd and freq_vector are of the same length,
and freq_vector[0] corresponds to DC, freq_vector[-1] corresponds to the highest frequency.
"""
# 频域内生成符合 PSD 的随机相位
phase_random = np.random.uniform(0, 2*np.pi, len(self.custom_psd))
# 幅度 = sqrt(PSD * 带宽),此处带宽 ~ freq_vector 间隔(简化处理)
# generate random phase in frequency domain
phase_random = np.random.uniform(0, 2*np.pi, len(self.custom_p
# amplitude = np.sqrt(self.custom_psd * df) df = bandwidth = (freq_vector[-1] - freq_vector[0]) / (len(freq_vector)-1)
df = (self.freq_vector[-1] - self.freq_vector[0]) / (len(self.freq_vector)-1)
amplitude = np.sqrt(self.custom_psd * df) # 简化近似
freq_domain_signal = amplitude * np.exp(1j * phase_random)
# 如果需要对称频谱,可拼接负频部分
# 这里仅演示正频部分,实际需要根据具体情况做镜像
# if need to be symmetric, add negative frequency part
# here we assume the custom_psd is symmetric, so we can just mirror the positive frequency part to negative frequency part
# ...
#FFT得到时域信号
#FFT to time domain
time_domain_signal = np.fft.irfft(freq_domain_signal, n=self.n_samples)
# 调整幅度到用户指定量级
# ajuste amplitude to user specified level
time_domain_signal = time_domain_signal / np.std(time_domain_signal) * self.noise_amplitude
self.generated_noise = time_domain_signal
def generate_noise(self, size=1):
"""
每次调用,返回 size 个噪声点(默认 1 个)。
如果一次性生成的序列用完了,就再次生成(或循环使用)。
return size of noise points (default 1).
if the sequence generated at once is used up, generate again (or loop).
Returns
-------
float or np.ndarray
返回相位噪声值(单位:弧度),可直接加到 cavity 或 generator 相位上。
returns phase noise value (in radians), can be directly added to cavity or generator phase.
Examples
--------
>>> noise = PhaseNoiseGenerator(fs=1.0e5, n_samples=1024, noise_amplitude=1e-3)
>>> noise.generate_noise()
"""
if self.generated_noise is None:
# 如果用户没有事先离线生成,就临时生成白噪声
# if not defined, generate white noise
val = np.random.normal(0.0, self.noise_amplitude, size=size)
return val if size > 1 else val[0]
# 如果已离线生成
#if pre-generated noise is used up, generate again or loop
if self.index + size <= self.n_samples:
val = self.generated_noise[self.index:self.index+size]
self.index += size
else:
# 若越界,重新生成或循环
# loop back to the beginning of the generated noise sequence
self.index = 0
val = self.generated_noise[self.index:self.index+size]
self.index += size
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment