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:
+30
-15
@@ -106,6 +106,12 @@ type
|
||||
SetAGCTop: TCATSetIntProc; // AGC-T порог, dB
|
||||
GetCTun: TCATGetBoolFunc; // Click-tune (CTUN) вкл/выкл
|
||||
SetCTun: TCATSetBoolProc;
|
||||
// Кромки фильтра в Гц (ZZFL/ZZFH). Главный приёмник настраивается пресетами
|
||||
// (callback'и nil → команды отвечают дефолтом режима), слайс — кромками.
|
||||
GetFilterLow: TCATGetIntFunc;
|
||||
GetFilterHigh: TCATGetIntFunc;
|
||||
SetFilterLow: TCATSetIntProc;
|
||||
SetFilterHigh: TCATSetIntProc;
|
||||
// Andromeda front panel input (ZZZD/ZZZU/ZZZE/ZZZP/ZZZS)
|
||||
OnPanelVfoStep: TCATSetIntProc; // знаковое число шагов энкодера VFO
|
||||
OnPanelEncoder: TCATPanelEncoderProc; // номер энкодера (0-based) + знаковый шаг
|
||||
@@ -119,6 +125,7 @@ type
|
||||
FLock: TCriticalSection;
|
||||
|
||||
function Pad(V: Int64; W: Integer): string;
|
||||
function SignedPad(V: Int64; W: Integer): string; // '+nnn' / '-nnn'
|
||||
function FreqToStr(Hz: Double): string;
|
||||
function StrToFreq(const S: string): Double;
|
||||
function ModeToK(M: Integer): Integer;
|
||||
@@ -363,6 +370,13 @@ begin
|
||||
if Length(Result) > W then Result := Copy(Result, Length(Result) - W + 1, W);
|
||||
end;
|
||||
|
||||
function TCATEngine.SignedPad(V: Int64; W: Integer): string;
|
||||
// Знаковое поле фиксированной ширины (ZZAR/ZZFL/ZZFH): знак + W цифр.
|
||||
begin
|
||||
if V >= 0 then Result := '+' + Pad(V, W)
|
||||
else Result := '-' + Pad(-V, W);
|
||||
end;
|
||||
|
||||
function TCATEngine.FreqToStr(Hz: Double): string;
|
||||
begin
|
||||
Result := Pad(Round(Hz), 11);
|
||||
@@ -1447,9 +1461,7 @@ begin
|
||||
if Assigned(FCtx.SetAGCTop) then FCtx.SetAGCTop(v);
|
||||
Result := '';
|
||||
end else if Length(s) = 0 then begin
|
||||
v := SafeGetAGCTop;
|
||||
if v >= 0 then Result := '+' + Pad(v, 3)
|
||||
else Result := '-' + Pad(-v, 3);
|
||||
Result := SignedPad(SafeGetAGCTop, 3);
|
||||
end else
|
||||
Result := CAT_ERROR;
|
||||
end;
|
||||
@@ -1653,26 +1665,29 @@ function TCATEngine.ZZFI(const s: string): string;
|
||||
begin Result := ZZBI(s); end;
|
||||
|
||||
function TCATEngine.ZZFL(const s: string): string;
|
||||
// ZZFL: filter low cut (signed Hz, e.g. "-150")
|
||||
// STUB: ewsdr uses indexed filters, not arbitrary Hz cutoffs
|
||||
// ZZFL: filter low cut (signed Hz, e.g. "-0150"). Работает, если контекст умеет
|
||||
// кромки (слайс-CAT); у главного приёмника фильтр индексный — заглушка.
|
||||
begin
|
||||
if Length(s) >= 4 then
|
||||
Result := '' // accept but ignore — STUB
|
||||
else if Length(s) = 0 then begin
|
||||
if SafeGetMode in [MODE_CWL, MODE_CWU] then Result := '-0500'
|
||||
if Length(s) >= 4 then begin
|
||||
if Assigned(FCtx.SetFilterLow) then FCtx.SetFilterLow(StrToIntDef(s, 0));
|
||||
Result := '';
|
||||
end else if Length(s) = 0 then begin
|
||||
if Assigned(FCtx.GetFilterLow) then Result := SignedPad(FCtx.GetFilterLow, 4)
|
||||
else if SafeGetMode in [MODE_CWL, MODE_CWU] then Result := '-0500'
|
||||
else Result := '-1500';
|
||||
end else
|
||||
Result := CAT_ERROR;
|
||||
end;
|
||||
|
||||
function TCATEngine.ZZFH(const s: string): string;
|
||||
// ZZFH: filter high cut (signed Hz)
|
||||
// STUB: ewsdr uses indexed filters, not arbitrary Hz cutoffs
|
||||
// ZZFH: filter high cut (signed Hz) — см. ZZFL.
|
||||
begin
|
||||
if Length(s) >= 4 then
|
||||
Result := '' // accept but ignore — STUB
|
||||
else if Length(s) = 0 then begin
|
||||
if SafeGetMode in [MODE_CWL, MODE_CWU] then Result := '+0500'
|
||||
if Length(s) >= 4 then begin
|
||||
if Assigned(FCtx.SetFilterHigh) then FCtx.SetFilterHigh(StrToIntDef(s, 0));
|
||||
Result := '';
|
||||
end else if Length(s) = 0 then begin
|
||||
if Assigned(FCtx.GetFilterHigh) then Result := SignedPad(FCtx.GetFilterHigh, 4)
|
||||
else if SafeGetMode in [MODE_CWL, MODE_CWU] then Result := '+0500'
|
||||
else Result := '+3000';
|
||||
end else
|
||||
Result := CAT_ERROR;
|
||||
|
||||
Reference in New Issue
Block a user