From ced8581ada4a71c232cc53c9236d9ffbab82733e Mon Sep 17 00:00:00 2001 From: Uladzimir Karpenka Date: Thu, 20 Aug 2026 16:25:47 +0300 Subject: [PATCH] =?UTF-8?q?fix(hpsdr):=20TX-=D0=B0=D0=BD=D1=82=D0=B5=D0=BD?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=82=D0=BE=D0=BD=D1=83=D0=BB=D0=B0=20=D0=B2=20?= =?UTF-8?q?Alex1,=20OC-=D0=BF=D0=B8=D0=BD=D1=8B=20=D0=B1=D0=B8=D0=BB=D0=B8?= =?UTF-8?q?=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20=D0=BE=D0=B4=D0=B8=D0=BD,?= =?UTF-8?q?=20SO=5FEXCLUSIVEADDRUSE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Три дефекта, найденные сверкой с прошивкой (Anvelina_PROIII, board_type=5) и с Thetis. 1. CalcAlex1 жёстко ставил бит 24 (ANT1) с комментарием «ADC1 дефолт». Старшие 16 бит Alex1 — не фильтры ADC1, а TX-СЛОВО Alex: прошивка кладёт байты 1428-1429 в Alex_Tx_data и на PTT подменяет им верхнюю половину Alex-слова целиком (Orion.v:2346, Alex_Tx_data_ok = Alex_Tx_data[10:8] — те же ANT1/2/3). Захардкоженный ANT1 делал условие истинным и затирал антенну, посчитанную в CalcAlex0: оператор с TX на ANT2/ANT3 передавал в ANT1. Thetis делает ровно то, что теперь делаем мы — netInterface.c:489 чистит биты 24-26 и ставит _TXANT_1/2/3 из tx_ant. 2. Байт 1401 (Open Collector) уходил без сдвига. Бит 0 не разведён, пины сидят на битах 1..7 (Orion.v:913, USEROUT0..6 = Open_Collector[1..7]) — та же раскладка, что у C2[7:1] в протоколе 1. Каждый включённый пин срабатывал на соседнем выходе, седьмой не срабатывал никогда. CalcOCBits остаётся в логических координатах (бит i = пин i+1), сдвиг делает SendFullHP на границе с проводом. 3. SO_EXCLUSIVEADDRUSE = not 5 ($FFFFFFFA) вместо ~SO_REUSEADDR = not 4 ($FFFFFFFB) — как и написано в собственном комментарии. Опции с таким номером нет: setsockopt возвращал ошибку (её результат не проверяется), и порт оставался перехватываемым, ровно от чего строка и спасала. Co-Authored-By: Claude Opus 5 --- HPSDRNetwork.pas | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/HPSDRNetwork.pas b/HPSDRNetwork.pas index ec5de6a..1197059 100644 --- a/HPSDRNetwork.pas +++ b/HPSDRNetwork.pas @@ -32,7 +32,12 @@ const SOCK_INVALID = TSocket(INVALID_SOCKET); INADDR_ANY = 0; IPPROTO_UDP = 17; - SO_EXCLUSIVEADDRUSE = LongInt(not 5); // = $FFFFFFFB — исключительное владение портом + // Winsock: SO_EXCLUSIVEADDRUSE == ~SO_REUSEADDR == ~$0004 == $FFFFFFFB + // (то же значение объявляет и сам WinSock2 — держим локально, как соседние + // INADDR_ANY/IPPROTO_UDP). Стояло `not 5` = $FFFFFFFA — несуществующая + // опция: setsockopt возвращал ошибку (её результат не проверяется), и порт + // оставался перехватываемым, ровно от чего эта строка и должна была спасать. + SO_EXCLUSIVEADDRUSE = LongInt(not 4); // = -5 = $FFFFFFFB type TInetSockAddr = WinSock2.TSockAddrIn; @@ -1486,12 +1491,25 @@ end; // Bit 8 = ALEX1_ANAN7000_RX_GNDonTX: заземляет ADC1-вход во время TX // чтобы предотвратить перегрузку второго приёмника. // Остальные биты — те же LPF/BPF/ANT что и у ALEX0. +// +// ★СТАРШИЕ 16 БИТ ЭТОГО СЛОВА — НЕ «фильтры ADC1», А TX-СЛОВО ALEX. +// Прошивка (Orion.v/High_Priority_CC.v) раскладывает байты так: +// 1428-1429 -> Alex_Tx_data[15:0] (TX-фильтры + выбор TX-антенны) +// 1430-1431 -> Alex_data[31:16] (RX1-фильтры — это НИЖНИЕ 16 бит Alex1) +// и на передаче ПОДМЕНЯЕТ верхнюю половину Alex-слова: +// Alex_Tx_data_ok = Alex_Tx_data[10:8] in {001,010,100} // ANT1/2/3 +// Alex_upper = (FPGA_PTT && Alex_Tx_data_ok) ? Alex_Tx_data : Alex_data[47:32] +// Биты 24..26 здесь — те же ANT1/2/3, что и в ALEX0, поэтому захардкоженный +// ANT1 делал Alex_Tx_data_ok=1 и на КАЖДОМ PTT затирал антенну, посчитанную +// в CalcAlex0: оператор с TX на ANT2/ANT3 передавал в ANT1. Антенну берём +// из той же таблицы и по той же частоте, что и CalcAlex0. function CalcAlex1(RXFreqHz, TXFreqHz: Double; Transmitting: Boolean; const Alex: TAlexSettings; XvtrDisablePA: Boolean = False): LongWord; var - txf: Double; + txf: Double; + AntVal: Byte; begin Result := 0; @@ -1517,7 +1535,15 @@ begin else if txf > 2500000 then Result := Result or $00400000 // bit 22: 80m LPF else Result := Result or $00800000; // bit 23: 160m LPF - Result := Result or $01000000; // bit 24: ANT1 (ADC1 дефолт) + // Биты 24-26: ANT 1/2/3 TX-слова (см. шапку). Ровно та же величина, что + // CalcAlex0 кладёт в TX-ветке, иначе прошивка на PTT её перебьёт. + AntVal := Alex.TxAnt[FreqToBandIndex(TXFreqHz)]; + if AntVal < 1 then AntVal := 1; + case AntVal of + 2: Result := Result or $02000000; // bit 25: ANT 2 + 3: Result := Result or $04000000; // bit 26: ANT 3 + else Result := Result or $01000000; // bit 24: ANT 1 (дефолт) + end; // Bit 8: заземление ADC1-входа во время TX (галочка "Ground BPF2 on TX") if Transmitting and Alex.GndBpf2OnTx then @@ -1552,7 +1578,9 @@ begin if FConnected then SendFullHP; end; -// Строит 8-битное слово OC (byte 1401 HP-пакета, биты 0..6 = пины 1..7). +// Строит ЛОГИЧЕСКУЮ маску OC-пинов: бит i = пин i+1 (i = 0..OC_PIN_COUNT-1) — +// в тех же координатах, в которых маски лежат в настройках и рисуются в UI. +// Сдвиг в проводное представление байта 1401 делает вызывающий (SendFullHP). // Портировано из Thetis (Penny.UpdateExtCtrl/adjustForTXAction), урезано: // без PA-override и split-pins VFO A/B (см. project_oc_control память). // RX: маска банда/слота как есть. TX: маска банда/слота, каждый включённый @@ -1969,10 +1997,14 @@ begin if not FSpeakerAudioEnabled then Buf[1400] := Buf[1400] or $02; - // OC Control — Open Collector outputs (byte 1401) + // OC Control — Open Collector outputs (byte 1401). + // ★СДВИГ НА БИТ ОБЯЗАТЕЛЕН. Бит 0 байта 1401 не разведён, пины сидят на + // битах 1..7 (Orion.v: USEROUT0..6 = Open_Collector[1..7]) — та же раскладка, + // что у C2[7:1] в протоколе 1. Без shl 1 каждый включённый пин срабатывал + // на соседнем физическом выходе, а седьмой не срабатывал никогда. if FOCConfig.Enabled then - Buf[1401] := CalcOCBits(FCurrentRXFreq, FCurrentTXFreq, FIsTransmitting, - FTuning, FTwoTone, FXvtrSlot, FOCConfig); + Buf[1401] := Byte(CalcOCBits(FCurrentRXFreq, FCurrentTXFreq, FIsTransmitting, + FTuning, FTwoTone, FXvtrSlot, FOCConfig) shl 1); // Step attenuators ADC0/ADC1 (bytes 1443/1442) // 0 dB = 0, max 31 dB