
MSK144 — цифровой протокол, разработанный Джо Тейлором (K1JT) и его командой в 2016 году для проведения связей через метеорное рассеивание. В этой статье будут рассмотрены подробности работы протокола.
Статья может быть интересна радиолюбителям, как знакомым, так и не знакомым с MSK144 и связью через метеорное рассеивание, а также тем, кто хочет понять устройство этого протокола.
Предыстория
Связь через метеорные потоки — вид связи, основанный на отражении радиоволны от ионизированных следов метеоров, сгорающих в атмосфере Земли. Ионизационный след от метеора существует от нескольких миллисекунд, до нескольких секунд, в зависимости от размеров метеора.
Проведение связей через метеорные потоки начало обретать популярность в 50-х годах прошлого столетия, тогда впервые их проводили на 15 и 20 метровых диапазонах. Также было установлено, что еще более впечатляющих результатов можно добиться на 6 и 2 метровых диапазонах, где шумовая картина значительно лучше, а большего усиления можно добиться с помощью направленных антенн, занимающих намного меньшие габариты.
Так как ионизационный след в достаточной мере эфемерен, радиолюбителям приходилось использовать телеграф со скоростью от 10 до 40 знаков в секунду. Сигналы записывались на магнитофон, отправлялись и воспроизводились с сильным ускорением и замедлением. С применением компьютеров удавалось увеличить скорость до 150 знаков в секунду.
Введение
В начале нулевых Джо Тейлор представил протокол FSK441 (использовавший частотную манипуляцию), давший большой толчок проведению метеорных УКВ связей на дальние расстояния. С ростом мощностей компьютеров в 2016 году Тейлор с командой представили протокол с более эффективной модуляцией — MSK144 (от Minimum Shift Keying, с длиной пакета в 144 бита), включающий в себя коды коррекции ошибок и проверку целостности данных, о котором пойдет дальнейшая речь.
Общие технические характеристики
Цикл передачи: от 5 до 30 секунд;
Тип модуляции: MSK (Minimum Shift Keying) на частотах 1000 и 2000 Гц;
Скорость передачи: 2000 бод;
Механизм коррекции ошибок LDPC (128,80);
Ширина полосы: 2.5 КГц;
Размер сообщения 83 бит + 13-бит CRC-13;
Структура протокола
На прикладном уровне структура протокола во многом заимствует логику работы протоколов FT8/FT4, описанных ранее в соответствующей статье, в связи с этим настоятельно рекомендуется с ней ознакомиться.
MSK144, как и любой протокол передачи данных, представляет собой "матрешку", в которой данные на каждом уровне претерпевают изменения перед передачей на уровень ниже.
Общая схема протокола приведена на рисунке 1. Разделение на уровни модели OSI условное.

На рисунке 1.А приведена схема взаимодействия алгоритмов кодирования при формировании исходящего сигнала; на рисунке 1.B — схема обработки и декодирования принятого сигнала.
Как видно, отличия от FTX заключаются в использовании контрольных сумм CRC-13, иных маркеров синхронизации и, собственно, в типе модуляции сигнала.
Прикладной радиообмен
Аналогично протоколам семейства FTX, участники разделяются на передающих и принимающих, чьи роли меняются каждый цикл, длящийся от 5 до 30 секунд (в зависимости от прохождения).
Передаваемые сообщения аналогичны сообщениям в протоколах семейства FTX:
CQ — общий вызов с передачей позывного и QTH-квадрата;
обмен SNR-рапортами;
завершение сессии посылками Roger-сигналов.
CRC-13
В отличие от протоколов семейства FTX, использующих CRC-14 для проверки целостности сообщений, протокол MSKX использует еще более компактную версию циклического избыточного кода — CRC-13, который, формирует 13-и битную контрольную сумму.
MSKX_CRC_POLYNOMIAL = 0x15D7
MSKX_CRC_WIDTH = 13
MSKX_CRC_TOP_BIT = 1 << (MSKX_CRC_WIDTH - 1)
MSKX_PAYLOAD_BITS = 96
MSKX_MESSAGE_BITS = MSKX_PAYLOAD_BITS - MSKX_CRC_WIDTH
def mskx_compute_crc(message: typing.ByteString, num_bits: int) -> int:
remainder = 0
idx_byte = 0
for idx_bit in range(num_bits):
if idx_bit % 8 == 0:
remainder ^= message[idx_byte] << (MSKX_CRC_WIDTH - 8)
idx_byte += 1
if remainder & MSKX_CRC_TOP_BIT != 0:
remainder = (remainder << 1) ^ MSKX_CRC_POLYNOMIAL
else:
remainder = remainder << 1
return remainder & ((MSKX_CRC_TOP_BIT << 1) - 1)
def mskx_add_crc(payload: typing.ByteString) -> typing.ByteString:
message = payload + (b"\x00" * (FTX_LDPC_K_BYTES - len(payload)))
message[-3] &= 0xfc
message[-2] = 0
checksum = mskx_compute_crc(message, MSKX_MESSAGE_BITS)
message[-3] |= byte(checksum >> 10)
message[-2] = byte(checksum >> 2)
message[-1] = byte(checksum << 6)
return message
Для CRC-13 используется полином 0x15D7
(MSKX_CRC_POLYNOMIAL
).
msg = bytearray(b'\x00\x00\x00 Y\xac\xff\x94\xc9\xc8')
msg_crc = mskx_add_crc(msg)
К примеру, сообщение "CQ R9FEU LO87
" после упаковки бит примет вид "0x00 0x00 0x00 0x20 0x59 0xac 0xff 0x94 0xc9 0xc8
" в HEX-представлении, CRC-13 этих данных "0x5e5
". В функции mskx_add_crc
после конкатенации сообщения с контрольной суммой, итоговое сообщение принимает вид "0x00 0x00 0x00 0x20 0x59 0xac 0xff 0x94 0xc9 0xc9 0x79 0x40
".
LDPC (низкоплотностный код)
Протоколы семейства MSKX используют низкоплотностные коды проверки на четность меньших размеров, по сравнению с протоколами семейства FTX. В отличии от FTX, где используется LDPC (174, 87), в MSKX применен LDPC (128, 80).
Параметры LDPC-кодировщика для MSKX сообщений:
MSKX_LDPC_K = 90
MSKX_LDPC_M = 38
MSKX_LDPC_N = MSKX_LDPC_K + MSKX_LDPC_M
MSKX_LDPC_N_BYTES = ((MSKX_LDPC_N + 7) // 8)
MSKX_LDPC_K_BYTES = ((MSKX_LDPC_K + 7) // 8)
Порождающая LDPC-матрица:
MSKX_LDPC_GENERATOR = [
[0xa0, 0x8e, 0xa8, 0x08, 0x79, 0x05, 0x0a, 0x5e, 0x94, 0xda, 0x99, 0x40],
[0x59, 0xf3, 0xb4, 0x80, 0x40, 0xca, 0x08, 0x9c, 0x81, 0xee, 0x88, 0x00],
...
[0x7e, 0x79, 0x36, 0x2c, 0x16, 0x77, 0x3e, 0xfc, 0x64, 0x82, 0xe3, 0x00],
]
Матрицу целиком можно посмотреть здесь.
Алгоритм LDPC-кодирования идентичен алгоритму используемому в протоколах семейства FTX:
def parity8(x: int) -> int:
for i in [4, 2, 1]:
x ^= x >> i
return byte(x % 2)
def mskx_encode(message: typing.ByteString) -> typing.ByteString:
codeword = bytearray(message[i] if i < MSKX_LDPC_K_BYTES else 0 for i in range(MSKX_LDPC_N_BYTES))
col_mask = 0x80 >> (MSKX_LDPC_K % 8)
col_idx = MSKX_LDPC_K_BYTES - 1
for i in range(MSKX_LDPC_M):
nsum = 0
for j in range(MSKX_LDPC_K_BYTES):
bits = message[j] & MSKX_LDPC_GENERATOR[i][j]
nsum ^= parity8(bits)
if nsum % 2:
codeword[col_idx] |= col_mask
col_mask >>= 1
if col_mask == 0:
col_mask = 0x80
col_idx += 1
return codeword
payload = mskx_encode(msg_crc)
Рассматриваемое ранее сообщение "CQ R9FEU LO87
" с вычисленной контрольной суммой ("0x00 0x00 0x00 0x20 0x59 0xac 0xff 0x94 0xc9 0xc9 0x79 0x40
") после LDPC-кодирования принимает вид "0x00 0x00 0x00 0x20 0x59 0xac 0xff 0x94 0xc9 0xc9 0x79 0x72 0x35 0x7c 0x80 0x91
" (128 бит).
Метки синхронизации
Перед тем как начать формирование сигнала, готового к отправке в эфир, к исходным данным добавляются метки синхронизации, используемые при декодировании для определения сигнала и его выравниванию во временной области.
Маркером синхронизации в протоколе MSK144 используется двоичная последовательность 0,1,1,1,0,0,1,0
(0x72
), располагаемая в самом начале и после 48 бит данных.
sync = b"\x72" # 0,1,1,1,0,0,1,0
envelope = sync + payload[0:6] + sync + payload[6:16]
После добавления маркеров синхронизации, сообщение принимает вид "0x72 0x00 0x00 0x00 0x20 0x59 0xac 0x72 0xff 0x94 0xc9 0xc9 0x79 0x72 0x35 0x7c 0x80 0x91
" (144 бит).
Примечание: в протоколе MSKMS для синхронизации используется последовательность бит 0,1,0,0,1,1,1,0, а в MSK40 — 1,0,1,1,0,0,0,1. Причины выбора таких значений констант неизвестны.
Minimum Shift Keying формирование сигнала
MSK (Minimum Shift Keying) — это подвид частотной манипуляции (FSK с непрерывной фазой, с индексом модуляции 0.5) с минимальным частотным сдвигом. Отличительной особенностью MSK является свойство, что разность частот между сигналами, формирующие нули и единицы, равняется половине скорости передачи символов, что, в том числе, с когерентностью фазы, обеспечивает ортогональность частот и высокую спектральную эффективность.
Под свойствами ортогональности частот, понимается, что частоты независимы и не оказывают друг на друга взаимного влияния, которые проявляются в виде интерференций.
На рисунке 2 представлены спектры сигналов, отстоящих друг от друга достаточно далеко по частоте. На рисунке 3 спектры сигналов с ортогональными частотами, как можно заметить, спектры сигналов с ортогональными частотами имеют взаимные пересечения в точках перехода через ноль.


На рисунке 4 показаны графики функций с ортогональными частотами. В период T сигнала, выделенного красным, укладывается 2T сигнала выделенного синим, что является свойством, что частота синего сигнала имеет вдвое большую частоту относительно частоты красного сигнала, при этом интеграл от суммы двух сигналов равен нулю.

Исходя из этого, в отличие от разнесения сигналов далеко друг от друга в частотной области, сигналы с ортогональными частотами могут быть расположены достаточно близко в спектре, тем самым более эффективно использовать полосу частот.
При формировании MSK сигнала, результирующий сигнал можно рассматривать как разность синфазного и квадратурного сигналов (1).
Кодирование бит осуществляется поочередно, нечетные биты формируются квадратурным (Q) сигналом, а четные синфазным (I) (рисунок 5).
При этом для кодирования единиц используется положительная часть полуволны, для нулей — соответственно отрицательная.

На рисунке 5 изображены графики I и Q сигналов, сформированные согласно последовательности бит 01100110101101010
.
Сообщение "720000002059ac72ff94c9c97972357c8091
" в двоичном виде имеет вид "011100100000000000000000000000000010000001011001101011000111001011111111100101001100100111001001011110010111001000110101011111001000000010010001
".
Для формирования I и Q сигналов, исходное битовое представление преобразуется в вид, где битам со значением 0
присваивается отрицательный знак, а битам со значением 1
— положительный соответственно.
signs = [2 * ((payload[i // 8] >> (7 - (i % 8))) & 1) - 1 for i in range(144)] + [0]
Результат: -1111-1-11-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-1-11-1-1-1-1-1-11-111-1-111-11-111-1-1-1111-1-11-1111111111-1-11-11-1-111-1-11-1-1111-1-11-1-11-11111-1-11-1111-1-11-1-1-111-11-11-111111-1-11-1-1-1-1-1-1-11-1-11-1-1-110
.
На основе полученной последовательности и правил кодирования четных и нечетных бит формируются номера тонов, определяющие сдвиг частоты сигнала. Так как в MSK возможен только минимальный сдвиг частоты, или иными словами, 2 тона, то результатом является двоичная последовательность, где 0
— основной тон, 1
— тон с минимальным сдвигом.
tones = [0 for _ in range(144)]
for i in range(72):
tones[2 * i - 0] = (signs[2 * i + 1] * signs[2 * i - 0] + 1) // 2
tones[2 * i + 1] = -(signs[2 * i + 1] * signs[2 * i + 2] - 1) // 2
for i in range(144):
tones[i] = -tones[i] + 1
Результат: 110000110101010101010101010101010011010110111111101000011100001001010101111010000000111100001110110111101100001100001010110100001101010011100111
.
На финальной стадии, из полученной последовательности тонов формируется сигнал, с когерентной фазой.
def mskx_gen_signal(
tones: typing.List[int], sample_rate: int,
carrier_freq: int = 1000, delta_freq: int = 1000,
sampling_factor: int = 1, sampling_rate_coef: int = 4):
dt = 1.0 / (sampling_factor * sample_rate)
samples_per_symbol = 6 * sampling_rate_coef
phase = 0.0
signal = np.zeros(len(tones) * samples_per_symbol)
for i, tone in enumerate(tones):
freq = carrier_freq + tone * delta_freq
phase_delta = 2 * np.pi * freq * dt
t_start = i * samples_per_symbol
t_end = t_start + samples_per_symbol
phases = np.fromiter(
(np.fmod(phase_delta * i + phase, 2 * np.pi)
for i in range(samples_per_symbol)),
dtype=np.float32
)
signal[t_start:t_end] = np.sin(phases)
phase = np.fmod(phase_delta * samples_per_symbol + phase, 2 * np.pi)
return signal
Функция mskx_gen_signal
принимает на вход список индексов ранее сформированных тонов, центральная частота в MSK144 составляет 1КГц, сдвиг частоты 1КГц. Значение в samples_per_symbol
определяет количество семплов на один символ. Результатом является дискретный сигнал с частотой дискретизации sample_rate
.
Синтезированный сигнал, готовый к отправке через радиоканал, можно записать в аудио-файл.
from scipy.io.wavfile import write
GEN_SAMPLE_RATE = 48000
signal = mskx_gen_signal(tones, sample_rate=GEN_SAMPLE_RATE)
amplitude = np.iinfo(np.int16).max
wave = np.concat([signal * amplitude] * 10)
write("signal.wav", GEN_SAMPLE_RATE, wave.astype(np.int16))
Длительность одного пакета данных в MSK144 составляет 0.072 секунды (72 мсек), для прослушивания примера сигнал был продублирован 10 раз.
При проведении сеанса связи через метеоры, один и тот же пакет данных многократно и непрерывно посылается в эфир в течении 15-30 секунд, тем самым компенсируя потери в канале связи избыточным количеством одних и тех же данных, что позволяет на принимающей стороне повысить вероятность принять и декодировать данные в условиях эфемерного канала связи.
На рисунках 6 и 7 представлены график части результирующего MSK-сигнала и спектр одного пакета данных.


Заключение
В статье были рассмотрены принципы формирования сигнала в протоколе MSK144 для проведения связей через метеорные потоки. Основными свойствами протокола является скорость передачи данных, размер передаваемого пакета, а также компенсация потерь в канале связи путем многократного повтора передаваемых данных.
Протоколы семейства MSKX во многом заимствует и использует архитектуру с принципами кодирования данных у протоколов семейства FTX. Также как и у протоколов семейства FTX, протоколам семейства MSKX присущи все те же недостатки, связанные с объемом передаваемых данных и сложностью адаптации его для передачи произвольных данных.
Технология также может предоставлять базис для радиолюбительских экспериментов с передачей данных любого типа. Так, например, изменяя механизм шквальной репликации пакетов в сессии, размер передаваемых данных и модульную привязку к времени, протокол может быть адаптирован для передачи датаграмм с высокой скоростью не только через метеорные потоки.
Ссылки
Комментарии (0)
ALT0105
15.09.2025 08:03Так как ионизационный след в достаточной мере эфемерен, радиолюбителям приходилось использовать телеграф со скоростью от 10 до 40 знаков в секунду. Сигналы записывались на магнитофон, отправлялись и воспроизводились с сильным ускорением и замедлением. С применением компьютеров удалось увеличить скорость до 150 знаков в секунду.
В доцифровые времена, когда самое сложное устройство у радиолюбителя был магнитофон, скорость 10-40 знаков в секунду понятна. Почему сейчас 150 знаков в секунду? Диапазон частот в метеорной связи - десятки мегагерц
bashkirtsevich Автор
15.09.2025 08:03Здесь имеется ввиду, в эпоху до появления альтернатив телеграфу. То есть, до появления цифровых протоколов, радиолюбители использовали телеграф для проведения метеорных связей.
Метеорные связи на 2м диапазоне с использованием MSK144 проводятся на частоте 144.370 МГц USB. Сам протокол MSK144 обрабатывается в слышимом для человека диапазоне частот (можно в качестве аналогии сравнить с диалап-модемом).
ALT0105
15.09.2025 08:03Но на частоте 144 МГц какая скорость передачи данных получается?
bashkirtsevich Автор
15.09.2025 08:03MSK144 имеет скорость передачи данных 2000 бод.
ALT0105
15.09.2025 08:03Протокол - это порядок действий и не имеот привязки к абсолютному времени. Он будет таким же и при скорости 2000 килобод
bashkirtsevich Автор
15.09.2025 08:03Подчеркну, что 150 знаков в телеграфе -- это было до появления FSK441 и MSK144, с появлением этих протоколов такая необходимость в применении сверхскоростного телеграфа минимизировалась.
ALT0105
15.09.2025 08:03На несущей 144 МГц вполне можно иметь скорость в мегабиты в секунду. Зачем передавать со скоростью в 1000 раз меньше? Это же не морзянка вручную - было же в тексте упоминание про цифровую технику
bashkirtsevich Автор
15.09.2025 08:03Пункт номер 3 в разделе ссылок.
Протокол узкополосный (2.5 КГц полоса сигнала, передается в USB модуляции), радиолюбительский, из полезных данных передает только CQ с позывными и SNR-рапорт.
ALT0105
15.09.2025 08:03А зачем нужна такая связь? Я понимаю, что любить можно что угодно, но гораздо интереснее подавать что-нибудь кроме позывных... Ну, не радиолюбитель я...
bashkirtsevich Автор
15.09.2025 08:03Радиохобби, спорт, установление дальних связей (проведение QSO) и рекордов дальности на УКВ. Протокол обмена достаточно аскетичен для того чтобы подтвердить факт установления двусторойнней связи (то есть базового набора метаданных для этого достаточно, в каком-то смысле этот процесс можно считать чем-то вроде ping).
Такова романтика радиолюбительства.
saiklo
15.09.2025 08:03Военная причина. Развитие метеорной радиосвязи связана с риском ядерной войны. Электромагнитный импульс. Все спутники сбиты. Тогда оптики не было. Протокол конечно интересный. Но можно и мегабайты передавать. Не всегда. А можно и ключи шифрования например передовать
ALT0105
15.09.2025 08:03Да, дополнительные резервные каналы всегда нужны. Но канал со скоростью 150 знаков в секунду без помехоустойчивого кодирования, видимый всем, не гарантирующий правильности передачи, нужен вряд ли. Метеорный канал образуется в непредсказуемый момент времени на доли секунды, в лучшем случае секунды, и если уж его использовать, протокол, модуляция и способ передачи должны быть другие
VT100
15.09.2025 08:03Микрометеориты - поступают в атмосферу достаточно непрерывно.
Ну и... новый плакат: "Не забывайте выполнять квоты по замусориванию на орбитах ниже 200 км - поддерживайте радиосвязь!"
ALT0105
15.09.2025 08:03Я бы устроил соревнование по количеству переданной информации по одному метеорному следу. Это интересно
ALT0105
15.09.2025 08:03В условиях кратковременного прохождения по метеорному следу нужно стараться передать максимум информации за минимальное время. Разве не так?
bashkirtsevich Автор
15.09.2025 08:03Так и есть. Отличительной особенностью является еще то, что пакеты данных идут шквалом в течении временного окна.
ahdenchik
15.09.2025 08:03А окна эти возникают раз 10-20 за сутки?
Какое примерно расстояние можно покрыть такой связью? (однократный хоп)
ra4hgn
15.09.2025 08:03Первые MS связи начал проводить FSK441, жаль что что его K1JT выпилил, пусть бы был, многие любители meteor scatter до сих пор по нему вздыхают. TNX, с нетерпением жду про Q65.
ArthPLM
Следующий этап проведения QSO через метеоры ))