mirror of
https://git.vladimir.cc/vladimir/ewsdr.git
synced 2026-08-25 20:37:33 +00:00
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:
+54
-12
@@ -112,6 +112,14 @@ type
|
||||
GetFilterHigh: TCATGetIntFunc;
|
||||
SetFilterLow: TCATSetIntProc;
|
||||
SetFilterHigh: TCATSetIntProc;
|
||||
// Телеграф (KS/KY/ZZKS/ZZKY/ZZKM). Скорость — WPM 5..60; SendCWText ставит
|
||||
// текст в очередь программной передачи, CWTextBusy = «очередь занята».
|
||||
GetCWSpeed: TCATGetIntFunc;
|
||||
SetCWSpeed: TCATSetIntProc;
|
||||
GetCWKeyerMode: TCATGetIntFunc; // 0=прямой ключ, 1=iambic A, 2=iambic B
|
||||
SetCWKeyerMode: TCATSetIntProc;
|
||||
SendCWText: procedure(const S: string) of object;
|
||||
CWTextBusy: TCATGetBoolFunc;
|
||||
// Andromeda front panel input (ZZZD/ZZZU/ZZZE/ZZZP/ZZZS)
|
||||
OnPanelVfoStep: TCATSetIntProc; // знаковое число шагов энкодера VFO
|
||||
OnPanelEncoder: TCATPanelEncoderProc; // номер энкодера (0-based) + знаковый шаг
|
||||
@@ -136,6 +144,7 @@ type
|
||||
function SafeGetVfoA: Double;
|
||||
function SafeGetVfoB: Double;
|
||||
function SafeGetMode: Integer;
|
||||
function SafeGetCWSpeed: Integer;
|
||||
function SafeGetVolume: Integer;
|
||||
function SafeGetDriveLevel: Integer;
|
||||
function SafeGetFilterIdx: Integer;
|
||||
@@ -812,21 +821,43 @@ begin
|
||||
Result := freq + step + incr + rit + xit + '000' + tx + mode + '0' + '0' + split + '0000';
|
||||
end;
|
||||
|
||||
function TCATEngine.SafeGetCWSpeed: Integer;
|
||||
begin
|
||||
if Assigned(FCtx.GetCWSpeed) then Result := FCtx.GetCWSpeed else Result := 20;
|
||||
end;
|
||||
|
||||
function TCATEngine.CmdKS(const s: string): string;
|
||||
// KS: CW keying speed (3 digits, WPM) — STUB: no CW keyer
|
||||
// KS: CW keying speed (3 digits, WPM).
|
||||
var v: Integer;
|
||||
begin
|
||||
if Length(s) = 3 then
|
||||
Result := '' // accepted but ignored
|
||||
begin
|
||||
if not TryStrToInt(s, v) then Exit(CAT_ERROR);
|
||||
if Assigned(FCtx.SetCWSpeed) then FCtx.SetCWSpeed(v);
|
||||
Result := '';
|
||||
end
|
||||
else if Length(s) = 0 then
|
||||
Result := '025' // default 25 WPM
|
||||
Result := Format('%.3d', [SafeGetCWSpeed])
|
||||
else
|
||||
Result := CAT_ERROR;
|
||||
end;
|
||||
|
||||
function TCATEngine.CmdKY(const s: string): string;
|
||||
// KY: send CW text — STUB: no CW keyer implemented
|
||||
// KY: передать текст телеграфом. Kenwood-семантика: первый символ — пробел-
|
||||
// заполнитель признака «не занято», далее сам текст; пустой аргумент = опрос
|
||||
// занятости ('1' — очередь занята, логгер ждёт).
|
||||
var Txt: string;
|
||||
begin
|
||||
Result := '0'; // queue not full
|
||||
if Length(s) = 0 then
|
||||
begin
|
||||
if Assigned(FCtx.CWTextBusy) and FCtx.CWTextBusy then Result := '1'
|
||||
else Result := '0';
|
||||
Exit;
|
||||
end;
|
||||
Txt := s;
|
||||
if (Length(Txt) > 0) and (Txt[1] = ' ') then Delete(Txt, 1, 1);
|
||||
if Assigned(FCtx.SendCWText) then FCtx.SendCWText(Txt);
|
||||
Result := '';
|
||||
end;
|
||||
|
||||
function TCATEngine.CmdMD(const s: string): string;
|
||||
@@ -1754,23 +1785,34 @@ begin
|
||||
end;
|
||||
|
||||
function TCATEngine.ZZKM(const s: string): string;
|
||||
// ZZKM: CW key mode — STUB: no CW keyer
|
||||
// ZZKM: режим кейера (0=прямой ключ, 1=iambic A, 2=iambic B).
|
||||
var v: Integer;
|
||||
begin
|
||||
if Length(s) = 1 then Result := '' else if Length(s) = 0 then Result := '0'
|
||||
if Length(s) = 1 then
|
||||
begin
|
||||
if not TryStrToInt(s, v) then Exit(CAT_ERROR);
|
||||
if (v < 0) or (v > 2) then Exit(CAT_ERROR);
|
||||
if Assigned(FCtx.SetCWKeyerMode) then FCtx.SetCWKeyerMode(v);
|
||||
Result := '';
|
||||
end
|
||||
else if Length(s) = 0 then
|
||||
begin
|
||||
if Assigned(FCtx.GetCWKeyerMode) then Result := IntToStr(FCtx.GetCWKeyerMode)
|
||||
else Result := '0';
|
||||
end
|
||||
else Result := CAT_ERROR;
|
||||
end;
|
||||
|
||||
function TCATEngine.ZZKS(const s: string): string;
|
||||
// ZZKS: CW speed (WPM) — STUB: no CW keyer
|
||||
// ZZKS: скорость кейера, WPM.
|
||||
begin
|
||||
if Length(s) = 3 then Result := '' else if Length(s) = 0 then Result := '025'
|
||||
else Result := CAT_ERROR;
|
||||
Result := CmdKS(s);
|
||||
end;
|
||||
|
||||
function TCATEngine.ZZKY(const s: string): string;
|
||||
// ZZKY: CW text to send — STUB: no CW keyer implemented
|
||||
// ZZKY: передать текст телеграфом (то же, что KY).
|
||||
begin
|
||||
Result := '0'; // buffer not full
|
||||
Result := CmdKY(s);
|
||||
end;
|
||||
|
||||
function TCATEngine.ZZMA(const s: string): string;
|
||||
|
||||
Reference in New Issue
Block a user