Создание пользовательских приложений в Engee
Проект 2: Анализ амплитудно-модулированных сигналов
Описание приложения
В этом разделе вы создадите приложение, которое формирует синусоидальный сигнал с амплитудной модуляцией, строит его график во временной области, рассчитывает спектр сигнала (спектральную плотность мощности) и строит график спектра.
Амплитудно-модулированные сигналы широко применяются в радиосвязи и в телевизионном вещании. Суть амплитудной модуляции (АМ) заключается в том, что амплитуда высокочастотного несущего колебания изменяется по некоторому низкочастотному закону. Низкочастотный сигнал называется модулирующим (или информационным). Рассмотрим простейший случай, когда модулирующий сигнал представляет собой синусоидальное колебание. Тогда АМ-сигнал имеет вид:
где – амплитуда несущего колебания, – коэффициент модуляции, – частота модулирующего колебания, – частота несущего колебания (), – время.
Спектр такого АМ-сигнала содержит пик на несущей частоте , и два боковых пика на частотах, равных и .
В этом разделе мы разработаем приложение следующего вида:
Пользователь с помощью текстовых полей задает четыре параметра АМ-сигнала:
- Частота несущего колебания ,
- Частота модулирующего колебания ,
- Амплитуда несущего колебания ,
- Коэффициент модуляции .
При нажатии на кнопку Построить графики рассчитываются сигнал и его спектр. Их графики выводятся на экран.
✏️Задание 1
Начните создание приложения:
- В папке данного курса создайте скрипт
app5.jl. - Добавьте в скрипт следующий код, создающий "пустое" приложение:
using GenieFramework, Stipple, PlotlyBase, DSP
@app begin
end
function ui()
end
@page("/", ui)
Здесь к подключаемым библиотекам GenieFramework, Stipple и PlotlyBase добавляется библиотека DSP, содержащая функции для расчета спектра сигнала.
Создание заголовка приложения
✏️Задание 2
В интерфейсную часть кода с помощью команды h4() добавьте заголовок "Анализ амплитудно-модулированных сигналов во временной и частотной области".
Решение
using GenieFramework, Stipple, PlotlyBase, DSP
@app begin
end
function ui()
h4("Анализ амплитудно-модулированных сигналов во временной и частотной области"),
end
@page("/", ui)
Ввод параметров сигнала
✏️Задание 3
Добавьте в приложение текстовые поля для ввода пользователем параметров сигнала.
- В интерфейсную часть кода добавьте четыре текстовых поля со следующими параметрами:
| Параметр | Надпись | Имя переменной | Ширина, пикселей |
|---|---|---|---|
"Частота несущего колебания, Гц:" |
fc |
300 |
|
"Частота модулирующего колебания, Гц:" |
fam |
300 |
|
"Амплитуда несущего колебания:" |
A |
300 |
|
"Коэффициент модуляции:" |
m |
300 |
-
В модель данных добавьте описание трех входных реактивных переменных
fc,fam,Aиmс начальными значениями100.0,10.0,1.0и0.5соответственно. -
На частоту модулирующего колебания необходимо наложить ограничение: она не должна превышать частоту несущего колебания. Это можно сделать с помощью макроса
@onchange– обработчика изменения значения переменнойfam. В модель данных (после описания реактивных переменных) добавьте макрос@onchange, который при изменении значения переменнойfamпроверяет условие: еслиfamбольшеfc, то переменнойfamприсваивается значениеfc. -
Аналогично нужно предотвратить ситуацию, когда при изменении частоты несущего колебания она становится меньше частоты модулирующего колебания. Для этого добавьте ниже еще один макрос
@onchange, который при изменении значения переменнойfcпроверяет условие: еслиfcменьшеfam, то переменнойfcприсваивается значениеfam.
Решение
using GenieFramework, Stipple, PlotlyBase, DSP
@app begin
@in fc = 100.0
@in fam = 10.0
@in A = 1.0
@in m = 0.5
@onchange fam begin
if fam > fc
fam = fc
end
end
@onchange fc begin
if fc < fam
fc = fam
end
end
end
function ui()
h4("Анализ амплитудно-модулированных сигналов во временной и частотной области"),
textfield("Частота несущего колебания, Гц:", :fc, style="width:300px"),
textfield("Частота модулирующего колебания, Гц:", :fam, style="width:300px"),
textfield("Амплитуда несущего колебания:", :A, style="width:300px"),
textfield("Коэффициент модуляции:", :m, style="width:300px"),
end
@page("/", ui)
Графики
✏️Задание 4
Добавьте в приложение два графика:
- В основную часть кода (сразу после строки
using ...) добавьте описание макетов графиков (объектыPlotlyBase.Layout()) со следующими параметрами:
| Название графика | Заголовок | Подпись на оси абсцисс | Подпись на оси ординат | Ширина | Высота |
|---|---|---|---|---|---|
layout1 |
"Сигнал во временной области" |
"t, с" |
"Сигнал" |
800 |
550 |
layout2 |
"Сигнал в частотной области" |
"Частота, Гц" |
"Спектральная плотность мощности" |
800 |
550 |
-
В модель данных добавьте описание четырех выходных переменных, задающих нулевые начальные состояния графиков и их макеты:
@out trace1 = Plot(scatter(x=0.0, y=0.0, line=attr(color="blue")))@out trace2 = Plot(scatter(x=0.0, y=0.0, line=attr(color="blue")))@out layout1 = layout1@out layout2 = layout2
-
В интерфейсную часть добавьте два полотна графиков с помощью функции
plotly()с аргументами:trace1иlayout=:layout1для первого графика и:trace2иlayout=:layout2для второго.
Решение
using GenieFramework, Stipple, PlotlyBase, DSP
layout1 = PlotlyBase.Layout(
title = "Сигнал во временной области",
xaxis=attr(title="t, с", showgrid=true),
yaxis=attr(title="Сигнал", showgrid=true),
width=800, height=550
)
layout2 = PlotlyBase.Layout(
title = "Сигнал в частотной области",
xaxis=attr(title="Частота, Гц", showgrid=true),
yaxis=attr(title="Спектральная плотность мощности", showgrid=true),
width=800, height=550
)
@app begin
@in fc = 100.0
@in fam = 10.0
@in A = 1.0
@in m = 0.5
@out trace1 = Plot(scatter(x=0.0, y=0.0, line=attr(color="blue")))
@out trace2 = Plot(scatter(x=0.0, y=0.0, line=attr(color="blue")))
@out layout1 = layout1
@out layout2 = layout2
@onchange fam begin
if fam > fc
fam = fc
end
end
@onchange fc begin
if fc < fam
fc = fam
end
end
end
function ui()
h4("Анализ амплитудно-модулированных сигналов во временной и частотной области"),
textfield("Частота несущего колебания, Гц:", :fc, style="width:300px"),
textfield("Частота модулирующего колебания, Гц:", :fam, style="width:300px"),
textfield("Амплитуда несущего колебания:", :A, style="width:300px"),
textfield("Коэффициент модуляции:", :m, style="width:300px"),
plotly(:trace1, layout=:layout1),
plotly(:trace2, layout=:layout2),
end
@page("/", ui)
Кнопка Построить графики
✏️Задание 5
Добавьте в приложение кнопку и обработчик ее нажатия:
- В интерфейсную часть кода добавьте кнопку с надписью
Построить графики. Логическую переменную, описывающую нажатие кнопки, назовитеButton_process. Цвет кнопки задайте равным"primary", а ширину –200пикселей. - В модель данных добавьте описание входной переменной
Button_processс начальным значениемfalse. - В модель данных (после макросов
@onchange) добавьте макрос@onbutton– обработчик нажатия кнопки. В нем будет выполняться расчет значений сигнала и его спектра.
@onbutton Button_process begin
fs = 4*fc
x1 = [0:1/fs:1;]
y1 = A*(1 .+ m*cos.(2*pi*fam*x1)).*cos.(2*pi*fc*x1)
spec = DSP.periodogram(y1, onesided=true, nfft=length(y1), fs=fs, window=DSP.hamming)
x2 = freq(spec)
y2 = power(spec)
trace1 = Plot(scatter(x=x1, y=y1, line=attr(color="blue")))
trace2 = Plot(scatter(x=x2, y=y2, line=attr(color="blue")))
end
Разбор кода:
fs = 4*fc– вычисляется частота дискретизацииfs(пусть она будет в 4 раза больше частоты несущего колебания).x1 = [0:1/fs:1;]– вычисляется вектор значений времени в диапазоне от0до1с шагом, обратным частоте дискретизации.y1 = A*(1 .+ m*cos.(2*pi*fam*x1)).*cos.(2*pi*fc*x1)– вычисляется вектор значений АМ-модулированного сигнала.spec = DSP.periodogram(y1, onesided=true, nfft=length(y1), fs=fs, window=DSP.hamming)– для расчета спектра сигнала используется функцияDSP.periodogram. В качестве аргументов задаются сигналy1,onesided=true– вычисляется односторонний спектр (т.е. только для положительных частот),nfft=length(y1)– длина преобразования Фурье задается равной длине сигнала,fs=fs– частота дискретизации задается равной значению переменнойfs,window=DSP.hamming– преобразование Фурье выполняется с оконной функцией Хэмминга.x2=freq(spec)иy2=power(spec)– на основе объектаspecвычисляются вектор дискретных частот и вектор спектральной плотности мощности сигнала соответственно.trace1 = Plot(scatter(x=x1, y=y1, line=attr(color="blue")))– создается новый график сигнала во временной области (зависимость сигнала от времени).trace2 = Plot(scatter(x=x2, y=y2, line=attr(color="blue")))– создается новый график сигнала в частотной области (зависимость спектральной плотности мощности сигнала от частоты).
Решение
using GenieFramework, Stipple, PlotlyBase, DSP
layout1 = PlotlyBase.Layout(
title = "Сигнал во временной области",
xaxis=attr(title="t, с", showgrid=true),
yaxis=attr(title="Сигнал", showgrid=true),
width=800, height=550
)
layout2 = PlotlyBase.Layout(
title = "Сигнал в частотной области",
xaxis=attr(title="Частота, Гц", showgrid=true),
yaxis=attr(title="Спектральная плотность мощности", showgrid=true),
width=800, height=550
)
@app begin
@in fc = 100.0
@in fam = 10.0
@in A = 1.0
@in m = 0.5
@in Button_process = false
@out trace1 = Plot(scatter(x=0.0, y=0.0, line=attr(color="blue")))
@out trace2 = Plot(scatter(x=0.0, y=0.0, line=attr(color="blue")))
@out layout1 = layout1
@out layout2 = layout2
@onchange fam begin
if fam > fc
fam = fc
end
end
@onchange fc begin
if fc < fam
fc = fam
end
end
@onbutton Button_process begin
fs = 4*fc
x1 = [0:1/fs:1;]
y1 = A*(1 .+ m*cos.(2*pi*fam*x1)).*cos.(2*pi*fc*x1)
spec = DSP.periodogram(y1, onesided=true, nfft=length(y1), fs=fs, window=DSP.hamming)
x2 = freq(spec)
y2 = power(spec)
trace1 = Plot(scatter(x=x1, y=y1, line=attr(color="blue")))
trace2 = Plot(scatter(x=x2, y=y2, line=attr(color="blue")))
end
end
function ui()
h4("Анализ амплитудно-модулированных сигналов во временной и частотной области"),
textfield("Частота несущего колебания, Гц:", :fc, style="width:300px"),
textfield("Частота модулирующего колебания, Гц:", :fam, style="width:300px"),
textfield("Амплитуда несущего колебания:", :A, style="width:300px"),
textfield("Коэффициент модуляции:", :m, style="width:300px"),
btn("Построить графики", @click(:Button_process), color = "primary", style="width:200px"),
plotly(:trace1, layout=:layout1),
plotly(:trace2, layout=:layout2),
end
@page("/", ui)
Расположение элементов интерфейса по строкам и столбцам
✏️Задание 6
Приложение почти готово. Вам осталось расположить элементы интерфейса в более удобном виде.
Вверху экрана расположите заголовок. Ниже в три столбца расположите элементы для ввода данных: в первом столбце – текстовые поля для ввода частот несущего и модулирующего колебания, во втором столбце – текстовые поля для ввода амплитуды несущего колебания и коэффициента модуляции, в третьем столбце – кнопку Построить графики. Ширину столбцов задайте равной 3, 3 и 1 единице соответственно.
Ниже в два столбца расположите полотна графиков. Ширину столбцов задайте равной 6 единиц.
Решение
using GenieFramework, Stipple, PlotlyBase, DSP
layout1 = PlotlyBase.Layout(
title = "Сигнал во временной области",
xaxis=attr(title="t, с", showgrid=true),
yaxis=attr(title="Сигнал", showgrid=true),
width=800, height=550
)
layout2 = PlotlyBase.Layout(
title = "Сигнал в частотной области",
xaxis=attr(title="Частота, Гц", showgrid=true),
yaxis=attr(title="Спектральная плотность мощности", showgrid=true),
width=800, height=550
)
@app begin
@in fc = 100.0
@in fam = 10.0
@in A = 1.0
@in m = 0.5
@in Button_process = false
@out trace1 = Plot(scatter(x=0.0, y=0.0, line=attr(color="blue")))
@out trace2 = Plot(scatter(x=0.0, y=0.0, line=attr(color="blue")))
@out layout1 = layout1
@out layout2 = layout2
@onchange fam begin
if fam > fc
fam = fc
end
end
@onchange fc begin
if fc < fam
fc = fam
end
end
@onbutton Button_process begin
fs = 4*fc
x1 = [0:1/fs:1;]
y1 = A*(1 .+ m*cos.(2*pi*fam*x1)).*cos.(2*pi*fc*x1)
spec = DSP.periodogram(y1, onesided=true, nfft=length(y1), fs=fs, window=DSP.hamming)
x2 = freq(spec)
y2 = power(spec)
trace1 = Plot(scatter(x=x1, y=y1, line=attr(color="blue")))
trace2 = Plot(scatter(x=x2, y=y2, line=attr(color="blue")))
end
end
function ui()
h4("Анализ амплитудно-модулированных сигналов во временной и частотной области"),
row([
column([
textfield("Частота несущего колебания, Гц:", :fc, style="width:300px"),
textfield("Частота модулирующего колебания, Гц:", :fam, style="width:300px"),
], size = 3),
column([
textfield("Амплитуда несущего колебания:", :A, style="width:300px"),
textfield("Коэффициент модуляции:", :m, style="width:300px"),
], size = 3),
column([
btn("Построить графики", @click(:Button_process), color = "primary", style="width:200px")
], size = 1),
]),
row([
column([plotly(:trace1, layout=:layout1),
], size = 6),
column([plotly(:trace2, layout=:layout2),
], size = 6)
])
end
@page("/", ui)
Запуск приложения
✏️Задание 7
Запустите приложение:
-
Находясь в папке данного курса, выполните в командной строке следующую команду:
engee.genie.start("app5.jl") -
Перейдите по ссылке, появившейся в ячейке вывода. Приложение откроется в новой вкладке браузера.
-
Постройте графики АМ-сигнала и его спектра с различными значениями входных параметров.
Решение
Начальное окно приложения будет иметь следующий вид:
При нажатии на кнопку Построить графики окно приложения будет иметь вид, показанный на рисунке в начале этого раздела.