DSP Basics: Signal generation
Let's consider the basic techniques for generating periodic, pulse, and noise signals for testing digital signal processing (DSP) algorithms, modeling communication systems, radars, and radio engineering systems in general. The main themes of the example:
- setting the periodic signal
- Discrete signal time grid
- generation of typical periodic signals (sinusoid, meander, saw)
- Discrete noise generation
- Pulse sequences
- frequency-modulated signals
Libraries used in the example:
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
Generation of periodic signals
A periodic signal is a repeating sequence. Let's create one period of signal values, and then repeat it five times with the function repeat. We visualize it in time using a stepped line:
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 = "Простой периодический сигнал")
Now let's try to generate a periodic signal using mathematical functions. For example, the function sin calculates the sine value for the input angle value in radians. In order to generate a sinusoid with the required repetition rate, it is worth using a time vector - a sequence of evenly increasing time samples with a specified sampling period. Let's set the time vector for signals with a sampling frequency of 1000 Hz:
fs = 1000; # частота дискретизации сигнала
dt = 1/fs; # период дискретизации
stoptime = 0.5; # время окончания сигнала
t = 0:dt:stoptime-dt # вектор отсчётов времени
Now we will create a periodic sinusoidal signal with a fundamental frequency of 10 Hz (the shape of the sinusoid will be repeated every 0.1 seconds). Variable sine_wave - this is a vector of 500 samples. Visualize the signal with the function plot:
sine_wave = sin.(2*pi*t*10);
plot(t, sine_wave, title = "Синусоида частотой 10 Гц")
With one line of code, we can generate several sinusoids with different amplitudes, frequencies, and initial phases. To do this, we will set these parameters as vectors. The resulting variable three_sines it will already be a 500x3 matrix. We can pass it to functions plot for simultaneous display of three graphs on the same axes:
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 Гц")
If we are interested in observing the shape of the sum of three sinusoids, then we can add up the columns of the matrix using the function sum:
sum_sines = sum(three_sines, dims = 2);
plot(t, sum_sines, title = "Сумма трёх синусоид")
Additional functions for signal generation
We often need rectangular, sawtooth, and triangular periodic signals, as well as random sequences to simulate noise.
Library Waveforms.jl It contains functions for generating the listed periodic signals, but as the "master" signal that determines the fundamental frequency, a uniformly increasing sawtooth signal is used, varying from 0 to 2pi. Let's create it from the time vector and loop it with the division operation. mod at 2pi:
base_signal = mod.(2*pi*t*10, 2*pi);
plot(t, base_signal, title = "Задающий сигнал для функций")
A popular signal in radio engineering is a meander. It is also a rectangular periodic signal with a borehole of 2 (or a fill factor of 50%). We can generate it with the function squarewave. This function can also take an additional input argument, a fill factor ranging from 0 to 1. Generate a second rectangular signal with 20% fill.:
sqw = squarewave.(base_signal);
sqw02 = squarewave.(base_signal, 0.2);
plot(t, sqw)
plot!(t, sqw02, title = "Прямоугольный сигнал")
A sawtooth signal varying from -1 to +1 can be obtained by the function sawtoothwave:
st = sawtoothwave.(base_signal);
plot(t, st, title = "Пилообразный сигнал")
Also consider the function trianglewave to generate a triangular signal:
trw = trianglewave.(base_signal);
plot(t, trw, title = "Треугольный сигнал")
Functions for generating random numbers with different distributions are available in Engee. Let's use the function rand to create a uniform noise signal:
noise = rand.(length(t));
noise = noise .- 0.5;
plot(t, noise, title = "Шум с равномерным распределением")
Now let's try to combine noise and a sawtooth signal, but we'll do it in such a way that the noise level increases with the signal level. Using the master signal, we will generate a sawtooth, varying from 0 to +1. Then we multiply the noise by the resulting sawtooth signal and sum the increasing "saw" with increasing noise.:
clean_sawtooth = mod.(t*10,1); # "чистый" пилообразный сигнал
amp_noise = noise .* clean_sawtooth; # нарастающий шумовой сигнал
noisy_sawtooth = clean_sawtooth .+ (0.25 .* amp_noise); # их сумма
plot(t, noisy_sawtooth, title = "Усиление сигнал и шума")
Generation of non-periodic signals and pulse packets
As an example of a non-periodic signal, consider the common sinc- an impulse. We get it using a trigonometric function and an input vector of angle values in radians. In the example, we generate a pulse in the range from -2pi to +2pi (in increments of pi/16).
rads = -2*pi:pi/16:2*pi;
rads = rads[1:end-1];
one_sinc = sinc.(rads);
plot(rads, one_sinc, title = "sinc-импульс")
Now let's create a bundle sinc-pulses following a certain period. To do this, add a number of zeros to the vector of one pulse at the end, and multiply the result by the function repeat. We will also add attenuation to our burst of pulses. The function will help us with this. 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-импульсов с затуханием")
Frequency-modulated signals
In conclusion, we will consider typical frequency-modulated signals, such as linear frequency modulation (LFM) and quadrature frequency modulation signals. To create such signals, we will use the function 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 = "Спектрограмма ЛЧМ-сигнала")
And to display the signals, we will call the function DSP.spectrogram, which visualizes changes in the spectrum of a signal (depending on its power and frequency) over time. We can observe a linear frequency increase for the LFM signal and a quadratic one in the case of the option 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 = "Спектрограмма КЧМ-сигнала")
Conclusion
We have reviewed the basic techniques for generating periodic, aperiodic, and noise signals that help us develop signal processing algorithms and simulate radio engineering systems.