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


Всем привет.

Здесь на сайте выложено много статей по замечательной программе Signals Analyzer, которая является уникальным инструментом, аналогов которому среди общедоступного софта просто нет. Но SA как известно, не работает с файлами больше 50 Мб, что при скоростях сегодняшних SDR довольно мало, да и часть функций (в том числе и открытие IQ-файлов) залочены в платной версии, которую увы, больше не купить.

Я покажу как сделать частотную демодуляцию сигнала на примере IQ файла, записанного участником форума на частоте 403 МГц. IQ-файл c метеозонда записан приемником rtl-sdr, имеет размер около 500 Мб, и работать с такими файлами в SA весьма затруднительно. Python в свою очередь, имеет немало библиотек для обработки и визуализации данных, что позволяет обойтись весьма простым и понятным кодом.

Если открыть IQ-файл в SDR#, видно что сигнал находится на 100 КГц выше от центральной частоты. Запомним это значение, оно нам пригодится.


Увеличить



А теперь перейдем к Python. Для использования кода из статьи понадобится Python 3.7 и библиотеки numpy, scipy и matplotlib. Установить их можно с помощью pip.exe (RTFM), который входит в дистрибутив Python. Код кроссплатформенный и может работать где угодно, на Windows, Linux Или OSX.

Шаг 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 os

wav_path = "SDRSharp_20201118_173512Z_403100kHz_IQ.wav"
fs, data = wav.read(wav_path, mmap=True)

Шаг 2. Вывод на экран

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

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

Преобразуем данные в комплексные числа и выводим на экран спектрограмму:

data_iq = data[:, 0] + 1j * data[:, 1]


plt.specgram(data_iq, NFFT=1024, Fs=fs)
plt.title(os.path.basename(wav_path))
plt.xlabel("Time")
plt.ylabel("Frequency")
plt.show()

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



Его можно увеличить, скроллить и пр. Можно навести мышь на сигнал и убедиться, что он находится на частоте 100 КГц. Запомним эту частоту как параметр.

sig_freq_hz = 100000

Шаг 3. Конвертация и фильтрация

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

sig_new_center_hz = 30000

sig_bandwith_hz = 10000
freq = sig_freq_hz - sig_new_center_hz
fc = np.exp(-1.0j * 2.0 * np.pi * freq / fs * np.arange(len(data_iq)))
y = data_iq * fc

def butter_bandpass(lowcut, highcut, fs, order=5):
nyq = 0.5 * fs
low = lowcut / nyq
high = highcut / nyq
b, a = signal.butter(order, [low, high], btype='band')
return b, a

def butter_bandpass_filter(data, lowcut, highcut, fs, order=5):
b, a = butter_bandpass(lowcut, highcut, fs, order=order)
y = signal.lfilter(b, a, data)
return y

real = y.real
real_filteded = butter_bandpass_filter(real, sig_new_center_hz - sig_bandwith_hz, sig_new_center_hz + sig_bandwith_hz, fs, order=4)

Шаг 4. Децимация и запись

Децимация позволяет уменьшить размер файла за счет удаления избыточных значений. Этот шаг является опциональным, значение можно поменять на другое при необходимости.

resample = 4

if resample > 1: real_filteded = real_filteded[::resample]

wav.write(wav_path.replace(".wav", "-converted.wav"), fs//resample, real_filteded.astype(np.int16))
print("WAV saved")

Если все было сделано правильно, на выходе мы получим сконвертированный файл.
Результат: из исходного IQ-файла размером 500 Мб мы получили сконвертированный и отфильтрованный файл размером 64 Мб. Для проверки его можно открыть в том же SA, в бесплатной версии:



Если тема будет интересна, декодирование до битового потока можно рассмотреть в следующей части.

Комментарии к статье
Автор Комментарий
atmel49
Участник
1.4
27 Ноя 2020 18:51


Если тема будет интересна
Конечно интересна! Мне по крайней мере, поскольку под Linux вообще нет никакого адекватного софта для радиоконтроля. Одна винда кругом, которая никак не загнется.
Ernk
Участник
2.6
25 Дек 2020 00:14


Не пойму где ошибка,но на выходе получается файл 2.74 мб
Ernk
Участник
2.6
05 Янв 2021 23:15 · Поправил: 05 Янв 2021 23:26


Не пойму где ошибка,но на выходе получается файл 2.74 мб
Понятно с ошибкой просто нужно При окончательной конвертации файла целиком эту строку можно закомментировать.

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


спасибо автору за помощь
Ernk
Участник
2.6
05 Янв 2021 23:18 · Поправил: 05 Янв 2021 23:21


Рабочий код:
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 os

wav_path = "SDRSharp_20201118_173512Z_403100kHz_IQ.wav"
fs, data = wav.read(wav_path, mmap=True)

# data = data[1*fs:4*fs]
data_iq = data[:, 0] + 1j * data[:, 1]

#plt.specgram(data_iq, NFFT=1024, Fs=fs)
#plt.title(os.path.basename(wav_path))
#plt.xlabel("Time")
#plt.ylabel("Frequency")
#plt.show()

sig_freq_hz = 100000

sig_new_center_hz = 30000
sig_bandwith_hz = 10000
freq = sig_freq_hz - sig_new_center_hz
fc = np.exp(-1.0j * 2.0 * np.pi * freq / fs * np.arange(len(data_iq)))
y = data_iq * fc

def butter_bandpass(lowcut, highcut, fs, order=5):

nyq = 0.5 * fs
low = lowcut / nyq
high = highcut / nyq
b, a = signal.butter(order, [low, high], btype='band')
return b, a


def butter_bandpass_filter(data, lowcut, highcut, fs, order=5):

b, a = butter_bandpass(lowcut, highcut, fs, order=order)
y = signal.lfilter(b, a, data)
return y


real = y.real
real_filteded = butter_bandpass_filter(real, sig_new_center_hz - sig_bandwith_hz, sig_new_center_hz + sig_bandwith_hz, fs, order=4)

resample = 4 # Для последующего декодирования заменить на 2
if resample > 1: real_filteded = real_filteded[::resample]

wav.write(wav_path.replace(".wav", "-converted.wav"), fs//resample, real_filteded.astype(np.int16))
print("WAV saved")
DVE
Участник
3.8
05 Янв 2021 23:33


Спасибо что выложили. Только тем кто будет копировать, имейте в виду что форматирование здесь в текстах не сохраняется, отступы (например внутри def) придется добавлять вручную.
Ernk
Участник
2.6
06 Янв 2021 14:26


Точно, сразу и не заметил
Добавлять комментарии могут только зарегистрированные, активировавшие регистрацию и не ограниченные в доступе участники сайта!
Файл создан: 26 Ноя 2020 21:30, посл. исправление: 08 Янв 2021 20:56
© radioscanner.ru, miniBB® 2006 | загрузка: с.