Давным-давно, когда мобильный телефон стоил 2000$ и минута звонка стоила 50 центов, была такая популярная штука как пейджинговая связь. Затем связь стала дешевле, и пейджер сначала превратился из престижного атрибута делового человека в непрестижный атрибут курьера или секретаря, а затем эта технология практически и вовсе сошла на нет.
Для тех, кто помнит шутку «читал пейджер, много думал», и хочет разобраться, как это работает, продолжение под катом.
Общая информация
Для тех, кто забыл или родился после 2000х, кратко напомню основные идеи.
С точки зрения пользователя, пейджинговая связь обладает двумя большими плюсами, которые в некоторых случаях актуальны и до сих пор:
— Связь односторонняя, без каких-либо подтверждений, поэтому пейджинговую сеть невозможно перегрузить, ее работоспособность не зависит от числа абонентов. Сообщения просто последовательно передаются в эфир «как есть», а пейджер принимает их если номер получателя совпадает с номером пейджера.
— Приемное устройство очень простое, так что пейджер может работать без подзарядки до месяца от 2х обычных батареек АА.
Для передачи сообщений существуют два основных стандарта — POCSAG (Post Office Code Standardization Advisory Group) и FLEX. Стандарты совсем не новые, POCSAG был утвержден еще в 1982г, поддерживаемые скорости 512, 1200 и 2400 бит/с. Для передачи используется частотная манипуляция (FSK — frequency shift keying) с разносом частот 4.5КГц. Более новый стандарт FLEX (был предложен Motorola в 90х) поддерживает скорости до 6400 бит/с и может использовать не только FSK2, но и FSK4.
Протоколы по своей сути достаточно просты, и еще лет 20 назад для них были написаны декодеры, способные дешифровывать сигнал со входа звуковой карты (шифрования сообщений не предусмотрено, так что читать их с такой программой в принципе может любой).
Посмотрим, как это работает.
Прием сигналов
Для начала, нам нужен образец для декодирования. Берем ноутбук, rtl-sdr приемник, машину времени, и принимаем нужный нам сигнал.
Т.к. модуляция частотная, режим приема также ставим FM. С помощью HDSDR записываем сигнал в формате WAV.
Посмотрим, что у нас получилось. Загружаем wav-файл в виде массива с помощью Python:
from scipy.io import wavfile
import matplotlib.pyplot as plt
fs, data = wavfile.read("pocsag.wav")
plt.plot(data)
plt.show()
Результат (биты подписаны вручную):
Как можно видеть, все просто, и даже «на глаз» в Paint можно дорисовать биты, где «0» а где «1». Но делать это для всего файла было бы слишком долго, процесс надо автоматизировать.
Если увеличить график, то можно видеть что ширина каждого «бита» равна 20 отсчетам, что при частоте дискретизации wav-файла 24000 семпла/c, соответствует скорости 1200 бит/с. Найдем в сигнале место перехода через ноль — это будет начало битовой последовательности. Выведем на экран маркеры, чтобы проверить что биты совпадают.
speed = 1200
fs = 24000
cnt = int(fs/speed)
start = 0
for p in range(2*cnt):
if data[p] < - 50 and data[p+1] > 50:
start = p
break
# Bits frame
bits = np.zeros(data.size)
for p in range(0, data.size - cnt, cnt):
bits[start + p] = 500
plt.plot(bits)
Как можно видеть, совпадение не идеально (частоты передатчика и приемника все же чуть различны), но для декодирования вполне достаточно.
Для длинных сигналов пришлось бы вводить алгоритм подстройки частоты, но в данном случае этого не требуется.
И последний шаг — переведем массив из wav в битовую последовательность. Тут все тоже просто, длину одного бита мы уже знаем, если данные за этот период положительны, то добавляем «1», иначе «0» (правка — как оказалось, сигнал нужно было реверсировать, так что 0 и 1 поменяны местами).
bits_str = ""
for p in range(0, data.size - cnt, cnt):
s = 0
for p1 in range(p, p+cnt):
s += data[p]
bits_str += "1" if s < 0 else "0"
print("Bits")
print(bits_str)
Возможно, код можно оптимизировать, отказавшись от цикла, хотя в данном случае это некритично.
Результат — готовая последовательность бит (в виде строки), сохраняющая наше сообщение.
101010101010101010101010101010101010101010101010101010101010101010101010101
010101010101010101010101010101010101010101010100111110011010010000101001101
100001111010100010011100000110010111011110101000100111000001100101110111101
010001001110000011001011101111010100010011100000110010111011110101000100111
000001100101110111101010001001110000011001011101111010100010011100000110010
011011110101000100111000001100101110111101010001001110000011001011101111010
100010011100000110010111011110101000100111000001100101110111101010001001110
…
111101111
Декодирование
Последовательность бит — это уже гораздо удобнее, чем просто wav-файл, из нее уже можно извлечь какие-либо данные. Разобьем файл на блоки по 4 байта, и получим более понятную последовательность:
10101010101010101010101010101010
10101010101010101010101010101010
10101010101010101010101010101010
10101010101010101010101010101010
01111100110100100001010011011000
01111010100010011100000110010111
01111010100010011100000110010111
01111010100010011100000110010111
01111010100010011100000110010111
00001000011011110100010001101000
10000011010000010101010011010100
01111100110100100001010111011000
11110101010001000001000000111000
01111010100010011100000110010111
01111010100010011100000110010111
01111010100010011100000110010111
00100101101001011010010100101111
Это все, что мы можем извлечь из файла, осталось понять что эти строки значат. Открываем документацию на формат, которая доступна в виде PDF.
Все более-менее понятно. Заголовок сообщения состоит из длинного блока «10101010101» который нужен, чтобы пейджер вышел из «спящего режима». Само сообщение состоит из блоков Batch-1… Batch-N, каждый из которых начинается с уникальной последовательности FSC (в тексте выделено жирным). Далее, как видно из мануала, если строка начинается с «0», то это адрес получателя. Адрес зашит в самом пейджере, и если он не совпадет, пейджер сообщение просто проигнорирует. Если строка начинается с «1», то это собственно, сообщение. Таких строк у нас две.
Теперь посмотрим на каждый блок. Мы видим коды Idle — пустые блоки 01111...0111, не несущие полезной информации. Удаляем их, информации остается весьма мало, все что остается:
01111100110100100001010011011000 — Frame Sync
00001000011011110100010001101000 — Address
10000011010000010101010011010100 — Message
01111100110100100001010111011000 — Frame Sync
11110101010001000001000000111000 — Message
00100101101001011010010100101111 — Address
Осталось понять, что внутри.
Ищем дальше в мануале, и выясняем, что сообщения могут быть цифровые или текстовые. Цифровые сообщения хранятся в виде 4-битных BCD-кодов, значит в 20 битах может поместиться 5 символов (еще остаются биты для контроля, мы их рассматривать не будем). Сообщение также может быть текстовым, в этом случае используется 7-битная кодировка, но для текстового наше сообщение слишком мало — суммарное количество бит сообщения не кратно 7.
Из строк 10000011010000010101010011010100 и 11110101010001000001000000111000 получаем следующие 4х битные последовательности:
1 0000 0110 1000 0010 10101 0011010100 — 0h 6h 8h 2h Ah
1 1110 1010 1000 1000 00100 0000111000 — Eh Ah 8h 8h 2h
И наконец, последний шаг — смотрим в документации таблицу соответствия символов.
Пишем несложную функцию вывода, чтобы не считать их вручную:
def parse_msg(block):
# 16 lines in a batch, each block has a length 32 bits
for cw in range(16):
cws = block[32 * cw:32 * (cw + 1)]
if cws[0] == "0":
addr = cws[1:19]
print(" Addr:" + addr)
else:
msg = cws[1:21]
print(" Msg: " + msg)
size = 4
s = ""
for ind in range(0, len(msg), size):
bcd_s = msg[ind:ind + size]
value = int(bcd_s, 2)
symbols = "0123456789*U -)("
s += symbols[value]
print(" ", s)
print()
В итоге получаем переданное сообщение «0682*)*882». Что оно значит, сказать сложно, но раз формат поддерживает цифровые сообщения, значит наверно оно кому-то нужно.
Выводы
Как можно видеть, формат POCSAG весьма простой, и по сути, может быть декодирован даже в школьной тетради. И хотя сейчас это имеет скорее исторический интерес, анализ таких протоколов весьма полезен с познавательной точки зрения.
Если у аудитории будет интерес, в следующей части можно продолжить и рассказать про декодирование текстовых сообщений.
Комментариев нет:
Отправить комментарий