Разбираем Протокол Пейджера Pocsag, Часть 1

Привет Хабр! Когда-то, когда сотовый телефон стоил 2000 долларов, а минута разговора стоила 50 центов, была такая популярная вещь, как пейджинг.

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



Разбираем протокол пейджера POCSAG, часть 1

Для тех, кто помнит анекдот «Я читал пейджер, много думал» и хочет разобраться, как он работает, продолжение — под катом.

Для тех, кто хочет разобраться еще подробнее, доступно Вторая часть .



Общая информация

Для тех, кто забыл или родился после 2000-х, кратко напомню основные идеи.

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

Сообщения просто передаются последовательно «как есть», и пейджер принимает их, если номер получателя совпадает с номером пейджера.

— Приемное устройство очень простое, поэтому пейджер может работать без подзарядки до месяца от 2-х обычных батареек типа АА.

Существует два основных стандарта передачи сообщений: ПОКСАГ (Консультативная группа по стандартизации кодов почтовых отделений) и ГИБКИЙ .

Стандарты вовсе не новы, POCSAG был утвержден еще в 1982 году, поддерживаемые скорости — 512, 1200 и 2400 бит/с.

Для передачи используется частотная манипуляция (FSK) с разносом частот 4,5 кГц.

Новый стандарт FLEX (предложенный Motorola в 90-х годах) поддерживает скорость до 6400 бит/с и может использовать не только FSK2, но и FSK4. Протоколы по своей сути довольно просты, и 20 лет назад для них были написаны декодеры, способные расшифровывать сигнал со входа звуковой карты (сообщения не шифруются, поэтому прочитать их с помощью такой программы в принципе может любой желающий).

Давайте посмотрим, как это работает.

Прием сигналов

Во-первых, нам нужен образец для декодирования.

Берем ноутбук, rtl-sdr приемник, машину времени и получаем нужный нам сигнал.



Разбираем протокол пейджера POCSAG, часть 1

Т.

к.

модуляция частотная, то режим приема тоже выставлен на 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()

Результат (биты подписаны вручную):

Разбираем протокол пейджера POCSAG, часть 1

Как видите, все просто, и даже «на глаз» в Paint можно дорисовать биты, где «0» и где «1».

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

Если увеличить график, то можно увидеть, что ширина каждого «бита» составляет 20 сэмплов, что при частоте дискретизации wav-файла 24000 семплов/с соответствует скорости 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)

Как видите, совпадение не идеальное (частоты передатчика и приемника все же немного отличаются), но для декодирования вполне достаточно.



Разбираем протокол пейджера POCSAG, часть 1

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

И последний шаг — преобразовать массив из 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 .



Разбираем протокол пейджера POCSAG, часть 1

Все более-менее понятно.

Заголовок сообщения состоит из длинного блока «10101010101», который нужен для выхода пейджера из спящего режима.

Само сообщение состоит из блоков Batch-1. Batch-N, каждый из которых начинается с уникальной последовательности FSC (выделен в тексте жирным шрифтом).

Далее, как видно из мануала, если строка начинается с «0», то это адрес получателя.

Адрес жестко запрограммирован в самом пейджере, и если он не совпадает, пейджер просто проигнорирует сообщение.

Если строка начинается с «1», то это на самом деле сообщение.

У нас есть две такие линии.

Теперь давайте рассмотрим каждый блок.

Видим Idle-коды – пустые блоки 01111.0111, не несущие полезной информации.

Удаляем их, информации осталось совсем мало, остается только: 01111100110100100001010011011000 - Синхронизация кадров 00001000011011110100010001101000 — Адрес 10000011010000010101010011010100 — Сообщение 01111100110100100001010111011000 - Синхронизация кадров 11110101010001000001000000111000 — Сообщение 00100101101001011010010100101111 — Адрес Осталось понять, что находится внутри.

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

Цифровые сообщения хранятся в виде 4-битных BCD-кодов, то есть в 20 битах может уместиться 5 символов (ещё остались биты для управления, мы их рассматривать не будем).

Сообщение может быть и текстовым, в этом случае используется 7-битная кодировка, но для текстового сообщения наше сообщение слишком маленькое — общее количество бит сообщения не кратно 7. Из строк 10000011010000010101010011010100 и 11110101010001000001000000111000 получаем следующие 4-битные последовательности: 1 0000 0110 1000 0010 10101 0011010100 — 0ч 6ч 8ч 2ч Ач 1 1110 1010 1000 1000 00100 0000111000 — Эх ах 8ч 8ч 2ч И, наконец, последний шаг — посмотреть таблицу соответствия символов в документации.



Разбираем протокол пейджера POCSAG, часть 1

Как видите, цифровое сообщение может содержать только цифры 0–9, букву U («угрент»), пробел и пару круглых скобок.

Напишем простую функцию вывода, чтобы не считать их вручную:

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 очень прост и фактически его можно расшифровать даже в школьной тетради.

И хотя сейчас это представляет скорее исторический интерес, анализ подобных протоколов очень полезен с образовательной точки зрения.

В следующая часть рассказывает о декодировании сообщений ASCII. Теги: #python #Гаджеты #Инженерные системы #мессенджеры #Старое железо #радио #обработка сигналов #пейджер #flex #Flex #P2000

Вместе с данным постом часто просматривают: