Специальные радиосистемы
Логин  Пароль   Регистрация   
На главную
наш магазин радио
объявления
радиорейтинг
радиостанции
радиоприемники
диапазоны частот
таблица частот
аэродромы
статьи
файлы
форум
поиск
Радиостанции Аргут в нашем магазине
Работа с IQ-файлами в Python. Часть 2
Декодирование FSK из WAV в битовый поток с помощью Python 3.7
Начало » Цифровая обработка сигналов
Разместил: DVE 3.8


Всем привет.

В первой части было показано, как сконвертировать IQ-запись с rtl-sdr приемника в "обычный" wav-файл. Посмотрим теперь, как декодировать запись до бинарного потока. В качестве исходного файла будет использоваться файл, сохраненный в первой части.

Шаг 1. Загрузка и отображение

import matplotlib.pyplot as plt

import numpy as np
import scipy.signal as signal
import scipy.io.wavfile as wav
from scipy.fftpack import fft, fftfreq, fftshift
import math
import os

wav_path = "SDRSharp_20201118_173512Z_403100kHz_IQ-conv.wav"
fs, data = wav.read(wav_path)
print("Sample rate:", fs)
print("Total len, s:", len(data)/fs)

Для анализа используем только 3 секунды записи, в противном случае отрисовка будет слишком медленной. Для окончательной конвертации эту строку нужно убрать.

data = data[1*fs:1*fs + 4*fs]


plt.plot(data)
plt.xlabel("Time")
plt.ylabel("Amplitude")
plt.title(os.path.basename(wav_path))
plt.show()

Результат не вызывает сомнений, это частотная модуляция:



Можно вывести спектр на экран, если мы хотим уточнить частоты:



Шаг 2. Демодуляция FSK

К сожалению, готовой библиотеки, чтобы декодировать FSK "из коробки" в одну строчку кода, указав лишь скорость и разнос частот, найти не удалось. Существует множество методов декодирования FSK, я возьму самый простой, почти "школьный" способ - просто буду определять текущую частоту по моментам перехода сигнала через ноль. Способ вероятно, будет работать плохо на зашумленном сигнале, но здесь сигнал вполне качественный, и проблем быть не должно.

Фиксируем переходы через ноль и записываем частоту каждого найденного блока:

data_fsk = np.zeros(len(data))

pos1 = 0
for p in range(len(data)-1):
if np.sign(data[p]) != np.sign(data[p+1]):
pr = p - pos1
data_fsk[pos1:p] = np.full(p - pos1, pr)
pos1 = p

Т.к. частота FSK симметрична относительно средней частоты сигнала, сдвинем график вниз. F1 и F2 - частоты манипуляции FSK, которые видны на спектре выше.

f1, f2 = 28000, 33333

fc = (f1 + f2)//2
period = fs//fc
data_fsk -= period//2

Результат вполне неплохой (желтая линия), модуляцию видно весьма уверенно:



Но как можно видеть, частоты были определены "на глаз" не совсем точно, и график сдвинут вверх. Просто сдвинем его вниз на 1:

data_fsk -= 1.0

Уберем высокочастотную компоненту с помощью ФНЧ:

def butter_lowpass_filter(data, cutoff, fs, order):

nyq = 0.5 * fs # Nyquist Frequency
normal_cutoff = cutoff / nyq
# Get the filter coefficients
b, a = signal.butter(order, normal_cutoff, btype='low', analog=False)
y = signal.filtfilt(b, a, data)
return y

data_filtered = butter_lowpass_filter(data_fsk, fc/4, fs, order=4)

Результат:



Последним шагом преобразуем данные в бинарный код. Воспользуемся тем же принципом и будем фиксировать переходы через ноль. С помощью мыши несложно "на глаз" определить, что ширина одного бита составляет 400 отсчетов. Можно определить это точнее аналитически, но такой точности вполне достаточно - разделив длину блока на размер бита, получаем число бит, а значение 1.9 или 1.95 без проблем округлится до 2.

bins = ""

data_binary = np.zeros(len(data))
bin_period = 400
pos1 = 0
for p in range(0, len(data_filtered)-1):
if np.sign(data_filtered[p]) != np.sign(data_filtered[p+1]):
pr = p - pos1
block_len = round((p - pos1)/bin_period)
s = "1" if sign(data_filtered[p]) > 0 else "0"
bins += block_len*s
pos1 = p

print(bins)

Если все было сделано правильно, на выходе получаем строку типа такой:

00000000000000000110101100110000000001001000001000110001100011011111111111111111100001011000110110000011000000000011001100101010000011111000000000000001000000011001110011001000001000101111101110110100010101010101010101011111100110101000000

Шаг 3. Декодирование кода Manchester

Последний шаг для данного примера. Из описания сигнала известно, что в нем используется Manchester Encoding. В нем "1" и "0" закодированы переходами 10 и 01 соответственно (подробности в Википедии). Декодер довольно прост:

def manchester_decode(sb: str) -> str:

# Manchester decode: ensure that bit sync is correct
start = 0
if (sb.find("11") % 2) != 1:
start = 1
# Manchester decode: start
bins_m = ""
for p in range(0, len(sb) - start - 2, 2):
try:
if sb[start + p] == '1' and sb[start + p + 1] == '0':
bins_m += '1'
elif sb[start + p] == '0' and sb[start + p + 1] == '1':
bins_m += '0'
else:
# print("Bit Sync lost", p)
bins_m += "\n"
start += 1
except:
pass
return bins_m

Последний шаг: вывод строки на экран

data_dec = manchester_decode(bins)

print(data_dec)

Повторяющуюся структуру передаваемых пакетов несложно увидеть прямо в консоли, если подобрать ширину окна:



Это лишь фрагмент, длину всего пакета данных можно прикинуть по картинке:



Формат передаваемых данных неизвестен, так что дальше продвинуться мы, увы, не можем. Теоретически, метеозонд должен передавать координаты, высоту, давление воздуха и прочие параметры, но не зная конкретного формата, более точно сказать сложно.

С другими записями желающие могут поэкспериментировать самостоятельно, в коде достаточно лишь поменять частоты манипуляции и размер ширины сигнала в сэмплах. Разумеется, не во всех форматах используется манчестерское кодирование, при необходимости эту часть можно не использовать.

Добавлять комментарии могут только зарегистрированные, активировавшие регистрацию и не ограниченные в доступе участники сайта!
Файл создан: 27 Ноя 2020 18:50, посл. исправление: 19 Фев 2021 11:09
© radioscanner.ru, miniBB® 2006 | загрузка: с.