В первой части был рассмотрен протокол обмена пейджерными сообщениями POCSAG. Были рассмотрены цифровые сообщения, перейдем теперь к более «полноценным» сообщениям в формате ASCII. Тем более, что декодировать их интереснее, т.к. на выходе будет читаемый текст.
![](https://habrastorage.org/webt/hu/ew/hr/huewhrcwg9oow3xpzglh8skuhqi.jpeg)
Для тех кому интересно, как это работает, продолжение под катом.
Прием сигнала
Сначала сигнал надо принять, для чего воспользуемся тем же самым rtl-sdr приемником и программой HDSDR. Мы уже знаем из первой части, что пейджинговые сообщения могут быть цифровые (содержание только цифры 0-9, букву U — «ugrent», пробел и пару скобок) и буквенно-цифровые, содержание полноценные ASCII-символы. Естественно, мы не знаем заранее тип сообщения (декодировать их «на слух» пока еще не получается), поэтому при записи просто выбираем сообщение подлиннее.
![](https://habrastorage.org/webt/lu/ue/sk/luuesk79iv_yalka_pchncpwzoi.jpeg)
Программа преобразования wav-файла в битовый поток уже рассматривалась, поэтому сразу покажем результат — пейджинговое сообщение целиком выглядит вот так:
![](https://habrastorage.org/webt/qb/wt/ud/qbwtud3zptfcwxw3xs0pam7pkr4.jpeg)
Некоторые особенности видны сразу невооруженным глазом — например видно, что стартовая последовательность 01010101010101 повторяется дважды. Т.е. это сообщение не только более длинное, но и по сути состоит из двух «склеенных» вместе, впрочем стандарт это не запрещает.
Декодирование
Для начала напомним краткое содержание предыдущей части. Пейджинговое сообщение начинается с длинного заголовка 0101010101, за которым следует последовательность «пакетов», показанных на картинке как Batch1..N:
![image](https://habrastorage.org/webt/3k/yt/dc/3kytdccgmehfmqstxxli1nt3fpa.png)
Каждый пакет начинается со стартовой последовательности Frame Sync Code (01111100...), за которой идут 32-битные блоки. Каждый блок может хранить либо адрес, либо тело сообщения.
В предыдущий раз мы рассматривали только цифровые сообщения, теперь же нас интересуют сообщения ASCII. Первым делом, нужно научиться их различать. Для этого нам понадобится поле «Function Bits» — если эти 2 бита равны 00, то сообщение цифровое, если 11, то текстовое.
Как видно из рисунка, на поле сообщения отводится 20 бит, что как раз идеально ложится в 5 4-х битных BCD-кода, если сообщение цифровое. А вот если сообщение текстовое, то текста в 20 бит много не уместить, да и 20 не делится ни на 7, ни на 8. Можно предположить, что первая версия протокола (а он был создан аж в 1982г) поддерживала только цифровые сообщения (
Рассмотрим один блок принятого сообщения (пробелы добавлены для наглядности):
0 0001010011100010111111110010010
1 00010100000110110011 11100111001
1 01011010011001110100 01111011100
1 11010001110110100100 11011000100
1 11000001101000110100 10011110111
1 11100000010100011011 11101110000
1 00110010111011001101 10011011010
1 00011001011100010110 10011000010
1 10101100000010010101 10110000101
1 00010110111011001101 00000011011
1 10100101000000101000 11001010100
1 00111101010101101100 11011111010
В первой строке «0» в первом бите указывает на то, что это поле адреса, а «11» в битах 20-21 указывает, что это сообщение символьное. Далее просто берем 20 бит из каждой строки и складываем их вместе.
Получаем такую битовую последовательность:
00010100000110110011010110100110011101001101000111011010010011000001101000
11010011100000010100011011001100101110110011010001100101110001011010101100
000010010101000101101110110011011010010100000010100000111101010101101
В протоколе POCSAG используется 7-битный ASCII, так что просто делим строку на блоки по 7 бит:
0001010 0000110 1100110 1011010 0110011 1010011 ...
Пытаемся декодировать коды символов (таблица ASCII легко гуглится в интернете), и… получаем на выходе мусор. Еще раз открываем документацию и находим малозаметную фразу «ASCII characters are placed from left to right (MSB to LSB). The LSB is transmitting first.». Т.е. сначала передается младший бит, а потом уже старший — для корректного декодирования ASCII-кодов 7-битные строки нужно перевернуть.
Чтобы не делать это вручную, пишем код на Python:
def parse_msg(block):
msgs = ""
for cw in range(16):
cws = block[32 * cw:32 * (cw + 1)]
# Skip the idle word
if cws.startswith("0111101010"):
continue
if cws[0] == "0":
addr, type = cws[1:19], cws[19:21]
print(" Addr:" + addr, type)
else:
msg = cws[1:21]
print(" Msg: " + msg)
msgs += msg
# Split long string to 7 chars blocks
bits = [msgs[i:i+7] for i in range(0, len(msgs), 7)]
# Get the message
msg = ""
for b in bits:
b1 = b[::-1] # Revert string
value = int(b1, 2)
msg += chr(value)
print("Msg:", msg)
print()
В результате, получаем такую последовательность (биты, коды символов, и сами символы):
0101000 40 (
0110000 48 0
0110011 51 3
0101101 45 -
1100110 102 f
1100101 101 e
1100010 98 b
0101101 45 -
0110010 50 2
0110000 48 0
0110001 49 1
0111001 57 9
0100000 32
0110001 49 1
0110011 51 3
0111010 58 :
0110011 51 3
0110001 49 1
0111010 58 :
0110100 52 4
0110101 53 5
0100000 32
0101010 42 *
0110100 52 4
0110111 55 7
0110110 54 6
0101001 41 )
0100000 32
1000001 65 A
1010111 87 W
1011010 90 Z
Объединяем символы вместе и получаем строку: "(03-feb-2019 13:31:45 *476) AWZ". Как и обещалось в начале статьи, текст вполне читабельный.
Кстати, еще один интересный момент в том, что как можно видеть, в протоколе используется 7-битный ASCII. Символы кирилицы не помещаются в этот диапазон, так что вопрос, как в пейджеры прошивали русский язык, остается открытым. Если кто знает, напишите в комментариях.
Выводы
Разумеется, протокол разобран не полностью, но самая интересная часть сделана, а дальше остается рутина, которая уже не так интересна. Как минимум, нет декодирования адресов получателей (capcodes), и не реализована поддержка кода коррекции ошибок (BCH Check Bits) — это позволило бы исправлять до 2х «испорченных» при передаче бит. Впрочем, цели сделать полноценный декодер и не стояло — такие декодеры уже есть, и еще один вряд ли нужен.
Желающие попробовать декодировать сообщения с помощью rtl-sdr, могут сделать это самостоятельно с помощью бесплатной программы PDW. Она не требует инсталляции, после запуска необходимо перенаправить выход HDSDR на вход PDW с помощью программы Virtual Audio Cable и выбрать в аудио-настройках PDW соответствующее устройство.
Результат работы программы выглядит примерно так:
![](https://habrastorage.org/webt/-7/g-/ka/-7g-kalvhmaqlv79qju-wmgpglg.jpeg)
На этом тему пейджинговых сообщений можно считать закрытой. Желающие изучить тему более подробно, могут изучить исходные коды программы multimon-ng, которая может декодировать множество протоколов, в том числе POCSAG и FLEX.
Комментарии (11)
Arcanum7
04.02.2019 09:32Интресно, если протокол был такой примитивный то при должной прямоте рук и хотении, возможно ли было носить с собой передатчик (пусть и с клавой) и транслировать пейджеру жертвы левые сообщения?
DmitrySpb79 Автор
04.02.2019 09:58Да, можно было бы наверно даже с портативного компьютера сообщения слать. Только имели они тогда такие размеры:
:)
И не стоит забывать, что то что сейчас просто и делается в 2 клика, в 90е было куда сложнее и дороже — тогда даже звуковая карта на компьютере и то была экзотикой, доступной только профи.Arcanum7
04.02.2019 12:35В девяностые было много голодных инженеров которые могли от безысходности сделать что угодно.
neiroman2k
04.02.2019 16:38ЕМНИП, то кроме частоты надо было еще знать код пейджера. А у моторолы были терминалы в виде телефона с полноценной клавиатурой, для самостоятельно отправки сообщений минуя оператора (человека).
В терминале был встроен модем, который подключался к пулу пейджинговой системы и отправлял сообщения, которые набирались на клавиатуре
ProstoUser
05.02.2019 10:25Я бы отметил еще пару интересных моментов.
Коррекцию ошибок только слегка упомянута, а алгоритм интересный. БЧХ называется — Боуз, Чоудхури, Хоквингем. Там действительно исправляется двойная ошибка, но есть burst mode. То есть в предположении, что перевернутые биты рядом, он может исправить и 4 неверных бита.
Еще там интересная система адресации. Дело в том, что младшие 3 бита адреса пейджера не передаются в адресном кодеворде, а берутся как номер адресного кодеворда в батче. А каждый пейджер знает свой адрес. Соответственно, если он работает штатным образом и хочет принимать только свои сообщения, он может засыпать и выключать приемник, пока идут «чужие» данные и после получения преамбулы просыпаться только на 1/8 часть времени, получать один кодеворд, понимать, что это не адресный, или адрес не тот и засыпать на следующие 8 кодевордов. В общем, помогает экономить батарейку.
Собственно эта фича и требует наличия пустых (Idle) кодевордов. Они нужны, чтобы заполнить место между концом предыдущего сообщения и адресным кодевордом следующего, если согласно адресу пейджера номер его адресного кодеворда должен быть не сразу за концом предыдущего сообщения.DmitrySpb79 Автор
05.02.2019 11:50Спасибо, интересно. Когда изучал протокол, не мог понять зачем эти idle нужны.
vladkorotnev
В моём пейджере (какой-то NEC) из комментариев к прошлому посту русские буквы прошиты были просто на место маленьких английских. И то не все, часть надо было заменять английскими. То есть сообщение «Привет, Хабр!» можно было бы отправить только как
ПPИBET, XAБP!
, что в сыром виде выглядит примерно какpPiBET, XAbP!
(на точность не претендую, так как саму таблицу не помню и даже не додумался куда-либо записать).ZiggiPop
Похоже на KOI-8:
Разработчики КОИ-8 поместили символы русского алфавита в верхней части кодовой таблицы таким образом, что позиции символов кириллицы соответствуют их фонетическим аналогам в английском алфавите из нижней части таблицы. Это означает, что если в тексте, написанном в КОИ-8, убрать восьмой бит каждого символа, то получится «читаемый» текст, подобный транслиту. Например, слова «Русский Текст» превратятся в «rUSSKIJ tEKST». Из?за этого символы кириллицы расположены не в алфавитном порядке. (Википедия)
vladkorotnev
Да, но было не совсем так, точно не помню. Кои-8 запомнился по МК-90, его бы я вспомнил :-)
Serge3leo
Тогда уж КОИ-7, который 7-битный.
$ echo 'ПРИВЕТ, ХАБР!' | iconv -t koi-7
priwet, habr!