|
Разместил: |
DVE |
|
Всем привет.
В первой части было показано, как сконвертировать 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:
Уберем высокочастотную компоненту с помощью ФНЧ:
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 |
|