Лабораторная 11.52 — Длинные сообщения и почему канал такой медленный¶
Идея¶
Пакет Лабораторной 11.49 несёт 27 байт, а строка чата в Лабораторной 11.51 обрезается до 24 символов после имени. Для длинных сообщений нужно дробление: разрезать текст на куски, пронумеровать их, отправить каждый с подтверждениями Лабораторной 11.51 и собрать на другой стороне. Это страница кода на C.
Замер времени кусков ставит второй вопрос этой лабораторной. Пачка длится полмиллисекунды, а сообщение в 384 байта идёт четыре с половиной секунды: больше 99 % времени канал ждёт. Разобраться, куда уходит время, хорошее упражнение в чтении протокола по часам, и ответ не в радио.
Куски¶
полезная нагрузка куска: 0x1E | номер сообщения | индекс << 4 | число - 1 | до 24 байт текста
- Первый байт помечает кусок.
0x1E(ASCII RS, разделитель записей) не может начинать набранную строку, так же как0x06подтверждения, поэтому приёмник различает три вида кадров по первому байту. - Номер сообщения говорит, какому сообщению принадлежит кусок. Когда приходит кусок с новым номером, всё, что осталось от старого, отбрасывается.
- Индекс и число умещаются в один байт: не больше 16 кусков по 24 байта, 384 байта на сообщение.
- Каждый кусок это обычный подтверждаемый кадр со своим номером последовательности. Если кусок не подтверждён за 5 попыток, отправитель отказывается от всего сообщения и говорит, какой кусок не прошёл. Приёмник выводит сообщение, только когда собраны все его куски.
Дробление входит в zmbox chat --arq: длинная строка просто уходит кусками.
board-a$ ./zmbox chat --name A --arq --tx-gain -30
board-b$ ./zmbox chat --name B --arq --tx-gain -50
На стенде курса (платы и пути Лабораторной 11.50, образ issue70-listen-window), подсчёт скриптом
чата со строками, дополненными до заданной длины:
cd blocks/block_11_integrated_sdr_project/python
python two_board_chat.py --rounds 10 --arq --line-len 381 --period 10 --json-out long.json
| сообщение | уровни A / B, дБ | A -> B | B -> A | повторов A / B | время на сообщение (медиана) |
|---|---|---|---|---|---|
| 103 Б, 5 кусков | −30 / −50 | 20 из 20 | 20 из 20 | 0 / 0 | 1,45 с |
| 384 Б, 16 кусков | −30 / −50 | 10 из 10 | 10 из 10 | 0 / 0 | 4,6 с |
| 103 Б, 5 кусков | −47 / −68 | 20 из 20 | 20 из 20 | 15 / 8 | 2,2 с / 1,4 с |
| 103 Б, 5 кусков | −50 / −71 | 16 из 20 | 20 из 20 | 103 / 70 | 7,0 с / 5,4 с |
Ни одно сообщение не было собрано неправильно. На самом плохом канале плата A назвала доставленными только 14 своих сообщений, а плата B вывела 16: дважды потерялось подтверждение последнего куска. Сообщение из многих кусков хрупко ровно настолько, насколько хрупок его самый слабый кусок, и неоднозначность Лабораторной 11.51 относится к нему целиком.
Первый прогон с сообщениями по 384 байта назвал неправильными все сообщения. Виноват был тест, а не протокол: 384 символа плюс «A: » это 387 байт, отправитель обрезал их до 384, и приёмник вывел ровно то, что было отправлено. С 381 символом дошли все сообщения. Когда тест падает весь разом, сначала проверьте тест.
Куда уходит время¶
Время от отправки одной строки до её подтверждения, по 40 строк в каждую сторону, уровни −30/−50 дБ:
| минимум | медиана | максимум | |
|---|---|---|---|
zmbox chat --arq как есть (ожидание 140 мс перед каждой отправкой) |
283 мс | 292 мс | 302 мс |
без этого ожидания (ZMBOX_TX_DELAY_MS=0) |
141 мс | 210 мс | 274 мс |
С ожиданием время почти постоянно и складывается: отправитель ждёт 140 мс, приёмник ждёт 140 мс
перед подтверждением, а всё остальное занимает около 12 мс. Две пачки длятся по 0,49 мс. Ожидание
140 мс появилось в Лабораторной 11.50, чтобы пачки не попадали на край окна прослушивания другой
платы; с приёмником, исправленным в issue70-listen-window, для этого оно больше не нужно.
Если его убрать, выигрыш меньше ожидаемого, и причина в контракте mailbox. Mailbox не принимает TX_START, пока идёт попытка прослушивания, а RX_STOP её не обрывает, поэтому отправка ждёт конца текущего 136-мс окна. Попытка отправителя находится в случайной точке своего окна. Приёмник только что перезапустил попытку, приняв строку, поэтому его подтверждение ждёт почти целое окно. Вместе это от 140 до 270 мс. (Объяснение прочитано по цифрам и контракту; сами попытки не трассировались.)
То же для длинных сообщений. Сообщение в 384 байта шло 4,6 с с ожиданием и 4,35 с без него, 84 и 88 байт в секунду, около 280 мс на кусок. В эфире кусок занимает 0,49 мс. Канал ждёт больше 99 % времени, и лекарство в ПЛИС: mailbox, у которого RX_STOP сразу заканчивает попытку прослушивания, сократил бы кусок до десятков миллисекунд. Это изменение RTL ещё предстоит сделать (issue #71).
Задания¶
- Почему приёмник может отбросить остатки незаконченного сообщения, как только пришёл кусок с новым номером сообщения, вместо того чтобы держать несколько сообщений параллельно?
- Плата A назвала доставленными 14 сообщений, плата B вывела 16. Подтверждение какого куска должно было потеряться и почему именно этого?
- Сообщение из n кусков идёт по каналу, где каждый кусок проходит за 5 попыток с вероятностью p. Какова вероятность, что дойдёт всё сообщение? Что это говорит о выборе размера куска?
- Оцените время на кусок, если бы RX_STOP сразу заканчивал попытку прослушивания. Что тогда ограничило бы скорость?
Ответы
- Отправитель шлёт сообщения по одному и переходит к следующему, только когда все куски подтверждены или он сдался. Поэтому кусок с новым номером значит, что старое сообщение у отправителя закончено, так или иначе, и недостающие куски уже не придут.
- Последнего. Плата B выводит сообщение, когда пришёл последний кусок, а плата A считает сообщение доставленным, только когда этот кусок подтверждён. Любой более ранний кусок с потерянным подтверждением был бы повторён, и повтор подтверждён, прежде чем ушёл следующий кусок.
- p в степени n. Шестнадцать кусков при p = 0,95 дают 0,44; пять дают 0,77. Мелкие куски значат, что их больше и шанс для целого ниже; крупные кадры каждый чаще задевает ошибка. Реальные каналы подбирают размер кадра под канал, а некоторые меняют его на ходу.
- Без ожиданий кусок стоит двух пачек (около 1 мс), приёма и копирования в mailbox и программного
опроса (
zmboxопрашивает каждые 2 мс, пока ждёт, и каждые 20 мс в простое): от единиц до десятков миллисекунд, в десять раз быстрее и больше. Тогда скорость ограничит сам принцип «отправил и жди»: отправитель простаивает, пока идёт подтверждение; следующим шагом было бы отправлять следующие куски, не дожидаясь подтверждений (окно).
Подтверждения¶
two-board-link-layer-20261017.md: дробление, задержка и скорость, со всеми строками консолей в JSON рядом;- issue #71: RX_STOP и ожидание.
Что лабораторная показывает и чего не показывает¶
Она показывает сообщения до 384 байт, перенесённые подтверждаемыми кусками и собранные без единого неправильного сообщения, и показывает, куда уходит время канала «отправил и жди». Быстрого канала она не показывает: пока mailbox не изменится, канал работает меньше чем на 100 байтах в секунду и ждёт больше 99 % времени. Одна пара плат, одна несущая, кабельные пути, от 10 до 40 сообщений на строку таблицы.