用于脉冲信号处理的奈奎斯特滤波器的实现
在现代数字通信系统中,信息传输的质量直接取决于信号处理的效率,其中形成滤波器起着关键作用。 其中,具有根凸起余弦(RRC)的滤波器特别重要,它提供了频谱效率和对码间干扰(ISI)的抵抗之间的最佳折衷。 这些滤波器已成为大多数现代通信系统的事实上的标准,包括5g蜂窝网络、卫星通信和高速调制解调器。
然而,设计RRC滤波器是一项复杂的工程任务,需要考虑许多参数:滚降因子、以字符为单位的滤波器长度、每个字符的样本数和数据表示的准确性。 开发此类滤波器的传统方法涉及费力的迭代过程,包括计算系数、选择实现结构、优化位深度和验证特性。 增加特定复杂性的是,需要在移动到定点硬件实现时考虑量化的影响。
本文演示了使用平滑系数为0.2,长度为10个字符和每个字符8个样本的RRC滤波器示例的方法的实际实现,包括直接和转置结构的比较分析,频率和时
让我们继续开发,下面的代码计算具有根余弦特性(rrc)的FIR滤波器的系数,该滤波器是现代数字通信系统中的主要整形滤波器。 功能 rrcosfilter 根据指定的参数计算滤波器的脉冲响应:滚降因子平滑系数、以字符为单位的滤波器长度以及每个字符的样本数。 滤波器确保了发射信号频谱的最佳形成,最大限度地减少了码间干扰并限制了频带。 系数的计算是滤波器设计的第一阶段及其基础。
using FFTW, DSP
function rrcosfilter(β::Float64, span::Int, sps::Int)
T = 1.0
t = range(-span/2, stop=span/2, length=span*sps+1)
h = similar(t)
ϵ = 1e-10
for (i, τ) in enumerate(t)
if abs(τ) < ϵ
h[i] = 1.0 - β + 4β/π
elseif abs(abs(τ) - T/(4β)) < ϵ
h[i] = (β/√2) * ((1 + 2/π)*sin(π/(4β)) + (1 - 2/π)*cos(π/(4β)))
else
num = sin(π*τ/T*(1-β)) + 4β*τ/T * cos(π*τ/T*(1+β))
den = π*τ/T * (1 - (4β*τ/T)^2)
h[i] = num / den
end
end
return h / √sum(h.^2)
end
rolloff_factor = 0.2
filter_span = 10
samples_per_symbol = 8
coef = rrcosfilter(rolloff_factor, filter_span, samples_per_symbol)
coef = coef[1:2:end]
println("Rolloff factor: ", rolloff_factor)
println("Filter span in symbols: ", filter_span)
println("Output samples per symbol: ", samples_per_symbol)
println("Number of coefficients: ", length(coef))
plot(coef)
FIR滤波器的直接形式是有限脉冲滤波器的经典实现结构,其中输入信号依次通过延迟元件链,其每个输出乘以相应的滤波器系数,并将结果求和以获得
代码说明:
该代码基于先前计算的RRC系数自动创建fir滤波器的直接形式的模型。 首先,生成唯一的模型名称,并确定具有16位和14小数位的定点格式。 系数转换为定点格式。
然后,在环路中创建滤波器结构:对于每个系数,添加增益,延迟和加法单元。 形成延迟元件的链,其中每个延迟信号乘以相应的系数并与先前的结果求和。 第一个系数处理当前输入信号,后续系数处理信号的延迟版本。
输入信号同时施加到第一增益元件和第一延迟,从而产生级联结构。 所有数学运算都以定点格式执行,以确保与硬件实现的兼容性。 构建完整结构后,模型将保存到文件中并上传以供进一步使用,从而以最小的开发人员干预提供过滤器架构的自动创建。
name_model = "fir_$(round(Int, rand() * 10000))"
Path = (@__DIR__) * "/" * name_model * ".engee"
println("Path: $Path")
FIXED_POINT_TYPE = "fixdt(1, 16, 14)"
coef = fi(coef, 1, 16, 15)
engee.create(name_model) # 创建模型
engee.add_block("/Basic/Ports & Subsystems/In1", name_model*"/")
engee.add_block("/Basic/Ports & Subsystems/Out1", name_model*"/")
# 设置输入端口的定点数据类型
engee.set_param!(name_model*"/In1",
"OutDataTypeStr" => "Fixed-point",
"OutDataTypeStrFixed" => FIXED_POINT_TYPE)
# 我们确定系数的数量
num_coef = length(coef)
@time for n in 1:num_coef-1
name_gain = "Gain-" * string(n)
engee.add_block("/Basic/Math Operations/Gain", name_model*"/"*name_gain)
engee.set_param!(name_model*"/"*name_gain,
"Gain" => coef[n],
"OutDataTypeStr" => "Fixed-point",
"OutDataTypeStrFixed" => FIXED_POINT_TYPE)
name_delay = "Delay-" * string(n)
engee.add_block("/Basic/Discrete/Unit Delay", name_model*"/"*name_delay) # 用UnitDelay替换延迟
name_add = "Add-" * string(n)
engee.add_block("/Basic/Math Operations/Add", name_model*"/"*name_add)
engee.set_param!(name_model*"/"*name_add,
"OutDataTypeStr" => "Fixed-point",
"OutDataTypeStrFixed" => FIXED_POINT_TYPE)
if n == 1
engee.add_line(name_gain*"/1", name_add*"/1")
end
if n > 1
name_delay_prev = "Delay-" * string(n-1)
engee.add_line(name_delay_prev*"/1", name_delay*"/1")
engee.add_line(name_delay_prev*"/1", name_gain*"/1")
name_add_prev = "Add-" * string(n-1)
engee.add_line(name_add_prev*"/1", name_add*"/1")
engee.add_line(name_gain*"/1", name_add_prev*"/2")
end
if n == num_coef-1
name_gain_last = "Gain-" * string(n+1)
engee.add_block("/Basic/Math Operations/Gain", name_model*"/"*name_gain_last)
engee.set_param!(name_model*"/"*name_gain_last,
"Gain" => coef[n+1],
"OutDataTypeStr" => "Fixed-point",
"OutDataTypeStrFixed" => FIXED_POINT_TYPE)
engee.add_line(name_delay*"/1", name_gain_last*"/1")
engee.add_line(name_gain_last*"/1", name_add*"/2")
engee.add_line(name_add*"/1", "Out1/1")
end
end
engee.add_line("In1/1", "Gain-1/1")
engee.add_line("In1/1", "Delay-1/1")
engee.save(Path)
model = engee.load(Path, force=true)
FIR滤波器的转置形式是一种替代的实现结构,其中所有乘以系数的运算都与输入信号并行执行,并且结果通过加法器和延迟元件的链顺序累加。
代码说明:
该代码创建了一个转置的FIR滤波器结构,它与直线形式有根本的不同。 在这种架构中,输入信号同时施加到所有乘法单元(增益),每个乘法单元将其乘以相应的系数。 然后通过一个加法器链依次求和得到的产物,在这些加法器之间插入延迟元件。 这就产生了流水线,数据在每个时钟周期流经整个结构。
Verilog代码生成的转换形式的优点:
- 最小临界延迟-从输入到输出的路径仅包含一个加法器和一个延迟元件,可让您实现更高的时钟频率
- 自然流水线-结构是理想的输送机加工,增加吞吐量
- *并行乘法—-所有乘法运算同时执行,这简化了硬件计算的并行化
- 简化路由—在Fpga中使用单个数据源实现并行结构更容易
- 更好的可扩展性-添加新系数不会增加关键路径
name_model = "firT_$(round(Int, rand() * 10000))"
Path = (@__DIR__) * "/" * name_model * ".engee"
println("Path: $Path")
FIXED_POINT_TYPE = "fixdt(1, 16, 14)"
coef = fi(coef, 1, 16, 15)
# 创建模型
engee.create(name_model)
# 创建转置结构
engee.add_block("/Basic/Ports & Subsystems/In1", name_model*"/")
engee.add_block("/Basic/Ports & Subsystems/Out1", name_model*"/")
num_coef = length(coef)
# 第一阶段是特殊的-只有加法器
engee.add_block("/Basic/Math Operations/Add", name_model*"/Add-1")
engee.add_block("/Basic/Math Operations/Gain", name_model*"/Gain-1")
engee.set_param!(name_model*"/Gain-1",
"Gain" => coef[1],
"OutDataTypeStr" => "Fixed-point",
"OutDataTypeStrFixed" => FIXED_POINT_TYPE)
# 为第一加法器添加固定点
engee.set_param!(name_model*"/Add-1",
"OutDataTypeStr" => "Fixed-point",
"OutDataTypeStrFixed" => FIXED_POINT_TYPE)
engee.add_line("In1/1", "Add-1/1")
engee.add_line("In1/1", "Gain-1/1")
engee.add_line("Gain-1/1", "Add-1/2")
# 中间级联(从2到num_coef)
for n in 2:num_coef
name_delay = "Delay-" * string(n-1)
name_add = "Add-" * string(n)
name_gain = "Gain-" * string(n)
engee.add_block("/Basic/Discrete/Unit Delay", name_model*"/"*name_delay)
engee.add_block("/Basic/Math Operations/Add", name_model*"/"*name_add)
engee.add_block("/Basic/Math Operations/Gain", name_model*"/"*name_gain)
engee.set_param!(name_model*"/"*name_gain,
"Gain" => coef[n],
"OutDataTypeStr" => "Fixed-point",
"OutDataTypeStrFixed" => FIXED_POINT_TYPE)
engee.set_param!(name_model*"/"*name_delay, "SampleTime" => st)
# 为加法器添加固定点
engee.set_param!(name_model*"/"*name_add,
"OutDataTypeStr" => "Fixed-point",
"OutDataTypeStrFixed" => FIXED_POINT_TYPE)
# 联系
if n == 2
engee.add_line("Add-1/1", name_delay*"/1")
else
prev_add = "Add-" * string(n-1)
engee.add_line(prev_add*"/1", name_delay*"/1")
end
engee.add_line(name_delay*"/1", name_add*"/1")
engee.add_line("In1/1", name_gain*"/1") # 输入对于所有系数
engee.add_line(name_gain*"/1", name_add*"/2")
end
# 我们把最后一个加法器的输出
last_add = "Add-" * string(num_coef)
engee.add_line(last_add*"/1", "Out1/1")
engee.save(Path)
model = engee.load(Path, force=true)
对于Verilog代码生成转置形式是优选的,因为它直接对应于具有流水线的高频硬件实现,提供更好的时间性能和更有效地利用FPGA资源,同时保持与直接形式
在工作过程中,实现了单个脉冲的插值和抽取过程。 初始脉冲由矢量设置 impulse = [1.0; zeros(100)] 然后用滤波器系数进行卷积 coef 来产生所述发射信号 tx_signal.
在接收侧,执行了反向操作-具有相同系数的接收信号的卷积,这给出了重建信号。 rx_signal. 结果的可视化-绘制初始脉冲,插值和抽取信号-显示出它们的高度相似性,这证实了算法实现的正确性。
impulse = [1.0; zeros(100)]
tx_signal = DSP.conv(impulse, coef)
rx_signal = DSP.conv(tx_signal, coef)
plot(impulse)
plot!(tx_signal)
plot!(rx_signal)
使用我们实施的算法在模型中成功地再现了类似的过程进行验证,从而进一步验证了方法的正确性。 生成的模型是一个现成的解决方案,适用于用Verilog语言生成代码。
# 定义当前目录
current_dir = @__DIR__
# 显示文件夹结构
println("目录下的文件夹结构:$current_dir")
println()
for (root, dirs, files) in walkdir(current_dir)
for dir in dirs
if startswith(dir, "one_impulse_Nyquist_filter")
println("$dir/")
dir_path = joinpath(root, dir)
for file in readdir(dir_path)
println(" └── $file")
end
println()
end
end
end
生成的Verilog代码是用于脉冲信号处理的发射(TX)和接收(RX)奈奎斯特滤波器的硬件实现。 主要组件:
-
顶级模块(
one_impulse_Nyquist_filter_TX/RX.v):-管理数据同步和验证
-包含用于数据流控制的有限状态机
-集成FIR滤波器和延迟单元
-两个滤波器使用相同的处理内核(FIR和延迟),但它们在控制逻辑和激活条件上有所不同。 -
FIR滤波器(
fir_liner_1.v):-实现线性FIR滤波器(41个系数)
-使用固定系数产生脉冲响应
-处理签名的16位数据 -
延迟单元(
Delay_40.v):-实现40个延迟寄存器链
-同步有效信号与滤波延迟
-提供临时数据核对
结论
仿真证实了脉冲处理实现的正确性。 在数学环境和硬件模型中获得的结果的相似性证明了算法的正确性和所选择的滤波器系数的准确性。
成功生成的硬件代码代表了成熟的发送和接收模块。 它们建立在一个共同的滤波器核心上,仅在控制逻辑上有所不同,这确保了传输的能量效率和接收的连续性。 该代码是模块化的,可用于合成,以便在目标数字平台上实现。