Сегодняшняя моя статья — это мысли вслух от человека, который встал на путь программирования почти случайно (хотя и закономерно).
Да, я понимаю, что мой опыт — это только мой опыт, однако он, мне кажется, хорошо попадает в общую тенденцию. Более того, опыт, описанный ниже, больше относится к сфере научной деятельности, однако чем чёрт не шутит — может пригодится и вне.
Источник: https://xkcd.com/664/
В общем, всем настоящим студентам от бывшего студента посвящается!
Ожидания
Когда в 2014 году я заканчивал бакалавриат по специальности "Инфокоммуникационные технологии и системы связи" я почти ничего не знал о мире программирования. Да, у меня, как и у многих, был на первом курсе предмет "Информатика" — но, господи, это же было на первом курсе! Прошла целая вечность!
В общем и целом, ничего особенно отличного от бакалавриата я не ждал, и поступая на магистерскую программу "Communication and Signal Processing" Германо-Российского Института Новых Технологий.
А зря...
Мы были всего вторым набором, и ребята с первого ещё только собирали чемоданы в далёкую Германию (стажировка занимает полгода на втором курсе магистратуры). Иначе говоря, никто из ближайшего круга ещё не сталкивался всерьёз с методами европейского образования, и спрашивать о деталях было особо не у кого.
Были у нас на первом курсе, конечно, разного рода практики, на которых обычно нам демократично предлагался выбор между написанием скриптов (преимущественно на языке MATLAB) и использованием разных узкоспециализированных GUI (в том смысле, что без написания скриптов — сред имитационного моделирования).
Стоит ли говорить, что мы, будущие Masters of Science, по своей юношеской дурости, как огня, сторонились написания кода. Вот, он, допустим, Simulink от компании MathWorks: вот они блоки, вот они связи, вот они всякого рода настройки и переключатели.
Источник: https://ch.mathworks.com/help/comm/examples/parallel-concatenated-convolutional-coding-turbo-codes.html
Так нам казалось...
Реальность
Одной из практических работ первого семестра была разработка приёмопередатчика OFDM сигнала в рамках предмета "Methods for Modeling and Optimization". Идея весьма удачная: технология и по сей день актуальная и довольно популярная в силу использования, например, в сетях Wi-Fi и LTE/LTE-A (в виде OFDMA). Самое то для магистров, чтобы потренировать навыки моделирования телеком систем.
И вот на руки нам выдают несколько вариантов ТЗ с заведомо непрактичными параметрами кадра (дабы не искать решение в Интернете), и мы накидываемся на уже упомянутый Simulink… И получаем чайником действительности по голове:
- Каждый блок таит в себе уйму неизвестных параметров, менять которые с кондачка — страшновато.
- Манипуляции с числами произвести нужно, вроде, простые, однако городить приходится всё равно дай боже.
- Кафедральные машины заметно подтормаживают от лихорадочного использование GUI, даже на этапе серфинга по библиотекам доступных блоков.
- Чтобы доделать что-то дома, нужно иметь такой же Simulink. И никаких, собственно, альтернатив.
Да, проект в итоге мы, конечно, доделали, но доделали с громким выдохом облегчения.
Прошло некоторое время, и мы подошли к окончанию первого курса магистратуры. Количество домашних работ с использованием GUI стало пропорционально спадать с увеличением доли немецких предметов, хотя ещё и не достигало точки смены парадигмы. Многие из нас, включая меня, преодолевая свою немалую амплитуду на раскачку, всё больше и больше использовали в своих научных проектах именно Matlab (пусть и в виде Toolbox'ов), а не знакомый, казалось бы, Simulink.
Точкой в наших сомнениях стала фраза одного из студентов второго курса (они как раз к тому времени вернулись в Россию):
- Забудьте, по крайне мере на время стажировки, про Similink, MathCad и прочий LabView — за бугром всё пишут на языке MATLAB, используя собственно сам MatLab или его бесплатную "версию" Octave.
Заявление оказалось верным отчасти: в Ильменау спор о выборе инструментария тоже не был решен до конца. Правда, выбор стоял по большей части между языками MATLAB, Python и C.
В тот же день меня взял закономерный азарт: а не перенести ли свою часть модели OFDM передатчика в скриптовую форму? Just for fun.
И я приступил к работе.
Step by step
Вместо теоретических выкладок я просто дам ссылку на эту прекрасную статью 2011 года от tgx и на слайды по физическому уровню LTE профессора Мишель-Тиля (ТУ Ильменау). Я думаю, этого будет достаточно.
"Итак, — подумал я, — повторим, что же мы будем моделировать?"
Моделировать будем генератор OFDM кадра (OFDM frame generator).
Что он будет включать:
- информационные символы
- пилотные сигналы
- нули (DC)
От чего (простоты ради) мы абстрагируемся:
- от моделирования циклического префикса (при знании основ, добавить оный уже не составит труда)
Блок-схема рассматриваемой модели. Остановимся мы до блока обратного БПФ (IFFT). Остальное для полноты картины каждый может продолжить сам — я обещал преподавателям с кафедры оставить что-то и для студентов.
Определим для себя тех. задание:
- фиксированное количество поднесущих (sub-carriers);
- фиксированная длина кадра;
- мы должны добавить один ноль в середину и по паре нулей к началу и концу кадра (итого, 5 штук);
- информационные символы модулируются с помощью M-PSK или M-QAM, где M — это порядок модуляции.
Приступаем к коду.
Скрипт целиком можно скачать по ссылке.
Определим входные параметры:
clear all; close all; clc
M = 4; % e.g. QPSK
N_inf = 16; % number of subcarriers (information symbols, actually) in the frame
fr_len = 32; % the length of our OFDM frame
N_pil = fr_len - N_inf - 5; % number of pilots in the frame
pilots = [1; j; -1; -j]; % pilots (QPSK, in fact)
nulls_idx = [1, 2, fr_len/2, fr_len-1, fr_len]; % indexes of nulls
Теперь определим индексы информационных символов, приняв предпосылку, что пилотные сигналы должны обязательно идти до и/или после нулей:
idx_1_start = 4;
idx_1_end = fr_len/2 - 2;
idx_2_start = fr_len/2 + 2;
idx_2_end = fr_len - 3;
Тогда позиции можно определить с помощью функции linspace, приведя значения к наименьшему из ближайших целых:
inf_idx_1 = (floor(linspace(idx_1_start, idx_1_end, N_inf/2))).';
inf_idx_2 = (floor(linspace(idx_2_start, idx_2_end, N_inf/2))).';
inf_ind = [inf_idx_1; inf_idx_2]; % simple concatenation
Добавим к этому индексы нулей и отсортируем:
%concatenation and ascending sorting
inf_and_nulls_idx = union(inf_ind, nulls_idx);
Соответственно, индексы пилотных сигналов — это всё остальное:
%numbers in range from 1 to frame length
% that don't overlape with inf_and_nulls_idx vector
pilot_idx = setdiff(1:fr_len, inf_and_nulls_idx);
Теперь давайте разбираться с пилотными сигналами.
У нас есть шаблон (переменная pilots), и, допустим, мы хотим, чтобы в наш кадр пилоты вставлялись из этого шаблона последовательно. Сделать это, конечно, можно в цикле. А можно немного помудрить с матрицами — благо MATLAB позволяет делать это с достаточным комфортом.
Во-первых, определим, сколько таких шаблонов помещается в кадр полностью:
pilots_len_psudo = floor(N_pil/length(pilots));
Далее формируем вектор, который состоит из наших шаблонов:
% linear algebra tricks:
mat_1 = pilots*ones(1, pilots_len_psudo); % rank-one matrix
resh = reshape(mat_1, pilots_len_psudo*length(pilots),1); % vectorization
И определяем небольшой вектор, который содержит только кусок шаблона — "хвост", не поместившийся полностью в кадр:
tail_len = fr_len - N_inf - length(nulls_idx) ...
- length(pilots)*pilots_len_psudo;
tail = pilots(1:tail_len); % "tail" of pilots vector
Получаем пилотные символы:
vec_pilots = [resh; tail]; % completed pilots vector that frame consists
Переходим к информационным символам, а именно сформируем сообщение и промодулируем его:
message = randi([0 M-1], N_inf, 1); % decimal information symbols
if M >= 16
info_symbols = qammod(message, M, pi/4);
else
info_symbols = pskmod(message, M, pi/4);
end
Всё готово! Собираем кадр:
%% Frame construction
frame = zeros(fr_len,1);
frame(pilot_idx) = vec_pilots;
frame(inf_ind) = info_symbols
Получится должно что-то такое:
frame =
0.00000 + 0.00000i
0.00000 + 0.00000i
1.00000 + 0.00000i
-0.70711 - 0.70711i
-0.70711 - 0.70711i
0.70711 + 0.70711i
0.00000 + 1.00000i
-0.70711 + 0.70711i
-0.70711 + 0.70711i
-1.00000 + 0.00000i
-0.70711 + 0.70711i
-0.70711 - 0.70711i
0.00000 - 1.00000i
0.70711 + 0.70711i
1.00000 + 0.00000i
0.00000 + 0.00000i
0.00000 + 1.00000i
0.70711 - 0.70711i
-0.70711 + 0.70711i
-1.00000 + 0.00000i
-0.70711 + 0.70711i
0.70711 + 0.70711i
0.00000 - 1.00000i
-0.70711 - 0.70711i
0.70711 + 0.70711i
1.00000 + 0.00000i
0.70711 - 0.70711i
0.00000 + 1.00000i
0.70711 - 0.70711i
-1.00000 + 0.00000i
0.00000 + 0.00000i
0.00000 + 0.00000i
"Кайф!" — подумал я довольно и закрыл ноутбук. Ушло у меня на всё про всё пару часов: включая написание кода, изучение некоторых матлабовских функций и продумывание математических трюков.
Какие выводы я тогда сделал
Субъективные:
- Писать код приятно и сродни поэзии!
- Написание скриптов — наиболее удобный метод исследований для области Communication and Signal Processing.
Объективные:
- Не надо палить из пушки по воробьям (если такая учебная цель, конечно, не стоит): использовав Simulink, мы взялись за решение простой задачи навороченным инструментом.
- GUI — это хорошо, но понимать что содержится "под капотом" — лучше.
И теперь, будучи уже далеко не студентом, я хочу сказать студенческой братии следующее:
- Дерзайте!
Пробуйте писать код, пусть даже поначалу он будет плохим. С программированием как и с любой другой деятельностью, лиха беда — начало. А начать лучше раньше: если вы ученый или даже просто технарь — рано или поздно этот навык вам понадобится.
- Требуйте!
Требуйте от преподавателей и научных руководителей прогрессивных подходов и инструментов. Если это, конечно, сколько-то возможно…
- Творите!
Где же ещё лучше всего переболеть всеми болячками новичка, как не в рамках образовательной программы? Творите и оттачивайте своё мастерство — опять же чем раньше начать, тем лучше.
Начинающие программисты всех стран, объединяйтесь!
P.S.
Дабы запротоколировать своё прямое отношение к студенчеству, прикладываю памятное фото 2017 года с двумя ректорами: Петером Шарффом (справа) и Альбертом Харисовичем Гильмутдиновым (слева).
Стоило закончить программу как минимум ради таких костюмов! (шучу)