<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>Семён сохраняет полезное_)</title><subtitle>Канал о электронике и связи.
Демократии тут нет и не будет! Бан раздается за любое &quot;Ну очевидно, же&quot;</subtitle><author><name>Семён сохраняет полезное_)</name></author><id>https://teletype.in/atom/pole_sam</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/pole_sam?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@pole_sam?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=pole_sam"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/pole_sam?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-09-06T09:55:15.983Z</updated><entry><id>pole_sam:uLxXaREJc5k</id><link rel="alternate" type="text/html" href="https://teletype.in/@pole_sam/uLxXaREJc5k?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=pole_sam"></link><title>Как превратить бинарный CP-лог модема SIMCom A7665E в читаемый</title><published>2026-08-29T19:53:33.456Z</published><updated>2026-08-29T19:53:33.456Z</updated><summary type="html">Отладочные логи мобильных модемов часто выглядят как поток непонятных бинарных данных. Внутри могут находиться сообщения о регистрации в сети, выборе PLMN, параметрах serving cell, APN, IP-адресах и состоянии протоколов LTE. Однако без знания транспортного формата и таблицы сообщений прошивки этот поток практически бесполезен.</summary><content type="html">
  &lt;h2 id=&quot;V5dc&quot;&gt;Введение&lt;/h2&gt;
  &lt;p id=&quot;hip4&quot;&gt;Отладочные логи мобильных модемов часто выглядят как поток непонятных бинарных данных. Внутри могут находиться сообщения о регистрации в сети, выборе PLMN, параметрах serving cell, APN, IP-адресах и состоянии протоколов LTE. Однако без знания транспортного формата и таблицы сообщений прошивки этот поток практически бесполезен.&lt;/p&gt;
  &lt;p id=&quot;8DSu&quot;&gt;В этой статье разберём, как устроено распознавание debug-лога модема SIMCom A7665E на базе ASR «Crane» ASR1603. Цель — пройти весь путь от байтов, поступающих через USB, до человекочитаемой строки:&lt;/p&gt;
  &lt;pre id=&quot;EHs6&quot;&gt;байтовый поток → бинарная запись → форматное сообщение → декодированные аргументы&lt;/pre&gt;
  &lt;p id=&quot;OK8k&quot;&gt;Практически задача состоит из двух независимых частей:&lt;/p&gt;
  &lt;ul id=&quot;OVAH&quot;&gt;
    &lt;li id=&quot;lzEQ&quot;&gt;определить границы бинарных записей;&lt;/li&gt;
    &lt;li id=&quot;iawi&quot;&gt;понять смысл каждой записи по её идентификаторам и payload.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;xfbd&quot;&gt;Первую проблему можно решить детерминированно. Вторая требует базы форматных строк, соответствующей конкретной сборке прошивки.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;1&quot;&gt;1. Исходные условия&lt;/h2&gt;
  &lt;p id=&quot;AabM&quot;&gt;В качестве устройства используется модем SIMCom A7665E&lt;/p&gt;
  &lt;p id=&quot;DqWX&quot;&gt;Важно не перепутать этот поток с Qualcomm DIAG. CP-log ASR имеет другой бинарный формат, поэтому инструменты, рассчитанные на Qualcomm DIAG, например &lt;code&gt;qc_debug_monitor&lt;/code&gt;, его не распознают.&lt;/p&gt;
  &lt;p id=&quot;Wzic&quot;&gt;Итоговая архитектура декодера выглядит так:&lt;/p&gt;
  &lt;pre id=&quot;6HGy&quot;&gt;/dev/ttyUSBx
     │
     │ AT+ECPLOG=1
     ▼
поток бинарных данных
     │
     ▼
asr_proto.py
байты → Record
     │
     ▼
asr_mdb.py
(msg_type, fmt_id) + payload → строка
     │
     ▼
asr_debug_monitor.py
захват, фильтрация, цветной вывод, replay&lt;/pre&gt;
  &lt;p id=&quot;6CmS&quot;&gt;Такое разделение ответственности удобно по нескольким причинам:&lt;/p&gt;
  &lt;ul id=&quot;zNWc&quot;&gt;
    &lt;li id=&quot;kPUJ&quot;&gt;транспортный парсер не зависит от базы форматов;&lt;/li&gt;
    &lt;li id=&quot;uxvp&quot;&gt;декодер семантики не зависит от последовательного порта;&lt;/li&gt;
    &lt;li id=&quot;xZzW&quot;&gt;один и тот же протокол можно использовать в CLI, web-мониторе или тестовой системе;&lt;/li&gt;
    &lt;li id=&quot;vhVC&quot;&gt;каждый слой можно тестировать отдельно.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;2--cp-log&quot;&gt;2. Поиск CP-log-порта&lt;/h2&gt;
  &lt;h2 id=&quot;ttyusb0&quot;&gt;Почему нельзя полагаться на &lt;code&gt;ttyUSB0&lt;/code&gt;&lt;/h2&gt;
  &lt;p id=&quot;CrGd&quot;&gt;После перезагрузки модема Linux может назначить интерфейсам другие имена:&lt;/p&gt;
  &lt;pre id=&quot;96W4&quot;&gt;до перезагрузки:     ttyUSB0, ttyUSB1, ttyUSB2
после перезагрузки:  ttyUSB3, ttyUSB4, ttyUSB5&lt;/pre&gt;
  &lt;p id=&quot;UqhE&quot;&gt;Поэтому поиск порта по имени ненадёжен. Лучше использовать USB VID и номер интерфейса в USB-топологии.&lt;/p&gt;
  &lt;p id=&quot;RQey&quot;&gt;В рассматриваемом устройстве CP-log находится на интерфейсе &lt;code&gt;#2&lt;/code&gt;. В &lt;code&gt;pyserial&lt;/code&gt; его можно найти по полю &lt;code&gt;location&lt;/code&gt;:&lt;/p&gt;
  &lt;pre id=&quot;JMin&quot;&gt;from serial.tools import list_ports

MODEM_VID = 0x1e0e
DEBUG_IFACE = 2

def find_debug_port(iface=DEBUG_IFACE, vid=MODEM_VID):
    fallback = None

    for port in list_ports.comports():
        if port.vid != vid:
            continue

        location = port.location or &amp;quot;&amp;quot;

        if location.endswith(f&amp;quot;:1.{iface}&amp;quot;):
            return port.device

        fallback = fallback or port.device

    return fallback&lt;/pre&gt;
  &lt;p id=&quot;NG7a&quot;&gt;Ключевая проверка:&lt;/p&gt;
  &lt;pre id=&quot;rKri&quot;&gt;location.endswith(&amp;quot;:1.2&amp;quot;)&lt;/pre&gt;
  &lt;p id=&quot;MAVU&quot;&gt;Номер &lt;code&gt;ttyUSBx&lt;/code&gt; может измениться, а номер USB-интерфейса в конкретной композиции обычно остаётся постоянным.&lt;/p&gt;
  &lt;h2 id=&quot;8Voq&quot;&gt;Включение потока&lt;/h2&gt;
  &lt;p id=&quot;MSSK&quot;&gt;CP-log не начинает передаваться автоматически. Сначала на тот же порт необходимо отправить AT-команду:&lt;/p&gt;
  &lt;pre id=&quot;AIQx&quot;&gt;ENABLE_CMD = b&amp;quot;AT+ECPLOG=1\r\n&amp;quot;
DISABLE_CMD = b&amp;quot;AT+ECPLOG=0\r\n&amp;quot;&lt;/pre&gt;
  &lt;p id=&quot;iM0M&quot;&gt;Пример открытия устройства:&lt;/p&gt;
  &lt;pre id=&quot;JJrL&quot;&gt;import serial
import time

def open_device(device, baudrate=115200):
    ser = serial.Serial(device, baudrate, timeout=0.2)

    ser.reset_input_buffer()
    ser.write(ENABLE_CMD)
    ser.flush()

    time.sleep(0.4)

    # Удаляем эхо команды и текстовый ответ OK
    ser.reset_input_buffer()

    return ser&lt;/pre&gt;
  &lt;p id=&quot;1IyD&quot;&gt;Здесь есть важная особенность: после &lt;code&gt;AT+ECPLOG=1&lt;/code&gt; модем сначала может вернуть обычный текстовый ответ:&lt;/p&gt;
  &lt;pre id=&quot;8Ysu&quot;&gt;AT+ECPLOG=1
OK&lt;/pre&gt;
  &lt;p id=&quot;vPCD&quot;&gt;Затем начинается бинарный поток. Если не очистить входной буфер, парсер может попытаться интерпретировать ASCII-ответ как начало бинарной записи.&lt;/p&gt;
  &lt;p id=&quot;H6GH&quot;&gt;При завершении работы полезно отключить лог:&lt;/p&gt;
  &lt;pre id=&quot;VVAC&quot;&gt;ser.write(DISABLE_CMD)
ser.flush()
time.sleep(0.1)
ser.reset_input_buffer()
ser.close()&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;3&quot;&gt;3. Формат бинарной записи&lt;/h2&gt;
  &lt;h2 id=&quot;ubjE&quot;&gt;Как была найдена граница сообщений&lt;/h2&gt;
  &lt;p id=&quot;fsPk&quot;&gt;В дампе регулярно повторяется последовательность:&lt;/p&gt;
  &lt;pre id=&quot;bBVb&quot;&gt;маленькое число → несколько нулей → полезная нагрузка&lt;/pre&gt;
  &lt;p id=&quot;PrUW&quot;&gt;Это позволяет предположить, что первые два байта являются длиной записи в формате little-endian.&lt;/p&gt;
  &lt;p id=&quot;iVXc&quot;&gt;Алгоритм проверки простой:&lt;/p&gt;
  &lt;ol id=&quot;ltNT&quot;&gt;
    &lt;li id=&quot;GBQz&quot;&gt;прочитать первые два байта;&lt;/li&gt;
    &lt;li id=&quot;gqSf&quot;&gt;интерпретировать их как &lt;code&gt;u16&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;uoWO&quot;&gt;перейти вперёд на указанное количество байт;&lt;/li&gt;
    &lt;li id=&quot;MD6Q&quot;&gt;проверить, похожи ли следующие два байта на очередную длину;&lt;/li&gt;
    &lt;li id=&quot;l7Ub&quot;&gt;повторить процедуру до конца буфера.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;ZIYu&quot;&gt;Если цепочка проходит через весь дамп без рассинхронизации, гипотеза подтверждается.&lt;/p&gt;
  &lt;p id=&quot;6Alv&quot;&gt;Такой формат обладает свойством самосинхронизации: если в поток попал мусор, длина обычно выходит за разумные пределы, и парсер может начать поиск следующей потенциальной записи.&lt;/p&gt;
  &lt;h2 id=&quot;7rQZ&quot;&gt;Заголовок&lt;/h2&gt;
  &lt;p id=&quot;Nurd&quot;&gt;Аргументы сообщения&lt;/p&gt;
  &lt;p id=&quot;VSwH&quot;&gt;В Python структура описывается так:&lt;/p&gt;
  &lt;pre id=&quot;Ub4l&quot;&gt;import struct

HDR = struct.Struct(&amp;quot;&amp;lt;HHHHHHI&amp;quot;)
HDR_LEN = HDR.size  # 16 байт&lt;/pre&gt;
  &lt;p id=&quot;aj7y&quot;&gt;Порядок полей:&lt;/p&gt;
  &lt;pre id=&quot;CwfQ&quot;&gt;length, flags, channel, seq, msg_type, fmt_id, timestamp&lt;/pre&gt;
  &lt;h2 id=&quot;ygey&quot;&gt;Таймстамп&lt;/h2&gt;
  &lt;p id=&quot;hfGU&quot;&gt;Поле &lt;code&gt;timestamp&lt;/code&gt; не является временем в микросекундах или миллисекундах. Экспериментальное сопоставление с известными интервалами показало, что устройство использует частоту 32768 Гц:&lt;/p&gt;
  &lt;pre id=&quot;InD3&quot;&gt;TS_HZ = 32768

def timestamp_seconds(timestamp):
    return timestamp / TS_HZ&lt;/pre&gt;
  &lt;p id=&quot;aoWG&quot;&gt;Например:&lt;/p&gt;
  &lt;pre id=&quot;01r3&quot;&gt;timestamp = 2 572 383
time      = 2 572 383 / 32 768 ≈ 78.503 с&lt;/pre&gt;
  &lt;p id=&quot;2Nmr&quot;&gt;Это абсолютное время работы устройства до момента включения ECPLOG. В пользовательском интерфейсе удобнее вычесть timestamp первой записи и показывать относительное время:&lt;/p&gt;
  &lt;pre id=&quot;ut3n&quot;&gt;[+0.000000] ...
[+0.003174] ...
[+0.006287] ...&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;4&quot;&gt;4. Потоковый парсер&lt;/h2&gt;
  &lt;p id=&quot;gX1F&quot;&gt;Парсер должен корректно работать в двух режимах:&lt;/p&gt;
  &lt;ul id=&quot;zva4&quot;&gt;
    &lt;li id=&quot;45NA&quot;&gt;с готовым бинарным файлом;&lt;/li&gt;
    &lt;li id=&quot;X9Zh&quot;&gt;с живым потоком, где одна запись может прийти несколькими частями.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2 id=&quot;3y14&quot;&gt;Разбор одной записи&lt;/h2&gt;
  &lt;pre id=&quot;Eg2b&quot;&gt;MIN_REC = 16
MAX_REC = 16384

def take_record(buf, offset, flush=False):
    end = len(buf)

    if offset + 2 &amp;gt; end:
        return None, offset, True

    length = buf[offset] | (buf[offset + 1] &amp;lt;&amp;lt; 8)

    if length &amp;lt; MIN_REC or length &amp;gt; MAX_REC:
        # Некорректная длина: пробуем синхронизацию со следующего байта
        return None, offset + 1, False

    if offset + length &amp;gt; end:
        if flush:
            # Финальный обрезанный хвост
            return None, offset + 1, False

        # Для живого потока ждём следующую порцию данных
        return None, offset, True

    fields = HDR.unpack_from(buf, offset)

    record = {
        &amp;quot;length&amp;quot;: fields[0],
        &amp;quot;flags&amp;quot;: fields[1],
        &amp;quot;channel&amp;quot;: fields[2],
        &amp;quot;seq&amp;quot;: fields[3],
        &amp;quot;msg_type&amp;quot;: fields[4],
        &amp;quot;fmt_id&amp;quot;: fields[5],
        &amp;quot;timestamp&amp;quot;: fields[6],
        &amp;quot;payload&amp;quot;: bytes(
            buf[offset + HDR_LEN:offset + length]
        ),
        &amp;quot;offset&amp;quot;: offset,
    }

    return record, offset + length, False&lt;/pre&gt;
  &lt;h2 id=&quot;HLZH&quot;&gt;Инкрементальный режим&lt;/h2&gt;
  &lt;pre id=&quot;pfxt&quot;&gt;class StreamParser:
    def __init__(self):
        self.buffer = bytearray()

    def feed(self, chunk):
        self.buffer.extend(chunk)

        offset = 0
        end = len(self.buffer)

        while offset &amp;lt; end:
            record, next_offset, need_more = take_record(
                self.buffer,
                offset,
                flush=False
            )

            if need_more:
                break

            offset = next_offset

            if record is not None:
                yield record

        if offset:
            del self.buffer[:offset]

        # Защита от бесконечного роста при потере синхронизации
        if len(self.buffer) &amp;gt; 1 &amp;lt;&amp;lt; 20:
            del self.buffer[:len(self.buffer) - (1 &amp;lt;&amp;lt; 16)]&lt;/pre&gt;
  &lt;p id=&quot;udSn&quot;&gt;Если в середине записи пришёл только заголовок и часть payload, она остаётся во внутреннем буфере. Следующий вызов &lt;code&gt;feed()&lt;/code&gt; продолжает разбор.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;5&quot;&gt;5. Разбор первой записи&lt;/h2&gt;
  &lt;p id=&quot;bfmo&quot;&gt;Рассмотрим пример записи размером 34 байта:&lt;/p&gt;
  &lt;pre id=&quot;eTHt&quot;&gt;22 00 | 00 00 | 00 00 | 01 00 | 2b 00 | 96 14 |
5f 40 27 00 |
02 00 63 00 78 08 12 00 01 00 00 00 02 00 00 00 00 00&lt;/pre&gt;
  &lt;p id=&quot;YQni&quot;&gt;Разметка:&lt;/p&gt;
  &lt;pre id=&quot;7mcY&quot;&gt;length     flags      channel    seq
22 00      00 00      00 00      01 00

msg_type   fmt_id     timestamp
2b 00      96 14      5f 40 27 00

payload
02 00 63 00 78 08 12 00 01 00 00 00 02 00 00 00 00 00&lt;/pre&gt;
  &lt;p id=&quot;QS7G&quot;&gt;В данном случае &lt;code&gt;msg_type = 0x2b&lt;/code&gt; соответствует текстовой записи. Однако payload содержит бинарный префикс, поэтому его нельзя безусловно интерпретировать как обычную C-строку.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;6&quot;&gt;6. Типы сообщений&lt;/h2&gt;
  &lt;p id=&quot;v9sT&quot;&gt;Поле &lt;code&gt;msg_type&lt;/code&gt; определяет, как нужно обрабатывать запись:&lt;/p&gt;
  &lt;pre id=&quot;dnnO&quot;&gt;TYPE_TEXT = 0x2B

TYPE_NAMES = {
    0x2B: &amp;quot;TEXT&amp;quot;,
    0x1D: &amp;quot;TRACE&amp;quot;,
    0x17: &amp;quot;SIGNAL&amp;quot;,
    0x1A: &amp;quot;T1A&amp;quot;,
    0x16: &amp;quot;T16&amp;quot;,
    0x0E: &amp;quot;T0E&amp;quot;,
    0x03: &amp;quot;T03&amp;quot;,
}&lt;/pre&gt;
  &lt;h2 id=&quot;text&quot;&gt;TEXT-записи&lt;/h2&gt;
  &lt;p id=&quot;nzXJ&quot;&gt;TEXT-записи уже содержат готовые фрагменты текста. Их можно декодировать без базы форматных строк, извлекая печатные ASCII-последовательности.&lt;/p&gt;
  &lt;pre id=&quot;tIqc&quot;&gt;def printable_runs(data, min_run=3):
    runs = []
    current = bytearray()

    for byte in data:
        if 32 &amp;lt;= byte &amp;lt; 127:
            current.append(byte)
        else:
            if len(current) &amp;gt;= min_run:
                runs.append(current.decode(&amp;quot;latin-1&amp;quot;))
            current.clear()

    if len(current) &amp;gt;= min_run:
        runs.append(current.decode(&amp;quot;latin-1&amp;quot;))

    return runs&lt;/pre&gt;
  &lt;p id=&quot;689M&quot;&gt;Для текстовых записей достаточно использовать короткий порог:&lt;/p&gt;
  &lt;pre id=&quot;lsqx&quot;&gt;runs = printable_runs(payload, min_run=3)
text = &amp;quot; &amp;quot;.join(runs)&lt;/pre&gt;
  &lt;h2 id=&quot;terse&quot;&gt;Terse-записи&lt;/h2&gt;
  &lt;p id=&quot;upv9&quot;&gt;Terse-записи содержат не сам текст, а:&lt;/p&gt;
  &lt;ul id=&quot;cu0T&quot;&gt;
    &lt;li id=&quot;uY5y&quot;&gt;идентификатор форматной строки;&lt;/li&gt;
    &lt;li id=&quot;Y65C&quot;&gt;бинарные аргументы.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;Dc7q&quot;&gt;Их можно представить как удалённый вызов:&lt;/p&gt;
  &lt;pre id=&quot;TC9k&quot;&gt;diagPrintf(&amp;quot;Serving Cell: rsrp = %d&amp;quot;, rsrp);&lt;/pre&gt;
  &lt;p id=&quot;UXSP&quot;&gt;На провод передаются только:&lt;/p&gt;
  &lt;pre id=&quot;95Ot&quot;&gt;fmt_id + бинарное значение rsrp&lt;/pre&gt;
  &lt;p id=&quot;qqtj&quot;&gt;Сама строка формата находится в базе данных прошивки.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;7--fileline&quot;&gt;7. Записи &lt;code&gt;file:line&lt;/code&gt; без базы&lt;/h2&gt;
  &lt;p id=&quot;ObEZ&quot;&gt;Часть assert-сообщений содержит имя исходного файла и номер строки:&lt;/p&gt;
  &lt;pre id=&quot;TfgR&quot;&gt;rrcdsutils.c\0\xa2\x0a\x00\x00&lt;/pre&gt;
  &lt;p id=&quot;dSCI&quot;&gt;То есть payload имеет вид:&lt;/p&gt;
  &lt;pre id=&quot;lgBf&quot;&gt;имя файла + NUL + номер строки u32&lt;/pre&gt;
  &lt;p id=&quot;95nX&quot;&gt;Такой формат можно распознать независимо от версии базы:&lt;/p&gt;
  &lt;pre id=&quot;ahNy&quot;&gt;import re

SOURCE_RE = re.compile(
    r&amp;quot;\.(c|h|cpp|cc|cxx)$&amp;quot;,
    re.IGNORECASE
)

def decode_file_line(payload):
    nul = payload.find(b&amp;quot;\x00&amp;quot;)

    if nul &amp;lt; 4:
        return None

    raw_name = payload[:nul]

    if not all(32 &amp;lt;= byte &amp;lt; 127 for byte in raw_name):
        return None

    name = raw_name.decode(&amp;quot;latin-1&amp;quot;)

    if not SOURCE_RE.search(name):
        return None

    name = name.replace(&amp;quot;\\&amp;quot;, &amp;quot;/&amp;quot;).rsplit(&amp;quot;/&amp;quot;, 1)[-1]

    if len(payload) &amp;lt; nul + 5:
        return None

    line = int.from_bytes(
        payload[nul + 1:nul + 5],
        &amp;quot;little&amp;quot;
    )

    return name, line&lt;/pre&gt;
  &lt;p id=&quot;ZL5B&quot;&gt;Пример результата:&lt;/p&gt;
  &lt;pre id=&quot;nsPi&quot;&gt;rrcdsutils.c:2722&lt;/pre&gt;
  &lt;p id=&quot;ViNf&quot;&gt;Строгая проверка необходима: случайный бинарный payload тоже может содержать печатные байты. Условие с расширением исходного файла снижает вероятность ложного распознавания.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;8&quot;&gt;8. База форматных строк&lt;/h2&gt;
  &lt;h2 id=&quot;m7nZ&quot;&gt;Зачем нужна база&lt;/h2&gt;
  &lt;p id=&quot;RaAb&quot;&gt;Terse-сообщение само по себе неполно. Например, запись может содержать:&lt;/p&gt;
  &lt;pre id=&quot;abea&quot;&gt;msg_type = 0x1d
fmt_id   = 0x0b3d
payload  = бинарные аргументы&lt;/pre&gt;
  &lt;p id=&quot;pBj1&quot;&gt;Чтобы понять, что означают эти байты, нужна запись из базы:&lt;/p&gt;
  &lt;pre id=&quot;Q0pV&quot;&gt;&amp;quot;updates ( %ld , %d ) measUpdateTime: ( 0x%lx ) , ( rsrp = %d )&amp;quot;&lt;/pre&gt;
  &lt;p id=&quot;84n3&quot;&gt;В случае ASR используется база &lt;code&gt;cp_MDB.txt&lt;/code&gt; формата CATStudio &lt;code&gt;TxtDbHeader&lt;/code&gt;.&lt;/p&gt;
  &lt;h2 id=&quot;mUgJ&quot;&gt;Секции базы&lt;/h2&gt;
  &lt;p id=&quot;uQjt&quot;&gt;Наиболее важны три секции:&lt;/p&gt;
  &lt;ul id=&quot;aBYu&quot;&gt;
    &lt;li id=&quot;WO2u&quot;&gt;&lt;code&gt;EnumValsRes&lt;/code&gt; — таблица форматных строк;&lt;/li&gt;
    &lt;li id=&quot;h2CU&quot;&gt;&lt;code&gt;Enums&lt;/code&gt; — преобразование числовых значений в имена;&lt;/li&gt;
    &lt;li id=&quot;QZXW&quot;&gt;&lt;code&gt;Signals&lt;/code&gt; — описания структур для &lt;code&gt;%S{...}&lt;/code&gt;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2 id=&quot;1Mvd&quot;&gt;Извлечение секций&lt;/h2&gt;
  &lt;p id=&quot;wnB3&quot;&gt;В начале файла находится индекс с байтовыми смещениями:&lt;/p&gt;
  &lt;pre id=&quot;0pPl&quot;&gt;EnumValsRes,1234,987654
Enums,987654,1048576
Signals,1048576,4567890&lt;/pre&gt;
  &lt;p id=&quot;Ehs3&quot;&gt;Пример загрузки:&lt;/p&gt;
  &lt;pre id=&quot;KyHY&quot;&gt;def parse_sections(raw):
    marker = raw.find(b&amp;quot;&amp;lt;end&amp;gt;&amp;quot;)

    if marker &amp;lt; 0:
        return {}

    header = raw[:marker].decode(
        &amp;quot;latin-1&amp;quot;,
        &amp;quot;replace&amp;quot;
    )

    sections = {}

    for line in header.splitlines():
        parts = line.split(&amp;quot;,&amp;quot;)

        if len(parts) != 3:
            continue

        name, start, end = parts

        if start.isdigit() and end.isdigit():
            sections[name] = (
                int(start),
                int(end)
            )

    return sections&lt;/pre&gt;
  &lt;h2 id=&quot;Rmba&quot;&gt;Таблица форматных строк&lt;/h2&gt;
  &lt;p id=&quot;ylkW&quot;&gt;Строка в &lt;code&gt;EnumValsRes&lt;/code&gt; выглядит примерно так:&lt;/p&gt;
  &lt;pre id=&quot;FhHA&quot;&gt;table,id,flag,flag,GROUP,MODULE,NAME,diagPrintf(&amp;quot;формат&amp;quot;, args);&lt;/pre&gt;
  &lt;p id=&quot;bb2Y&quot;&gt;Запятые могут встречаться внутри самой форматной строки, поэтому разделять строку нужно только по первым семи запятым:&lt;/p&gt;
  &lt;pre id=&quot;NoFM&quot;&gt;parts = line.split(&amp;quot;,&amp;quot;, 7)&lt;/pre&gt;
  &lt;p id=&quot;bFob&quot;&gt;Для поддержки всех вариантов вызова используется регулярное выражение:&lt;/p&gt;
  &lt;pre id=&quot;HPWv&quot;&gt;import re

FORMAT_RE = re.compile(
    r&amp;#x27;diag(?:Text|Struct)?Printf\s*\(&amp;#x27;
    r&amp;#x27;\s*&amp;quot;((?:[^&amp;quot;\\]|\\.)*)&amp;quot;&amp;#x27;
)&lt;/pre&gt;
  &lt;p id=&quot;p2Pr&quot;&gt;Оно распознаёт:&lt;/p&gt;
  &lt;pre id=&quot;VUpv&quot;&gt;diagPrintf(...)
TextPrintf(...)
StructPrintf(...)&lt;/pre&gt;
  &lt;p id=&quot;QmvP&quot;&gt;Это исправляет важную ошибку: если искать только &lt;code&gt;diagPrintf&lt;/code&gt;, записи &lt;code&gt;diagTextPrintf&lt;/code&gt; и &lt;code&gt;diagStructPrintf&lt;/code&gt; останутся без форматной строки и будут ошибочно показаны как hex.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;9&quot;&gt;9. Подбор подходящей базы&lt;/h2&gt;
  &lt;p id=&quot;ssBR&quot;&gt;База форматных строк должна соответствовать сборке прошивки. Даже если две версии используют один и тот же чипсет, идентификаторы сообщений могут отличаться.&lt;/p&gt;
  &lt;p id=&quot;AF9D&quot;&gt;Для выбора наиболее подходящей базы удобно использовать измеримые метрики.&lt;/p&gt;
  &lt;h2 id=&quot;key-match&quot;&gt;Key-match&lt;/h2&gt;
  &lt;p id=&quot;jypT&quot;&gt;Первая метрика — доля пар &lt;code&gt;(msg_type, fmt_id)&lt;/code&gt;, найденных в базе:&lt;/p&gt;
  &lt;pre id=&quot;VIVM&quot;&gt;distinct_keys = {
    (record[&amp;quot;msg_type&amp;quot;], record[&amp;quot;fmt_id&amp;quot;])
    for record in terse_records
}

matched = {
    key for key in distinct_keys
    if key in mdb_entries
}

key_match = len(matched) / len(distinct_keys)&lt;/pre&gt;
  &lt;p id=&quot;lUoq&quot;&gt;Эта метрика показывает, совпадает ли схема идентификаторов.&lt;/p&gt;
  &lt;h2 id=&quot;scalar-clean&quot;&gt;Scalar-clean&lt;/h2&gt;
  &lt;p id=&quot;mXzA&quot;&gt;Вторая метрика проверяет, удалось ли полностью разобрать аргументы скалярных сообщений без маркера &lt;code&gt;&amp;lt;?&amp;gt;&lt;/code&gt;.&lt;/p&gt;
  &lt;pre id=&quot;N5kE&quot;&gt;scalar-clean =
число сообщений без underflow /
общее число проверяемых сообщений&lt;/pre&gt;
  &lt;p id=&quot;p0Vu&quot;&gt;Она проверяет уже не только идентификаторы, но и ширины аргументов.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;10---int--16&quot;&gt;10. Главная особенность: &lt;code&gt;int&lt;/code&gt; занимает 16 бит&lt;/h2&gt;
  &lt;p id=&quot;Zdfj&quot;&gt;Это наиболее нетривиальная часть реверса.&lt;/p&gt;
  &lt;h2 id=&quot;i6fj&quot;&gt;Симптом&lt;/h2&gt;
  &lt;p id=&quot;f3EN&quot;&gt;При стандартной интерпретации printf-аргументов каждый &lt;code&gt;%d&lt;/code&gt; ожидает 4 байта. Но результаты выглядели неправильно:&lt;/p&gt;
  &lt;pre id=&quot;26rT&quot;&gt;rsrp = 104922215&lt;/pre&gt;
  &lt;p id=&quot;T0Lm&quot;&gt;или:&lt;/p&gt;
  &lt;pre id=&quot;9Y9L&quot;&gt;rsrp = &amp;lt;?&amp;gt;&lt;/pre&gt;
  &lt;p id=&quot;1z6P&quot;&gt;Причина — payload заканчивался раньше, чем ожидал декодер.&lt;/p&gt;
  &lt;h2 id=&quot;YdnK&quot;&gt;Проверка гипотезы&lt;/h2&gt;
  &lt;p id=&quot;CTkG&quot;&gt;Для каждого сообщения можно вычислить ожидаемый размер аргументов и сравнить его с фактической длиной payload:&lt;/p&gt;
  &lt;pre id=&quot;spNI&quot;&gt;def fit_score(records, short_width):
    exact = 0
    underflow = 0
    leftover = 0

    for record in records:
        fmt = get_format(record)

        if not fmt:
            continue

        required = calculate_argument_size(
            fmt,
            short_width=short_width,
            long_width=4
        )

        actual = len(record[&amp;quot;payload&amp;quot;])

        if required == actual:
            exact += 1
        elif required &amp;gt; actual:
            underflow += 1
        else:
            leftover += 1

    return exact, underflow, leftover&lt;/pre&gt;
  &lt;p id=&quot;rjXk&quot;&gt;При ширине 2 байта количество underflow уменьшается примерно в пять раз.&lt;/p&gt;
  &lt;h2 id=&quot;gRsK&quot;&gt;Практическое правило&lt;/h2&gt;
  &lt;p id=&quot;1W9r&quot;&gt;Для ASR-терс-сообщений используется следующее соответствие:&lt;/p&gt;
  &lt;pre id=&quot;zjgb&quot;&gt;if conversion in &amp;quot;eEfFgG&amp;quot;:
    width = 8 if length in (&amp;quot;l&amp;quot;, &amp;quot;ll&amp;quot;, &amp;quot;L&amp;quot;) else 4
elif conversion == &amp;quot;p&amp;quot;:
    width = 4
elif length == &amp;quot;ll&amp;quot;:
    width = 8
elif length == &amp;quot;hh&amp;quot;:
    width = 1
elif length == &amp;quot;l&amp;quot;:
    width = 4
elif length in (&amp;quot;L&amp;quot;, &amp;quot;z&amp;quot;, &amp;quot;j&amp;quot;, &amp;quot;t&amp;quot;):
    width = 4
elif length == &amp;quot;h&amp;quot;:
    width = 2
else:
    width = 2&lt;/pre&gt;
  &lt;p id=&quot;bOlS&quot;&gt;Значения &lt;code&gt;%d&lt;/code&gt; и &lt;code&gt;%i&lt;/code&gt; необходимо интерпретировать как знаковые:&lt;/p&gt;
  &lt;pre id=&quot;1QeR&quot;&gt;value = int.from_bytes(
    payload[pos:pos + width],
    &amp;quot;little&amp;quot;,
    signed=conversion in &amp;quot;di&amp;quot;
)&lt;/pre&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;11&quot;&gt;11. Сквозной пример декодирования&lt;/h2&gt;
  &lt;p id=&quot;5cVd&quot;&gt;Рассмотрим запись:&lt;/p&gt;
  &lt;pre id=&quot;iLf0&quot;&gt;msg_type = 0x1d
fmt_id   = 0x0b3d
payload  = e40c0000 d100 021c0f00 61e4&lt;/pre&gt;
  &lt;p id=&quot;8Y5t&quot;&gt;Форматная строка:&lt;/p&gt;
  &lt;pre id=&quot;NtiQ&quot;&gt;&amp;quot;updates ( %ld , %d ) &amp;#x27;s measUpdateTime: &amp;quot;
&amp;quot;( 0x%lx ) , ( rsrp = %d ) &amp;quot;&lt;/pre&gt;
  &lt;p id=&quot;m4OD&quot;&gt;Результат:&lt;/p&gt;
  &lt;pre id=&quot;KW0g&quot;&gt;updates ( 3300, 209 ) measUpdateTime:
( 0xf1c02 ), ( rsrp = -7071 )&lt;/pre&gt;
  &lt;p id=&quot;Qibr&quot;&gt;При ошибочной ширине &lt;code&gt;%d = 4 байта&lt;/code&gt; декодер начал бы читать следующие аргументы с неправильных смещений и в итоге получил бы &lt;code&gt;&amp;lt;?&amp;gt;&lt;/code&gt;.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;12--s&quot;&gt;12. Поддержка &lt;code&gt;%S{...}&lt;/code&gt; и структур&lt;/h2&gt;
  &lt;p id=&quot;QBRT&quot;&gt;Некоторые диагностические сообщения передают не скаляры, а структуры:&lt;/p&gt;
  &lt;pre id=&quot;4EsE&quot;&gt;%S{EmmPrintDebugInfoInd_MobileId}&lt;/pre&gt;
  &lt;p id=&quot;bCST&quot;&gt;Описание полей находится в секции &lt;code&gt;Signals&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;tL4X&quot;&gt;Минимальная модель структуры:&lt;/p&gt;
  &lt;pre id=&quot;9ngN&quot;&gt;structs = {
    &amp;quot;Example&amp;quot;: [
        (&amp;quot;field_a&amp;quot;, &amp;quot;int&amp;quot;, 1, &amp;quot;&amp;quot;),
        (&amp;quot;field_b&amp;quot;, &amp;quot;enum&amp;quot;, 1, &amp;quot;StateType&amp;quot;),
        (&amp;quot;field_c&amp;quot;, &amp;quot;pointer&amp;quot;, 1, &amp;quot;&amp;quot;),
    ]
}&lt;/pre&gt;
  &lt;p id=&quot;dcrL&quot;&gt;Декодер обрабатывает:&lt;/p&gt;
  &lt;ul id=&quot;JXXS&quot;&gt;
    &lt;li id=&quot;gWlK&quot;&gt;скалярные поля;&lt;/li&gt;
    &lt;li id=&quot;tGyp&quot;&gt;enum;&lt;/li&gt;
    &lt;li id=&quot;4bsU&quot;&gt;указатели;&lt;/li&gt;
    &lt;li id=&quot;k97N&quot;&gt;вложенные структуры;&lt;/li&gt;
    &lt;li id=&quot;cBXV&quot;&gt;массивы;&lt;/li&gt;
    &lt;li id=&quot;1mQp&quot;&gt;рекурсию.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;0KcX&quot;&gt;Упрощённая схема:&lt;/p&gt;
  &lt;pre id=&quot;ec6w&quot;&gt;def decode_struct(name, payload, pos=0, depth=0):
    if depth &amp;gt; 6:
        return f&amp;quot;{{{name}?}}&amp;quot;, pos

    fields = structs.get(name)

    if fields is None:
        return f&amp;quot;{{{name}?}}&amp;quot;, pos

    result = []

    for field_name, field_type, count, detail in fields:
        values = []

        for _ in range(count):
            if field_type == &amp;quot;enum&amp;quot;:
                value = read_u32(payload, pos)
                pos += 4
                value = enums.get(detail, {}).get(
                    value,
                    str(value)
                )

            elif field_type == &amp;quot;pointer&amp;quot;:
                value = read_u32(payload, pos)
                pos += 4
                value = hex(value)

            elif field_type == &amp;quot;int&amp;quot;:
                value = read_u32(payload, pos, signed=True)
                pos += 4

            elif detail in structs:
                value, pos = decode_struct(
                    detail,
                    payload,
                    pos,
                    depth + 1
                )

            else:
                value = &amp;quot;&amp;lt;?&amp;gt;&amp;quot;

            values.append(value)

        rendered = (
            values[0]
            if count == 1
            else &amp;quot;[&amp;quot; + &amp;quot;,&amp;quot;.join(map(str, values)) + &amp;quot;]&amp;quot;
        )

        result.append(f&amp;quot;{field_name}={rendered}&amp;quot;)

    return &amp;quot;{&amp;quot; + &amp;quot;, &amp;quot;.join(result) + &amp;quot;}&amp;quot;, pos&lt;/pre&gt;
  &lt;h2 id=&quot;vwkz&quot;&gt;Почему структуры могут декодироваться неверно&lt;/h2&gt;
  &lt;p id=&quot;yUmc&quot;&gt;Секция &lt;code&gt;Signals&lt;/code&gt; из близкой базы может не полностью соответствовать конкретной сборке модема. Например, поля большой EMM-структуры могут иметь другую раскладку или тип.&lt;/p&gt;
  &lt;p id=&quot;b6JO&quot;&gt;В результате появляются значения вроде:&lt;/p&gt;
  &lt;pre id=&quot;fOaO&quot;&gt;mcc = -1727901696&lt;/pre&gt;
  &lt;p id=&quot;ZHLK&quot;&gt;В таких случаях проблема находится не в транспортном парсере, а в несовпадении описания структуры. Исправить её можно только точной базой &lt;code&gt;cp_MDB.txt&lt;/code&gt; для конкретной версии прошивки.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;13&quot;&gt;13. Приоритет способов декодирования&lt;/h2&gt;
  &lt;p id=&quot;DAx5&quot;&gt;Одна запись может быть распознана несколькими способами. Поэтому форматтеру нужен строгий порядок выбора:&lt;/p&gt;
  &lt;pre id=&quot;jWY2&quot;&gt;1. TEXT-запись
   └─ готовая строка, база не нужна

2. file:line
   └─ имя исходного файла и номер строки

3. terse-декодирование через MDB
   └─ форматная строка + бинарные аргументы

4. встроенная ASCII-строка
   └─ длинный печатный фрагмент внутри payload

5. raw hex
   └─ неизвестный или неподдерживаемый payload&lt;/pre&gt;
  &lt;p id=&quot;lRIn&quot;&gt;Формат вывода может выглядеть так:&lt;/p&gt;
  &lt;pre id=&quot;HjYG&quot;&gt;[+0.000000] [TEXT] +ZDON
[+0.003174] [NAS] New Selected PLMN (250,1)
[+0.006287] [RRC] Serving Cell rsrp=-110.5 dBm
[+0.009460] [ASSERT] rrcdsutils.c:2722&lt;/pre&gt;
  &lt;p id=&quot;FcLg&quot;&gt;Большие необработанные записи лучше скрывать по умолчанию и включать отдельным флагом:&lt;/p&gt;
  &lt;pre id=&quot;sjDl&quot;&gt;./asr_debug_monitor.py --replay capture.bin -t&lt;/pre&gt;
  &lt;p id=&quot;wJnW&quot;&gt;где &lt;code&gt;-t&lt;/code&gt; показывает terse-записи, а:&lt;/p&gt;
  &lt;pre id=&quot;hF41&quot;&gt;./asr_debug_monitor.py --replay capture.bin -a&lt;/pre&gt;
  &lt;p id=&quot;MmN8&quot;&gt;показывает все записи, включая объёмные signal-сообщения.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;14----seq&quot;&gt;14. Контроль потерь по &lt;code&gt;seq&lt;/code&gt;&lt;/h2&gt;
  &lt;p id=&quot;ES8X&quot;&gt;Поле &lt;code&gt;seq&lt;/code&gt; — 16-битный счётчик записей. Оно позволяет заметить потерю данных на USB или переполнение буфера.&lt;/p&gt;
  &lt;p id=&quot;8XKj&quot;&gt;Поскольку счётчик циклический, сравнение выполняется по модулю 2162^{16}216:&lt;/p&gt;
  &lt;pre id=&quot;0PEP&quot;&gt;def sequence_gap(previous, current):
    return (current - previous - 1) &amp;amp; 0xFFFF&lt;/pre&gt;
  &lt;p id=&quot;JeNR&quot;&gt;Практическая проверка:&lt;/p&gt;
  &lt;pre id=&quot;GclP&quot;&gt;gap = (record.seq - last_seq - 1) &amp;amp; 0xFFFF

if 0 &amp;lt; gap &amp;lt; 0x8000:
    dropped += gap&lt;/pre&gt;
  &lt;p id=&quot;wWXc&quot;&gt;Ограничение &lt;code&gt;gap &amp;lt; 0x8000&lt;/code&gt; нужно, чтобы отличать нормальное переполнение счётчика от большого обратного скачка.&lt;/p&gt;
  &lt;p id=&quot;jGpF&quot;&gt;В интерфейсе можно показывать:&lt;/p&gt;
  &lt;pre id=&quot;Sd1i&quot;&gt;[warning] потеряно записей: 12&lt;/pre&gt;
  &lt;p id=&quot;6So1&quot;&gt;Это особенно полезно при высокоскоростном логе, когда USB-порт, пользовательский процесс или внутренний буфер не успевают обработать весь поток.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;15&quot;&gt;15. Верификация&lt;/h2&gt;
  &lt;p id=&quot;EM1m&quot;&gt;Декодер необходимо проверять на двух уровнях.&lt;/p&gt;
  &lt;h2 id=&quot;oR4J&quot;&gt;Автономные тесты&lt;/h2&gt;
  &lt;p id=&quot;Unbt&quot;&gt;Полезно протестировать отдельно:&lt;/p&gt;
  &lt;ul id=&quot;ezl6&quot;&gt;
    &lt;li id=&quot;8Gqq&quot;&gt;разбор заголовка;&lt;/li&gt;
    &lt;li id=&quot;IifS&quot;&gt;length-framing;&lt;/li&gt;
    &lt;li id=&quot;AMFg&quot;&gt;повторную синхронизацию после мусора;&lt;/li&gt;
    &lt;li id=&quot;qezt&quot;&gt;инкрементальный &lt;code&gt;StreamParser&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;6Eqo&quot;&gt;TEXT-записи;&lt;/li&gt;
    &lt;li id=&quot;47sS&quot;&gt;&lt;code&gt;file:line&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;4BJP&quot;&gt;разбор MDB;&lt;/li&gt;
    &lt;li id=&quot;9Ihr&quot;&gt;&lt;code&gt;%S{...}&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;uOGj&quot;&gt;&lt;code&gt;%e{...}&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;rAzS&quot;&gt;16-битные аргументы.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;cKzK&quot;&gt;В исходном проекте такой набор из девяти тестов проходит на синтетической базе и искусственно сформированных записях.&lt;/p&gt;
  &lt;h2 id=&quot;3dgQ&quot;&gt;Сквозная проверка&lt;/h2&gt;
  &lt;p id=&quot;izYy&quot;&gt;Затем необходимо проверить полный путь:&lt;/p&gt;
  &lt;pre id=&quot;5Aog&quot;&gt;A7665E
  → USB CP-log
  → StreamParser
  → MDB decoder
  → человекочитаемый вывод&lt;/pre&gt;
  &lt;p id=&quot;5uis&quot;&gt;В корректно декодированных сообщениях должны распознаваться, например:&lt;/p&gt;
  &lt;ul id=&quot;tSvn&quot;&gt;
    &lt;li id=&quot;P5e1&quot;&gt;&lt;code&gt;+ZDON&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;PGLL&quot;&gt;выбранная PLMN;&lt;/li&gt;
    &lt;li id=&quot;ZYBz&quot;&gt;serving cell;&lt;/li&gt;
    &lt;li id=&quot;JbUY&quot;&gt;RSRP и RSRQ;&lt;/li&gt;
    &lt;li id=&quot;j28R&quot;&gt;APN;&lt;/li&gt;
    &lt;li id=&quot;2awn&quot;&gt;IP-адрес;&lt;/li&gt;
    &lt;li id=&quot;gYrv&quot;&gt;события NAS и RRC;&lt;/li&gt;
    &lt;li id=&quot;xncU&quot;&gt;сообщения IPC между L1 и protocol stack.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;16&quot;&gt;16. Ограничения метода&lt;/h2&gt;
  &lt;p id=&quot;wdN1&quot;&gt;Даже при правильно найденном транспортном формате часть сообщений может оставаться неполностью декодированной.&lt;/p&gt;
  &lt;p id=&quot;WKFB&quot;&gt;Особенно важно различать ошибки разных уровней:&lt;/p&gt;
  &lt;pre id=&quot;JnVN&quot;&gt;неправильная длина записи
    → проблема транспортного парсера

правильная запись, но &amp;lt;?&amp;gt;
    → проблема формата или ширины аргумента

правильный текст, но неверные поля структуры
    → несовпадение Signals с конкретной прошивкой&lt;/pre&gt;
  &lt;p id=&quot;6NXp&quot;&gt;Транспортный формат при этом может быть полностью понятен, даже если семантическая расшифровка остаётся неполной.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;17&quot;&gt;17. Запуск декодера&lt;/h2&gt;
  &lt;p id=&quot;T3fE&quot;&gt;Примерный набор команд:&lt;/p&gt;
  &lt;pre id=&quot;Kj1K&quot;&gt;cd /home/sam/work/modems_debug/asr_debug&lt;/pre&gt;
  &lt;p id=&quot;ThEK&quot;&gt;Живой захват с автоматическим поиском порта:&lt;/p&gt;
  &lt;pre id=&quot;XL0x&quot;&gt;./asr_debug_monitor.py&lt;/pre&gt;
  &lt;p id=&quot;cRFI&quot;&gt;Разбор сохранённого файла:&lt;/p&gt;
  &lt;pre id=&quot;h8Cm&quot;&gt;./asr_debug_monitor.py \
    --replay boot_registration.bin&lt;/pre&gt;
  &lt;p id=&quot;qRL0&quot;&gt;Разбор terse-записей:&lt;/p&gt;
  &lt;pre id=&quot;YfcI&quot;&gt;./asr_debug_monitor.py \
    --replay boot_registration.bin \
    -t&lt;/pre&gt;
  &lt;p id=&quot;oHIg&quot;&gt;Вывод всех записей:&lt;/p&gt;
  &lt;pre id=&quot;QuAd&quot;&gt;./asr_debug_monitor.py \
    --replay boot_registration.bin \
    -a&lt;/pre&gt;
  &lt;p id=&quot;jhVH&quot;&gt;Использование точной базы:&lt;/p&gt;
  &lt;pre id=&quot;yD3T&quot;&gt;./asr_debug_monitor.py \
    -m /path/to/exact/cp_MDB.txt&lt;/pre&gt;
  &lt;p id=&quot;gXAP&quot;&gt;Для захвата, переживающего ребут модема, нужен внешний wrapper:&lt;/p&gt;
  &lt;ol id=&quot;r2E9&quot;&gt;
    &lt;li id=&quot;luXb&quot;&gt;дождаться появления нужного USB-интерфейса;&lt;/li&gt;
    &lt;li id=&quot;HBc7&quot;&gt;открыть порт;&lt;/li&gt;
    &lt;li id=&quot;oNsb&quot;&gt;отправить &lt;code&gt;AT+ECPLOG=1&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;RCOp&quot;&gt;записывать необработанный поток;&lt;/li&gt;
    &lt;li id=&quot;Fk5F&quot;&gt;обнаружить исчезновение порта;&lt;/li&gt;
    &lt;li id=&quot;DaXC&quot;&gt;дождаться повторной USB-энумерации;&lt;/li&gt;
    &lt;li id=&quot;w0La&quot;&gt;открыть новую сессию в отдельном файле.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;DuEe&quot;&gt;Так можно захватывать загрузку модема с самого начала, даже если во время ребута меняются имена &lt;code&gt;ttyUSBx&lt;/code&gt;.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;x0s0&quot;&gt;Заключение&lt;/h2&gt;
  &lt;p id=&quot;FRgz&quot;&gt;Распознавание бинарного CP-лога ASR состоит из нескольких независимых уровней:&lt;/p&gt;
  &lt;pre id=&quot;k5L8&quot;&gt;USB-порт
  → AT+ECPLOG=1
  → length-framing
  → 16-байтный заголовок
  → msg_type + fmt_id
  → база форматных строк
  → декодирование printf-аргументов
  → структуры, enum и timestamp
  → читаемый трейс&lt;/pre&gt;
  &lt;p id=&quot;hX32&quot;&gt;Самая важная практическая находка — нестандартная упаковка аргументов: обычные &lt;code&gt;%d&lt;/code&gt;, &lt;code&gt;%u&lt;/code&gt; и &lt;code&gt;%x&lt;/code&gt; в terse-сообщениях ASR занимают 2 байта, а &lt;code&gt;%l...&lt;/code&gt; — 4 байта. Без этого правила идентификаторы сообщений могут находиться правильно, но значения будут «плыть», а декодер будет регулярно выдавать &lt;code&gt;&amp;lt;?&amp;gt;&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;0WEU&quot;&gt;Второй важный вывод — транспорт и семантику нужно разрабатывать отдельно. Формат записи можно восстановить и протестировать независимо от базы. А база, в свою очередь, может заменяться без изменений в протоколе и потоковом парсере.&lt;/p&gt;
  &lt;p id=&quot;xDt1&quot;&gt;Для полного покрытия остаётся получить точную &lt;code&gt;cp_MDB.txt&lt;/code&gt; под прошивку &lt;code&gt;A7665M5_B01V01&lt;/code&gt;. После этого должны исчезнуть ошибки в больших структурах, неизвестные enum и большая часть необработанных бинарных хвостов.&lt;/p&gt;

</content></entry><entry><id>pole_sam:CAcPKudWlw4</id><link rel="alternate" type="text/html" href="https://teletype.in/@pole_sam/CAcPKudWlw4?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=pole_sam"></link><title>Как используются данные SIM‑карты в GSM (2G): аутентификация, ключи, IMSI, A3/A8</title><published>2026-08-15T18:36:57.708Z</published><updated>2026-08-15T18:36:57.708Z</updated><summary type="html">Всем привет! В GSM (2G) SIM‑карта хранит постоянный секретный ключ абонента Ki и идентификатор IMSI; при регистрации в сети используется механизм «вызов‑ответ»: сеть посылает случайное число RAND, SIM вычисляет подписанный ответ SRES = A3(Ki, RAND) и сеансовый ключ шифрования Kc = A8(Ki, RAND), после чего сеть проверяет SRES и при успехе включает шифрование A5.</summary><content type="html">
  &lt;p id=&quot;VDme&quot;&gt;Всем привет! В GSM (2G) SIM‑карта хранит постоянный секретный ключ абонента Ki и идентификатор IMSI; при регистрации в сети используется механизм «вызов‑ответ»: сеть посылает случайное число RAND, SIM вычисляет подписанный ответ SRES = A3(Ki, RAND) и сеансовый ключ шифрования Kc = A8(Ki, RAND), после чего сеть проверяет SRES и при успехе включает шифрование A5.&lt;/p&gt;
  &lt;p id=&quot;uy4r&quot;&gt;&lt;strong&gt;Важно: Ki никогда не передаётся по радиоинтерфейсу и не выводится из SIM; вся криптография «вызов‑ответ» выполняется внутри SIM.jucs+2&lt;/strong&gt;&lt;/p&gt;
  &lt;h2 id=&quot;2&quot;&gt;2. Роли сетевых элементов (кратко)&lt;/h2&gt;
  &lt;ul id=&quot;Qrmv&quot;&gt;
    &lt;li id=&quot;dzx6&quot;&gt;MS (Mobile Station) = ME (телефон) + SIM.&lt;/li&gt;
    &lt;li id=&quot;HqES&quot;&gt;BTS/BSC — радиодоступ.&lt;/li&gt;
    &lt;li id=&quot;eIA2&quot;&gt;MSC/VLR — коммутация и локальное управление сессиями; VLR хранит «триплеты» аутентификации для абонента.&lt;/li&gt;
    &lt;li id=&quot;hRa0&quot;&gt;HLR — домашний реестр абонентов.&lt;/li&gt;
    &lt;li id=&quot;ANBL&quot;&gt;AuC (Authentication Center) — генерирует RAND, SRES, Kc (триплеты) на основе IMSI и Ki.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2 id=&quot;3---gsm-2g&quot;&gt;3. Протокол аутентификации GSM (2G): пошагово&lt;/h2&gt;
  &lt;p id=&quot;VB6Q&quot;&gt;Ниже — упрощённая последовательность при регистрации/доступе к услугам.&lt;/p&gt;
  &lt;pre id=&quot;qj9P&quot;&gt;text[MS/SIM]                         [MSC/VLR]                         [HLR/AuC]
    |                                |                                |
    | 1. Location Update / CM Service Request (IMSI или TMSI)         |
    |---------------------------------------------------------------&amp;gt;|
    |                                | 2. Запрос триплетов для IMSI   |
    |                                |------------------------------&amp;gt;|
    |                                | 3. Возврат N триплетов         |
    |                                | (RAND_i, SRES_i, Kc_i)         |
    |                                |&amp;lt;------------------------------|
    |                                |                                |
    | 4. AUTHENTICATION REQUEST      |                                |
    |    (RAND)                      |                                |
    |&amp;lt;-------------------------------|                                |
    | 5. SIM: SRES&amp;#x27; = A3(Ki, RAND)   |                                |
    |     Kc&amp;#x27;   = A8(Ki, RAND)       |                                |
    | 6. AUTHENTICATION RESPONSE     |                                |
    |    (SRES&amp;#x27;)                     |                                |
    |------------------------------&amp;gt;|                                |
    |                                | 7. Проверка: SRES&amp;#x27; == SRES?    |
    |                                |    Если да → аутентификация OK |
    | 8. Cipher Mode Command (A5/x)  |                                |
    |&amp;lt;-------------------------------|                                |
    | 9. Включение шифрования A5/x с Kc&amp;#x27;                               |
    |&amp;lt;================================================================&amp;gt;|&lt;/pre&gt;
  &lt;p id=&quot;r1XJ&quot;&gt;Ключевые моменты:&lt;/p&gt;
  &lt;ul id=&quot;JYUI&quot;&gt;
    &lt;li id=&quot;w02J&quot;&gt;Сеть заранее получает от AuC «триплеты» (RAND, SRES, Kc) для данного IMSI; при аутентификации она посылает только RAND.&lt;/li&gt;
    &lt;li id=&quot;3MKM&quot;&gt;SIM вычисляет SRES&amp;#x27; и Kc&amp;#x27; по тем же формулам; VLR сравнивает SRES&amp;#x27; с ожидаемым SRES.&lt;/li&gt;
    &lt;li id=&quot;loVT&quot;&gt;После успеха сеть инициирует Cipher Mode Command и далее трафик шифруется A5/x с ключом Kc.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2 id=&quot;4--a3--a8&quot;&gt;4. Алгоритмы A3 и A8: что именно считается&lt;/h2&gt;
  &lt;p id=&quot;SMqY&quot;&gt;Формально стандарт GSM не фиксирует одну конкретную реализацию A3/A8; операторы выбирают алгоритм (часто COMP128‑1/2/3, milenage‑подобные варианты в 2G‑совместимых реализациях и др.).&lt;/p&gt;
  &lt;ul id=&quot;w5zA&quot;&gt;
    &lt;li id=&quot;kg76&quot;&gt;A3: SRES = A3(Ki, RAND) → 32‑битный ответ.&lt;/li&gt;
    &lt;li id=&quot;fNwD&quot;&gt;A8: Kc = A8(Ki, RAND) → 64‑битный сеансовый ключ для A5/x.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;niDa&quot;&gt;На практике A3 и A8 часто реализованы как единая функция, выдающая (SRES, Kc) из (Ki, RAND).&lt;/p&gt;
  &lt;h2 id=&quot;5--imsi&quot;&gt;5. Роль IMSI и почему он важен&lt;/h2&gt;
  &lt;ul id=&quot;MVE8&quot;&gt;
    &lt;li id=&quot;AHqq&quot;&gt;IMSI используется для идентификации абонента в HLR/AuC и получения правильного Ki (и триплетов).&lt;/li&gt;
    &lt;li id=&quot;LHax&quot;&gt;При первой регистрации в новой зоне MS обычно передаёт IMSI (если нет валидного TMSI), что делает IMSI видимым для любой базовой станции в радиусе.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;ul id=&quot;v2Yb&quot;&gt;
    &lt;li id=&quot;5NY1&quot;&gt;В дальнейшем сеть старается использовать TMSI вместо IMSI для конфиденциальности, но IMSI всё равно может быть запрошен (например, при сбоях или специальных процедурах).&lt;/li&gt;
    &lt;li id=&quot;qoc1&quot;&gt;TMSI (Temporary Mobile Subscriber Identity) — это временный идентификатор абонента, который сеть выдаёт мобильному устройству после успешной регистрации, чтобы вместо постоянного IMSI использовать его в радиоканале для повышения конфиденциальности.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2 id=&quot;6---gsm-2g---sim&quot;&gt;6. Риски безопасности GSM (2G), связанные с SIM и аутентификацией&lt;/h2&gt;
  &lt;p id=&quot;rEuK&quot;&gt;GSM (2G) имеет ряд фундаментальных ограничений, которые напрямую касаются использования данных SIM:&lt;/p&gt;
  &lt;ol id=&quot;Pgy3&quot;&gt;
    &lt;li id=&quot;uoRn&quot;&gt;&lt;strong&gt;Односторонняя аутентификация&lt;/strong&gt;&lt;/li&gt;
    &lt;ul id=&quot;Df8A&quot;&gt;
      &lt;li id=&quot;4473&quot;&gt;Сеть аутентифицирует телефон (MS), но телефон не аутентифицирует сеть.&lt;/li&gt;
      &lt;li id=&quot;Aoyn&quot;&gt;Это позволяет IMSI‑catcher&amp;#x27;у (фейковой базовой станции) притворяться «легальной» сетью: телефон отправляет IMSI/TMSI, проходит «аутентификацию» у злоумышленника, а трафик может не шифроваться или шифроваться слабым алгоритмом.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;xPOc&quot;&gt;&lt;strong&gt;Слабость шифрования A5/1 (и A5/2)&lt;/strong&gt;&lt;/li&gt;
    &lt;ul id=&quot;YwyZ&quot;&gt;
      &lt;li id=&quot;WKaj&quot;&gt;A5/1 (64‑битный ключ Kc) уязвим к атакам по заранее вычисленным таблицам (rainbow tables); практическая расшифровка возможна за секунды–минуты на обычном железе.&lt;/li&gt;
      &lt;li id=&quot;LNgs&quot;&gt;A5/2 ещё слабее и практически не защищает.&lt;/li&gt;
      &lt;li id=&quot;TBQc&quot;&gt;Злоумышленник может принудительно переключить сессию на A5/0 (без шифрования) или слабый A5/x.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;ANCp&quot;&gt;&lt;strong&gt;Зависимость от реализации A3/A8&lt;/strong&gt;&lt;/li&gt;
    &lt;ul id=&quot;9YIl&quot;&gt;
      &lt;li id=&quot;n2TF&quot;&gt;Ранние популярные реализации (например, COMP128‑1) имели криптографические слабости, позволяющие при достаточном числе запросов извлекать Ki.&lt;/li&gt;
      &lt;li id=&quot;XaXw&quot;&gt;Даже при более стойких A3/A8, 32‑битный SRES и 64‑битный Kc ограничивают криптостойкость по современным меркам.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;v3LU&quot;&gt;&lt;strong&gt;Перехват IMSI&lt;/strong&gt;&lt;/li&gt;
    &lt;ul id=&quot;k8J8&quot;&gt;
      &lt;li id=&quot;XMVy&quot;&gt;Поскольку IMSI передаётся в открытом виде при первой регистрации, IMSI‑catcher может собирать IMSI всех устройств в зоне покрытия.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;VwaX&quot;&gt;Эти ограничения — причина, по которой в 3G/4G/5G введена взаимная аутентификация (сеть тоже доказывается абоненту), более длинные ответы (RES вместо SRES), ключи CK/IK и более стойкие алгоритмы (Milenage, AES‑based и др.).&lt;/p&gt;
  &lt;h2 id=&quot;7----sim&quot;&gt;7. Сводная схема «данные SIM → параметры сессии»&lt;/h2&gt;
  &lt;pre id=&quot;6q7I&quot;&gt;textSIM (Ki, IMSI, A3/A8)
       │
       │ IMSI → HLR/AuC → выбор Ki
       │
       └─► AuC: для каждого RAND_i
               SRES_i = A3(Ki, RAND_i)
               Kc_i   = A8(Ki, RAND_i)
               триплет (RAND_i, SRES_i, Kc_i) → VLR

При аутентификации:
  VLR → MS: RAND
  SIM: SRES&amp;#x27; = A3(Ki, RAND)
       Kc&amp;#x27;   = A8(Ki, RAND)
  MS → VLR: SRES&amp;#x27;
  VLR: если SRES&amp;#x27; == SRES → OK, шифрование A5/x с Kc&amp;#x27;&lt;/pre&gt;
  &lt;hr /&gt;

</content></entry><entry><id>pole_sam:MUgk4kD28C5</id><link rel="alternate" type="text/html" href="https://teletype.in/@pole_sam/MUgk4kD28C5?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=pole_sam"></link><title>Данные вещательных каналов сотовой вышки (2G/GSM и LTE): что можно получить и как декодировать</title><published>2026-08-03T07:11:42.451Z</published><updated>2026-08-03T07:11:42.451Z</updated><summary type="html">Сотовая вышка (BTS в 2G, eNodeB в LTE) непрерывно передаёт в эфир «системную информацию» — набор параметров, которые нужны телефону (UE), чтобы обнаружить сеть, синхронизироваться и подготовиться к подключению. Это не «перехват трафика» — это открытые широковещательные (broadcast) данные, которые предназначены для приёма всеми устройствами в зоне покрытия. Их декодирование полностью пассивно, законно в большинстве юрисдикций и не затрагивает трафик абонентов.</summary><content type="html">
  &lt;p id=&quot;aRmQ&quot;&gt;Сотовая вышка (BTS в 2G, eNodeB в LTE) непрерывно передаёт в эфир «системную информацию» — набор параметров, которые нужны телефону (UE), чтобы обнаружить сеть, синхронизироваться и подготовиться к подключению. Это не «перехват трафика» — это открытые широковещательные (broadcast) данные, которые предназначены для приёма всеми устройствами в зоне покрытия. Их декодирование полностью пассивно, законно в большинстве юрисдикций и не затрагивает трафик абонентов.&lt;/p&gt;
  &lt;p id=&quot;ytlS&quot;&gt;Ниже — какие каналы есть, что они несут и что из этого можно понять о вышке и сети.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;1&quot;&gt;1. Что вообще вещает вышка&lt;/h2&gt;
  &lt;p id=&quot;MJ6S&quot;&gt;Любая вышка передаёт на нисходящей частоте (downlink) набор логических каналов. Для пассивного наблюдателя важны два класса:&lt;/p&gt;
  &lt;ul id=&quot;35bH&quot;&gt;
    &lt;li id=&quot;jnhI&quot;&gt;&lt;strong&gt;Каналы синхронизации&lt;/strong&gt; — позволяют обнаружить сигнал, получить тайминг и идентификатор соты.&lt;/li&gt;
    &lt;li id=&quot;WETF&quot;&gt;&lt;strong&gt;Вещательные каналы&lt;/strong&gt; (BCCH в 2G, BCH/DL-SCH в LTE) — несут системную информацию (System Information) с параметрами сети.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;o79B&quot;&gt;Телефон при включении делает ровно то же самое: ищет сигнал синхронизации, читает системную информацию и решает, можно ли подключиться. С помощью SDR (Software Defined Radio) и открытых инструментов этот процесс можно повторить без телефона.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;2-gsm-2g&quot;&gt;2. GSM (2G)&lt;/h2&gt;
  &lt;h2 id=&quot;4okX&quot;&gt;Каналы и сообщения&lt;/h2&gt;
  &lt;p id=&quot;J1Kn&quot;&gt;Вещательные данные GSM передаются на &lt;strong&gt;BCCH&lt;/strong&gt; (Broadcast Control Channel), который передаётся непрерывно и служит стабильным опорным «маяком» соты. Основное содержимое BCCH — это сообщения &lt;strong&gt;System Information (SI)&lt;/strong&gt;, описанные в &lt;a href=&quot;https://www.etsi.org/deliver/etsi_ts/144000_144099/144018/17.00.00_60/ts_144018v170000p.pdf&quot; target=&quot;_blank&quot;&gt;ETSI TS 144 018&lt;/a&gt; (3GPP TS 44.018). &lt;/p&gt;
  &lt;h2 id=&quot;bcch&quot;&gt;Что можно извлечь из BCCH&lt;/h2&gt;
  &lt;ul id=&quot;4uKZ&quot;&gt;
    &lt;li id=&quot;Llja&quot;&gt;&lt;strong&gt;MCC&lt;/strong&gt; (Mobile Country Code) — страна оператора&lt;/li&gt;
    &lt;li id=&quot;R8mq&quot;&gt;&lt;strong&gt;MNC&lt;/strong&gt; (Mobile Network Code) — сам оператор&lt;/li&gt;
    &lt;li id=&quot;qBqZ&quot;&gt;&lt;strong&gt;LAC&lt;/strong&gt; (Location Area Code) — код зоны местоположения&lt;/li&gt;
    &lt;li id=&quot;gMDE&quot;&gt;&lt;strong&gt;Cell ID (CI)&lt;/strong&gt; — идентификатор соты → вместе с MCC+MNC+LAC+CI образует &lt;strong&gt;CGI&lt;/strong&gt; (Cell Global Identity)&lt;/li&gt;
    &lt;li id=&quot;qGAb&quot;&gt;&lt;strong&gt;ARFCN&lt;/strong&gt; — номер радиоканала → пересчитывается в частоту&lt;/li&gt;
    &lt;li id=&quot;ZhGq&quot;&gt;&lt;strong&gt;BSIC&lt;/strong&gt; (Base Station Identity Code) — цветовой код вышки&lt;/li&gt;
    &lt;li id=&quot;CDVM&quot;&gt;&lt;strong&gt;CCCH конфигурация&lt;/strong&gt; — сколько каналов paging, AGCH и т.д.&lt;/li&gt;
    &lt;li id=&quot;Kqfb&quot;&gt;&lt;strong&gt;Параметры RACH&lt;/strong&gt; — как телефону запрашивать доступ&lt;/li&gt;
    &lt;li id=&quot;SF4w&quot;&gt;&lt;strong&gt;Параметры выбора соты&lt;/strong&gt; — минимальный уровень приёма (C1/C2), приоритеты&lt;/li&gt;
    &lt;li id=&quot;yuDJ&quot;&gt;&lt;strong&gt;Список соседей&lt;/strong&gt; — частоты соседних сот (для handover/reselection)&lt;/li&gt;
    &lt;li id=&quot;qVYn&quot;&gt;&lt;strong&gt;Признак GPRS/EDGE&lt;/strong&gt; — поддерживает ли сота пакетную передачу&lt;/li&gt;
    &lt;li id=&quot;dznm&quot;&gt;&lt;strong&gt;Cell Barring&lt;/strong&gt; — заблокирована ли сота для доступа&lt;/li&gt;
    &lt;li id=&quot;KGy2&quot;&gt;&lt;strong&gt;Информация о 3G-соседях&lt;/strong&gt; — частоты UMTS&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2 id=&quot;pvZR&quot;&gt;Синхронизация&lt;/h2&gt;
  &lt;p id=&quot;0KwF&quot;&gt;До чтения BCCH нужно сначала найти сигнал. В GSM для этого служат:&lt;/p&gt;
  &lt;ul id=&quot;GAA5&quot;&gt;
    &lt;li id=&quot;sECW&quot;&gt;&lt;strong&gt;FCCH&lt;/strong&gt; (Frequency Correction Channel) — чистый синус на фиксированном смещении, позволяет подстроить частоту приёмника.&lt;/li&gt;
    &lt;li id=&quot;faTb&quot;&gt;&lt;strong&gt;SCH&lt;/strong&gt; (Synchronisation Channel) — несёт &lt;strong&gt;BSIC&lt;/strong&gt; и номер кадра, позволяет выровнять тайминг.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;3-lte-4g&quot;&gt;3. LTE (4G)&lt;/h2&gt;
  &lt;h2 id=&quot;YrkS&quot;&gt;Каналы и сообщения&lt;/h2&gt;
  &lt;p id=&quot;bWmS&quot;&gt;В LTE системная информация разделена на &lt;strong&gt;MIB&lt;/strong&gt; (Master Information Block) и набор &lt;strong&gt;SIB&lt;/strong&gt; (System Information Blocks), описанные в &lt;a href=&quot;https://www.3glteinfo.com/messages/lte/lte-rrc/&quot; target=&quot;_blank&quot;&gt;3GPP TS 36.331&lt;/a&gt;.&lt;/p&gt;
  &lt;h2 id=&quot;mib&quot;&gt;MIB&lt;/h2&gt;
  &lt;p id=&quot;9cBk&quot;&gt;Передаётся на &lt;strong&gt;PBCH&lt;/strong&gt; (Physical Broadcast Channel) с периодичностью 40 мс (с повторениями каждые 10 мс). Содержит самый минимум:&lt;/p&gt;
  &lt;ul id=&quot;mRpc&quot;&gt;
    &lt;li id=&quot;XPzT&quot;&gt;&lt;strong&gt;DL Bandwidth&lt;/strong&gt; соты (в числе Resource Blocks: 1.4 / 3 / 5 / 10 / 15 / 20 МГц)&lt;/li&gt;
    &lt;li id=&quot;qkIW&quot;&gt;&lt;strong&gt;PHICH&lt;/strong&gt; конфигурация&lt;/li&gt;
    &lt;li id=&quot;TSb4&quot;&gt;&lt;strong&gt;SFN&lt;/strong&gt; (System Frame Number)&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2 id=&quot;sib1&quot;&gt;SIB1&lt;/h2&gt;
  &lt;p id=&quot;zNrI&quot;&gt;Передаётся на DL-SCH с периодичностью 80 мс (с повторениями). Несёт:&lt;/p&gt;
  &lt;ul id=&quot;S5JF&quot;&gt;
    &lt;li id=&quot;zkK3&quot;&gt;&lt;strong&gt;PLMN identity list&lt;/strong&gt; (MCC+MNC, может быть несколько)&lt;/li&gt;
    &lt;li id=&quot;DWYU&quot;&gt;&lt;strong&gt;Tracking Area Code (TAC)&lt;/strong&gt;&lt;/li&gt;
    &lt;li id=&quot;sYoX&quot;&gt;&lt;strong&gt;Cell Identity&lt;/strong&gt; — 28-битный E-UTRAN Cell Identifier (ECI). В макросетях часто раскладывается на eNodeB ID (старшие 20 бит) и локальный/секторный Cell ID (младшие 8 бит), но это не универсальное правило&lt;/li&gt;
    &lt;li id=&quot;HegO&quot;&gt;&lt;strong&gt;Cell bar status&lt;/strong&gt; (доступна ли сота)&lt;/li&gt;
    &lt;li id=&quot;1Pxm&quot;&gt;&lt;strong&gt;Cell selection info&lt;/strong&gt; (минимальный требуемый уровень приёма, Q-RxLevMin)&lt;/li&gt;
    &lt;li id=&quot;mnBe&quot;&gt;&lt;strong&gt;p-Max&lt;/strong&gt; — максимальная разрешённая мощность UE&lt;/li&gt;
    &lt;li id=&quot;e262&quot;&gt;&lt;strong&gt;Frequency band indicator&lt;/strong&gt;&lt;/li&gt;
    &lt;li id=&quot;iTlj&quot;&gt;&lt;strong&gt;TDD конфигурация&lt;/strong&gt; (если TDD)&lt;/li&gt;
    &lt;li id=&quot;Schg&quot;&gt;&lt;strong&gt;SI-window length&lt;/strong&gt; и &lt;strong&gt;schedulingInfoList&lt;/strong&gt; — расписание остальных SIB&lt;/li&gt;
    &lt;li id=&quot;14cD&quot;&gt;&lt;strong&gt;System info value tag&lt;/strong&gt; — версия информации&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h2 id=&quot;sib&quot;&gt;Остальные SIB&lt;/h2&gt;
  &lt;p id=&quot;6Nh1&quot;&gt;Передаются в сообщениях SystemInformation (SI) по расписанию из SIB1:&lt;/p&gt;
  &lt;p id=&quot;V15w&quot;&gt;&lt;strong&gt;SIB2&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;kzvV&quot;&gt;Общая конфигурация радио: параметры RACH, PRACH, UL-частота, ширина UL-канала&lt;/p&gt;
  &lt;p id=&quot;HXGN&quot;&gt;&lt;strong&gt;SIB3&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;smxn&quot;&gt;Параметры reselection внутри той же частоты&lt;/p&gt;
  &lt;p id=&quot;m5Uu&quot;&gt;&lt;strong&gt;SIB4&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;Nc2Q&quot;&gt;Информация о соседях intra-frequency&lt;/p&gt;
  &lt;p id=&quot;VVSn&quot;&gt;&lt;strong&gt;SIB5&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;AlF6&quot;&gt;Информация о соседях inter-frequency (другие частоты LTE)&lt;/p&gt;
  &lt;p id=&quot;2s0k&quot;&gt;&lt;strong&gt;SIB6&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;pT1W&quot;&gt;Информация о соседях 3G (UTRA)&lt;/p&gt;
  &lt;p id=&quot;32EY&quot;&gt;&lt;strong&gt;SIB7&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;MbiJ&quot;&gt;Информация о соседях 2G (GERAN)&lt;/p&gt;
  &lt;p id=&quot;CEze&quot;&gt;&lt;strong&gt;SIB8&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;CaL0&quot;&gt;Информация о соседях CDMA2000&lt;/p&gt;
  &lt;p id=&quot;XVyR&quot;&gt;&lt;strong&gt;SIB9&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;9De5&quot;&gt;Home eNodeB identifier (femtocell)&lt;/p&gt;
  &lt;p id=&quot;CqlW&quot;&gt;&lt;strong&gt;SIB10&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;IPfu&quot;&gt;ETWS — первичное уведомление (Earthquake &amp;amp; Tsunami Warning)&lt;/p&gt;
  &lt;p id=&quot;MJCC&quot;&gt;&lt;strong&gt;SIB11&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;7udv&quot;&gt;ETWS — вторичное уведомление&lt;/p&gt;
  &lt;p id=&quot;gsFi&quot;&gt;&lt;strong&gt;SIB12&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;WhPS&quot;&gt;CMAS — коммерческие предупреждения (Cell Broadcast)&lt;/p&gt;
  &lt;p id=&quot;JZCV&quot;&gt;&lt;strong&gt;SIB13&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;a3oO&quot;&gt;MBMS/MBSFN-конфигурация&lt;/p&gt;
  &lt;p id=&quot;iGBQ&quot;&gt;&lt;strong&gt;SIB14&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;AX3y&quot;&gt;Extended Access Barring (EAB)&lt;/p&gt;
  &lt;p id=&quot;bJB2&quot;&gt;&lt;strong&gt;SIB15&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;V0tN&quot;&gt;MBMS Service Area Info&lt;/p&gt;
  &lt;p id=&quot;DuMz&quot;&gt;&lt;strong&gt;SIB16&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;uNqO&quot;&gt;Информация о времени (GPS/UTC)&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;4&quot;&gt;4. Как это декодируется на практике (концептуальный пайплайн)&lt;/h2&gt;
  &lt;pre id=&quot;A4JK&quot;&gt;textSDR-приёмник (I/Q) → настройка на частоту → синхронизация → демодуляция → L2/L3 декодирование → Wireshark / ASN.1&lt;/pre&gt;
  &lt;h2 id=&quot;gsm-2g&quot;&gt;Инструменты для GSM (2G)&lt;/h2&gt;
  &lt;ul id=&quot;R95X&quot;&gt;
    &lt;li id=&quot;5MoA&quot;&gt;&lt;strong&gt;gr-gsm&lt;/strong&gt; (GNU Radio GSM decoder) — основной инструмент. Компоненты:&lt;/li&gt;
    &lt;ul id=&quot;ZXLZ&quot;&gt;
      &lt;li id=&quot;01gM&quot;&gt;&lt;code&gt;grgsm_scanner&lt;/code&gt; — сканирование эфира, поиск вышек, вывод ARFCN, BSIC, Cell ID&lt;/li&gt;
      &lt;li id=&quot;xGyf&quot;&gt;&lt;code&gt;grgsm_capture&lt;/code&gt; — запись сигнала в .cfile&lt;/li&gt;
      &lt;li id=&quot;NTUX&quot;&gt;&lt;code&gt;grgsm_decode&lt;/code&gt; — декодирование BCCH, SDCCH и др. → GSMTAP в Wireshark&lt;/li&gt;
      &lt;li id=&quot;aj9f&quot;&gt;&lt;code&gt;grgsm_livemon&lt;/code&gt; — интерактивный мониторинг одного канала в реальном времени&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;GIBQ&quot;&gt;&lt;strong&gt;Wireshark&lt;/strong&gt; — анализ декодированных пакетов с фильтром &lt;code&gt;gsmtap&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;jqwn&quot;&gt;&lt;strong&gt;DragonOS&lt;/strong&gt; — готовый Linux-дистрибутив со всем предустановленным&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;BNT1&quot;&gt;(&lt;a href=&quot;https://github.com/ptrkrysik/gr-gsm/wiki/Usage:-Decoding-How-To&quot; target=&quot;_blank&quot;&gt;gr-gsm Wiki: Usage&lt;/a&gt;, &lt;a href=&quot;https://www.rtl-sdr.com/rtl-sdr-tutorial-analyzing-gsm-with-airprobe-and-wireshark/comment-page-1/&quot; target=&quot;_blank&quot;&gt;RTL-SDR Tutorial&lt;/a&gt;)&lt;/p&gt;
  &lt;h2 id=&quot;lte-4g&quot;&gt;Инструменты для LTE (4G)&lt;/h2&gt;
  &lt;ul id=&quot;hgFk&quot;&gt;
    &lt;li id=&quot;gIB0&quot;&gt;&lt;strong&gt;srsRAN&lt;/strong&gt; (бывший srsLTE) — открытый стек 4G/5G. Утилита &lt;code&gt;cell_search&lt;/code&gt; находит соты и получает параметры из MIB; для декодирования SIB нужны специальные receive-only конфигурации/примеры srsRAN, LTE-Cell-Scanner или обёртки вроде lte-sib-parser. &lt;code&gt;srsue&lt;/code&gt; в режиме только приёма может парсить SIB2–SIB6.&lt;/li&gt;
    &lt;li id=&quot;bgOg&quot;&gt;&lt;strong&gt;LTE-Cell-Scanner&lt;/strong&gt; — OpenCL-ускоренный сканер от JiaoXianjun: от I/Q-сэмплов до SIB-сообщений в ASN.1. Поддерживает FDD и TDD, RTL-SDR/HackRF/BladeRF.&lt;/li&gt;
    &lt;li id=&quot;jAFF&quot;&gt;&lt;strong&gt;lte-sib-parser&lt;/strong&gt; — обёртка над cell_search и srsue для рекурсивного сканирования по SIB5.&lt;/li&gt;
    &lt;li id=&quot;T7cP&quot;&gt;&lt;strong&gt;free5GRAN&lt;/strong&gt; — для 5G NR (MIB + SIB1).&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;PiUa&quot;&gt;(&lt;a href=&quot;https://github.com/JiaoXianjun/LTE-Cell-Scanner&quot; target=&quot;_blank&quot;&gt;LTE-Cell-Scanner на GitHub&lt;/a&gt;, &lt;a href=&quot;https://docs.srsran.com/projects/4g/en/latest/app_notes/source/nbiot/source/index.html&quot; target=&quot;_blank&quot;&gt;srsRAN docs&lt;/a&gt;, &lt;a href=&quot;https://github.com/godfuzz3r/lte-sib-parser&quot; target=&quot;_blank&quot;&gt;lte-sib-parser&lt;/a&gt;)&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;5&quot;&gt;&lt;strong&gt;Что это даёт на практике&lt;/strong&gt;&lt;/h2&gt;
  &lt;ul id=&quot;vOM0&quot;&gt;
    &lt;li id=&quot;sOp2&quot;&gt;&lt;strong&gt;Идентификация оператора и страны&lt;/strong&gt; по MCC+MNC.&lt;/li&gt;
    &lt;li id=&quot;NUfU&quot;&gt;&lt;strong&gt;Картирование покрытия&lt;/strong&gt; — зная Cell ID + частоту + уровень сигнала, можно составить карту расположения и охвата базовых станций (без доступа к базам операторов). Сторонние проекты вроде &lt;a href=&quot;https://opencellid.org&quot; target=&quot;_blank&quot;&gt;OpenCelliD&lt;/a&gt; и &lt;a href=&quot;https://cellmapper.net&quot; target=&quot;_blank&quot;&gt;CellMapper&lt;/a&gt; агрегируют именно такие данные.&lt;/li&gt;
    &lt;li id=&quot;PAqV&quot;&gt;&lt;strong&gt;Понимание топологии сети&lt;/strong&gt; — какие соты являются соседями, какие частоты используются в зоне, какие поколения (2G/3G/LTE) присутствуют.&lt;/li&gt;
    &lt;li id=&quot;3Cwh&quot;&gt;&lt;strong&gt;Оценка нагрузки&lt;/strong&gt; — косвенно, по количеству paging-сообщений (но это уже не чисто broadcast, аpaging-канал, требующий чуть более активного прослушивания CCCH).&lt;/li&gt;
    &lt;li id=&quot;lcAw&quot;&gt;&lt;strong&gt;Проверка конфигурации&lt;/strong&gt; — для исследовательских и образовательных целей: какая ширина канала, какие параметры RACH, закрыта ли сота и т.д.&lt;/li&gt;
    &lt;li id=&quot;tQJB&quot;&gt;&lt;strong&gt;Косвенная оценка активности&lt;/strong&gt; — по объёму paging-трафика можно грубо судить о нагрузке, но это не системная информация и не позволяет отслеживать конкретных абонентов.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;6---broadcast&quot;&gt;6. Чего из broadcast-данных получить нельзя&lt;/h2&gt;
  &lt;p id=&quot;HGWU&quot;&gt;Важно зафиксировать границы:&lt;/p&gt;
  &lt;ul id=&quot;feIE&quot;&gt;
    &lt;li id=&quot;GVDG&quot;&gt;&lt;strong&gt;GPS-координаты вышки&lt;/strong&gt; не передаются в эфир. Координаты можно определить только косвенно — по триангуляции сигнала или по внешним базам данных (OpenCelliD).&lt;/li&gt;
    &lt;li id=&quot;RBrj&quot;&gt;&lt;strong&gt;Содержимое звонков, SMS и интернет-трафика&lt;/strong&gt; — не относится к broadcast и может быть зашифровано. В GSM может использоваться A5/0 (без шифрования), A5/1 или A5/3; в LTE — SNOW 3G или AES. Перехват и расшифровка трафика выходят за рамки данной заметки.&lt;/li&gt;
    &lt;li id=&quot;NgN6&quot;&gt;&lt;strong&gt;IMSI абонентов&lt;/strong&gt; не передаётся в broadcast/System Information. В 2G/3G/LTE постоянный идентификатор может появляться в отдельных процедурах идентификации до установления защиты, но это не вещательные данные. В 5G для этого используется SUCI.&lt;/li&gt;
    &lt;li id=&quot;DYr9&quot;&gt;&lt;strong&gt;Точная нагрузка/количество абонентов&lt;/strong&gt; — broadcast не несёт такой информации. Paging-канал может дать лишь грубую оценку совокупной активности сети и не является средством отслеживания конкретных абонентов.&lt;/li&gt;
    &lt;li id=&quot;VE1L&quot;&gt;&lt;strong&gt;Точное местоположение абонента&lt;/strong&gt; — требует данных из ядра сети (core network) или активного слежения за конкретным устройством, что выходит далеко за рамки пассивного декодирования вещательных каналов.&lt;/li&gt;
  &lt;/ul&gt;

</content></entry><entry><id>pole_sam:t8AbMPpD8pM</id><link rel="alternate" type="text/html" href="https://teletype.in/@pole_sam/t8AbMPpD8pM?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=pole_sam"></link><title>POLQA и с чем его едят</title><published>2026-07-18T18:53:47.479Z</published><updated>2026-07-18T18:55:25.883Z</updated><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/89/f5/89f55740-20d0-46f0-846c-51831fb28972.png&quot;&gt;Привет всем! Сегодня постараюсь рассказать о POLQA.
POLQA расшифровывается как Perceptual Objective Listening Quality Analysis. Это full-reference метод: он сравнивает эталонный речевой сигнал с ухудшенной версией после прохождения через сеть, кодек или устройство, а затем вычисляет оценку, близкую к MOS — Mean Opinion Score. Проще говоря, POLQA пытается математически смоделировать то, как человеческое ухо и восприятие оценивают качество речи.</summary><content type="html">
  &lt;p id=&quot;gnUR&quot;&gt;Привет всем! Сегодня постараюсь рассказать о POLQA.&lt;br /&gt;POLQA расшифровывается как &lt;em&gt;Perceptual Objective Listening Quality Analysis&lt;/em&gt;. Это full-reference метод: он сравнивает эталонный речевой сигнал с ухудшенной версией после прохождения через сеть, кодек или устройство, а затем вычисляет оценку, близкую к MOS — Mean Opinion Score. Проще говоря, POLQA пытается математически смоделировать то, как человеческое ухо и восприятие оценивают качество речи.&lt;/p&gt;
  &lt;h3 id=&quot;kHVZ&quot;&gt;&lt;br /&gt;&lt;strong&gt;Зачем он нужен&lt;/strong&gt;&lt;/h3&gt;
  &lt;p id=&quot;dx8b&quot;&gt;Главная ценность POLQA в том, что он помогает измерять качество голоса &lt;strong&gt;воспроизводимо и сравнимо&lt;/strong&gt; между лабораториями, устройствами и сценариями тестирования. Его используют для бенчмаркинга сетей, проверки кодеков, оптимизации VoIP/VoLTE и диагностики проблем с голосом в канале связи. Это особенно важно, когда качество портят не только потери пакетов, но и джиттер, компенсация задержек, time-scaling, шумоподавление и другие современные механизмы обработки.&lt;/p&gt;
  &lt;h3 id=&quot;X2PH&quot;&gt;&lt;br /&gt;Что было раньше&lt;/h3&gt;
  &lt;p id=&quot;42Kf&quot;&gt;До POLQA в инженерной практике использовали разные методы объективной оценки качества речи, включая простые энергетические и шумовые метрики, а также более продвинутые психоакустические модели; однако они либо слабо коррелировали с субъективным восприятием, либо были ограничены узким классом искажений. Затем появился PESQ, который стал важным шагом вперед благодаря моделированию слухового восприятия.&lt;/p&gt;
  &lt;h3 id=&quot;lk6M&quot;&gt;Как возник POLQA&lt;/h3&gt;
  &lt;p id=&quot;OlFK&quot;&gt;&lt;br /&gt;POLQA создавался как ответ на эти ограничения, то есть преемник PESQ, с акцентом на более точное моделирование восприятия речи в условиях современных телекоммуникаций. PESQ был спроектирован в первую очередь для &lt;strong&gt;узкополосной телефонной речи&lt;/strong&gt; и сравнительно стабильных соединений, а не для современных сетей с переменной задержкой, джиттером и более широким частотным диапазоном. PESQ хорошо решал задачу оценки качества для классических телефонных и кодековых сценариев, но в VoIP и IP-сетях сигнал часто сталкивается с &lt;strong&gt;непостоянной задержкой&lt;/strong&gt; и адаптацией jitter buffer, из-за чего точное выравнивание эталонного и тестового сигнала становится сложнее. Кроме того, в новых сетях ухудшения связаны не только с кодированием, но и с пакетными потерями, перепаковкой, временными сдвигами и другими эффектами передачи по IP, которые выходят за рамки более ранней модели применения PESQ&lt;/p&gt;
  &lt;h2 id=&quot;5it9&quot;&gt;История создания&lt;/h2&gt;
  &lt;p id=&quot;ipev&quot;&gt;Работы над POLQA начались в ITU-T Study Group 12 в 2006 году под рабочим названием P.OLQA. В 2009–2010 годах прошёл конкурс кандидатов. Лучшие модели от трёх компаний — &lt;strong&gt;OPTICOM&lt;/strong&gt; (Германия), &lt;strong&gt;SwissQual&lt;/strong&gt; (Rohde &amp;amp; Schwarz, Швейцария) и &lt;strong&gt;TNO&lt;/strong&gt; (Нидерланды) — были объединены в единый алгоритм.&lt;/p&gt;
  &lt;ul id=&quot;U6QE&quot;&gt;
    &lt;li id=&quot;l8up&quot;&gt;Первая редакция — 2011 год.&lt;/li&gt;
    &lt;li id=&quot;Owkn&quot;&gt;Вторая (исправления и улучшения) — 2014 год.&lt;/li&gt;
    &lt;li id=&quot;USzp&quot;&gt;Третья (POLQA v3 / Edition 3) — 2018 год (актуальная), с поддержкой Full Band и оптимизацией под современные сети.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;GmJM&quot;&gt;POLQA разработан специально для новых реалий: VoIP, HD Voice, 3G/4G/VoLTE, 5G, современных кодеков (EVS, Opus и др.).&lt;/p&gt;
  &lt;h3 id=&quot;xBAw&quot;&gt;Основные характеристики&lt;/h3&gt;
  &lt;ul id=&quot;WiMx&quot;&gt;
    &lt;li id=&quot;qXU0&quot;&gt;&lt;strong&gt;Тип алгоритма&lt;/strong&gt;: Full Reference (FR) — требует и reference-сигнал (чистый), и degraded-сигнал (после прохождения через систему).&lt;/li&gt;
    &lt;li id=&quot;z7JH&quot;&gt;&lt;strong&gt;Диапазоны&lt;/strong&gt;:&lt;/li&gt;
    &lt;ul id=&quot;4JQ2&quot;&gt;
      &lt;li id=&quot;pWdB&quot;&gt;Narrowband (NB): 300–3400 Гц (классическая телефония).&lt;/li&gt;
      &lt;li id=&quot;sbBY&quot;&gt;Super-Wideband (SWB): до 14 кГц (HD Voice).&lt;/li&gt;
      &lt;li id=&quot;ckrD&quot;&gt;Full Band (FB, с v3): до 20–24 кГц (48 кГц сэмплирование).&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;N7UG&quot;&gt;&lt;strong&gt;Выход&lt;/strong&gt;: MOS-LQO (Listening Quality Objective) от 1 (плохо) до 5 (отлично). Также дополнительные индикаторы.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;h3 id=&quot;JvLU&quot;&gt;Как работает POLQA (высокоуровнево)&lt;/h3&gt;
  &lt;p id=&quot;Am7n&quot;&gt;Алгоритм состоит из двух основных частей:&lt;/p&gt;
  &lt;ol id=&quot;ONsb&quot;&gt;
    &lt;li id=&quot;pe4n&quot;&gt;&lt;strong&gt;Temporal Alignment (временное выравнивание)&lt;/strong&gt;:&lt;/li&gt;
    &lt;ul id=&quot;Rucq&quot;&gt;
      &lt;li id=&quot;0FKJ&quot;&gt;Решает сложные проблемы VoIP: внезапные скачки задержки (shift-jitter), постепенное time-scaling (растяжение/сжатие речи), packet loss и т.д.&lt;/li&gt;
      &lt;li id=&quot;nkpI&quot;&gt;Использует macro frame, корреляцию, backtracking (аналог Viterbi алгоритма), обработку pitch-preserving техник (типа PSOLA).&lt;/li&gt;
      &lt;li id=&quot;OU2F&quot;&gt;Оценивает sample rate differences и компенсирует их.&lt;/li&gt;
    &lt;/ul&gt;
    &lt;li id=&quot;bNnX&quot;&gt;&lt;strong&gt;Perceptual Model (психоакустическая модель)&lt;/strong&gt;:&lt;/li&gt;
    &lt;ul id=&quot;clVW&quot;&gt;
      &lt;li id=&quot;T2VU&quot;&gt;Моделирует человеческое слуховое восприятие: преобразование в Bark-scale (критические полосы), loudness (Sone), masking effects (частотное и временное).&lt;/li&gt;
      &lt;li id=&quot;s8my&quot;&gt;&lt;strong&gt;Idealization&lt;/strong&gt;: &amp;quot;идеализирует&amp;quot; reference-сигнал (убирает мелкие шумы, корректирует timbre/level), чтобы имитировать Absolute Category Rating (ACR) тесты, где слушатели оценивают без прямого сравнения.&lt;/li&gt;
      &lt;li id=&quot;v0Uq&quot;&gt;Вычисляет disturbance density (плотность искажений) отдельно для additive и subtractive distortion, с учётом сильных и слабых эффектов.&lt;/li&gt;
      &lt;li id=&quot;SrRI&quot;&gt;Cognitive model преобразует perceptibility в annoyance (раздражаемость), учитывая level variations, reverberation, noise и т.д.&lt;/li&gt;
      &lt;li id=&quot;leSV&quot;&gt;Финальный mapping в MOS-LQO с полиномиальной коррекцией.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/ol&gt;

</content></entry></feed>