Формирование пакетов для очень коротких последовательных пакетов
Мы разработали простой протокол фиксированной длины для встроенного устройства. Каждый пакет занимает всего два байта:
bits | 15..12 | 11..4 | 3..0 |
| OpCode | DATA | CRC4 |
Мы используем "кадрирование на основе crc", то есть получатель собирает два байта, вычисляет CRC4 и, если он соответствует кадру, считается действительным. Как вы можете видеть, нет начала или конца кадра.
Есть одна загвоздка: рекомендуемая длина сообщения для CRC4 составляет 11 бит, а здесь она рассчитывается для 12 бит. Насколько я понимаю, это означает, что свойства обнаружения ошибок CRC ухудшаются (но я не уверен, насколько).
(Кстати, если кому-то нужен код для CRC4 (или любой другой) и он не чувствует себя достаточно опытным, чтобы написать его сам, boost имеет очень хорошую функцию boost::crc, которая может вычислить любой crc)
Проблема в том, что это основанное на crc кадрирование не работает, и мы получаем ошибки кадрирования, то есть второй байт из одного сообщения и первый байт из следующего сообщения иногда образуют правильное сообщение.
Мой вопрос - есть ли способ исправить кадрирование, не добавляя больше байтов? Мы тратим довольно много времени на то, чтобы сжать все в этих двух байтах, и было бы грустно просто выбросить это так. У нас есть запасной бит в поле кода операции.
- Кадрирование по времени не будет очень надежным, потому что нашему радиоканалу нравится "выплевывать" несколько пакетов одновременно
- Может быть, есть какой-то другой метод обнаружения ошибок, который будет работать лучше, чем CRC4?
Если мы должны добавить больше байтов, что будет лучшим способом сделать это?
- Мы можем использовать начало байта и заполнение байтов (например, COBS) ( +2 байта, но я не уверен, что делать с поврежденными сообщениями)
- Мы можем использовать отрывок начала кадра и расширить CRC до CRC8 ( +1 байт)
- Что-то другое?
3 ответа
Обычный способ сделать то, что вы просите, - это "искать кадры" при запуске и требовать N последовательных хороших пакетов, прежде чем принимать какие-либо пакеты. Это может быть реализовано с использованием конечного автомата с 3 состояниями: HUNT, LOF (потеря кадра), SYNC
Это может быть что-то вроде:
#define GOOD_PACKETS_REQUIRED_BEFORE_SYNC 8
int state = HUNT;
int good_count = 0;
Packet GetPacket(void)
{
unsigned char fb = 0;
unsigned char sb = 0;
while (1)
{
if (state == HUNT)
{
fb = sb;
sb = GetNextByteFromUART();
if (IsValidCRC(fb, sb))
{
state = LOF;
good_count = 1;
}
}
else if (state == LOF)
{
fb = GetNextByteFromUART();
sb = GetNextByteFromUART();
if (IsValidCRC(fb, sb))
{
good_count++;
if (good_count >= GOOD_PACKETS_REQUIRED_BEFORE_SYNC)
{
state = SYNC;
}
}
else
{
state = HUNT;
good_count = 0;
}
}
else if (state == SYNC)
{
fb = GetNextByteFromUART();
sb = GetNextByteFromUART();
if (IsValidCRC(fb, sb))
{
return packet(fb, sb);;
}
// SYNC lost! Start a new hunt for correct framing
state = HUNT;
good_count = 0;
}
}
}
Вы можете найти несколько стандартных протоколов связи, которые используют этот (или аналогичный) метод, например, ATM и E1 ( https://en.wikipedia.org/wiki/E-carrier). Существуют разные варианты принципа. Например, вы можете перейти от SYNC к LOF при получении первого плохого пакета (с уменьшением good_count) и затем перейти от LOF к HUNT для второго последовательного плохого пакета. Это сократит время, необходимое для перекадровки. Выше просто показывает очень простой вариант.
Примечание. В реальном коде вы, вероятно, не можете принять блокирующую функцию, подобную приведенной выше. Приведенный выше код предназначен только для описания принципа.
Нужен ли вам CRC или вы можете использовать фиксированное фрейм-слово (например, 0xB), зависит от вашего носителя.
Есть одна загвоздка: рекомендуемая длина сообщения для CRC4 составляет 11 бит, а здесь она рассчитывается для 12 бит.
Нет, здесь он рассчитан на 16 бит.
Насколько я понимаю, это означает, что свойства обнаружения ошибок CRC ухудшаются (но я не уверен, насколько).
Рекомендации по CRC, вероятно, относятся к тому, есть ли у вас 100% -ный шанс найти однобитовую ошибку или нет. Все CRC борются с многоразрядными ошибками и не обязательно их найдут.
При работе с расчетами надежности CRC UART вы также должны учитывать начальные и конечные биты. Там также могут появиться битовые ошибки, и в этом случае аппаратное обеспечение может помочь, а может и не помочь найти ошибку.
второй байт из одного сообщения и первый байт из следующего сообщения иногда образуют правильное сообщение
Конечно. У вас нет механизма синхронизации, что вы ожидаете? Это не имеет ничего общего с CRC.
Мой вопрос - есть ли способ исправить кадрирование, не добавляя больше байтов?
Либо вам придется пожертвовать один бит на байт в качестве флага синхронизации, либо увеличить длину пакета. В качестве альтернативы вы можете использовать разные задержки между битами данных. Может быть, отправить два байта непосредственно друг за другом, а затем использовать задержку.
Какой метод выбрать, зависит от характера данных и вашей спецификации. Никто на SO не может сказать вам, как выглядит ваша спецификация.
Может быть, есть какой-то другой метод обнаружения ошибок, который будет работать лучше, чем CRC4?
Скорее всего, не. CRC - практически единственный профессиональный алгоритм контрольной суммы. Полиномы выбираются на основе исключенного характера шума - они выбирают полином, который напоминает как можно меньше шума. Однако это в основном представляет академический интерес, так как ни один гуру CRC не может знать, как выглядит шум в вашем конкретном приложении.
Альтернативами являются суммы, xor, четность, число отсчетов 1 и т.д.... все они довольно плохие, с вероятностной вероятностью.
Если мы должны добавить больше байтов, что будет лучшим способом сделать это?
Никто не может ответить на этот вопрос, не зная природу данных.
Если CRC в основном для паранойи (из комментариев), вы можете отказаться от некоторой надежности проверки ошибок и времени процессора для кадрирования.
Поскольку в коде операции есть свободный бит, всегда устанавливайте старший бит первого байта в ноль. Затем перед передачей, но после вычисления CRC, установите старший бит второго байта в единицу.
Кадр - это два последовательных байта, где первый старший бит равен нулю, а второй равен единице. Если эти два байта не проходят проверку CRC, установите самый старший бит второго байта в ноль и пересчитайте его, чтобы увидеть, был ли пакет перевернут бит перед передачей.
Недостатком является то, что CRC будет рассчитываться дважды примерно в половине случаев. Кроме того, установка бита для кадрирования может привести к тому, что недопустимые данные будут соответствовать CRC.