fix(tci): передача с слайса — маркер TX_CHRONO под номером клиента, фронт rfTransmitting

Живой прогон с MSHV на втором слайсе (openHPSDR): приём в порядке, эфир по
trx:1,true,tci поднимается, а звук от клиента не доходит. Два независимых
дефекта, оба видны только на приёмнике > 0 — на rx0 передача работала, потому
стенд их и не ловил.

1. Маркеры TX_CHRONO уходили с receiver = 0 жёстко. MSHV шлёт TX-аудио ТОЛЬКО
   в ответ на маркер и фильтрует все входящие бинарные блоки по номеру
   приёмника первой же строкой обработчика (network.cpp: `if
   (pStream->receiver != tci_trx) return;`, ветка TxChrono там же и собирает
   блок). У клиента на слайсе tci_trx = 1, так что маркеры отбрасывались
   целиком. Теперь вместе с клиентом-модулятором запоминается номер приёмника
   из его же TRX (FTxRx), и маркеры идут под ним.

2. Changed(rfTransmitting) из SetTxSlice стирал TCIMicRequested. Контроллер
   шлёт это поле и просто как «перерисуй TX-бейджи», а адаптер понимал любой
   такой сигнал при FTransmitting = false как «передача кончилась». Приходил
   он посередине нашей же команды: SyncSetTRX ставит просьбу → RequestSliceTx
   → SetTxSlice → Changed → просьба стёрта → SetMOX выбирает микрофон уже без
   неё. В эфир шёл микрофон оператора (тишина), а TX-аудио клиента
   отбрасывалось — TCIMicActive не поднят. Теперь ловится фронт «было → стало»
   (FLastTxOn), а не всякое уведомление.

Стенд test/tci: 217 проверок (было 214), все зелёные. Новое — часть E, слайс
как приёмник 1: по trx:1,true,tci модуляция из TCI взята, маркеры TX_CHRONO
идут и названы номером 1. Негативный контроль разделён: каждая правка краснит
свою проверку.

Проверено на железе: MSHV на втором слайсе передаёт.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 21:47:30 +03:00
co-authored by Claude Opus 5
parent 3dc7035f9e
commit ecd42c32f8
3 changed files with 99 additions and 13 deletions
+35 -1
View File
@@ -1566,7 +1566,7 @@ var
Op: Byte;
Pay: TBytes;
H: TTCIStreamHeader;
AudioBlocks, IQBlocks, BadHdr: Integer;
AudioBlocks, IQBlocks, BadHdr, BadRx: Integer;
SliceId: Integer;
SV: TSliceView;
S: string;
@@ -1758,6 +1758,40 @@ begin
Inc(n);
end;
Check('аудио слайса идёт под номером 1', n > 0, IntToStr(n));
// ── ★TX со слайса: маркер обязан нести НОМЕР ЭТОГО приёмника ─────────
// MSHV шлёт TX-аудио только в ответ на маркер TX_CHRONO и отбрасывает
// ЛЮБОЙ входящий блок с чужим receiver (network.cpp:231 — `if
// (pStream->receiver != tci_trx) return;`, ветка TxChrono — network.cpp:288
// и далее). С жёстким нулём в заголовке клиент, сидящий на втором слайсе
// (tci_trx = 1), поднимал эфир и молчал: маркеры до него не доходили, а
// без них он не отправляет ни одного блока. Ровно это и наблюдалось на
// живом железе с MSHV.
Ctrl.FWDSPReady := True;
Ctrl.SetSliceSlotAutoTx(Ctrl.SliceSlotOf(SliceId), True);
C.SendText('trx:1,true,tci;');
C.WaitText('trx:', 1500);
Check('TX слайса: модуляция из TCI взята', Ctrl.TCIMicActive);
C.Pump(400);
n := 0;
BadRx := 0;
while C.NextFrame(Op, Pay) do
if (Op = $02) and (Length(Pay) >= SizeOf(H)) then
begin
Move(Pay[0], H, SizeOf(H));
if H.StreamType = LongWord(Ord(tstTXChrono)) then
begin
Inc(n);
if H.Receiver <> 1 then Inc(BadRx);
end;
end;
Check('TX слайса: маркеры TX_CHRONO идут', n > 0, IntToStr(n));
Check('TX слайса: маркер назван номером приёмника (MSHV фильтрует)',
(n > 0) and (BadRx = 0), IntToStr(BadRx));
C.SendText('trx:1,false;');
C.WaitText('trx:', 1000);
Ctrl.SetSliceSlotAutoTx(Ctrl.SliceSlotOf(SliceId), False);
C.SendText('audio_stop:1;');
C.Pump(200);