Голосовая связь — то, ради чего MeshTRX вообще появился, и то, чего в похожих проектах на LoRa обычно нет. Ниже — как голос в MeshTRX устроен: почему его не должно было получиться, что пришлось выбросить и чем мы за это заплатили.
Все числа здесь взяты из исходников прошивки и клиентов, а не из описания проекта.
Почему голос по LoRa считается невозможным
LoRa придумывали не для разговоров. Её задача — донести до шлюза десяток байт с датчика влажности, потратив на это микроватты и добравшись за несколько километров сквозь стены. Всё остальное следует отсюда: медленная модуляция, узкая полоса, маленькие пакеты.
На параметрах MeshTRX — SF7, полоса 250 кГц, избыточность 4/7 — канал даёт около 7,8 кбит/с. Это на бумаге: без учёта преамбулы, заголовков и пауз между передачами, так что на деле заметно меньше.
Для сравнения: голос в GSM — 13 кбит/с, самый скромный интернет-звонок — 8–16 кбит/с. То есть привычные способы передать речь в этот канал не помещаются вовсе. Не «работают плохо», а не помещаются. Задача, которую решает MeshTRX, начинается ровно здесь.
Codec2: голос как описание, а не как звук
Выход нашёлся не в сжатии, а в смене подхода.
Обычный кодек хранит звуковую волну — точнее или грубее, но именно волну. Codec2 Дэвида Роу устроен иначе: это вокодер. Он не передаёт волну совсем. Вместо неё в эфир уходит описание того, как речь была устроена в этот момент: основной тон, энергия, форма спектра. На другом конце декодер синтезирует речь заново — по этому описанию, с нуля.
MeshTRX использует режим 3200 — 3200 бит в секунду. Это тот же кодек и тот же режим во всех трёх частях проекта: в прошивке рации, в приложении для Android и в настольном клиенте. Один кадр это 20 мс звука, 160 отсчётов при частоте дискретизации 8 кГц, и укладывается он в 8 байт.
Восемь байт на двадцать миллисекунд речи. Ради этого всё и затевалось: 3200 бит/с влезают в канал с запасом почти вдвое, и остаётся место на служебные данные, текст и файлы.
Что лежит в пакете
Каждые 80 мс прошивка MeshTRX собирает один пакет: четыре кадра Codec2 подряд — 32 байта. К ним семь байт заголовка:
| Байт | Поле | Зачем |
|---|---|---|
| 0 | type | Что это: аудио, текст, файл, служебное |
| 1 | channel | Номер канала, 0–22 |
| 2 | seq | Порядковый номер пакета |
| 3 | flags | Начало и конец передачи, голосовая активация |
| 4 | ttl | Сколько ещё пересылок разрешено |
| 5–6 | sender | Последние два байта MAC — кто говорит |
Целиком голосовой пакет MeshTRX выглядит так:
Итого 39 байт в эфире, из них 32 — собственно речь. Заголовок занимает почти пятую часть пакета, и это не расточительность: без seq приёмник не заметит потерю, без flags не поймёт, что передача кончилась, без ttl пакет пойдёт по сети кругами.
Почему четыре кадра, а не восемь и не шестнадцать? Чем больше кадров в пакете, тем меньше доля заголовка — но тем дольше копится звук перед отправкой и тем больнее обходится каждая потеря. Восемьдесят миллисекунд оказались компромиссом: потерянный пакет срезает меньше десятой доли секунды речи, и это слышно как щелчок, а не как провал в середине слова.
Полудуплекс и предел в десять секунд
У рации MeshTRX одна антенна и один приёмопередатчик. Пока она передаёт — она не принимает. Не «принимает хуже», а не принимает вовсе.
Из этого следует вещь, которую в описаниях mesh-сетей обычно не пишут: пока один говорит, остальных в канале нет. Не только его собеседника — всех. Никто не может ответить, вклиниться или позвать на помощь. Канал принадлежит тому, кто нажал кнопку, и ровно до тех пор, пока он её держит.
Поэтому передача в MeshTRX ограничена десятью секундами. Ограничение стоит в двух местах сразу: в приложении, где на кнопке идёт обратный отсчёт, и в самой рации — константой в прошивке. Второе не дубль ради надёжности кода, а защита от вполне бытового случая: телефон завис, приложение убила система, кнопку зажало в кармане. Рация в этом случае замолкает сама и пишет на экране LIMIT 10s.
Десяти секунд хватает на осмысленную фразу и не хватает на монолог — так и задумано.
Откуда берётся задержка
Ждать приходится в нескольких местах, и первое ожидание неустранимо: пока не набралось 80 мс речи, отправлять нечего. Это нижняя граница, заданная размером пакета.
Дальше добавляется передача в рацию по Bluetooth, время пакета в эфире — несколько десятков миллисекунд, обратный путь по Bluetooth в чужой телефон и буфер приёма, который сглаживает неровный приход пакетов.
В сумме получается задержка, к которой привыкаешь за минуту, но которую нельзя не заметить. Разговор в MeshTRX от неё меняется по форме: он идёт не как по телефону, а как по рации — сказал, отпустил, дождался ответа. Перебивать собеседника всё равно бесполезно, его в этот момент никто не слышит.
Чем мы за это заплатили
Честная часть.
Голос узнаётся хуже. Codec2 3200, на котором работает MeshTRX, — это не сжатый звук, а заново синтезированная речь. Слова разборчивы, интонация в основном на месте, но тембр вокодер отдаёт первым: близкого человека по голосу вы, скорее всего, узнаете, а незнакомого от незнакомого отличите не всегда.
Только речь. Модель рассчитана на одного говорящего. Музыка, шум ветра, второй голос на фоне не передаются в принципе — на выходе будет невнятица.
Эфир открыт. Шифрования в MeshTRX пока нет, пакеты идут открытым текстом, и принять их может кто угодно с таким же модулем. Общий ключ на канал — в планах, но сегодня это так.
Дальность честная, а не рекламная. Пять километров, которые заявлены у MeshTRX, — это прямая видимость. В городе, в лесу, между этажами будет меньше, иногда сильно меньше. Ретранслятор помогает, но он же занимает канал.
Что дальше
Голос — самая заметная часть MeshTRX, но не единственная: в той же сети живут текст, файлы, позиции на карте и радар. Как устроен MeshTRX целиком и как им пользоваться — в документации. Что мы делаем дальше и от чего сознательно отказались — на странице о проекте.
Вопросы и замечания — в группе в Telegram. Исходники, включая всё, о чём здесь написано, — на GitHub.