diff --git a/HPSDRProtocol.pas b/HPSDRProtocol.pas index 94e65e8..6a20cc0 100644 --- a/HPSDRProtocol.pas +++ b/HPSDRProtocol.pas @@ -392,6 +392,12 @@ function PhaseWordToFreq(PhaseWord: LongWord): Double; // Returns -1.0 if the board has no supply voltage measurement. function ADCToSupplyVolts(RawADC: Word; BoardType: Integer): Double; +// True when the board type reports supply voltage in HP Status. +function BoardSupportsSupplyVoltage(BoardType: Integer): Boolean; + +// True when the board type reports supply current in HP Status. +function BoardSupportsSupplyCurrent(BoardType: Integer): Boolean; + // Convert raw ADC value to supply current in Amps (Board 5 / Orion MkII only) // Uses UserADC1 (bytes 55-56). Returns -1 for unsupported boards. function ADCToSupplyCurrent(RawADC: Word; BoardType: Integer): Double; @@ -448,16 +454,28 @@ begin // The PCB voltage divider (from Thetis source) must also be applied. // Board 0 (Atlas): no supply voltage measurement. // Boards 1,2,3 (Hermes/Angelia): Vref=3.3V, PCB divider R1=4.7kΩ/R2=0.82kΩ. + // Board 4 (Orion): protocol reports supply voltage with a 5.0V constant. // Board 5 (Orion MkII): Vref=5.0V, PCB divider R1=22kΩ/R2=1.1kΩ. // NOTE: for board 5 caller must pass UserADC0, not supply_volts bytes. - // Board 4 (Orion) and others: not available (return -1). + // Other boards: not available (return -1). case BoardType of 1, 2, 3: Result := (RawADC / 4095.0) * 3.3 * ((4.7 + 0.82) / 0.82); + 4: Result := (RawADC / 4095.0) * 5.0; 5: Result := (RawADC / 4095.0) * 5.0 * ((22.0 + 1.0) / 1.1); else Result := -1.0; end; end; +function BoardSupportsSupplyVoltage(BoardType: Integer): Boolean; +begin + Result := BoardType in [1, 2, 3, 4, 5]; +end; + +function BoardSupportsSupplyCurrent(BoardType: Integer): Boolean; +begin + Result := BoardType = 5; +end; + function ADCToSupplyCurrent(RawADC: Word; BoardType: Integer): Double; begin // Board 5 (Orion MkII): UserADC1, Vref=5V → mV = adc/4095*5000. diff --git a/MainForm.pas b/MainForm.pas index 7029c2f..b16bea7 100644 --- a/MainForm.pas +++ b/MainForm.pas @@ -241,6 +241,12 @@ type FActiveDDC: Integer; // DDC index для текущей платы (0 или 2) FLastDDCSeq: LongWord; // последний seq (пишется из сетевого потока) FLastDDCIndex: Integer; // последний DDC index + FDDCLastSeq: array[0..6] of LongWord; + FDDCSeqValid: array[0..6] of Boolean; + FSeqErrorCount: LongWord; + FLastSeqErrorDDC: Integer; + FLastSeqErrorDelta: Int64; + FSeqOkStreak: Integer; FLastFwdW: Double; FLastSWR: Double; // Эти значения приходят с HP Status пакетами (50..200 раз/с). UI обновляется @@ -427,7 +433,9 @@ type PbWaterfall: TPaintBox; // ---- Status bar ---- - StatusBar1: TStatusBar; + StatusPanel: TPanel; + StatusCells: array[0..7] of TPanel; + StatusLabels: array[0..7] of TLabel; // ---- Helpers ---- procedure BuildUI; @@ -435,6 +443,9 @@ type procedure SetLightTheme(V: Boolean); procedure StyleButton(B: TFlatButton; Active: Boolean = False); procedure StyleSpanButton(B: TFlatButton; Active: Boolean = False); + procedure SetStatusText(Index: Integer; const Text: string); + procedure SetRadioOfflineStatus(const ConnText: string; ClearDevice: Boolean = False); + procedure ApplyStatusTheme(const T: TAppTheme); procedure PositionSpanOverlayButtons; procedure UpdateVfoDisplay; function FormatFreq(Hz: Double): string; @@ -1299,6 +1310,12 @@ begin FActiveDDC := 0; FLastDDCSeq := 0; FLastDDCIndex := 0; + FillChar(FDDCLastSeq, SizeOf(FDDCLastSeq), 0); + FillChar(FDDCSeqValid, SizeOf(FDDCSeqValid), 0); + FSeqErrorCount := 0; + FLastSeqErrorDDC := -1; + FLastSeqErrorDelta:= 0; + FSeqOkStreak := 0; FLastFwdW := 0; FLastSWR := 1; FLastSupplyV:= -1; @@ -1507,6 +1524,30 @@ var L.Font.Size := 7; end; + procedure MakeStatusCell(Index, CellWidth: Integer; const Cap: string); + begin + StatusCells[Index] := TPanel.Create(Self); + StatusCells[Index].Parent := StatusPanel; + StatusCells[Index].BevelOuter := bvNone; + StatusCells[Index].Align := alLeft; + StatusCells[Index].Width := CellWidth; + StatusCells[Index].BorderSpacing.Left := 1; + StatusCells[Index].BorderSpacing.Top := 1; + StatusCells[Index].BorderSpacing.Bottom := 1; + + StatusLabels[Index] := TLabel.Create(Self); + StatusLabels[Index].Parent := StatusCells[Index]; + StatusLabels[Index].AutoSize := False; + StatusLabels[Index].Align := alClient; + StatusLabels[Index].BorderSpacing.Left := 8; + StatusLabels[Index].BorderSpacing.Right := 8; + StatusLabels[Index].Alignment := taLeftJustify; + StatusLabels[Index].Layout := tlCenter; + StatusLabels[Index].Caption := Cap; + StatusLabels[Index].Font.Name := 'Courier New'; + StatusLabels[Index].Font.Size := 8; + end; + begin // ---- Toolbar ---- PanelToolbar := TPanel.Create(Self); @@ -1537,19 +1578,23 @@ begin // ---- Separator line ---- // ---- Status bar ---- - StatusBar1 := TStatusBar.Create(Self); - StatusBar1.Parent := Self; - StatusBar1.Align := alBottom; - StatusBar1.SimplePanel := False; - with StatusBar1.Panels do - begin - Add; Items[0].Width := 150; Items[0].Text := 'IP: --'; - Add; Items[1].Width := 120; Items[1].Text := 'Board: --'; - Add; Items[2].Width := 200; Items[2].Text := 'Supply: --'; - Add; Items[3].Width := 250; Items[3].Text := 'RX: waiting...'; - Add; Items[4].Width := 200; Items[4].Text := 'Not connected'; - Add; Items[5].Width := 280; Items[5].Text := 'TX: --'; - end; + StatusPanel := TPanel.Create(Self); + StatusPanel.Parent := Self; + StatusPanel.Align := alBottom; + StatusPanel.Height := 26; + StatusPanel.BevelOuter := bvNone; + + // alLeft controls are arranged by Z-order in LCL; create them in reverse + // visual order so the final bar reads left-to-right as intended. + MakeStatusCell(6, 130, 'SEQ --'); + MakeStatusCell(5, 180, 'TX idle'); + MakeStatusCell(4, 190, 'RX idle'); + MakeStatusCell(7, 90, 'PLL --'); + MakeStatusCell(3, 150, 'Supply --'); + MakeStatusCell(1, 120, 'IP --'); + MakeStatusCell(0, 220, 'Board --'); + MakeStatusCell(2, 170, 'Disconnected'); + StatusCells[6].Align := alClient; // ---- Left panel ---- PanelLeft := TPanel.Create(Self); @@ -2493,6 +2538,7 @@ begin PbWaterfall.Color := T.BG; if PanelSMeterRight <> nil then PanelSMeterRight.Color := T.Panel; if PbSMeterRight <> nil then PbSMeterRight.Color := T.Panel; + ApplyStatusTheme(T); // Метки DL(LblAGCTop, False); @@ -2555,6 +2601,62 @@ begin UpdateVfoDisplay; end; +procedure TMainForm.SetStatusText(Index: Integer; const Text: string); +begin + if (Index < Low(StatusLabels)) or (Index > High(StatusLabels)) then Exit; + if StatusLabels[Index] = nil then Exit; + if StatusLabels[Index].Caption <> Text then + StatusLabels[Index].Caption := Text; +end; + +procedure TMainForm.SetRadioOfflineStatus(const ConnText: string; ClearDevice: Boolean); +begin + SetStatusText(2, ConnText); + SetStatusText(3, 'Supply --'); + SetStatusText(4, 'RX idle'); + SetStatusText(5, 'TX idle'); + SetStatusText(6, 'SEQ --'); + SetStatusText(7, 'PLL --'); + FillChar(FDDCSeqValid, SizeOf(FDDCSeqValid), 0); + FSeqErrorCount := 0; + FLastSeqErrorDDC := -1; + FLastSeqErrorDelta := 0; + FSeqOkStreak := 0; + if ClearDevice then + begin + SetStatusText(0, 'Board --'); + SetStatusText(1, 'IP --'); + end; +end; + +procedure TMainForm.ApplyStatusTheme(const T: TAppTheme); +var + I: Integer; +begin + if StatusPanel = nil then Exit; + StatusPanel.Color := T.Border; + for I := Low(StatusCells) to High(StatusCells) do + begin + if StatusCells[I] <> nil then + begin + StatusCells[I].Color := T.Panel; + StatusCells[I].Font.Color := T.Text; + end; + if StatusLabels[I] <> nil then + begin + if I in [4, 5] then + StatusLabels[I].Font.Color := T.TextDim + else if I = 6 then + StatusLabels[I].Font.Color := T.Amber + else if I = 2 then + StatusLabels[I].Font.Color := T.TbStartText + else + StatusLabels[I].Font.Color := T.Text; + StatusLabels[I].Color := T.Panel; + end; + end; +end; + procedure TMainForm.SetLightTheme(V: Boolean); begin if FLightTheme = V then Exit; @@ -2878,22 +2980,17 @@ begin // HP Status пакеты идут ~150 раз/с, при таком rate цифры дёргаются. UpdateTXMeters; - // Диагностика TX mic-пути — в отдельной панели [5], чтобы её не перетирали - // другие источники (DDC seq, audio-логи и т.п.). if FWDSPReady then begin if FTransmitting then begin if FDSPEngine.TXMicSource = txmsSoundCard then - StatusBar1.Panels[5].Text := - Format('TX sc: avail=%d ring=%d', - [FAudioIn.Available, FDSPEngine.TXMicRingAvail]) + SetStatusText(5, 'TX sound card') else - StatusBar1.Panels[5].Text := - Format('TX hw: ring=%d', [FDSPEngine.TXMicRingAvail]); + SetStatusText(5, 'TX radio mic'); end else - StatusBar1.Panels[5].Text := 'TX: idle'; + SetStatusText(5, 'TX idle'); end; end; @@ -2929,7 +3026,7 @@ begin StyleButton(BtnStartStop, False); FRunning := False; FNetwork.SetRunAndFreq(False, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), 0); - StatusBar1.Panels[4].Text := 'ТРАНСИВЕР НЕДОСТУПЕН'; + SetRadioOfflineStatus('Hardware timeout'); BtnMOX.Enabled := False; Exit; end; @@ -2940,7 +3037,7 @@ begin StyleButton(BtnStartStop, False); FRunning := False; FNetwork.SetRunAndFreq(False, XvtrTranslate(FCenterFreq), XvtrTranslate(FCenterFreq), 0); - StatusBar1.Panels[4].Text := 'ПОТЕРЯ СВЯЗИ С ТРАНСИВЕРОМ'; + SetRadioOfflineStatus('Connection lost'); BtnMOX.Enabled := False; Exit; end; @@ -2948,10 +3045,12 @@ begin if FRunning then begin - // Обновляем StatusBar из таймера — без Synchronize в сетевом потоке - StatusBar1.Panels[3].Text := - Format('RX DDC%d | seq=%d | pkts=%d', - [FLastDDCIndex, FLastDDCSeq, FRXPacketCount]); + // Обновляем статус из таймера — без Synchronize в сетевом потоке. + SetStatusText(4, 'RX running'); + if FSeqErrorCount > 0 then + SetStatusText(6, Format('SEQ ERR DDC%d', [FLastSeqErrorDDC])) + else + SetStatusText(6, 'SEQ OK'); if FWDSPReady then begin @@ -3082,7 +3181,7 @@ begin if Assigned(FDeviceDialog) then FDeviceDialog.AddDiscovered(Dev.IPAddress, Entry, Dev.BoardType); - StatusBar1.Panels[4].Text := Format('Found: %s', [Dev.IPAddress]); + SetStatusText(2, Format('Found: %s', [Dev.IPAddress])); end; procedure TMainForm.OnHPStatusCB(const Status: THighPriorityStatus); @@ -3101,22 +3200,27 @@ begin ExcPwr := (Status.ExciterPwr0Hi shl 8) or Status.ExciterPwr0Lo; FwdPwr := (Status.FwdPwrAlex0Hi shl 8) or Status.FwdPwrAlex0Lo; RevPwr := (Status.RevPwrAlex0Hi shl 8) or Status.RevPwrAlex0Lo; + SupplyV := -1.0; + SupplyA := -1.0; + // Board 5 (Orion MkII): voltage on UserADC0 (bytes 57-58), // current on UserADC1 (bytes 55-56) — matching Thetis behaviour. - if FNetwork.Device.BoardType = 5 then + // Other supported boards report voltage in SupplyVolts bytes and no current. + if BoardSupportsSupplyVoltage(FNetwork.Device.BoardType) then begin - SupplyV := ADCToSupplyVolts( - (Status.UserADC0Hi shl 8) or Status.UserADC0Lo, - FNetwork.Device.BoardType); - SupplyA := ADCToSupplyCurrent( - (Status.UserADC1Hi shl 8) or Status.UserADC1Lo, - FNetwork.Device.BoardType); - end else - begin - SupplyV := ADCToSupplyVolts( - (Status.SupplyVoltsHi shl 8) or Status.SupplyVoltsLo, - FNetwork.Device.BoardType); - SupplyA := -1.0; + if FNetwork.Device.BoardType = 5 then + SupplyV := ADCToSupplyVolts( + (Status.UserADC0Hi shl 8) or Status.UserADC0Lo, + FNetwork.Device.BoardType) + else + SupplyV := ADCToSupplyVolts( + (Status.SupplyVoltsHi shl 8) or Status.SupplyVoltsLo, + FNetwork.Device.BoardType); + + if BoardSupportsSupplyCurrent(FNetwork.Device.BoardType) then + SupplyA := ADCToSupplyCurrent( + (Status.UserADC1Hi shl 8) or Status.UserADC1Lo, + FNetwork.Device.BoardType); end; // FLastSMeter обновляется только из WDSP (GetSMeterDBm) в SpectrumTimerTick — // ExciterPwr это мощность TX, не уровень принятого сигнала. @@ -3188,14 +3292,17 @@ begin FLastSupplyV := SupplyV // первое значение — без сглаживания else FLastSupplyV := ALPHA_SUPPLY * SupplyV + (1.0 - ALPHA_SUPPLY) * FLastSupplyV; - end; + end else + FLastSupplyV := -1.0; + if SupplyA >= 0 then begin if FLastSupplyA < 0 then FLastSupplyA := SupplyA else FLastSupplyA := ALPHA_SUPPLY * SupplyA + (1.0 - ALPHA_SUPPLY) * FLastSupplyA; - end; + end else + FLastSupplyA := -1.0; FLastPLLLock := PLLLock; // Hardware PTT (foot switch / mic PTT) — обнаружение фронта (нужно делать @@ -3229,33 +3336,84 @@ end; procedure TMainForm.UpdateTXMeters; // Вызывается из MeterTimerTick (10 Гц). Использует FLastFwdW/FLastSWR/ -// FLastSupplyV/FLastPLLLock — они обновляются на rate HP Status (~150/с) +// FLastSupplyV/FLastSupplyA обновляются на rate HP Status (~150/с) // в DoUpdateStatus. -var - PLLStr: string; begin - if FLastPLLLock then PLLStr := 'PLL OK' else PLLStr := 'PLL?'; + if not FRunning then + begin + SetStatusText(3, 'Supply --'); + SetStatusText(7, 'PLL --'); + Exit; + end; + + if FLastPLLLock then + SetStatusText(7, 'PLL OK') + else + SetStatusText(7, 'PLL?'); + + if not BoardSupportsSupplyVoltage(FNetwork.Device.BoardType) then + begin + SetStatusText(3, 'Supply n/a'); + Exit; + end; + if FLastSupplyV >= 0 then begin if FLastSupplyA >= 0 then - StatusBar1.Panels[2].Text := - Format('Supply: %.1fV / %.1fA | %s', [FLastSupplyV, FLastSupplyA, PLLStr]) + SetStatusText(3, + Format('Supply %.1fV %.1fA', [FLastSupplyV, FLastSupplyA])) else - StatusBar1.Panels[2].Text := - Format('Supply: %.1fV | %s', [FLastSupplyV, PLLStr]); + SetStatusText(3, + Format('Supply %.1fV', [FLastSupplyV])); end else - StatusBar1.Panels[2].Text := PLLStr; + SetStatusText(3, 'Supply --'); end; procedure TMainForm.OnDDCIQCB(DDCIndex: Integer; const Data: TDDCIQPacket); var SamplesPerFrame: Integer; + Seq, ExpectedSeq: LongWord; + Delta: Int64; begin // Вызывается из сетевого потока — UI не трогаем напрямую // Счётчик принятых пакетов Inc(FRXPacketCount); FRXLastPktTime := GetTickCount64; // фиксируем время последнего пакета + Seq := (LongWord(Data.Seq[0]) shl 24) or (LongWord(Data.Seq[1]) shl 16) + or (LongWord(Data.Seq[2]) shl 8) or LongWord(Data.Seq[3]); + + if (DDCIndex >= Low(FDDCLastSeq)) and (DDCIndex <= High(FDDCLastSeq)) then + begin + if Seq <> 0 then + begin + ExpectedSeq := FDDCLastSeq[DDCIndex] + 1; + if FDDCSeqValid[DDCIndex] and (Seq <> ExpectedSeq) then + begin + Inc(FSeqErrorCount); + FLastSeqErrorDDC := DDCIndex; + Delta := Int64(Seq) - Int64(ExpectedSeq); + FLastSeqErrorDelta := Delta; + FSeqOkStreak := 0; + end + else if FDDCSeqValid[DDCIndex] then + begin + if FSeqErrorCount > 0 then + begin + Inc(FSeqOkStreak); + if FSeqOkStreak >= 20 then + begin + FSeqErrorCount := 0; + FLastSeqErrorDDC := -1; + FLastSeqErrorDelta := 0; + FSeqOkStreak := 0; + end; + end; + end; + end; + FDDCLastSeq[DDCIndex] := Seq; + FDDCSeqValid[DDCIndex] := True; + end; // 1. Подаём IQ данные в DSP — только с активного DDC if (DDCIndex = FActiveDDC) and FWDSPReady then @@ -3266,16 +3424,14 @@ begin FDSPEngine.PushDDCPacket(Data.IQData, 0, SamplesPerFrame); end; - // 2. Запоминаем последний seq — StatusBar обновит таймер (без Synchronize в горячем пути) - FLastDDCSeq := (LongWord(Data.Seq[0]) shl 24) or (LongWord(Data.Seq[1]) shl 16) - or (LongWord(Data.Seq[2]) shl 8) or LongWord(Data.Seq[3]); + // 2. Запоминаем последний seq — статус обновит таймер (без Synchronize в горячем пути) + FLastDDCSeq := Seq; FLastDDCIndex := DDCIndex; end; procedure TMainForm.DoUpdateDDCSeq(DDCIdx: Integer; Seq: LongWord); begin - StatusBar1.Panels[3].Text := - Format('RX DDC%d | seq=%d | pkts=%d', [DDCIdx, Seq, FRXPacketCount]); + SetStatusText(4, 'RX running'); end; procedure TMainForm.OnMicPacketCB(const Data: TMicDataPacket); @@ -3494,15 +3650,15 @@ procedure TMainForm.DoUpdateDDCSeqOrNoDevice(DDCIdx: Integer; Seq: LongWord); begin if DDCIdx = -1 then begin - if Assigned(FDeviceDialog) then - begin - FDeviceDialog.ClearDiscovered; - FDeviceDialog.AddDiscovered('', '-- no device found --'); - end; - StatusBar1.Panels[4].Text := 'No hardware found'; - end + if Assigned(FDeviceDialog) then + begin + FDeviceDialog.ClearDiscovered; + FDeviceDialog.AddDiscovered('', '-- no device found --'); + end; + SetStatusText(2, 'No hardware found'); + end else - StatusBar1.Panels[3].Text := Format('DDC%d seq=%d', [DDCIdx, Seq]); + SetStatusText(4, 'RX running'); end; procedure TMainForm.RecreateDSPEngine(ASampleRate: Integer); @@ -3546,7 +3702,7 @@ begin end; BtnStartStop.Enabled := False; - StatusBar1.Panels[3].Text := 'Creating WDSP wisdom...'; + SetStatusText(2, 'Creating WDSP wisdom...'); BuildThread := TWisdomBuildThread.Create(WisdomDir); try @@ -3577,9 +3733,9 @@ begin end; if Result then - StatusBar1.Panels[3].Text := 'WDSP wisdom ready' + SetStatusText(2, 'DSP ready') else - StatusBar1.Panels[3].Text := 'WDSP wisdom missing'; + SetStatusText(2, 'WDSP wisdom missing'); end; procedure TMainForm.BtnDiscoverClick(Sender: TObject); @@ -3601,7 +3757,7 @@ procedure TMainForm.BtnDiscoverFromDialog(Sender: TObject); begin SetLength(FDevices, 0); FDeviceCount := 0; - StatusBar1.Panels[4].Text := 'Discovering...'; + SetStatusText(2, 'Discovering...'); FNetwork.DirectIP := ''; TDiscoverThread.Create(FNetwork, Self); end; @@ -3632,7 +3788,6 @@ begin end; FRXPacketCount := 0; FActiveDDC := 0; - StatusBar1.Panels[3].Text := 'RX: waiting...'; // Сохраняем текущую частоту/настройки чтобы следующий START восстановил их if FDevConnected then begin @@ -3652,9 +3807,7 @@ begin FNetwork.Disconnect; BtnStartStop.Caption := 'START'; StyleButton(BtnStartStop, False); - StatusBar1.Panels[4].Text := 'Not connected'; - StatusBar1.Panels[0].Text := 'IP: --'; - StatusBar1.Panels[1].Text := 'Board: --'; + SetRadioOfflineStatus('Disconnected', True); BtnMOX.Enabled := False; Exit; end; @@ -3727,7 +3880,7 @@ begin // --- Открываем WDSP асинхронно чтобы не блокировать UI --- FPendingDev := Dev; BtnStartStop.Enabled := False; - StatusBar1.Panels[3].Text := 'Opening WDSP...'; + SetStatusText(2, 'Opening DSP...'); if FWDSPReady then // WDSP уже открыт — сразу подключаемся DoConnectDevice(Dev) @@ -3742,7 +3895,7 @@ begin FWDSPReady := Success; if not Success then begin - StatusBar1.Panels[3].Text := 'WDSP open failed'; + SetStatusText(2, 'DSP failed'); BtnStartStop.Enabled := True; BtnStartStop.Caption := 'START'; StyleButton(BtnStartStop, False); @@ -3889,8 +4042,8 @@ begin and FXvtrSettings.Entries[G_Settings.LastXvtr].Enabled then ActivateXvtrBand(G_Settings.LastXvtr); - StatusBar1.Panels[0].Text := 'IP: ' + FNetwork.Device.IPAddress; - StatusBar1.Panels[1].Text := 'Board: ' + BoardTypeName(FNetwork.Device.BoardType); + SetStatusText(1, 'IP: ' + FNetwork.Device.IPAddress); + SetStatusText(0, 'Board: ' + BoardTypeName(FNetwork.Device.BoardType)); // General packet FillChar(GenPkt, SizeOf(GenPkt), 0); @@ -3937,6 +4090,13 @@ begin FRunning := True; FRXStartTime := GetTickCount64; FRXLastPktTime := 0; + FRXPacketCount := 0; + FillChar(FDDCSeqValid, SizeOf(FDDCSeqValid), 0); + FSeqErrorCount := 0; + FLastSeqErrorDDC := -1; + FLastSeqErrorDelta := 0; + FSeqOkStreak := 0; + SetStatusText(6, 'SEQ OK'); // DDC и DUC Specific — теперь потоки эмулятора слушают на своих портах. // DUC Specific содержит mic-конфигурацию (boost/bias/line/PTT) из FTXSettings. @@ -3952,7 +4112,7 @@ begin BtnStartStop.Caption := 'STOP'; StyleButton(BtnStartStop, True); - StatusBar1.Panels[4].Text := 'RUNNING'; + SetStatusText(2, 'Running'); BtnMOX.Enabled := True; end; @@ -6678,13 +6838,8 @@ begin FAudioOut.DeviceIndex := DevIndex; FAudioOutDevName := DevName; try - if not FAudioOut.Open then - StatusBar1.Panels[3].Text := 'Audio out: ' + FAudioOut.LastError - else - StatusBar1.Panels[3].Text := 'Audio out: ' + IfThen(DevName = '', 'Default', DevName); + FAudioOut.Open; except - on E: Exception do - StatusBar1.Panels[3].Text := 'Audio out err: ' + E.Message; end; end; @@ -6697,17 +6852,10 @@ begin if DevIndex >= 0 then begin try - if not FAudioIn.Open then - StatusBar1.Panels[3].Text := 'Mic: ' + FAudioIn.LastError - else - StatusBar1.Panels[3].Text := 'Mic: ' + DevName; + FAudioIn.Open; except - on E: Exception do - StatusBar1.Panels[3].Text := 'Mic err: ' + E.Message; end; - end - else - StatusBar1.Panels[3].Text := 'Mic: none'; + end; end; procedure TMainForm.ApplyVisibility(ShowSpectrum, ShowWaterfall: Boolean); @@ -6830,15 +6978,9 @@ begin // Вызывается один раз через 200 мс после старта формы FAfterShowTimer.Enabled := False; - // Открываем PortAudio — теперь FPC уже перехватывает сигналы try - if not FAudioOut.Open then - StatusBar1.Panels[3].Text := 'Audio: ' + FAudioOut.LastError - else - StatusBar1.Panels[3].Text := 'Audio OK (PortAudio)'; + FAudioOut.Open; except - on E: Exception do - StatusBar1.Panels[3].Text := 'Audio: ' + E.Message; end; EnsureWDSPWisdom;