mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 18:43:51 +00:00
feat(tci): приёмник = слот слайса; TRX/TUNE адресуют передатчик; стенд «как MSHV»
Модель приёмников переделана: приёмник TCI — это СЛОТ СЛАЙСА, а не панадаптер.
rx0 — главный тракт (каналы A/B = VFO A/B), rx N — слайс слота N−1, то есть
буквы B..G с флага, на каком бы пане он ни стоял. Панорама стала свойством
приёмника: от неё берутся DDS (у слайса только чтение) и поток IQ.
Почему: у Pluto панорама ровно одна (MaxPans = 1), и второй приёмник там
существует ТОЛЬКО как слайс главного пана — при нумерации по панам он был
недоступен вовсе, а TRX_COUNT навсегда равнялся единице. С другого конца —
клиенты: у MSHV в настройках всего «TCI Client rx1/rx2», то есть приёмники 0 и
1, третьего номера ввести некуда. Со слотами правило «первый созданный слайс =
приёмник 1» держится на любом железе: на openHPSDR слайс второго пана и на
Pluto слайс главного одинаково занимают слот B. Номер совпадает с буквой на
экране и с портом слайс-CAT. Цена: панорама без слайсов из TCI пропала, а
второй слайс пана перестал быть «каналом B» и стал своим приёмником — у канала
B в протоколе только частота, IF и громкость, у приёмника же всё.
TRX/TUNE раньше игнорировали arg1 (номер передатчика) целиком: клиент доп.
приёмника уводил в эфир слайс ОПЕРАТОРА — чужая частота, а с кросс-бандовым
мультислайс-TX и чужой диапазон, с чужими антенной и фильтрами; trx:9,true жал
PTT. Теперь номер разбирается и проверяется, приёмник N > 0 идёт через
RequestSliceTx (та же дверь, что у CAT-порта слайса: «в эфире только один» и
Auto TX), у контроллера появился параметр Tune для TUN тем же путём. Чужую
передачу не трогаем вовсе — ни источник модуляции, ни тон: SetMOX(True) поверх
идущей передачи не выходит рано, а заново выбирает микрофон. Хозяином эфира
клиент становится, только если передача началась именно от его команды, и
решает это Sync-метод в потоке контроллера (снимок «шла ли передача», взятый в
потоке клиента, врал: между разбором и исполнением влезает PTT оператора).
Разбор исходников MSHV (он фильтрует ВСЕ строки и бинарные блоки по номеру
приёмника) дал ещё три правки:
* ответ на TRX/TUNE адресуется номером АВТОРА, состояние в нём — «в эфире
именно твой слайс»; в рассылку идёт номер реально передающего;
* tx_enable рассылается каждому живому приёмнику со своим номером и входит в
картину нового приёмника — без этого у MSHV молча мёртвая PTT
(set_ptt начинается с `if (!tci_tx_enable) return;`);
* про несуществующий приёмник молчим целиком (LiveRx), в том числе на чтение:
ответ «vfo:1,0,0» MSHV принимал бы за конец инициализации.
Попутно, вне TCI: SendDUCSpecificFromSettings трогала FNetwork без Assigned, а
зовут её по любому PTT/TUN (SetMOX → SyncCWKeyer → она) — до подключения
устройства это была Access violation, у TCI её глотал обработчик команды.
Стенды: новый test/tci/mshv_sim.py — точная копия логики клиента MSHV, отвечает
на вопрос «почему он не подключается» одной строкой (на живом приложении
воспроизвёл ошибку инициализации для rx2 до правки). tcitest — 158/158, в
сквозном прогоне добавлено создание слайса на главном пане: он становится
приёмником 1, отвечает на vfo:1,0, слушается командой, отдаёт аудио с
receiver = 1 и замолкает после удаления.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+22
-12
@@ -958,10 +958,13 @@ type
|
||||
function SliceSlotOutDev(Slot: Integer): string;
|
||||
function SliceSlotInDev(Slot: Integer): string;
|
||||
procedure SetSliceSlotAudio(Slot: Integer; const OutDev, InDev: string);
|
||||
// Заявка на передачу ОТ ИМЕНИ слайса (CAT-порт слайса). Единственная точка,
|
||||
// где внешняя PTT может сменить TX-источник, поэтому здесь же — защита
|
||||
// «в эфире одновременно только один слайс». Выполнять в потоке контроллера.
|
||||
procedure RequestSliceTx(SliceId: Integer; On_: Boolean);
|
||||
// Заявка на передачу ОТ ИМЕНИ слайса (CAT-порт слайса, TCI с номером
|
||||
// приёмника). Единственная точка, где внешняя PTT может сменить TX-источник,
|
||||
// поэтому здесь же — защита «в эфире одновременно только один слайс».
|
||||
// Tune = True — то же самое, но несущей настройки (TUN) вместо модуляции.
|
||||
// Выполнять в потоке контроллера.
|
||||
procedure RequestSliceTx(SliceId: Integer; On_: Boolean;
|
||||
Tune: Boolean = False);
|
||||
// Диапазон захваченной полосы пана (0 = главный), куда можно ставить слайс.
|
||||
procedure CapturedSpan(out LoHz, HiHz: Double; PanId: Integer = 0);
|
||||
function SliceFitsCapture(TargetHz: Double; PanId: Integer = 0): Boolean;
|
||||
@@ -3313,11 +3316,13 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TRadioController.RequestSliceTx(SliceId: Integer; On_: Boolean);
|
||||
// PTT, пришедшая по CAT-порту слайса. Аналог «Auto Switch TX Slice» в SmartSDR
|
||||
// CAT: с Auto TX слайс сам становится TX-источником, без него — передаёт,
|
||||
// только если оператор уже выбрал его источником (бейдж TX на флаге).
|
||||
// Выполняется в потоке контроллера (CAT маршалит через Invoke), поэтому
|
||||
procedure TRadioController.RequestSliceTx(SliceId: Integer; On_: Boolean;
|
||||
Tune: Boolean = False);
|
||||
// PTT, пришедшая по CAT-порту слайса или по TCI с номером приёмника. Аналог
|
||||
// «Auto Switch TX Slice» в SmartSDR CAT: с Auto TX слайс сам становится
|
||||
// TX-источником, без него — передаёт, только если оператор уже выбрал его
|
||||
// источником (бейдж TX на флаге).
|
||||
// Выполняется в потоке контроллера (CAT и TCI маршалят через Invoke), поэтому
|
||||
// проверка «уже в эфире» и смена источника атомарны относительно GUI.
|
||||
var idx: Integer;
|
||||
begin
|
||||
@@ -3347,7 +3352,7 @@ begin
|
||||
SetTxSlice(SliceId);
|
||||
if FTxSliceId <> SliceId then Exit; // SetTxSlice отказал (DMR/нет слайса)
|
||||
end;
|
||||
SetMOX(True);
|
||||
if Tune then SetTune(True) else SetMOX(True);
|
||||
end;
|
||||
|
||||
// Снимок кадра для web-зеркала: потолок точек задаёт настройка web.spec_pixels
|
||||
@@ -5836,7 +5841,12 @@ var
|
||||
RateKsps: Word;
|
||||
HangMs: Word;
|
||||
begin
|
||||
if not (FNetwork.Connected and FNetwork.Running) then Exit;
|
||||
// Assigned обязателен: сюда приходят и по PTT/TUN, а нажать их можно и до
|
||||
// того, как устройство выбрано, — тогда FNetwork ещё nil (то же и с движком
|
||||
// ниже). Раньше на этом месте была Access violation, и разбирать её приходил
|
||||
// уже кто-то другой: SetMOX зовёт SyncCWKeyer, а тот — эту процедуру.
|
||||
if not (Assigned(FNetwork) and FNetwork.Connected and FNetwork.Running) then Exit;
|
||||
if not (FWDSPReady and Assigned(FDSPEngine)) then Exit;
|
||||
FillChar(DUCPkt, SizeOf(DUCPkt), 0);
|
||||
// DUC0 rate в ksps берём из WDSP-движка (фиксированный TX rate, не RX).
|
||||
RateKsps := FDSPEngine.TXSampleRate div 1000;
|
||||
@@ -5938,7 +5948,7 @@ begin
|
||||
FDriveLevel := CalcDriveByte;
|
||||
if FWDSPReady then
|
||||
FDSPEngine.SetDriveLevel(EnsureRange(FTXSettings.TUNLevel, 0, 100) / 100.0);
|
||||
if FNetwork.Connected and FNetwork.Running then
|
||||
if Assigned(FNetwork) and FNetwork.Connected and FNetwork.Running then
|
||||
begin
|
||||
FNetwork.UpdateState(XvtrTranslate(FCenterFreq), TXTuneFreqHz, FDriveLevel,
|
||||
FTransmitting, True, True, FTuning, FTwoTone);
|
||||
|
||||
Reference in New Issue
Block a user