середа, 9 жовтня 2024 р.

Водоспад та панорамний спектр. Алгоритм віконного накопичення та візуалізації

Розглянемо методику реалізації водоспаду в аналізаторах спектру. Що таке цей «водоспад»? Таку назву отримала градієнтна за кольором візуалізація спектральних зрізів (відображень) за часом завдяки своїй схожості з потоком води, що падає, водоспадом. Водоспад може бути як з горизонтальним, так і з вертикальним (боковим) розташуванням, проте найбільшого поширення набув горизонтальний, зважаючи на зручність свого розташування безпосередньо під трендом, що відображає спектр у поточний (миттєвий) момент часу...

Що це дає? 

Це дозволяє візуально оцінити зміни у характері (спектрі) сигналу з часом, тобто свого роду мініархів REALTIME перед очима. Таким чином, наприклад, легко виявити частотну модуляцію, нестабільність опорнику передавача або навіть передавати повідомлення картинкою

Невеликий екскурс в основи

Пояснимо термін «градієнтна за кольором». Спектр – це розподіл енергії за частотою, що характеризується розкладанням у ряді Фур'є сигналу з такими характеристиками кожної гармоніки як частота та амплітуда. Уявіть ці дані у вигляді двох одномірних лінійних масивів однакової розмірності. З частотами все зрозуміло, у водоспаді вони задають положення мітки (точки) по-горизонталі, по-вертикалі ж положення всіх цих точок буде в даний конкретний момент часу завжди однаковим (саме тому масив і одномірний). Але як розрізнити ці точки (пікселі) одна від одної, якщо вони на одному рівні? Колір. Адже ми можемо керувати кольором кожного пікселя. Уявіть собі, що ми зіставили якесь мінімальне значення амплітуди якомусь початковому кольору, а максимальне деякому кінцевому кольору і тому співвідношення (Am до Amax) задаємо колір конкретному пікселю частоти по його амплітуді, ось така каша.


Здавалося б це співвідношення – проста математична пропорція, проте не все так просто. Якщо ми тупо поставимо просту пропорцію, то будемо розчаровані, отриманий водоспад буде виглядати як бляке полотно з різкими переходами і можна сказати, що художник не вдався. Читач у темі резонно помітить: «…ну звичайно ж потрібна градієнтна заливка» і… матиме рацію.

Покажемо, як можна зробити подібний лінійний градієнт

Для цього спочатку слід ознайомитися з колірною моделлю RGB. RGB (Red, Green, Blue) - це адитивна (англ. addition) колірна модель, як правило, що описує спосіб синтезу кольору для відтворення кольору. Кольори такої моделі виходять шляхом додавання до чорного кольору, за чорний колір прийнятий нуль. Інтенсивність основних кольорів прийнято вимірювати цілими числами в діапазоні від 0 до 255. Нуль означає відсутність цієї колірної складової, відповідно число 255 - максимальну інтенсивність. Вибір основних 2 кольорів обумовлений особливостями фізіології сприйняття кольору сітківкою людського ока. При змішуванні основних кольорів максимальної інтенсивності отримуємо білий, а похідні кольори виходять в результаті додавання або змішування базових, основних кольорів
Градієнт – вид заливки в комп'ютерній графіці, в якій необхідно задати колір та прозорість певних точок, а колір та прозорість інших точок розраховуються щодо них за математичними алгоритмами, що дає можливість отримувати плавні переходи з одного кольору до іншого, задавши координати та колір початкової та кінцевої точок
За аналогією в ТБ та моніторах, діапроекторах та цифрових фотоапаратах застосовуються три електронні гармати (світлодіоди/світлофільтри) для червоного, зеленого та синього каналів. Можна підрахувати, що загальна кількість кольорів (відтінків), що породжує адитивна модель, дорівнює 256*256*256 = 16777216.

Для подальших дій нам необхідно виділити з початкового та кінцевого кольорів (суміші) їх основні складові R, G та B (а по суті інтенсивності). Оскільки максимальна інтенсивність кожного з основних кольорів дорівнює 255 ($FF або один байт), то вся колірна модель RGB має розмірність 3 байти (у числовому еквіваленті доповнюється до 4-х тип DWORD) і наступну структуру: нульовий байт R, перший байт G , другий байт B. 

Звідси можемо записати три функції вибірки основних складових:
// виділення R-складової
функція GetRValue(rgb: DWORD): Byte;
begin
 Result := Byte(rgb);
end;

// виділення G-складової
function GetGValue(rgb: DWORD): Byte;
begin
 Result := Byte(rgb shr 8);
end;

// Виділення B-складової
функція GetBValue(rgb: DWORD): Byte;
begin
 Result := Byte(rgb shr 16);
end;
де: shr - функція, що зсуває значення цілого числа вправо на вказане число біт, а byte() в даному контексті виконує роль маски для приведення типів до розмірності в один байт.

Згадаймо наше співвідношення (поточної амплітуди гармоніки до максимальної) Am до Amax. Це співвідношення є коефіцієнт (відсоткову величину), отже якщо ми поставимо різницею інтенсивностей основних кольорів початкового і кінцевого кольору (суміші), то помноживши цю різницю даний коефіцієнт ми отримаємо ступінь інтенсивності кожного основного кольору наведену до величини поточної амплітуди. Враховуючи принцип адитивності кольорів, нам слід відняти отриману величину з інтенсивності початкового основного кольору. Якщо застосувати ці операції до всіх трьох основних складових, то можемо вивести наступне співвідношення, що зв'язує між собою початковий і кінцевий колір градієнтного заповнення з поточним і максимальним значенням амплітуди (див. формулу 1).
ResultRGB = RGB( RVALUE1 – round((RVALUE1 - RVALUE2) * Am/Amax),
GVALUE1 – round((GVALUE1 - GVALUE2) * Am/Amax),
BVALUE1 – round((BVALUE1 - BVALUE2) * Am/Amax) ); (1)
де: ResultRGB - результуюче значення RGB; RVALUE1, GVALUE1, BVALUE1 – червона, зелена та синя складові початкового кольору; RVALUE2, GVALUE2, BVALUE2 – червона, зелена та синя складові кінцевого кольору; round() – функція округлення числа до цілого у бiльшу сторону.

Таким чином, весь алгоритм створення водоспаду та його відмальовування зводиться до наступних кроків:
  1. Зміщення на мінус один (-1) піксель по вертикалі візуалізації для імітації руху водоспаду
  2. Нормування поточного значення N-ї гармоніки за максимальним значенням у тренді спектру
  3. Розрахунок кольору градієнтної заливки кожного пікселя, зіставленого своїй частоті та амплітуді за формулою 1
  4. Відображення лінії заввишки 1 піксель і шириною, що дорівнює кількості гармонік у спектрі в першому буфері FFTI
  5. Копіювання області першого буфера в другій з масштабуванням даної області, яка залежить від екранних координат і роздільної здатності екрана. Адже нам необхідно, щоб водоспад чітко збігався по ширині з екранним відмальовуванням того самого спектра, під яким він розташовується, а кількість точок у спектрі, а значить і ширина першого буфера (бітмапа) може бути різною. Відображення другого буфера на канві візуалізації водоспаду
  6. Прив'язка положення водоспаду до координат тренду спектру

Алгоритм віконного накопичення та візуалізації

Слід виконати такі кроки:
  1. Задатися мінімальною Fmin та максимальною Fmax частотою сканування (перегляду), скажімо від 138 до 153 МГц
  2. Задати смугу пропускання приймального тракту, що визначається бітрейтом потоку через USB порт (для RTLSDR максимум polosa = 3.2 МГц)
  3. Поставити кількість точок перетворення метелика БПФ для однієї смуги, скажімо N = 4096, а по суті дискретність кроку частоти в одиночній смузі STEP = polosa / N
  4. Розрахувати кількість точок CNT, які помістяться у сумарній смузі = (різниці максимальної та мінімальної частот сканування) / ширину одиночної смуги пропускання * кількість точок перетворення N
  5. Ініціалізувати чіп
  6. Здійснити розрахунок точок центральної частоти кожної з смуг, що накопичуються. Особливістю завдання частоти при одиночному скануванні є те, що задається центральна частота, а відліки йдуть у діапазоні частот рівним ± половини смуги пропускання від центральної частоти. Таким чином, для реалізації панорамного режиму потрібно в нескінченному циклі - перебирати точки центральної частоти всіх смуг, отримувати вибірки кожної з одиночних смуг і накопичувати їх доти, доки не будуть перебрані всі смуги. Кількість смуг є округлення по п.4 без множення на кількість точок в одиночній смузі N. Зрозуміло, що перша центральна частота буде зі зсувом половини пропускання від мінімальної частоти сканування Fmin, а всі наступні будуть йти зі зсувом цілу смугу пропускання
У циклі це виглядатиме так:
Fo = round (Fmin + ((polosa/2) * cnt))
де: початкове значення змінної cnt = 1, що йде з кроком = 2

Далi передати вибірки в процедуру БПФ (тут можна варіювати - або накопичувати вибірки і потім FFT, або спочатку FFT, а накопичувати шматки спектра, останнє швидше) та відобразити накопичене:

    

Постскриптум

Шановні читачі, якщо мої дописи вас зацікавили – пiдтримайте збiр або ставайте спонсорами Youtube-каналу LaboratoryW з ексклюзивними лайфхаками