以带有初始数据的典型项目为例,介绍 Engee 中的数字信号处理技术
在本示例中,我们将通过 Engee 演示一个典型的数据处理、滤波和分析项目。数据将从二进制文件中加载,这些文件定义了底层混凝土表面和输出信号的特征。
开始处理时,我们将从 .ana 文件中读取原始信号的振幅值:
# Pkg.add(["XMLDict", "ContinuousWavelets", "DelimitedFiles", "DSP", "MAT", "Wavelets"])
Pkg.add("DelimitedFiles")
using DelimitedFiles
filename = joinpath(("$(@__DIR__)/signal/sig0001.ana"))
io = open(filename, "r") # Открываем файл в режиме чтения двоичных данных
A = reinterpret(Float32, read(io)) # Читаем весь файл целиком в массив типа Float32
close(io) # Закрываем файл
println("Количество элементов: ", length(A))
println("Первые несколько элементов массива:")
show(A[1:min(length(A), 5)])
现在从 .xml 文件中读取输入信号的频率值:
Pkg.add("XMLDict")
using XMLDict
# Открываем файл
filename = joinpath("$(@__DIR__)/signal/sig0001.xml") # Предполагаю, что nom - строковая переменная с путём к директории
xml_data = read(filename, String)
# Находим индекс начала строки с частотой
k = findfirst("frequency", xml_data)
if isnothing(k)
error("Строка 'frequency' не найдена.")
end
# Извлекаем значение частоты
i = k.start + 11
ss = Char[]
while xml_data[i] != '"'
push!(ss, xml_data[i])
i += 1
end
# Преобразуем строку в число
fd = parse(Float64, join(ss))
# Выводим результат
println("Частота: ", fd)
根据获得的信号频率值,我们创建一个时间采样向量。
t = 0:(1/fd):((1/fd)*(length(A)-1))
将计算值和信号参数保存到 MAT 文件中。
using MAT
# Преобразуйте t в Vector
vectorized_t = collect(t)
# Создайте словарь с данными
variables = Dict(
"A" => A,
"fd" => fd,
"vectorized_t" => vectorized_t
)
# Сохранить данные в файл .mat
filename = joinpath(@__DIR__, "signal/res.mat")
matwrite(filename, variables)
加载之前创建的低通滤波器的系数:
# Загрузка данных из файла f2500.mat
file = matopen(joinpath(@__DIR__, "signal", "f2500.mat")) # Используем joinpath для безопасного построения пути
vars = read(file)
close(file)
# Доступ ко всем загруженным данным
# for var in vars
# println(var)
# end
# Получение значения Num2
Num2 = vec(vars["Num2"])
假设我们需要将输入信号的采样频率从 2500 Hz 变为 200 Hz。让我们找出这些频率的比值,以设定采样频率变化的倍数,并形成正确的滤波算法。
rat = rationalize(200/2500)
up = rat.num
down = rat.den
print("Числитель и знаменатель: $rat")
首先将采样频率提高 2 倍,然后降低 25 倍。让我们以递减的采样频率进行滤波。
滤波后,计算新的时间采样,并绘制输入和输出信号振幅比较图。
using DSP
using FFTW
new_a = vcat([A zeros(length(A))]...) # Создаем новый массив new_a, чередуя элементы A с нулями
diskr = DSP.conv(new_a, Num2) # Фильтрация
new_A = [diskr[1]] # Уменьшаем частоту дискретизации
for i in 1+down:down:length(diskr)
push!(new_A, diskr[i])
end
new_t = 0:(down/(up*fd)):(length(new_A)-1)*(down/(up*fd))
plot(t,A)
plot!(new_t,new_A)
我们可以看到,输出信号的时间长度大大缩短,而信号行为的总体趋势却得以保留。
现在,让我们构建一个经典的傅里叶频谱,对输入和输出信号进行频率分析。
# Расчёт спектра сигнала
function compute_spectrum(signal, fs)
n = length(signal)
spectrum = abs.(fft(signal)) / n
freqs = (0:n-1) .* (fs / n)
spectrum[1:Int(n/2)], freqs[1:Int(n/2)] # Вернуть половину спектра (для удобства)
end
spectrum_A, freqs_A = compute_spectrum(A, 2500)
plot(freqs_A, spectrum_A, xlabel="Frequency (Hz)", ylabel="Amplitude", legend=:topright)
spectrum_A, freqs_A = compute_spectrum(new_A, 200)
plot(freqs_A, spectrum_A, xlabel="Frequency (Hz)", ylabel="Amplitude", legend=:topright)
通过频谱分析我们可以看到,输出信号的振幅比输入信号小,频率范围比输入信号窄。
接下来,让我们进行小波变换。它用于分析信号、声音或图像等时间序列,以识别它们在时间和频率上的局部特征。
它允许同时在时间和频率两个域中表示信号,因此在分析频率随时间变化的非稳态信号时特别有用。
Pkg.add("Wavelets")
Pkg.add("ContinuousWavelets")
using ContinuousWavelets, Wavelets
让我们来详细分析一下下面描述的代码。
小波是一个创建波形转换器对象的函数。在我们的例子中,它需要三个参数。
- morlet 是参数为 π 的莫列特波函数。Morlet 函数能有效提取不同尺度的信号特征,因此被广泛应用于信号分析。
averagingType=NoAve() 定义了平均化类型。此处选择无平均。这意味着结果数组中的每个点都将对应于波形系数的值,而无需额外的平滑处理。
β 是决定莫雷特函数窗口宽度的参数。β 值越大,窗口越窄,时域信号定位越精确,但频域分辨率越差。
cwt - 该函数使用创建的波形转换器对源信号执行连续波变换(CWT)。
c = wavelet(Morlet(π*2), averagingType=NoAve(), β=3)
res = cwt(new_A, c)
heatmap(abs.(res)', xlabel= "time",ylabel="frequency")
xlims!(0, 3000)
让我们执行傅立叶频率-时间变换。Engee 中没有用于此类变换的标准函数,必须手动执行。下面是详细的实现方法。
# Окно Хэмминга
function hamming(N::Int)
n = 0:N-1
return 0.54 .- 0.46 .* cos.(2π * n / (N - 1))
end
l=6000 # Размер окна
window=vcat(hamming(l),zeros(length(new_A)-l))
P = DSP.periodogram(new_A; fs=200, window=window, nfft=length(new_A))
Power_log = 20*log10.(power(P));
Freq = freq(P);
# Визуализация спектра
plot(Freq, Power_log, xlabel="Frequency (Hz)", ylabel="Power Spectrum (dB)", title="Power Spectrum", linewidth=2)
# Вычисление спектограммы
S = DSP.spectrogram(new_A, fs=200,)
# Построение графика
heatmap(S.time, S.freq, S.power, xlabel="Время, с", ylabel="Частота, Гц", title="Диаграмма мощности сигнала", colorbar_title="Мощность, м²/c⁴",xlim =(0, 15))
根据上述图表,我们可以确定
- 哪些频率主导我们的信号;
- 这些频率是如何随时间分布的。
现在我们来进行 cepstra 分析。
倒频谱是一种基于信号对数频谱傅里叶变换的信号分析方法。倒频谱广泛应用于语音信号处理、声学和振动分析。倒频谱有多种类型,每种类型都能提供有关信号的独特信息。
在 Engee 中,FFTW 软件包和执行傅立叶变换的标准函数可用于进行倒频谱分析。以下是我们已经实现的函数。
using FFTW # Для выполнения быстрого преобразования Фурье
# Комплексный кепстр
function cceps(x::Vector{Float64})
X = fft(x,1) # Фурье-преобразование
p = atan.(imag(X), real(X))
log_mag = complex.(log.(abs.(X)),p) # Логарифм модуля спектра (добавляем малое значение для стабильности)
cepstrum = real(ifft(log_mag,1)) # Обратное преобразование Фурье
return cepstrum
end
# Обратный комплексный кепстр
function icceps(c::Vector{Float64})
exp_spectrum = exp.(fft(c,1)) # Экспонента спектра
signal = real(ifft(exp_spectrum,1)) # Обратное преобразование Фурье
return signal
end
# Действительный кепстр
function rceps(x::Vector{Float64})
X = fft(x,1)
log_mag = log.(abs.(X) .+ 1e-10) # Логарифм модуля (с добавлением стабилизатора)
cepstrum = real(ifft(log_mag))
return cepstrum
end
cepstrum = cceps(new_A)
restored_signal = abs.(icceps(cepstrum))
real_cepstrum = abs.(rceps(new_A))
# # Визуализация
p1 = plot(abs.(cepstrum), title="Cepstrum", ylabel="Magnitude")
p2 = plot(abs.(restored_signal), title="Restored Signal", ylabel="Amplitude")
p3 = plot(abs.(real_cepstrum), title="Real Cepstrum", ylabel="Amplitude")
plot(p1, p2, p3, layout=(3,1), legend=false)
复倒频谱提供的信息:
1.复倒谱有助于分离信号源,尤其是当信号源具有不同的时间延迟时。例如,这在回声消除或多径传播识别系统中非常有用。
2.由于复倒谱包含相位信息,因此可用于检测和纠正信号中的相位失真。
反复倒频谱提供的信息如下。
1- 通过存储相位信息,逆复倒频谱可以在处理后准确地重建原始信号。
2 反复倒频谱由于保留了重要的信号成分,因此有助于去除噪声和干扰。
有效倒频谱提供的信息如下。
1.有效倒频谱通常用于估计语音或音乐的基音,因为倒频谱中的峰值与信号的周期成分相对应。
2.有效倒频谱能有效抑制加性白噪声,因为它能平滑对数频谱。
结论
在这个例子中,我们揭示了数字信号处理的原理,并应用了各种分析这些信号的方法来解决不同的工程问题。