mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 17:27:32 +00:00
Восемь дефектов, найденных прогоном настоящего TCI-клиента (три приёмника: NFM, DIGU, FMRAW) и его отчётом. 1. UI доп. панорам не перерисовывался: rfSliceState рассылался, но ветки в MainForm.OnControllerState не было (частоту несёт отдельный rfSliceFreq). 2. Stream.length у аудио — сэмплы НА КАНАЛ (§4.3), у IQ — вещественные отсчёты (§3.4: комплексных = length/channels). Было ×каналы везде, у стерео получалось вдвое больше. Развилка в TCIFillHeader + разбор TX-аудио в HandleBinary. 3+4. Дыры в маршрутах аудио движка: demod-тап звался только для DMR/FMRAW (у DIGU не было RX_AUDIO), а пост-громкостный — только для нецифровых (у FMRAW не было LINEOUT). Плюс мьют слайса больше не убивает RX_AUDIO: движку сообщают SetAudioTapsActive. 5. Клиент, поставивший TRX, уходил — MOX оставался. FTrxOwner + StopTxOf; TCIMicRequested снимается и по окончании любой передачи. 6. Гонка снятия IQ-тапа: SetIQTap(nil) возвращался раньше, чем DSP-поток выходил из вызова. FIQTapLock (порядок FSliceLock → FIQTapLock). 7. Рекордер был кольцом «последние N секунд», а §4.3 говорит про МАКСИМАЛЬНОЕ время записи с удалением по истечении. Переделан в линейный буфер с окном по часам от START. 8. TCIServer.HandleClient считал recv = 0 таймаутом: ноль — это EOF, errno при нём не трогается и несёт EAGAIN от прошлого истёкшего TCI_POLL_MS. Обычный TCP-разрыв без close-кадра не освобождал слот до остановки сервера, и после нескольких аварийных отключений новые клиенты упирались в TCI_MAX_CLIENTS. Теперь R = 0 рвёт связь безусловно, errno спрашивается только при R < 0. Попутно: MainForm.RecreateDSPEngine (смена sample rate до START) терял внутренние колбэки контроллера — введён AttachEngineCallbacks. Стенд test/tci заведён в репозиторий (run.sh, 126/126 зелёных, включая сквозной прогон через живой WDSP), доп. проверки на оба пути отключения клиента. doc/TCI.md приведена в соответствие: правило про recv = 0 в §1.1, единицы Stream.length, линейный буфер рекордера. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
48 lines
2.6 KiB
Markdown
48 lines
2.6 KiB
Markdown
# Стенд TCI
|
||
|
||
Прогон:
|
||
|
||
```sh
|
||
test/tci/run.sh
|
||
```
|
||
|
||
Собирает `tcitest.pas` и запускает его; код возврата ненулевой, если хоть одна
|
||
проверка провалена. Внешних библиотек стенд не требует — WebSocket-клиент в нём
|
||
написан на сыром сокете. `libwdsp` нужна только последней части (сквозной
|
||
прогон через движок); без неё эта часть сообщает о пропуске, а остальные идут
|
||
как обычно.
|
||
|
||
Что проверяется — в шапке `tcitest.pas`; чего стенд **не** покрывает —
|
||
в `doc/TCI.md`, §5.
|
||
|
||
Две грабли, из-за которых стенд собирается именно так (обе стоили по часу):
|
||
|
||
* **`-Mobjfpc` обязателен.** С `-Mdelphi` в командной строке выключаются
|
||
вложенные комментарии, и `{$MODE Delphi}` внутри шапки `WebUtils.pas`
|
||
закрывает комментарий раньше времени — компиляция падает на
|
||
«illegal character».
|
||
* **Каталог `.ppu` — свой, с именем не `lib`, и путь абсолютный.**
|
||
Относительное `lib/<cpu>-<os>` компилятор ищет и относительно `-Fu`, то есть
|
||
находит units GUI-сборки в корне проекта, а там `PlatformUtils` собран с LCL:
|
||
стенд падает на линковке с `undefined reference to TC_$FORMS_$$_SCREEN`.
|
||
По той же причине сборка идёт с `-dHEADLESS`, как у демона.
|
||
|
||
Каталоги `units/` и `bin/` — выход сборки, в git не попадают.
|
||
|
||
## Проверка на живом эфире без передачи
|
||
|
||
```sh
|
||
python3 test/tci/live_rx_test.py --host 127.0.0.1 --port 40001
|
||
```
|
||
|
||
Наглядный UI-прогон с автоматическим возвратом к исходным значениям:
|
||
|
||
```sh
|
||
python3 -u test/tci/live_rx_test.py --visible --commands-only --dwell 1.5
|
||
```
|
||
|
||
Стенд проверяет receive-only команды, IQ, аудио демодулятора и
|
||
line-out для всех обнаруженных приёмников. Он никогда не посылает
|
||
`START`, `STOP`, установку `TRX`/`TUNE`, TX-аудио и CW-текст. Если радио
|
||
уже на передаче, прогон аварийно прекращается.
|