Потолок 128 МБ всё ещё пробивался примерно вдвое на пике: Take собирал
линейную копию ДО освобождения FChunks, поэтому при полном бюджете рядом
жили ~128 МБ кусков и ~128 МБ копии, а счётчик показывал 128. Передача
резерва писателю (9b77809) закрывала учёт, но не сам пик.
Копии больше нет вовсе. Take отдаёт куски КАК ЕСТЬ — новый TTCIRecTake
(Chunks + Chunk + Count + Reserved), — и вместе с ними уезжает их место в
бюджете целиком. TTCIWavWriter пишет куски в файл подряд: в WAV сэмплы и
так лежат встык, а хвост последнего куска за Count просто не наш. Место
отпускается в ReleaseData, ровно один раз на любом пути выхода Execute.
Продовый путь теперь не выделяет под запись ни одного лишнего байта:
TTCIPcm остался только внутри куска. Склейка нужна одному стенду, чтобы
проверять порядок и уровень, — она и живёт в стенде (FlatTake).
Стенд 199/199: 2.5 с записи отдаются ТРЕМЯ кусками по секунде (свёрнутая
копия дала бы один), счёт бюджета при Take не меняется, резерва хватает
на отданные куски, склеенные куски дают непрерывный звук, писатель
возвращает резерв по окончании. Проверено, что копирующая реализация Take
краснит четыре проверки. GUI (--ws=qt6) и демон зелёные.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Стенд TCI
Прогон:
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 не попадают.
Проверка на живом эфире без передачи
python3 test/tci/live_rx_test.py --host 127.0.0.1 --port 40001
Наглядный UI-прогон с автоматическим возвратом к исходным значениям:
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-текст. Если радио
уже на передаче, прогон аварийно прекращается.
Стенд «как MSHV»
test/tci/mshv_sim.py --rx 0 # в MSHV это «TCI Client rx1»
test/tci/mshv_sim.py --rx 1 --audio # «TCI Client rx2»
mshv_sim.py повторяет логику TCI-клиента MSHV по его исходникам — включая обе
особенности, из-за которых он ведёт себя не так, как наш стенд:
- фильтр по номеру приёмника —
if (ls2.at(0)!=tci_trx) continue;: строка, адресованная не его приёмнику, для MSHV не существует (и то же самое для бинарных блоков, по полюreceiverзаголовка); - инициализация — один запрос и один ответ: он шлёт
vfo:<rx>,0;раз в 500 мс, максимум пять раз, и ждётvfo:<rx>,0,<частота>. Не дождался — «Error To Initialize TCI Server». Других причин у этой ошибки нет.
Поэтому стенд отвечает на вопрос «почему MSHV не подключается» одной строкой, а
заодно ловит то, что иначе проявляется молча: без tx_enable со своим
номером приёмника у MSHV не работает PTT (set_ptt начинается с
if (!tci_tx_enable) return;).
По умолчанию стенд безопасен на приём: ни TRX, ни TUNE, ни START/STOP,
ни TX-аудио он не шлёт. Передачу включает явный --tx.