mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
feat(cat): CAT-порт на каждый слайс + Auto TX (эталон SmartSDR CAT)
Внешняя программа управляет отдельным слайсом как отдельным трансивером
через свой TCP-порт. COM-порты и 19090 остаются за главным приёмником
(слайс A) и не тронуты.
Протокол не дублируется: TCATEngine — чистый парсер поверх записи
callback'ов TCATContext, TCATTcpServer — транспорт поверх движка, поэтому
слайс-CAT = тот же движок с другим контекстом. Новый TCATSliceEndpoint
(движок + TCP-сервер на слот), массивом владеет TCATAdapter. Привязка к
СЛОТУ (буква B..G), не к Id: порт живёт, даже когда слайса нет (PS0).
Auto TX = «Auto Switch TX Slice» из SmartSDR CAT. Вся логика в одной точке
— TRadioController.RequestSliceTx, исполняется в потоке контроллера:
• пока кто-то уже в эфире (любой источник) — заявка игнорируется,
перехвата передачи нет никогда;
• без Auto TX слайс передаёт, только если уже выбран TX-источником;
• RX; снимает только СВОЮ передачу;
• TX-источник после отпускания остаётся на слайсе (как в SmartSDR).
На флаге слайса бейдж TX → AutoTX.
Частота слайса — TuneSliceInBand: свобода в пределах ВКЛЮЧЁННОГО диапазона
(трансвертер → его FreqBegin/FreqEnd, иначе band-план), другой диапазон —
отказ. Вне захваченной полосы окно DDC переезжает: доп. пан — центром на
цель (rfPanFreq → UI перекладывает флаги/шапку/зум-бар), пан 0 — центр
посередине между целью и главным VFO и только если оба влезают (общее
железное окно, главный приёмник не оглушаем).
Настройки: вкладка Slices (enable/порт 19091../Auto TX на слайс), персист
в секции cat + device-blob.
Попутно:
• ZZFL/ZZFH больше не заглушки — новые callback'и кромок фильтра
(у главного nil → прежний дефолт режима), хелпер SignedPad;
• FilterIdxFor, AGCModeToUI, BoardUtils.BandEdges;
• DSP-состояние слайса (NR/NB/SNB/ANF) зеркалится в TCtrlSlice — флаг
слайса рисовал нули и AGC главного;
• FUIReady в TMainForm.OnControllerState: адаптеры, создаваемые по ходу
FormCreate, больше не могут уронить старт рендером до постройки
виджетов (на этом падал Auto TX из настроек).
Проверено вживую: все порты 19091..19096 слушают, PS;/ZZFA;/MD;/ID;/SM0;/
ZZTX; отвечают; старт со всеми включёнными слайсами чист под gdb.
Документ — doc/CAT_SLICES.md.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+24
-9
@@ -82,6 +82,7 @@ type
|
||||
FTx: Boolean; // передача идёт (красный)
|
||||
FTxSel: Boolean; // этот слайс выбран для передачи (янтарный)
|
||||
FRxMuteTx: Boolean; // RXM: приём слайса глушится на передаче (умолч. True)
|
||||
FAutoTx: Boolean; // Auto TX: PTT по CAT-порту слайса сама берёт передачу
|
||||
FSlice: Char; // буква слайса ('A')
|
||||
FSMeterThreshold: Double; // мин. изменение S-метра (дБ) для перерисовки
|
||||
FDMRText: string; // стабильный краткий статус рядом с полосой
|
||||
@@ -205,6 +206,8 @@ type
|
||||
procedure SetSquelchState(AOn: Boolean; ALevel: Integer);
|
||||
procedure SetExtState(AVolume: Integer; AMuted, ASplit, ATx, ATxSel: Boolean);
|
||||
procedure SetRxMuteTxState(AOn: Boolean); // бейдж RXM (self-monitor на TX)
|
||||
// Auto TX слайса (настройка CAT): бейдж TX превращается в AutoTX.
|
||||
procedure SetAutoTxState(AOn: Boolean);
|
||||
procedure SetAudioDevices(Names: TStrings; const Indices: array of Integer;
|
||||
const CurName: string);
|
||||
// Список устройств ВВОДА + текущий выбор ('' = общий вход приложения).
|
||||
@@ -1091,8 +1094,8 @@ end;
|
||||
|
||||
procedure TVfoOverlay.DrawSelf(C: TCanvas; W, H: Integer);
|
||||
var
|
||||
FreqStr, SStr, BWStr, DMRStr: string;
|
||||
CurY, X, RW, i, DMRX, DMRRight, TxRight: Integer;
|
||||
FreqStr, SStr, BWStr, DMRStr, TxStr: string;
|
||||
CurY, X, RW, i, DMRX, DMRRight, TxRight, TxW: Integer;
|
||||
R: TRect;
|
||||
BarL, BarW, MeterX: Integer;
|
||||
begin
|
||||
@@ -1117,6 +1120,11 @@ begin
|
||||
// Кнопка закрытия × — только у неглавных слайсов (главный приёмник не закрыть).
|
||||
// Она крайняя справа, TX и RXM выстраиваются левее неё.
|
||||
TxRight := W - PAD;
|
||||
// Слайс с Auto TX (PTT по своему CAT-порту сама берёт передачу) подписан
|
||||
// AutoTX — бейдж шире, остальные едут левее.
|
||||
TxStr := 'TX';
|
||||
TxW := 26;
|
||||
if FAutoTx and (not FIsMain) then begin TxStr := 'AutoTX'; TxW := 46; end;
|
||||
if not FIsMain then
|
||||
begin
|
||||
R := Rect(W - PAD - 14, CurY, W - PAD, CurY + HDR_BADGE_H);
|
||||
@@ -1126,7 +1134,7 @@ begin
|
||||
// RXM — приём слайса на СВОЕЙ передаче. Подсвечен (умолч.) = глушим, т.е.
|
||||
// себя не слышим; погашен = self-monitor (свой downlink через транспондер).
|
||||
// У главного приёмника то же самое делает кнопка RX MUTE на левой панели.
|
||||
R := Rect(TxRight - 26 - 4 - 32, CurY, TxRight - 26 - 4, CurY + HDR_BADGE_H);
|
||||
R := Rect(TxRight - TxW - 4 - 32, CurY, TxRight - TxW - 4, CurY + HDR_BADGE_H);
|
||||
if FRxMuteTx then DrawBadge(C, R, 'RXM', CLR_ACCENT, CLR_BADGE_TXT)
|
||||
else DrawBadge(C, R, 'RXM', CLR_BTN_NORM, CLR_DIM);
|
||||
RegisterHit(R, HIT_RXMUTE);
|
||||
@@ -1144,7 +1152,7 @@ begin
|
||||
begin
|
||||
DMRStr := FDMRText;
|
||||
DMRX := X + 22 + C.TextWidth(BWStr) + 8;
|
||||
DMRRight := TxRight - 26 - 4 - 32 - 5; // до бейджей RXM, TX и ×
|
||||
DMRRight := TxRight - TxW - 4 - 32 - 5; // до бейджей RXM, TX и ×
|
||||
while (Length(DMRStr) > 1) and
|
||||
(C.TextWidth(DMRStr) > DMRRight - DMRX) do
|
||||
SetLength(DMRStr, Length(DMRStr) - 1);
|
||||
@@ -1153,12 +1161,12 @@ begin
|
||||
C.TextOut(DMRX, CurY + 1, DMRStr);
|
||||
end;
|
||||
// TX badge: a DMR slice is visibly disabled and has no hit target.
|
||||
R := Rect(TxRight - 26, CurY, TxRight, CurY + HDR_BADGE_H);
|
||||
R := Rect(TxRight - TxW, CurY, TxRight, CurY + HDR_BADGE_H);
|
||||
if not TxSelectionAllowed then
|
||||
DrawBadge(C, R, 'TX', CLR_CARD_BG, CLR_DIM)
|
||||
else if FTx then DrawBadge(C, R, 'TX', CLR_TX_ON, clWhite)
|
||||
else if FTxSel then DrawBadge(C, R, 'TX', CLR_TX_SEL, CLR_BADGE_TXT)
|
||||
else DrawBadge(C, R, 'TX', CLR_BTN_NORM, CLR_DIM);
|
||||
DrawBadge(C, R, TxStr, CLR_CARD_BG, CLR_DIM)
|
||||
else if FTx then DrawBadge(C, R, TxStr, CLR_TX_ON, clWhite)
|
||||
else if FTxSel then DrawBadge(C, R, TxStr, CLR_TX_SEL, CLR_BADGE_TXT)
|
||||
else DrawBadge(C, R, TxStr, CLR_BTN_NORM, CLR_DIM);
|
||||
if TxSelectionAllowed then RegisterHit(R, HIT_TX);
|
||||
// SPLIT (левее TX) — кликабельно; только у главного: split = TX на VFO B,
|
||||
// у слайса своего B-VFO нет (кросс-частотный TX делается TX-бейджем слайса).
|
||||
@@ -1653,6 +1661,13 @@ begin
|
||||
RequestInvalidate;
|
||||
end;
|
||||
|
||||
procedure TVfoOverlay.SetAutoTxState(AOn: Boolean);
|
||||
begin
|
||||
if FAutoTx = AOn then Exit;
|
||||
FAutoTx := AOn;
|
||||
RequestInvalidate;
|
||||
end;
|
||||
|
||||
procedure TVfoOverlay.SetSliceInfo(ASliceId: Integer; ALetter: Char; AIsMain: Boolean);
|
||||
begin
|
||||
if (FSliceId = ASliceId) and (FSlice = ALetter) and (FIsMain = AIsMain) then Exit;
|
||||
|
||||
Reference in New Issue
Block a user