feat(cw): телеграф — FPGA-кейер, pitch, сайдтон, передача текста

CW не работал ни на приём, ни на передачу: фильтр стоял симметрично вокруг
нуля (тона не было), понятия pitch не существовало, MOX в CWL отправлял в
эфир голос 2.9 кГц (в WDSP TXA_CWL — ветка SSB), байт CW-опций DUC всегда
был нулевым, поэтому кейер, сайдтон и break-in в прошивке молчали.

Pitch сделан ОДНИМ сдвигом в движке. Кромки фильтра везде остаются
относительно VFO (у CW — симметрично нулю), а гетеродин и полосу двигают
три метода WDSPEngine: CWLOOffset / PushShift / PushPassband. Прямых
вызовов SetRXAShiftFreq и RXASetPassband в проекте больше нет. Так таблица
фильтров не перестраивается при смене pitch (в отличие от Thetis), полоска
на спектре и маркер VFO верны без правок в UI, а слайсы получают свой
сдвиг по своему режиму.

Правила эфира:
- голосовой TXA в CW не запускается вообще (гард по режиму), MOX = только
  PTT; несущую даёт кейер, бит CWX или тон TUN;
- байт 5 собирается из настроек и перепосылается на каждой смене режима и
  TX-слайса — вне CW он обязан быть нулевым, иначе прошивка поднимет PTT на
  замыкание ключа посреди SSB;
- TUN в CW: тон = pitch и встречный сдвиг DUC, несущая встаёт ровно на VFO;
- приёмник на передаче в CW не глушится, иначе умолкает программный
  сайдтон и эфир между посылками.

Программная передача текста (CWMorse): поток с абсолютными дедлайнами
дёргает бит CWX в High Priority, элементы рисует прошивка. Касание
манипулятора или снятие MOX обрывают передачу. Память сообщений — окно
CW Messages и F1..F8 в главном окне. Оживлены CAT KS/KY/ZZKM/ZZKS/ZZKY.

Настройки — вкладка Transmit, подвкладка Hardware, перед блоком FM/CTCSS.

Программный сайдтон точен для передачи текста и прямого ключа; при иамбике
таймингом владеет FPGA, поэтому там верен только аппаратный тон.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 12:20:56 +03:00
co-authored by Claude Opus 5
parent 0ec120c2d5
commit 53329e82e8
12 changed files with 1421 additions and 46 deletions
+58
View File
@@ -38,6 +38,7 @@ type
FSyncFreq: Double;
FSyncInt: Integer;
FSyncBool: Boolean;
FSyncStr: string;
// ── Геттеры (CAT-поток, read-only) ──
function GetVfoA: Double;
@@ -86,6 +87,15 @@ type
procedure SetSNB(V: Boolean);
procedure SetANF(V: Boolean);
procedure SetTransmitting(V: Boolean);
function GetCWSpeed: Integer;
procedure SetCWSpeed(V: Integer);
function GetCWKeyerMode: Integer;
procedure SetCWKeyerMode(V: Integer);
procedure SendCWText(const S: string);
function GetCWTextBusy: Boolean;
procedure SyncCWSpeed;
procedure SyncCWKeyerMode;
procedure SyncCWText;
procedure DoBandUp;
procedure DoBandDown;
procedure DoTuneUp;
@@ -217,6 +227,13 @@ begin
Ctx.SetAGCTop := @SetAGCTop;
Ctx.GetCTun := @GetCTun;
Ctx.SetCTun := @SetCTun;
// Телеграф: скорость/режим кейера и программная передача текста (KY/ZZKY).
Ctx.GetCWSpeed := @GetCWSpeed;
Ctx.SetCWSpeed := @SetCWSpeed;
Ctx.GetCWKeyerMode := @GetCWKeyerMode;
Ctx.SetCWKeyerMode := @SetCWKeyerMode;
Ctx.SendCWText := @SendCWText;
Ctx.CWTextBusy := @GetCWTextBusy;
Ctx.OnPanelVfoStep := @PanelVfoStep;
Ctx.OnPanelEncoder := @PanelEncoder;
Ctx.OnPanelButton := @PanelButton;
@@ -418,6 +435,47 @@ begin FController.SetANF(FSyncBool); end;
procedure TCATAdapter.SetTransmitting(V: Boolean);
begin FSyncBool := V; FController.Invoke(@SyncMOX); end;
// ---- Телеграф (KS/KY/ZZKM/ZZKS/ZZKY) --------------------------------------
// Правки настроек и запуск передачи маршалим в поток контроллера, как всё
// остальное в адаптере: SetCWSettings трогает движок и пишет конфиг.
function TCATAdapter.GetCWSpeed: Integer;
begin Result := FController.CWSettings.Speed; end;
procedure TCATAdapter.SetCWSpeed(V: Integer);
begin FSyncInt := V; FController.Invoke(@SyncCWSpeed); end;
procedure TCATAdapter.SyncCWSpeed;
var C: TCWSettings;
begin
C := FController.CWSettings;
C.Speed := FSyncInt;
FController.SetCWSettings(C);
end;
function TCATAdapter.GetCWKeyerMode: Integer;
begin Result := FController.CWSettings.KeyerMode; end;
procedure TCATAdapter.SetCWKeyerMode(V: Integer);
begin FSyncInt := V; FController.Invoke(@SyncCWKeyerMode); end;
procedure TCATAdapter.SyncCWKeyerMode;
var C: TCWSettings;
begin
C := FController.CWSettings;
C.KeyerMode := FSyncInt;
FController.SetCWSettings(C);
end;
procedure TCATAdapter.SendCWText(const S: string);
begin FSyncStr := S; FController.Invoke(@SyncCWText); end;
procedure TCATAdapter.SyncCWText;
begin FController.CWXSend(FSyncStr); end;
function TCATAdapter.GetCWTextBusy: Boolean;
begin Result := FController.CWXBusy; end;
procedure TCATAdapter.SyncMOX;
begin FController.SetMOX(FSyncBool); end;