Основы ЦОС: генерация сигналов
Рассмотрим основные техники генерации периодических, импульсных и шумовых сигналов для тестирования алгоритмов цифровой обработки сигналов (ЦОС), моделирования систем связи, радаров и радиотехнических систем в целом. Основные темы примера:
- задание периодического сигнала
- временная сетка дискретного сигнала
- генерация типичных периодических сигналов (синусоида, меандр, пила)
- генерация дискретного шума
- последовательности импульсов
- частотно-модулированные сигналы
Используемые в примере библиотеки:
let
intalled_packages = collect(x.name for (_, x) in Pkg.dependencies() if x.is_direct_dep)
list_packages = ["DSP","ChirpSignal","Waveforms"]
for pack in list_packages
pack in intalled_packages || Pkg.add(pack)
end
end
using DSP, ChirpSignal, Waveforms
Генерация периодических сигналов
Периодический сигнал - это повторяющаяся последовательность. Создадим один период значений сигнала, а затем повторим его пять раз функцией repeat
. Визуализируем во времени ступенчатой линией:
one_period = [1; 4; 6; 8; 7; 5; 0; 0; 0; 0];
five_period = repeat(one_period, 5);
plot(five_period,
linewidth = 2,
marker = "circle",
markersize = 3,
linetype=:steppost,
title = "Простой периодический сигнал")
Теперь попробуем сгенерировать периодический сигнал при помощи математических функций. К примеру, функция sin
высчитывает значение синуса для входного значения угла в радианах. Для того, чтобы сгенерировать синусоиду с требуемой частотой повторения, стоит воспользоваться вектором времени - последовательностью равномерно нарастающих отсчётов времени с указанным периодом дискретизации. Зададим вектор времени для сигналов с частотой дискретизации 1000 Гц:
fs = 1000; # частота дискретизации сигнала
dt = 1/fs; # период дискретизации
stoptime = 0.5; # время окончания сигнала
t = 0:dt:stoptime-dt # вектор отсчётов времени
Теперь создадим периодический синусоидальный сигнал с основной частотой 10 Гц (форма синусоиды будет повторяться каждые 0.1 сек). Переменная sine_wave
- это вектор из 500 отсчётов. Визуализируем сигнал функцией plot
:
sine_wave = sin.(2*pi*t*10);
plot(t, sine_wave, title = "Синусоида частотой 10 Гц")
Мы можем одной строчкой кода сгенерировать несколько синусоид с разными амплитудами, частотами и начальными фазами. Для этого зададим эти параметры векторами. Результирующая переменная three_sines
уже будет матрицей размером 500х3. Мы можем передать её функции plot
для одновременного отображения трёх графиков на одних осях:
three_sines = [1.4 0.6 1] .* sin.(2*pi*t.*[10 15 30] .+ [pi/4 0 pi/6]);
plot(t, three_sines, title = "Три синусоиды 10 Гц, 15 Гц и 30 Гц")
Если нам интересно наблюдать форму суммы трёх синусоид, то мы можем сложить столбцы матрицы при помощи функции sum
:
sum_sines = sum(three_sines, dims = 2);
plot(t, sum_sines, title = "Сумма трёх синусоид")
Дополнительные функции для генерации сигналов
Часто нам требуются прямоугольные, пилообразные и треугольные периодические сигналы, а также случайные последовательности для моделирования шума.
Библиотека Waveforms.jl
содержит функции для генерации перечисленных периодических сигналов, но в качестве "задающего" сигнала, определяющего основную частоту, используется равномерно нарастающий пилообразный сигнал, изменяющийся в пределах от 0 до 2pi. Создадим его из вектора времени и зациклим операцией деления mod
на 2pi:
base_signal = mod.(2*pi*t*10, 2*pi);
plot(t, base_signal, title = "Задающий сигнал для функций")
Популярный сигнал в радиотехнике - меандр. Он же прямоугольный периодический сигнал со скважностью 2 (или коэффициентом заполнения в 50%). Мы можем сгенерировать его функцией squarewave
. Также эта функция может принимать дополнительный входной аргумент - коэффициент заполнения в пределах от 0 до 1. Сгенерируем второй прямоугольный сигнал с 20% заполнения:
sqw = squarewave.(base_signal);
sqw02 = squarewave.(base_signal, 0.2);
plot(t, sqw)
plot!(t, sqw02, title = "Прямоугольный сигнал")
Пилообразный сигнал, изменяющийся в пределах от -1 до +1 можно получить функцией sawtoothwave
:
st = sawtoothwave.(base_signal);
plot(t, st, title = "Пилообразный сигнал")
Также рассмотрим функцию trianglewave
для генерации треугольного сигнала:
trw = trianglewave.(base_signal);
plot(t, trw, title = "Треугольный сигнал")
В Engee доступны функции для генерации случайных чисел с различными распределениями. Воспользуемся функцией rand
для создания шумового сигнала с равномерным распределением:
noise = rand.(length(t));
noise = noise .- 0.5;
plot(t, noise, title = "Шум с равномерным распределением")
Теперь попробуем объединить шум и пилообразный сигнал, но сделаем это таким образом, чтобы уровень шума возрастал с уровнем сигнала. При помощи задающего сигнала сгенерируем пилообразный, изменяющийся в пределах от 0 до +1. Затем умножим шум на получившийся пилообразный сигнал и просуммируем нарастающую "пилу" с нарастающим шумом:
clean_sawtooth = mod.(t*10,1); # "чистый" пилообразный сигнал
amp_noise = noise .* clean_sawtooth; # нарастающий шумовой сигнал
noisy_sawtooth = clean_sawtooth .+ (0.25 .* amp_noise); # их сумма
plot(t, noisy_sawtooth, title = "Усиление сигнал и шума")
Генерация непериодических сигналов и пачки импульсов
В качестве примера непериодического сигнала рассмотрим распространённый sinc
-импульс. Мы получаем его при помощи тригонометрической функции и входного вектора значений угла в радианах. В примере мы генерируем импульс в пределах от -2pi до +2pi (с шагом pi/16).
rads = -2*pi:pi/16:2*pi;
rads = rads[1:end-1];
one_sinc = sinc.(rads);
plot(rads, one_sinc, title = "sinc-импульс")
Теперь создадим пачку sinc
-импульсов, следующих с определённым периодом. Для этого добавим к вектору одного импульса некоторое количество нулей в конце, и размножим результат функцией repeat
. Также добавим нашей пачке импульсов затухание - в этом нам поможет функция LinRange
:
zero_padding = [one_sinc; zeros(2*length(one_sinc))]; # sinc-импульс с нулями
four_sinc = repeat(zero_padding, 4); # пачка импульсов без затухания
decay = LinRange(1, 0.4, length(four_sinc)); # сигнал затухания
pulse_train = four_sinc .* decay; # пачка импульсов с затуханием
plot(pulse_train, title = "Пачка sinc-импульсов с затуханием")
Частотно-модулированные сигналы
В заключении рассмотрим типичные частотно-модулированные сигналы, такие как сигналы с линейной частотной модуляцией (ЛЧМ) и сигналы с квадратурной частотной модуляцией. Для создания подобных сигналов мы воспользуемся функцией chirp
:
LFM = chirp(10, fs, 0, 500; method = "linear");
sg1 = DSP.spectrogram(LFM, 511, 256; fs = fs);
heatmap(sg1.time, sg1.freq, pow2db.(sg1.power),
xlabel = "Время, сек",
ylabel = "Частота, Гц",
title = "Спектрограмма ЛЧМ-сигнала")
А для отображения сигналов мы вызовем функцию DSP.spectrogram
, которая визуализирует изменения спектра сигнала (зависимости его мощности от частоты) во времени. Мы можем наблюдать линейный рост частоты для ЛЧМ-сигнала и квадратичный в случае опции method = "quadratic"
:
QFM = chirp(10, fs, 0, 500; method = "quadratic");
sg2 = DSP.spectrogram(QFM, 511, 256; fs = fs);
heatmap(sg2.time, sg2.freq, pow2db.(sg2.power),
xlabel = "Время, сек",
ylabel = "Частота, Гц",
title = "Спектрограмма КЧМ-сигнала")
Заключение
Мы рассмотрели основные техники генерации периодических, апериодических и шумовых сигналов, помогающих нам разрабатывать алгоритмы обработки сигналов и моделировать радиотехнические системы.