mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-26 04:47:35 +00:00
chore: refine project documentation
This commit is contained in:
+25
-27
@@ -71,7 +71,7 @@ const
|
||||
// Панадаптером приёмник быть перестал: у Pluto пан ровно один (MaxPans=1),
|
||||
// и «второй приёмник» там существует только как слайс главного пана — при
|
||||
// нумерации по панам он был бы недоступен вовсе. А клиенты умеют мало
|
||||
// номеров: у MSHV в настройках всего rx1/rx2, то есть приёмники 0 и 1.
|
||||
// номеров: некоторые клиенты позволяют выбрать только приёмники 0 и 1.
|
||||
// Со слотами правило «первый добавленный слайс = приёмник 1» держится на
|
||||
// любом железе: на openHPSDR слайс второго пана и на Pluto слайс главного
|
||||
// одинаково занимают слот B. Пан стал свойством слайса (его центр и IQ).
|
||||
@@ -114,9 +114,9 @@ const
|
||||
TCI_TX_GRACE_MS = 300; // grace на старте: клиент собирает первый блок
|
||||
TCI_TX_MARKERS_PER_TICK = 8; // явный потолок пачки за одно пробуждение
|
||||
TCI_TX_STALL_MAX_MS = 250; // и не дольше этого: ждать больше нечего
|
||||
// ★Аванс под ЗЕРНИСТОСТЬ КЛИЕНТА. Мелкого запроса мало: MSHV пишет свой
|
||||
// TX-буфер granule'ами STREAM_C = 4096 отсчётов при 96 кГц (network.cpp), то
|
||||
// есть 42.7 мс, и отвечает на маркеры ПАЧКАМИ по 4-5 блоков раз в ~44 мс — как
|
||||
// ★Аванс под ЗЕРНИСТОСТЬ КЛИЕНТА. Мелкого запроса мало: клиент может писать
|
||||
// TX-буфер гранулами по 4096 отсчётов при 96 кГц, то есть 42.7 мс, и отвечать
|
||||
// на маркеры ПАЧКАМИ по 4-5 блоков раз в ~44 мс — как
|
||||
// ни дроби запрос. Замер на железе: 218 осушений очереди за 11 с, ровно по
|
||||
// одному на пачку, из них 12 длиннее подушки. Лечится только запасом не меньше
|
||||
// пачки. Это НЕ регулятор по уровню очереди (тот управлял бы темпом запроса,
|
||||
@@ -125,7 +125,7 @@ const
|
||||
TCI_TX_GAP_MAX_US = 200000; // пауза длиннее — это не зернистость, а сбой
|
||||
TCI_TX_LEAD_MAX_MS = 120; // потолок аванса (он же задержка передачи)
|
||||
// ★Посев аванса, пока про клиента ничего не известно. Первая передача после
|
||||
// подключения иначе стартует с pre-roll 31.75 мс против первого ответа MSHV
|
||||
// подключения иначе стартует с pre-roll 31.75 мс до первого ответа клиента
|
||||
// на 41-й миллисекунде — и сохнет. Обучение к этому моменту физически не
|
||||
// успевает: аванс появляется только после первого ответа.
|
||||
TCI_TX_LEAD_DEF_MS = 50;
|
||||
@@ -254,7 +254,7 @@ type
|
||||
// ★Мерим ПЕРИОД между пачками, а не их размер. Размер зависит и от того,
|
||||
// сколько мы запросили: попросили больше — клиент ответил длиннее — аванс
|
||||
// подрос — попросили ещё больше. Это положительная обратная связь. Период
|
||||
// же равен внутренней грануле клиента (у MSHV — STREAM_C/96 кГц = 42.7 мс)
|
||||
// же равен внутренней грануле клиента (например, 4096/96 кГц = 42.7 мс)
|
||||
// и от нашего темпа не зависит вовсе.
|
||||
FTxGapUs: Double; // оценка периода между пачками, мкс
|
||||
FTxLead: Integer; // выданный аванс под зернистость клиента, кадров
|
||||
@@ -857,8 +857,8 @@ begin
|
||||
FServer.Broadcast(TCIBuild('rx_channel_enable',
|
||||
[TCIIntStr(Rx), '1', TCIBoolStr(N > 1)]));
|
||||
// ★С адресом приёмника — иначе клиент его не увидит. Клиенты фильтруют
|
||||
// входящие по arg1 (см. StrTrx), а MSHV на этом ещё и завязывает передачу:
|
||||
// `set_ptt` начинается с `if (!tci_tx_enable) return;`. Слайс, созданный
|
||||
// входящие по arg1 (см. StrTrx), а некоторые также связывают с этим флагом
|
||||
// разрешение передачи. Слайс, созданный
|
||||
// ПОСЛЕ подключения клиента, без этой строки получал бы молча мёртвую PTT.
|
||||
FServer.Broadcast(StrTxEnable(Rx));
|
||||
end;
|
||||
@@ -1195,8 +1195,8 @@ end;
|
||||
|
||||
function TTCIAdapter.StrTrx(Rx: Integer): string;
|
||||
// Состояние передатчика ДЛЯ ПРИЁМНИКА Rx: «true» значит, что в эфире именно
|
||||
// его слайс. Клиенты фильтруют входящие строки по номеру приёмника (MSHV,
|
||||
// например, отбрасывает всё, что адресовано не ему), поэтому автору команды
|
||||
// его слайс. Клиенты могут фильтровать входящие строки по номеру приёмника,
|
||||
// поэтому автору команды
|
||||
// отвечаем ЕГО номером: `trx:1,false` — это «твоя заявка не прошла», а
|
||||
// `trx:0,false` он бы просто не увидел. В рассылку уходит номер того, чей
|
||||
// слайс сейчас источник передачи (`TxRx`).
|
||||
@@ -1309,8 +1309,7 @@ end;
|
||||
procedure TTCIAdapter.BroadcastTxEnable;
|
||||
// TX_ENABLE — величина всего радио, но адресуется приёмником, и клиент читает
|
||||
// только строки со СВОИМ номером. Одной строки `tx_enable:0,…` мало: клиент на
|
||||
// приёмнике 1 её отбрасывает, а у MSHV на этом флаге висит вся передача
|
||||
// (`set_ptt`: `if (!tci_tx_enable) return;`).
|
||||
// приёмнике 1 может её отбросить и не разрешить передачу.
|
||||
var Rx: Integer;
|
||||
begin
|
||||
if (FServer = nil) or (FServer.ClientCount = 0) then Exit;
|
||||
@@ -1830,8 +1829,7 @@ begin
|
||||
if (Ch < 0) or (Ch >= TCI_CHANNELS) then Exit;
|
||||
// ★Про приёмник, которого нет (слайс не создан или уже удалён), молчим
|
||||
// целиком — и на чтение тоже. Ответ «vfo:1,0,0» клиент принял бы за
|
||||
// настоящую частоту, а MSHV именно этим ответом завершает инициализацию
|
||||
// (network.cpp, Network::initAll) — то есть подключился бы к пустоте.
|
||||
// настоящую частоту и завершить инициализацию несуществующего приёмника.
|
||||
if (Ch >= ChanCount(Rx)) or not RxActive(Rx) then Exit;
|
||||
|
||||
// Частота ставится, только если её удалось разобрать И она годная: «vfo:0,0,abc»
|
||||
@@ -2157,7 +2155,7 @@ begin
|
||||
begin
|
||||
// arg3 — источник сигнала (только у TRX). Наш только 'tci': модуляция
|
||||
// берётся из аудиопотока этого клиента. Остальные значения
|
||||
// (mic1/mic2/micpc/ecoder2) называют физические входы ExpertSDR3, которых
|
||||
// (mic1/mic2/micpc/ecoder2) называют физические входы TCI, которых
|
||||
// у нас нет, — они значат «микрофон, выбранный в программе», то есть ровно
|
||||
// то, что и без arg3. Требование «включен аудиопоток по TCI» (§4.2)
|
||||
// проверяем буквально: без AUDIO_START модулировать нечем, и молча
|
||||
@@ -2196,8 +2194,8 @@ begin
|
||||
// ничего не изменила, всё равно её перебивала: клиент, уже
|
||||
// передававший с приёмника 1, присылает trx:0,true,tci — передатчик
|
||||
// занят, SyncSetTRX не делает ничего, а маркеры его же идущей передачи
|
||||
// с этого мига уходят под номером 0. MSHV такие блоки отбрасывает
|
||||
// (network.cpp:231), то есть передача просто замолкает.
|
||||
// с этого мига уходят под номером 0. Клиент второго приёмника может
|
||||
// отбросить такие блоки, и передача просто замолкнет.
|
||||
if B then
|
||||
begin
|
||||
if Started and FromTCI then
|
||||
@@ -2236,7 +2234,7 @@ begin
|
||||
FTxRx := 0;
|
||||
// ★Тот же инвариант, что и в ClearTxClient: нет клиента — нет и
|
||||
// идущей передачи. Раньше эти две строки расходились, и штатный
|
||||
// конец посылки (trx:N,false у MSHV) оставлял FTxRunning поднятым.
|
||||
// конец посылки (`trx:N,false`) оставлял FTxRunning поднятым.
|
||||
FTxRunning := False;
|
||||
end;
|
||||
finally FTxLock.Leave; end;
|
||||
@@ -2789,7 +2787,7 @@ begin
|
||||
if TCITryArgInt(M, 0, V) and TCIValidAudioRate(V) and (V <> Client.AudioRate) then
|
||||
begin
|
||||
Client.AudioRate := V;
|
||||
// Число сэмплов в блоке у ExpertSDR3 своё на каждую частоту (§4.3), и
|
||||
// Число сэмплов в блоке у TCI своё на каждую частоту (§4.3), и
|
||||
// клиент вправе на это рассчитывать, пока не задал своё явно.
|
||||
Client.AudioSamples := TCIDefaultAudioSamples(V);
|
||||
RestartStreams(Client, tstRXAudio);
|
||||
@@ -3740,7 +3738,8 @@ end;
|
||||
procedure TTCIAdapter.TxPreWarm(Client: TTCIClient; Rx: Integer);
|
||||
// Поток клиента, ДО SetMOX. Один маркер вперёд всей подготовки тракта.
|
||||
//
|
||||
// ★Зачем: MSHV отвечает на первый маркер только через ~41 мс, а сам маркер
|
||||
// ★Зачем: некоторые клиенты отвечают на первый маркер только через ~41 мс,
|
||||
// а сам маркер
|
||||
// уходил лишь после того, как SetMOX отработает реле, pre-roll и PureSignal —
|
||||
// ещё 9-17 мс. Эти два ожидания шли последовательно, и нулевого pre-roll не
|
||||
// хватало: очередь DUC сохла на старте КАЖДОЙ первой передачи. Отправив запрос
|
||||
@@ -4001,9 +4000,8 @@ begin
|
||||
(Sent < TCI_TX_MARKERS_PER_TICK) do
|
||||
begin
|
||||
// ★Номер приёмника — ТОТ, которым назвался клиент в TRX, а не 0.
|
||||
// Клиент фильтрует ВХОДЯЩИЕ БИНАРНЫЕ блоки по receiver (MSHV,
|
||||
// network.cpp:231: `if (pStream->receiver != tci_trx) return;`), а
|
||||
// TX-аудио шлёт ровно в ответ на этот маркер (там же, ветка TxChrono).
|
||||
// Клиент может фильтровать ВХОДЯЩИЕ БИНАРНЫЕ блоки по receiver, а
|
||||
// TX-аудио отправлять только в ответ на этот маркер.
|
||||
// С нулём клиент на втором слайсе (tci_trx = 1) поднимал эфир и молчал:
|
||||
// маркеры до него не доходили вовсе.
|
||||
TCIFillHeader(H, tstTXChrono, Rx, Rate, ST, Q, Chans);
|
||||
@@ -4069,8 +4067,8 @@ begin
|
||||
// ровно то, сколько их и распаковалось. Верим меньшему из двух: клиент
|
||||
// вправе прислать короткий хвост, но не длиннее уместившегося. Заведомо
|
||||
// чужое число (мусор в заголовке) просто игнорируем — длину нам и так
|
||||
// ограничил размер кадра. Так же считает и MSHV, когда сам заполняет
|
||||
// заголовок: `t_txStream->length = (cr3/bit_s)`, где cr3 — байты блока.
|
||||
// ограничил размер кадра. Значение заголовка задаётся числом распакованных
|
||||
// отсчётов, а не числом байт блока.
|
||||
if H.DataLength > 0 then
|
||||
begin
|
||||
Want := Integer(H.DataLength);
|
||||
@@ -4103,8 +4101,8 @@ begin
|
||||
// Долгу разрешено уйти в минус — клиент вправе прислать больше, чем
|
||||
// просили, и зажим в ноль заставил бы переспросить уже полученное.
|
||||
// Прогресс отмечаем по САМОМУ ФАКТУ валидного блока, независимо от значений
|
||||
// отсчётов: первые ответы MSHV — законные нули (network.cpp, ветка
|
||||
// _reset_sta_ <= 5), и сторож не должен считать их отсутствием прогресса.
|
||||
// отсчётов: первые ответы клиента могут быть законными нулями, и сторож не
|
||||
// должен считать их отсутствием прогресса.
|
||||
NowUs := MonotonicUs;
|
||||
// Границы пачки: клиент отвечает не на каждый маркер по отдельности, а
|
||||
// очередями по нескольку блоков — по своей внутренней зернистости записи.
|
||||
|
||||
Reference in New Issue
Block a user