# Формат журнала фронтов ИК (`IR_EDGE_TRACE`) Включается в `IR_config.h`: раскомментируйте `#define IR_EDGE_TRACE`. Размер кольца задаётся `IR_EDGE_TRACE_CAPACITY` (по умолчанию 512 записей). Память: примерно `IR_EDGE_TRACE_CAPACITY × 6` байт на экземпляр `IR_DecoderRaw`. ## Назначение В ISR на каждый аппаратный фронт на линии приёмника пишется запись: абсолютное время `micros()`, уровень линии после фронта, флаги. Это **отдельное** кольцо от `subBuffer`: при переполнении `subBuffer` фронты в журнале всё равно сохраняются, пока не заполнится это кольцо. При **передаче ИК по DMA** на STM32 важно, чтобы **прерывание приёма (EXTI)** имело **более высокий приоритет NVIC**, чем DMA канала передачи — иначе метки времени и сам поток фронтов в логе искажаются. См. **[`IR_DMA_ISR_signal_analysis.md`](../IR_DMA_ISR_signal_analysis.md)** (раздел 2.1). ## Вывод в Serial При включённом `IR_EDGE_TRACE` протокол **сам** сбрасывает накопленные фронты в конце каждого `IR_DecoderRaw::tick()` (и на ветке «subBuffer пуст»): в цикле вызывается `edgeTraceFlushChunk(Serial, 48)`, пока в кольце есть записи. Вручную тот же смысл: `edgeTraceFlushChunk(Print &out, maxRec)` печатает **ровно одну строку** (если есть что выгрузить; при пустом кольце выход без печати): 1. Символ перевода строки `\n` (отделяет блок от предыдущего вывода). 2. Литеральный префикс **`@IRF1v1:`** (удобно grep-ать; не используйте этот текст в других `Serial.print`, чтобы строки не сливались). 3. **Нижний регистр hex** без пробелов: полезная нагрузка бинарного блока ниже. 4. `\n` в конце. Другой текст (например `IR raw:` из `IRDEBUG_SERIAL_PACK`) не содержит `@IRF1v1:`, поэтому визуально и по парсеру блоки разделимы. Если авто-сброс в `tick()` отключён или нужен другой `Print`, вызывайте `edgeTraceFlushChunk` в цикле, пока возвращаемое значение > 0. ## Бинарная полезная нагрузка (до кодирования в hex) Все многобайтовые целые — **little-endian**, порядок байт от младшего к старшему. | Смещение | Размер | Поле | |----------|--------|------| | 0 | 1 | **meta** | | 1 | 2 | **count** — число записей в этой строке (`uint16_t`) | | 3 | `count × 6` | Массив записей | ### meta (байт) | Бит | Значение | |-----|----------| | 0 | **overflow**: кольцо хотя бы раз переполнилось с момента последнего `edgeTraceClear()`; новые фронты терялись, пока не освободилось место. Сбрасывается только `edgeTraceClear()`. | | 1 | **truncated**: после этой выгрузки в буфере ещё есть записи (chunk урезан лимитом `maxRec` или внутренним максимумом 64). | Биты 2–7 зарезервированы (0). ### Одна запись (6 байт) | Смещение в записи | Размер | Поле | |-------------------|--------|------| | 0 | 4 | **t_us** — значение `micros()` на момент фронта (`uint32_t`). При переполнении `micros()` (~70 мин) разницы между соседними записями всё ещё корректны, если обрабатывать как unsigned. | | 4 | 1 | **level** — уровень входа приёмника после фронта: `0` = LOW, `1` = HIGH (как `port->IDR & mask` в ISR). | | 5 | 1 | **flags** | | | | бит 0 **SKIP_DECODE** (`IR_EDGE_TRACE_F_SKIP_DECODE`): фронт записан, но в `subBuffer` **не** попал, потому что был активен `isPairSending` (пара передаёт). Алгоритм декодирования этот фронт не видит. | ## Минимальный разбор (Python 3) ```python import binascii, re def parse_irf1_line(line: str): m = re.search(r"@IRF1v1:([0-9a-f]+)\s*$", line.strip()) if not m: return None raw = binascii.unhexlify(m.group(1)) meta, cnt_lo, cnt_hi = raw[0], raw[1], raw[2] count = cnt_lo | (cnt_hi << 8) recs = [] p = 3 for _ in range(count): t = raw[p] | (raw[p+1]<<8) | (raw[p+2]<<16) | (raw[p+3]<<24) level, flags = raw[p+4], raw[p+5] recs.append((t, level, flags)) p += 6 return { "overflow": bool(meta & 1), "truncated": bool(meta & 2), "count": count, "records": recs, } ``` ## Рекомендации по съёму - При очень плотном потоке фронтов кольцо всё же может переполниться до следующего `tick()` — увеличьте `IR_EDGE_TRACE_CAPACITY` или уменьшите нагрузку на ISR. - Для «чистого» лога отключите или сильно урежьте `IRDEBUG_SERIAL_PACK`, иначе объём Serial будет очень большим. - Для полного сброса состояния перед тестом: `edgeTraceClear()`.