{
    This file is part of SkantiControl

    Skanti TRP8000 series control program (CU8000 control unit)
    This program was developed for a CU8000 control unit marked
    TRP 8255 S R GB1. May work or not with other units. The CU
    firmware version can be either 80R or 92.

    Copyright (C) 2012-2025 G. Perotti, I1EPJ, i1epj@aricasale.it

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/.


    Main unit - XML Config version
}

unit skanti;

{$mode objfpc}{$H+}

{$include defines.inc}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  Menus, ExtCtrls, Buttons, serial, types, LCLType, ActnList, mouse, dateutils,
  user, UAbout, UGNU, UMAN, umanager, uscan, JButton, lNetComponents, strutils,
  math, FileInfo, DefaultTranslator, lNet, UParse, UTCP, DOM, XMLRead, XMLWrite;

type

  { TTRP }

  TTRP = class(TForm)
      B0: TJButton;
      B1: TJButton;
      B2: TJButton;
      B2182: TJButton;
      B3: TJButton;
      B4: TJButton;
      B5: TJButton;
      B500: TJButton;
      B6: TJButton;
      B7: TJButton;
      B8: TJButton;
      B9: TJButton;
      BAGCFAST: TJButton;
      BAGCOFF: TJButton;
      BAGCON: TJButton;
      BAGCSLOW: TJButton;
      BAM: TJButton;
      BBFODN: TJButton;
      BBFOUP: TJButton;
      BCW: TJButton;
      BDIMDN: TJButton;
      BDIMUP: TJButton;
      BDUP: TJButton;
      BENTER: TJButton;
      BFULL: TJButton;
      BINTER: TJButton;
      BLOW: TJButton;
      BLSB: TJButton;
      BMCW: TJButton;
      BMED: TJButton;
      BMINUS: TJButton;
      BNAR: TJButton;
      BONOFF: TJButton;
      BR3E: TJButton;
      BRATE: TJButton;
      BRCL: TJButton;
      BRFAMP: TJButton;
      BRFATT: TJButton;
      BRX: TJButton;
      BRXEQTX: TJButton;
      BSCAN: TJButton;
      BSENSDN: TJButton;
      BSENSUP: TJButton;
      BSETT: TJButton;
      BSPKR: TJButton;
      BSQL: TJButton;
      BSTO: TJButton;
      BTELEX: TJButton;
      BTRANS: TJButton;
      BTUNE: TJButton;
      BTUNEDN: TJButton;
      BTUNEUP: TJButton;
      BTX: TJButton;
      BTXEQRX: TJButton;
      BTXONOFF: TJButton;
      BUSB: TJButton;
      BVNAR: TJButton;
      BVOLDN: TJButton;
      BVOLUP: TJButton;
      BWIDE: TJButton;
      LKHZ: TLabel;
      LKHZ1: TLabel;
      LRP1: TLabel;
      LRP2: TLabel;
      MSHOWPROGSTEP: TMenuItem;
      MSTDORMST: TMenuItem;
      MSHOWHAMLIB: TMenuItem;
      MMAGAF: TMenuItem;
      Separator2: TMenuItem;
      Separator1: TMenuItem;
      MTCHF: TMenuItem;
      MTCPSERVER: TMenuItem;
      TRPServer: TLTCPComponent;
      MenuItem18: TMenuItem;
      MCU920: TMenuItem;
      MenuItem19: TMenuItem;
      MenuItem20: TMenuItem;
      MSCANW: TMenuItem;
      MRECCU: TMenuItem;
      N2: TMenuItem;
      MMAG100: TMenuItem;
      MMAG125: TMenuItem;
      MMAG150: TMenuItem;
      MMAG175: TMenuItem;
      MMAG200: TMenuItem;
      MMAG110: TMenuItem;
      MICDNONE: TMenuItem;
      MICD2: TMenuItem;
      MICD5: TMenuItem;
      MICD10: TMenuItem;
      MINTDELAY: TMenuItem;
      MREREADCONF: TMenuItem;
      MPROGSTEP: TMenuItem;
      MSWANTOFF: TMenuItem;
      MSETBEEP: TMenuItem;
      MSETSTEP: TMenuItem;
      N1: TMenuItem;
      MReadHours: TMenuItem;
      RXFreqGroupBox: TGroupBox;
      FLSP_RCMD: TTimer;
      CMDRPT: TTimer;
      TSHConnected: TShape;
      Timer2: TTimer;
      TXFreq100H: TStaticText;
      TXFreq100k: TStaticText;
      TXFreq10k: TStaticText;
      TXFreq10M: TStaticText;
      TXFreq1k: TStaticText;
      TXFreq1M: TStaticText;
      TXFreqDOT: TStaticText;
      RXFreqDOT: TStaticText;
      TXFreqGroupBox: TGroupBox;
      LBKHZ1: TLabel;
      LNET: TLabel;
      LBPWR: TLabel;
      LBREC: TLabel;
      LBSIG: TLabel;
      LBTX: TLabel;
      MenuItem17: TMenuItem;
      RXFreq100H: TStaticText;
      RXFreq100k: TStaticText;
      RXFreq10H: TStaticText;
      RXFreq10k: TStaticText;
      RXFreq10M: TStaticText;
      RXFreq1k: TStaticText;
      RXFreq1M: TStaticText;
      SMGroupBox: TGroupBox;
      SMLabel: TLabel;
      SMShape1: TShape;
      SMShape10: TShape;
      SMShape11: TShape;
      SMShape12: TShape;
      SMShape13: TShape;
      SMShape14: TShape;
      SMShape15: TShape;
      SMShape16: TShape;
      SMShape17: TShape;
      SMShape18: TShape;
      SMShape19: TShape;
      SMShape2: TShape;
      SMShape20: TShape;
      SMShape3: TShape;
      SMShape4: TShape;
      SMShape5: TShape;
      SMShape6: TShape;
      SMShape7: TShape;
      SMShape8: TShape;
      SMShape9: TShape;
      STEP100H: TShape;
      STEP10H: TShape;
      STEP1K: TShape;
      PMGroupBox: TGroupBox;
      MenuItem10: TMenuItem;
      MenuItem11: TMenuItem;
      MenuItem12: TMenuItem;
      MenuItem14: TMenuItem;
      MADVSCPTR: TMenuItem;
      MALWLP: TMenuItem;
      MChannels: TMenuItem;
      MDEFU: TMenuItem;
      MDISU: TMenuItem;
      MenuItem15: TMenuItem;
      MUSEUTC: TMenuItem;
      MMANCH: TMenuItem;
      MMANST: TMenuItem;
      MIMMU: TMenuItem;
      MSCHANN: TMenuItem;
      MRELCH: TMenuItem;
      MenuItem16: TMenuItem;
      MEXITCHAN: TMenuItem;
      MRSTSCBP: TMenuItem;
      MFILLBUF: TMenuItem;
      MTESTFMAUTO: TMenuItem;
      MTESTMAN: TMenuItem;
      MTESTFMMAN: TMenuItem;
      MSETGR: TMenuItem;
      MSETPR: TMenuItem;
      MSETOR: TMenuItem;
      MSHOWC: TMenuItem;
      MSTOBFO: TMenuItem;
      MRSTATE: TMenuItem;
      MSTPALRM: TMenuItem;
      MTSTALRM: TMenuItem;
      MSNDALRM: TMenuItem;
      MenuItem13: TMenuItem;
      MRed: TMenuItem;
      MGreen: TMenuItem;
      MYellow: TMenuItem;
      MenuItem2: TMenuItem;
      MenuItem6: TMenuItem;
      MenuItem8: TMenuItem;
      MenuItem9: TMenuItem;
      MTESTAUTO: TMenuItem;
      MSG: TMemo;
      MENALL: TMenuItem;
      MSETBFO: TMenuItem;
      MenuItem4: TMenuItem;
      MRESET: TMenuItem;
      MRELPRIO: TMenuItem;
      MenuItem3: TMenuItem;
      MOTFUN: TMenuItem;
      MGNULIC: TMenuItem;
      MABOUT: TMenuItem;
      MMAN: TMenuItem;
      MHelp: TMenuItem;
      MRCONF: TMenuItem;
      MLSTATE: TMenuItem;
      MSSTATE: TMenuItem;
      MenuItem5: TMenuItem;
      MRTU: TMenuItem;
      MRCU: TMenuItem;
      MRBFO: TMenuItem;
      MTRANS: TMenuItem;
      MStatus: TMenuItem;
      MenuItem1: TMenuItem;
      MCustom: TMenuItem;
      FLASH: TTimer;
      Clock: TTimer;
      OpenDialog1: TOpenDialog;
      PMShape1: TShape;
      PMShape10: TShape;
      PMShape11: TShape;
      PMShape12: TShape;
      PMShape13: TShape;
      PMShape14: TShape;
      PMShape15: TShape;
      PMShape16: TShape;
      PMShape17: TShape;
      PMShape18: TShape;
      PMShape19: TShape;
      PMShape2: TShape;
      PMShape20: TShape;
      PMShape3: TShape;
      PMShape4: TShape;
      PMShape5: TShape;
      PMShape6: TShape;
      PMShape7: TShape;
      PMShape8: TShape;
      PMShape9: TShape;
      SaveDialog1: TSaveDialog;
      SCUR: TShape;
      SM: TTimer;
      PMLabel: TLabel;
      TXFUpTimer: TTimer;
      RPT: TTimer;
      TStatus: TTimer;
      SPLOW: TShape;
      SRPWR: TShape;
      TX: TTimer;
      MainMenu1: TMainMenu;
      MFile: TMenuItem;
      M300b: TMenuItem;
      M2400B: TMenuItem;
      MExit: TMenuItem;
      MOptions: TMenuItem;
      MPort: TMenuItem;
      MSpeed: TMenuItem;
      Mttys0: TMenuItem;
      Mttys1: TMenuItem;
      procedure BAGCFASTClick(Sender: TObject);
      procedure BAGCOFFClick(Sender: TObject);
      procedure BAGCONClick(Sender: TObject);
      procedure BAGCSLOWClick(Sender: TObject);
      procedure B2182Click(Sender: TObject);
      procedure BBFODNMouseDown(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BBFODNMouseUp(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BBFOUPClick(Sender: TObject);
      procedure BBFOUPMouseDown(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BBFOUPMouseUp(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BDIMDNMouseDown(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BDIMDNMouseUp(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BDIMUPMouseDown(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BDIMUPMouseUp(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BDUPClick(Sender: TObject);
      procedure BRXEQTXClick(Sender: TObject);
      procedure BSCANClick(Sender: TObject);
      procedure BSENSDNClick(Sender: TObject);
      procedure BSENSDNMouseDown(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BSENSDNMouseUp(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BSENSUPMouseDown(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BSENSUPMouseUp(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BSETTClick(Sender: TObject);
      procedure BMINUSClick(Sender: TObject);
      procedure BTELEXClick(Sender: TObject);
      procedure BTUNEDNMouseDown(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BTUNEDNMouseUp(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BTUNEUPClick(Sender: TObject);
      procedure BFULLClick(Sender: TObject);
      procedure BBFODNClick(Sender: TObject);
      procedure BTUNEDNClick(Sender: TObject);
      procedure BSENSUPClick(Sender: TObject);
      procedure BDIMDNClick(Sender: TObject);
      procedure BDIMUPClick(Sender: TObject);
      procedure B500Click(Sender: TObject);
      procedure BTUNEUPMouseDown(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BTUNEUPMouseUp(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BVOLDNClick(Sender: TObject);
      procedure BLOWClick(Sender: TObject);
      procedure BMEDClick(Sender: TObject);
      procedure BRCLClick(Sender: TObject);
      procedure BRFATTClick(Sender: TObject);
      procedure BSQLClick(Sender: TObject);
      procedure BTXEQRXClick(Sender: TObject);
      procedure BRATEClick(Sender: TObject);
      procedure BRXClick(Sender: TObject);
      procedure BTXClick(Sender: TObject);
      procedure BNUMClick(Sender: TObject);
      procedure BTXONOFFClick(Sender: TObject);
      procedure BVOLDNMouseDown(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BVOLDNMouseUp(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BVOLUPClick(Sender: TObject);
      procedure BVOLUPMouseDown(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BVOLUPMouseUp(Sender: TObject; Button: TMouseButton;
              Shift: TShiftState; X, Y: Integer);
      procedure BWIDEClick(Sender: TObject);
      procedure BTRANSClick(Sender: TObject);
      procedure BINTERClick(Sender: TObject);
      procedure BNARClick(Sender: TObject);
      procedure BVNARClick(Sender: TObject);
      procedure BSPKRClick(Sender: TObject);
      procedure BRFAMPClick(Sender: TObject);
      procedure BSTOClick(Sender: TObject);
      procedure ClockTimer(Sender: TObject);
      procedure BENTERClick(Sender: TObject);
      procedure BMCWClick(Sender: TObject);
      procedure CMDRPTTimer(Sender: TObject);
      procedure FLSP_RCMDTimer(Sender: TObject);
      procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
      procedure FormKeyDown(Sender: TObject; var Key: Word;
          Shift: TShiftState);
      procedure FormResize(Sender: TObject);
      procedure FormShow(Sender: TObject);
      procedure MABOUTClick(Sender: TObject);
      procedure MADVSCPTRClick(Sender: TObject);
      procedure MCU920Click(Sender: TObject);
      procedure MDEFUClick(Sender: TObject);
      procedure MENALLClick(Sender: TObject);
      procedure MMAGAFClick(Sender: TObject);
      procedure MenuOptionClick(Sender: TObject);
      procedure MSCANWClick(Sender: TObject);
      procedure MSHOWPROGSTEPClick(Sender: TObject);
      procedure MSTDORMSTClick(Sender: TObject);
      procedure MRECCUClick(Sender: TObject);
      procedure MPROGSTEPClick(Sender: TObject);
      procedure MSETSTEPClick(Sender: TObject);
      procedure MReadHoursClick(Sender: TObject);
      procedure MREREADCONFClick(Sender: TObject);
      procedure MSWANTOFFClick(Sender: TObject);
      procedure MMANCHClick(Sender: TObject);
      procedure MMANSTClick(Sender: TObject);
      procedure MIMMUClick(Sender: TObject);
      procedure MRELCHClick(Sender: TObject);
      procedure MEXITCHANClick(Sender: TObject);
      procedure MFILLBUFClick(Sender: TObject);
      procedure MRSTSCBPClick(Sender: TObject);
      procedure MSGDblClick(Sender: TObject);
      procedure MStatusClick(Sender: TObject);
      procedure MTCHFClick(Sender: TObject);
      procedure MTCPSERVERClick(Sender: TObject);
      procedure MTESTFMAUTOClick(Sender: TObject);
      procedure MTESTFMMANClick(Sender: TObject);
      procedure MGreenClick(Sender: TObject);
      procedure MRedClick(Sender: TObject);
      procedure MRSTATEClick(Sender: TObject);
      procedure MSETBFOClick(Sender: TObject);
      procedure MGNULICClick(Sender: TObject);
      procedure MLSTATEClick(Sender: TObject);
      procedure MMANClick(Sender: TObject);
      procedure MRCONFClick(Sender: TObject);
      procedure MRELPRIOClick(Sender: TObject);
      procedure MRESETClick(Sender: TObject);
      procedure MRTUClick(Sender: TObject);
      procedure MExitClick(Sender: TObject);
      procedure MRBFOClick(Sender: TObject);
      procedure MRCUClick(Sender: TObject);
      procedure MSETGRClick(Sender: TObject);
      procedure MSETORClick(Sender: TObject);
      procedure MSETPRClick(Sender: TObject);
      procedure MSNDALRMClick(Sender: TObject);
      procedure MSSTATEClick(Sender: TObject);
      procedure MSTOBFOClick(Sender: TObject);
      procedure MSTPALRMClick(Sender: TObject);
      procedure MTESTAUTOClick(Sender: TObject);
      procedure MTESTMANClick(Sender: TObject);
      procedure MTSTALRMClick(Sender: TObject);
      procedure MYellowClick(Sender: TObject);
      procedure MSETBEEPClick(Sender: TObject);
      procedure TRPServerAccept(aSocket: TLSocket);
      procedure TRPServerDisconnect(aSocket: TLSocket);
      procedure TRPServerError(const amsg: string; aSocket: TLSocket);
      procedure TRPServerReceive(aSocket: TLSocket);
      procedure RPTTimer(Sender: TObject);
      procedure RXfreqMouseUp(Sender: TObject; Button: TMouseButton;
                Shift: TShiftState; X, Y: Integer);
      procedure RXFreqMouseWheel(Sender: TObject; Shift: TShiftState;
          WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
      procedure STEP100HMouseUp(Sender: TObject; Button: TMouseButton;
                Shift: TShiftState; X, Y: Integer);
      procedure STEP10HMouseUp(Sender: TObject; Button: TMouseButton;
                Shift: TShiftState; X, Y: Integer);
      procedure STEP1KMouseUp(Sender: TObject; Button: TMouseButton;
                Shift: TShiftState; X, Y: Integer);
      procedure FLASHTimer(Sender: TObject);
      procedure SMTimer(Sender: TObject);
      procedure TStatusTimer(Sender: TObject);
      procedure BTUNEClick(Sender: TObject);
      procedure FormMouseWheel(Sender: TObject; Shift: TShiftState;
                WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
      procedure BONOFFClick(Sender: TObject; CloseComm: boolean);
      procedure BCWClick(Sender: TObject);
      procedure FormCreate(Sender: TObject);
      procedure M2400BClick(Sender: TObject);
      procedure M300BClick(Sender: TObject);
      procedure MCustomClick(Sender: TObject);
      procedure Mttys0Click(Sender: TObject);
      procedure Mttys1Click(Sender: TObject);
      procedure BLSBClick(Sender: TObject);
      procedure BAMClick(Sender: TObject);
      procedure BR3EClick(Sender: TObject);
      procedure TXfreqMouseUp(Sender: TObject; Button: TMouseButton;
                Shift: TShiftState; X, Y: Integer);
      procedure TXFreqMouseWheel(Sender: TObject; Shift: TShiftState;
          WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
      procedure TXFUpTimerTimer(Sender: TObject);
      procedure TXTimer(Sender: TObject);
      procedure BUSBClick(Sender: TObject);
      procedure KeyboardHandler(keyp: string);
      procedure DispRXf(Interactive: boolean);
      procedure DispTXf(Interactive: boolean);
      procedure UpdatePanel;
      function SendCommand(cmd, par: string): boolean;
      function GetReply(ReplyLen: integer; ReplyTimeout: extended): string;
      function OpenRemote: boolean;
      function CloseRemote: boolean;
      function SetRXFreq: boolean;
      function SetTXFreq: boolean;
      function SetRXMode: boolean;
      function SetTXMode: boolean;
      function SetRXTXFreq: boolean;
      procedure SaveLastState;
      procedure LoadLastState;
      procedure LoadChannel(Sender: TObject);
      procedure RestoreState;
      procedure SetDefaultState;
      procedure SaveConfig;
      procedure ReadConfig;
      procedure ReadConfig_old;
      procedure LoadChannelsDir;
      function FormatBFOFreq(s: string): string;
      procedure DisableAllControls;
      procedure DisableAllButtons;
      procedure EnableAllControls;
      procedure SetAllLEDsOff;
      function GetAutoSelfTestResponse: boolean;
      function GetManSelfTestResponse: boolean;
      procedure SetActiveButtons;
      procedure CloseProgram;
      function AdjustDecimalSeparator(s: string): string;
      procedure SetSmeter(value: integer);
      procedure SetPmeter(value: integer);
      procedure SetMetersColor;
      function GetRXString: string;
      function GetTXString: string;
      procedure PutRXString(s: string );
      procedure PutTXString(S: string);
      function CheckProgrammableTuneStep: boolean;
      function Check_V92_Firmware: boolean;
      procedure LoadStateFromCU;
      procedure RestoreFilterAndAGC;
      procedure SetDefaultConfig;
      procedure LoadFFile(Sender: TObject);
      procedure CheckOwnF;
      procedure SetLowPower;
      procedure EnableProgTuneStep;
      procedure MFONTMAGNClick(Sender: TObject);
      procedure FontMagnify(Scaling: boolean=FALSE);
      procedure WaitTimeSecs(seconds:integer; Mesg: string; DisplayProgress:boolean);
      function ShowControlChars(cmd: string): string;
      procedure RunGUI;
  private
      { private declarations }
      procedure Message(ms: string);
  public
      { public declarations }
      property MsgToShow: string write Message;
   end;

    // The actual RTX state
  TRTXState = record
    Freq_rx: longint;
    Freq_tx: longint;
    Freq_step: integer;
    BFO_freq: integer;
    Mode_RX: integer;
    Mode_TX: integer;
    Filter: integer;
    AGC: integer;
    Speaker_Enabled: boolean;
    RFamp_Enabled: boolean;
    RFAtt_Enabled: boolean;
    Squelch_Enabled: boolean;
    Sensitivity: integer;
    Volume: integer;
    TX_power: integer;
    TX_Enabled: boolean;
    uscan: boolean;
    Duplex: boolean;
    PTT_State: boolean;
    Dimmer_Level: integer;
    Programmable_step: integer;
    Antenna_OFF: boolean;
    Programmable_Step_Set: boolean;
  end;

  // The keyboard states
  TKeyboardState = (
      POWEROFF,
      NORMAL,
      SETRXF,
      SETRXM,
      SETTXF,
      SETTXM,
      SETRXTXF,
      SETTXRXF,
      RCLCH,
      RCLRXCH,
      RCLTXCH,
      RCLPROMRXCH, // Boh???
      RCLPROMTXCH,
      STOCH,
      SETTIME,
      SETBFO,
      SETWAKEUP,
      SETOR,
      SETPR,
      SETGR,
      SELFTSTA,
      SELFTSTM,
      SELFTSTFMA,
      SELFTSTFMA1,
      SELFTSTFMM,
      SELFTSTFMM1,
      FILLSCNBUF,
      CHANNEL,
      CHANNELRX,
      CHANNELTX,
      SCANSETUP1,
      SCANSETUP2,
      SCANDWELL,
      SCANSTART,
      SCANCH,
      SCANTUBUF,
      READSETSTEP,
      SETBEEPER,
      READONHOURS,
      ANTENNAOFF,
      LASTKBSTATE // Must be the last item
);

// The scan parameters
TScanparams = record
  StartChan: integer;
  StopChan: integer;
  DwellTime: integer;
  OpenControlPort: boolean;
end;


Const
  // Object resize array max components number
  MAXCOMPONENTS = 300;

  //
  // Skanti command values
  // Taken from Skanti CU8000 remote commands list by Dave, G0WBX/G8KBV
  // Updates by Giuseppe, I1EPJ
  //
  RemoteEnable:   String = #1#2;    // SOH STX
  RemoteDisable:  String = #16;     // DLE
  RTX_Reset:      String = '!';     // Reset
  TX_Cmd:         String = '"';     // " Double quote mark (has 3 sec timeout)
  RX_Cmd:         String = '#';     // #

  TX_Prefix:      String = ';';     // ; + ASCII value + cr
  RX_Prefix:      String = ':';     // : + ASCII value + cr
  //	NOTE!  The value represents the frequency in multiples of 100Hz

  Recall:         String = '<';     // + ASCII value + cr
  Tune_Dn:        String = '=';     // TUNE down. See 'w'
  Tune_Up:        String = '>';     // TUNE up. See 'w'
  BFO_Tune_Dn:    String = '@';     // CW Beat tone Down 100Hz
  BFO_Tune_Up:    String = 'A';     // CW Beat tone Up 100Hz
  Filt_Wide:      String = 'B';     // Where enabled
  Filt_Inter:     String = 'C';     // Where enabled
  Filt_Narrow:    String = 'D';     // Where enabled
  Filt_VNarrow:   String = 'E';     // Where enabled
  Speaker_Toggle: String = 'F';     // Toggle On/Off
  PreAmp_Toggle:  String = 'G';     // Toggle On/Off
  Atten_Toggle:   String = 'H';     // Toggle On/Off
  Squelch_Toggle: String = 'I';     // Toggle On/Off
  Agc_On:         String = 'J';
  Agc_Fast:       String = 'K';
  Agc_Slow:       String = 'L';
  Agc_Off:        String = 'M';
  Sensitivity_Dn: String = 'N';     // Increment RF Gain Down (AGC must be OFF)
  Sensitivity_Up: String = 'O';     // Increment RF Gain Up (AGC must be OFF)
  AF_GainDec:     String = 'P';     // 'P';     // Increment Volume DAC Down.
  AF_GainInc:     String = 'Q';     // 'Q';     // Increment Volume DAC Up.
  TX_Tune:        String = 'R';     // Invoke TX tuning.
  TX_LowPwr:      String = 'S';
  TX_MediumPwr:   String = 'U';
  TX_HighPwr:     String = 'W';
  Mode_USB:       String = 'X';
  Mode_LSB:       String = 'Y';
  Mode_AM:        String = 'Z';
  Mode_Telex:     String = '[';
  Mode_R3E:       String = '\';
  Mode_CW:        String = ']';
  Mode_MCW:       String = '^';

  Test_Alarm:     String = 'a';     // Has 7s timeout - repeat
                                    // TEST ALARM command to override
  Stop_Alarm:     String = 'b';
  Send_Alarm:     String = 'c';     // Has 7s timeout - repeat
                                    // SEND ALARM command to override

  Store:          String = 'd';     // d + ASCII value + cr
  Scan:		  String = 'e';     // Has 7s timeout - repeat
                                    // SCAN command to override
  Set_Time:       String = 'f';     // 'f1234'#13 will set the time to 12:34
  Duplex:         String = 'g';

  Dimmer_Dn:      String = 'h';     // Display intensity down
  Dimmer_Up:      String = 'i';     // Display intensity up
  TX_OnOff:       String = 'j';     // Toggle TX Enable (On/Off)
  Speaker_On:     String = 'k';     // Speaker enable
  Speaker_Off:    String = 'l';     // lower case L
  PreAmp_On:      String = 'm';     // RF Pre-Amp Enable
  PreAmp_Off:     String = 'n';     // RF Pre-Amp Disable
  Attenuator_On:  String = 'o';     // Antenna Attenuator Enable
  Attenuator_Off: String = 'p';     // Antenna Attenuator Disable
  Squelch_On:     String = 'q';     // Squelch enable
  Squelch_Off:    String = 'r';     // Squelch disable
  Duplex_On:      String = 's';     // Duplex enable
  Duplex_Off:     String = 't';     // Duplex disable
  TX_On:          String = 'u';     // Enable (turn on) Transmitter
  TX_Off:         String = 'v';     // Disable (turn off) Transmitter

  TuneStepProg:   string = 'w3'#13; // Programmable rate
  TuneStep1k:     String = 'w2'#13; // RX TUNE Step 1kHz
  TuneStep100:    String = 'w1'#13; // RX TUNE Step 100Hz
  TuneStep10:     String = 'w0'#13; // RX TUNE Step 10Hz

  Set_BFO:        string = 'x';     // Set BFO frequency (-30 ... +30, 100 Hz units)
  AF_GainSet:     String = 'y';     // 'y..'#13
                                    //   .. = 00 = maximum (!! LOUD !!)
                                    //   .. = 99 = minimum

  Dimmer_Set:     string = 'z';     // 'z.'#13
                                    //   . = 0 = display off
                                    //   . = 5 = display maximum

  EQTXRX:         string = ':;'#13; // Copy RX frequency to TX

  F500k:          string = '`';     // Go to 500 kHz
  F2182k:         string = '_';     // Go to 2182 kHz
  Get_Status:     string = '*';     // Read status (S-meter, etc.)
  Read_Config:	  string = '(';     // Read configuration string

  Fill_Scan_Buf:  string = '$';     // + {<freq.rx><freq tx>}<CR>
  Adv_ScnBuf_Ptr: string = '%';     // + [RX display frequency]
  RST_ScnBuf_Ptr: string = '%'#13;  // Reset scan buffer pointer

  Set_Option_Reg: string = '{';     // + <value 0..255><ENTER>
  Set_Preset_Reg: string = '|';     // + <value 0..255><ENTER>
  Set_Guard_Reg:  string = '}';     // + <value 0..255><ENTER>

  Read_CU_Status: string = ')';     // firmware V92 only

  Set_Wakeup_Time:  string = 'f:';  // + time (HHMM)+CR
  Read_Wakeup_Time: string = 'f<';  // read wakeup time, 7s timeout
  Start_Dorm_State: string = 'f'#13;// Start dormant state

  // Second functions commands (2XX)
  Exec_AST:       string = '200'#13;  // Automatically stepped self test
  Exec_MST:       string = '201'#13;  // Manually stepped self test
  Exec_AST_Num:   string = '202'#13;  // + <test#><CR> Auto starting at test#
  Exec_MST_Num:   string = '203'#13;  // + <test#><CR> Manual starting at test#

  Read_ON_Time:   string = '241'#13;  // Read accumulated on-time
                                      // 7s timeout, repeat command to override,
                                      // value read is shown only in the CU
                                      // RX display
  Read_Set_Step:  string = '242'#13;  // Read or program receiver tune step
                                      // + <value 1...999><ENTER> (100 Hz units)
                                      // Has 7s timeout, value read is shown
                                      // only in the CU RX display

  Read_BFO_Freq:  string = '244'#13;  // Read BFO frequency
                                      // <+-><frequency in 100Hz units>
  Store_BFO_Freq: string = '244'#13'd'#13; // Store default BFO frequency

  Read_CU_Vers:   string = '246'#13;  // + <CR>
  Read_TU_Vers:   string = '247'#13;  // + <CR>
  Set_Beeper_Lev: string = '248'#13;  // + <CR> (does not work from remote)
  Switch_Ant_Off: string = '249'#13;  // + <CR>
  Read_Option_Reg:string  = '279'#13; // + <CR>

  // Control characters as strings
  stSOH:          string = #$01;
  stSTX:          string = #$02;
  stETX:          string = #$03;
  stEOT:          string = #$04;
  stACK:          string = #$06;
  stBEL:          string = #$07;
  stCR:           string = #$0D;
  stLF:           string = #$0A;
  stDLE:          string = #$10;
  stNAK:          string = #$15;
  stCAN:          string = #$18;

  // Valid range of meter reading
  MetersRange = ['`'..'t'];

  // Valid range of status messages
  StatusRange = ['u'..'z'];

  // Filenames & paths
  StateFileName:   string = 'LastState.dat';
  ConfigFileName:  string = 'Config.dat';
  XMLConfigFileName: string = 'Config.xml';
  BandScansDir: string = 'BandScans';

  // Mode values
  M_USB = 1;
  M_LSB = 2;
  M_AM  = 3;
  M_R3E = 4;
  M_CW  = 5;
  M_MCW = 6;
  M_TLX = 7;
  M_500K = 8;
  M_2182K = 9;

  // Filter values
  FILTER_Default = 0;
  FILTER_WIDE = 1;
  FILTER_INTERMEDIATE = 2;
  FILTER_NARROW = 3;
  FILTER_VERYNARROW = 4;

  // AGC values
  AVC_OFF = 0;
  AVC_ON = 1;
  AVC_FAST = 2;
  AVC_SLOW = 3;

  // TX power
  LOW_POWER = 1;
  MEDIUM_POWER = 2;
  FULL_POWER = 3;

  // frequency coverage
  MINRXF = 10000;
  MAXRXF = 29999990;
  MINTXF = 1605000;
  MAXTXF = 29999900;

  //Programmable tune step
  MINTUNESTEP = 1;
  MAXTUNESTEP = 999;

  // keyboard commands
  CommandKeys = [' ','+','-','*','/','R','T','L','U','A','C','M','E','X','.',#13];

  // Delays for sleep (milliseconds)
  RETRYDELAY = 100;
  SLOWCMDDELAY = 100;
  BFOREADDELAY = 300;

  // Timeouts in milliseconds
  CMDTIMEOUT = 1600*OneMillisecond;
  TUNETIMEOUT = 15000*OneMillisecond;
  TESTTIMEOUT = 15000*OneMillisecond;
  DLETIMEOUT = 15000*OneMillisecond;
  TXUPTIMEOUT = 1000*OneMillisecond;

  // Wait times for WaitTimeSecs() (seconds)
  SHORTWAITTIME = 3;
  LONGWAITTIME = 5;
  RESETWAITTIME = 7;
  TESTWAITTIME = 10;
  DLEWAITTIME = 10;

  // Maximum number of retries in SendCommand
  MAXRETRY = 5;

  // LED to S units conversion array. Values are largely approximate, since
  // they have been measured on my own TRP8255 at 14.000 MHz with two old
  // signal generators of uncertain calibration: a HP 8657B and a Marconi 2019A.
  // Anyone with a calibrated signal generator is welcome to check these
  // values on his own TRP8255 and provide corrections.
  LEDSToSUnits: array[1..20] of string =(
  // #LEDS     1    2    3    4    5    6    7    8    9    10
              'S1','S2','S2','S2','S3','S3','S3','S4','S4','S5',
  // #LEDS     11   12   13   14   15    16      17      18      19      20
              'S5','S6','S7','S8','S9','S9+10','S9+20','S9+30','S9+40','S9+50');

  // Supply ON/OFF and TX ON/OFF base captions
  BaseBONOFFCaption = 'SUPPLY'+LineEnding+'ON/OFF';
  BaseBTXONOFFCaption = 'TX'+LineEnding+'ON/OFF';

 var
  TRP: TTRP;
  ProgVersion: TVersionQuad;
  Major, Minor, Revision, Build: integer;
  ShortVersion, LongVersion: string;
  RTXState, OldRTXState: TRTXState;
  SerPort: TSerialHandle = 0;
  SerPortName: string;
  SerPortSpeed: integer = 2400;
  s,t: integer;
  status: string;
  STimer1, STimer2: integer;
  RXFBuf, TXFBuf, OldRXFreq, OldTXFreq: string;
  sMode_RX: string;
  sMode_TX: string;
  KeyboardState: TKeyboardState = NORMAL;
  Enable_status: boolean = FALSE;
  In_CMDRPT: boolean = FALSE;
  In_Repeat: boolean = FALSE;
  In_Command: boolean = FALSE;
  In_Resize: boolean = FALSE;
  In_NETRX: boolean = FALSE;
  In_MouseWheel: boolean = FALSE;
  TXFreqModified: boolean = FALSE;
  Time_Flash: boolean = TRUE;
  Mode_Flash: boolean = TRUE;
  enough: boolean = FALSE;
  ButtonToFlashRX: TJButton;
  ButtonToFlashTX: TJButton;
  ButtonToRepeat: TJButton;
  CmdToRepeat: string;
  tmp: longint;
  HomeDir,StateDir,ChannelsDir,ImgDir,DocDir: string;
  timeout,UpdateFTXTimeout: TTime;
  Step10HzSelected: boolean = FALSE;

  PImgON, PImgOFF, PImgArrUpOFF, PImgArrUpON,
  PImgArrDnOFF, PImgArrDnON: TPicture;

  LEDColorOFF: TCOlor = clYellow;
  LEDColorON: TCOlor = ClOlive;
  sLEDColor: string = 'yellow';
  HaveConfig: boolean = FALSE;
  HaveState: boolean = FALSE;
  HighSWR: boolean = FALSE;
  ReducedPower: boolean = FALSE;
  ScanBuf: array[1..100, 1..2] of string;
  NextTest,AbortTest: boolean;
  n,m: integer;
  ErrMsg: string;
  BandFileName: string;
  Last_AGC: integer;
  SMshapes: array[1..20] of TShape;
  PMShapes: array[1..20] of TShape;
  EnterNormalWidth,EnterSetBFOWidth: integer;
  Flash_Speaker: boolean = TRUE;
  SPKRLed: boolean = TRUE;
  Own500Frequency: boolean;
  Own500Filename: string = '';
  Own2182Frequency: boolean;
  Own2182Filename: string = '';
  PPI_X, PPI_Y: integer;
  FontMagn: integer = 100;
  ImgFilesOK: boolean;
  Cmd_Aborted: boolean;
  TuneAtFreqChange: boolean = FALSE;
  ScanParams: TScanParams =
      (StartChan: 0; StopChan: 75; DwellTime: 10; OpenControlPort: FALSE);
  MsgToShow: string;


  {$IFDEF AUTOSCALE}
  // Variables used by autoscale code
  OH, OW: integer;
  Xs: array[0..MAXCOMPONENTS] of integer;
  Ys: array[0..MAXCOMPONENTS] of integer;
  Ws: array[0..MAXCOMPONENTS] of integer;
  Hs: array[0..MAXCOMPONENTS] of integer;
  Wt: array[0..MAXCOMPONENTS] of Integer;
  Ht: Integer;
  Wc: array[0..MAXCOMPONENTS] of Integer;
  Hc: Integer;
  Wn: array[0..MAXCOMPONENTS] of Integer;
  Hn: Integer;
  Hf: array[0..MAXCOMPONENTS] of Integer;
  StartFontHeight: integer;
  {$ENDIF}

  // Variables used to save and restore main window position and size
  StartHeight, StartWidth: integer;
  StartX, StartY: integer;

  // Variables for net rigctl server
  RXLine: string;
  NumTCPConnections: integer = 0;
  RemFreqChange: boolean = FALSE;
  TuneRequired: boolean = FALSE;
  PanelUpdateRequired: boolean = FALSE;

  {$IFDEF DEBUG_KBSTATE}
  OldKeyboardState: TKeyboardState = LASTKBSTATE;
  KBStateNames: array[0..int64(LASTKBSTATE)-1] of string = (
    'POWEROFF',
    'NORMAL',
    'SETRXF',
    'SETRXM',
    'SETTXF',
    'SETTXM',
    'SETRXTXF',
    'RCLCH',
    'RCLRXCH',
    'RCLTXCH',
    'RCLPROMRXCH',
    'RCLPROMTXCH',
    'STOCH',
    'SETTIME',
    'SETBFO',
    'SETWAKEUP',
    'SETOR',
    'SETPR',
    'SETGR',
    'SELFTSTA',
    'SELFTSTM',
    'SELFTSTFMA',
    'SELFTSTFMA1',
    'SELFTSTFMM',
    'SELFTSTFMM1',
    'FILLSCNBUF',
    'CHANNEL',
    'CHANNELRX',
    'CHANNELTX',
    'SCANSETUP1',
    'SCANSETUP2',
    'SCANDWELL',
    'SCANSTART',
    'SCANCH',
    'SCANTUBUF',
    'READSETSTEP',
    'SETBEEPER',
    'READONHOURS',
    'ANTENNAOFF',
    'LASTKBSTATE');
  {$ENDIF}

 // Messages are declared here for i18n
 resourcestring

  // Messages & window titles
  ANTTXOFF = 'Antenna & TX OFF, press TX ON/OFF to resume.';
  AREUSURE = '       Are you sure?       ';
  BFO = 'BFO = ';
  BFOFINVALID = 'BFO frequency invalid.';
  BFOSTORED = 'BFO frequency stored: ';
  CANTOPENSERIAL = 'Can''t open serial port ';
  CANTRELEASE = 'Priority cannot be released with Status reading enabled.';
  CANTWLASTSTATE = 'Can''t write last state file.';
  CANTWRITECONF = 'Can''t write config file.';
  CANTWRITESTATE = 'Can''t write state file.';
  CHANMANAGER = 'Channel Manager';
  CHANNELMODE = 'Channel mode, state unknown.';
  CHANNOTDEF = 'Channel %d not defined.'+LineEnding+
               'Restoring previous state.';
  CHECKINST = 'Check your installation.';
  CLOSEPROGR = 'Close program';
  CLOSECONTROLPORT = 'Scan control port closed';
  COLFILESNOTF = 'One or more color files were not found.';
  COMMANDFAILED = 'Command failed.';
  COMMANDSENT = 'Command sent: ';
  CONFIGMESSEDUP = 'Very old or messed up config file,'+LineEnding+
                   'starting with a default one.';
  CONFIGNOTFOUND = 'Config file not found, loading and saving'+LineEnding+
                   'default configuration.';
  CONFIGURATION = 'Configuration: ';
  COPYRIGHTST = ' (C) 2012-2025 G. Perotti, I1EPJ';
  CUV = 'CU8000 version: ';
  CURRENTSCANPARAMS = 'Current scan parameters:'+Lineending+
                      'Start=%d, Stop=%d, Dwell=%d.%ds, OpenControlPort=%s';
  CUV92NOTFOUND = 'CU8000 V92.0 NOT found.';
  CUV92ONLY = 'Only available in CU8000 V92.0.';
  DEFAULTSTEP = 'Default step is ';
  DIMLEVEL = 'Dimmer level = ';
  DIMLEVELMAX = 'Dimmer level 5 (maximum).';
  DIMLEVMIN = 'Dimmer level 0 (minimum).';
  DIR = 'Directory';
  DLEREC = 'DLE received, attempting CU reconnection.';
  DOCFILESNOTF = 'Manual and/or license files not found.';
  DONE = 'Done.';
  DORMSTSTA = 'Dormant state started.'+LineEnding+
              'After this delay, press [SUPPLY ON/OFF]'+LineEnding+
              'on the CU. Leave the power supply on.';
  DWELLTIMESET = 'Dwell time set to %1.1fs';
  ENTERBFOFREQ = 'Enter BFO frequency in kHz.';
  ENTERCHANNEL = 'Enter channel number (0..75).';
  ENTERSCANSTART = 'Enter scan start channel # (0..74)' + Lineending +
                   'Press [SET TIME] to define dwell.' + Lineending +
                   'Press [RX]/[TX] to open/close scan control port.' + LineEnding +
                   'Press [SCAN] to start scanning.' + LineEnding +
                   'Press [ENTER] to stop on current channel.';
  ENTERSCANSTOP = 'Enter scan stop channel # (%d..75).';
  ENTERDWELL = 'Enter scan dwell time (0.1..9.9).';
  ENTERFPAIRS = 'Enter one or more frequency pairs.';
  ENTERREGVALUE = 'Enter register value in binary.';
  ENTERSTEP = 'Enter value (0.1...99.9 kHz).';
  ENTERTESTNUM = 'Enter start test number.';
  ENTERTIME = 'Enter time (4 digits).'+ LineEnding+
              'Press SET TIME again for PC time.'+LineEnding+
              'Press RCL to display wakeup time'+LineEnding+
              'Press RX to set wakeup time'+LineEnding+
              'Press ENTER when done.';
  ENTERWAKEUP = 'Enter wakeup time as HHMM and press ENTER when done.';
  ERROR = 'Error';
  ERRRDNGSTATE = 'Error reading current state.';
  ERRORNET = 'Net error occurred: %s';
  EXPECTSTBE = 'Expect strange behaviour in scaling routines.';
  FAILEDTOINIT = 'Failed to initialize communications.'+LineEnding
                            +'Retry?';
  FILENOTFOUND = 'File not found.';
  GETPROGVFAIL = 'GetProgramVersion failed, please enable'+LineEnding
                            +'version informations in Project Options.';
  GOTGREATER = 'Status: got >';
  INITPROGRAM = 'Initializing SkantiControl...';
  INVALIDCHANNEL = 'Invalid channel number.';
  INVALIDREPLY = 'Invalid Reply received.';
  INVALIDSPLIT = 'Can''t set that split mode.';
  INVALIDSTEP = 'Programmable step must be 0.1 kHz...99.9 kHz.';
  INVALIDTESTNUM = 'Start test number invalid.';
  INVALIDTIME = 'Invalid time.';
  LISTENFAILED = 'Remote server: listen() failed.';
  LOADINGLASTSTA = 'Loading last state from disk.';
  LOADINGSFROMCU = 'Loading state from CU8000.';
  NOCHANNELS = 'No channels defined';
  NONSQUAREPIX = 'This screen does not have square pixels:';
  NOPREVSTATE = 'No previous state found, using default.';
  NOREPLY = 'No reply, status reading disabled.';
  NORMALMODE = 'Normal mode.';
  NOTALLOWED = 'Mode change not allowed here.';
  NOTF = ' not found.';
  NOTHING = 'nothing';
  NOTINTHATSTATE = 'Not in that state, ignored.';
  NOVERSINFO = 'No version info.';
  OF20 = ' of 20 (';
  OKCUV92FOUND = 'OK, CU8000 V92.0 found.';
  OLDSTYLECONF = 'Old style config file found, updating and saving.';
  OLDUPDATING = 'Old format config file found, read and updated to the new XML format.';
  ONLYINTHECU = 'Required value is shown only in the CU';
  OPENCONTROLPORT = 'Scan control port opened.';
  OWN2182FREQ = 'Own 2182 frequency found.';
  OWN500FREQ = 'Own 500 frequency found.';
  POWNORMAL = 'Power normal.';
  POWREDUCED = 'Power reduced.';
  PRESSENTER = 'Press ENTER when done.';
  PREVIOUSSTATE = 'Previous state was %s, frequency unknown.'+LineEnding+
                  'Please set RX and/or TX frequencies.';
  PROGNAME = 'SkantiControl ';
  PROGDESC = 'Skanti TRP8000 series control program ';
  PROGTUNESTEPDI = 'Programmable tune step disabled.';
  PROGTUNESTEPEN = 'Programmable tune step enabled.';
  PROMPT = '_';
  PSEWAIT = 'Please wait';
  READINGBFOFREQ = 'Reading BFO frequency.';
  READY = 'Ready!';
  REALLYEXIT = '    Really exit?    ';
  REGISTERSET = '%s register set to %sb (%dd).';
  RELEASED = 'Remote priority released.';
  RESETDONE = 'Reset done.';
  RESTSTATE = 'Restoring state.';
  SRESULT = ' Result: ';
  RETRYCEXC = 'Retry count exceeded executing  command ''%s'', giving up.';
  RETRYNO = 'Retry #';
  RXCANTTUNE = 'RX can''t tune to that frequency.';
  SAVECHAN = 'Save Channel File';
  SAVECHANFILT = 'Channel files|*.dat';
  SAVESTATE = 'Save state file';
  SAVESTATEFILT = 'State files|*.dat';
  SCANBUFMODE = 'ScanBuf mode, frequency unknown.';
  SCANNOTIMPLEM = 'Scan syntax not yet implemented.';
  SCANPARAMS1 = 'Scan set from channel %d to channel %d.';
  SCANPARAMS2 = 'Scan parameters: start=%d, stop= %d, dwell=%1.1f';
  SCANTU = 'SCAN_TU';
  SENDALARM = 'REALLY SEND ALARM?';
  SENDCRECVD = 'SendCommand received %s.';
  SENDINGALARM = 'SENDING ALARM!';
  SPEEDTOOSLOW = 'Serial speed too slow, status reading disabled.';
  STOPGREATERSTART = 'Stop channel must be greater than start channel';
  STATEMANAGER = 'State manager';
  STOPALARM = 'Alarm stopped.';
  SWRGREAT4 = 'SWR > 4.';
  SWRLESS4 = 'SWR < 4.';
  TESTALSTA = 'Test alarm started.';
  TESTNO = 'Test #%s%s %s%s';
  TESTTIMEOUTEXPIRED  = 'Timeout waiting for test result.';
  TESTTIMEOUTEXPIRED1 = 'Cycle RTX power and press [POWER ON/OFF].';
  TIM7S = 'WARNING: 7s timeout!';
  TIMEOUTFORSTAR = 'Timeout waiting for ''*''.';
  TIMEOUTWDLE = 'Timeout waiting for DLE.';
  TIMEXPCMD = 'Timeout expired (CMD).';
  TOEXIT = 'To exit Chan/Scan select ''Exit Chan/Scan modes'' menu.';
  TRANSSTATUSENA = 'WARNING!'+
                    LineEnding+'Remote connected with Transceiver'+
                    LineEnding+'and/or Status reading enabled,'+
                    LineEnding+'expect possible strange effects!';
  TRYTOREC = 'Trying to reconnect to the CU.';
  TUC = 'TU8000 configuration: ';
  TUV = 'TU8000 version: ';
  TUNCOMPLETED = 'Tuning completed.';
  TUNINGERROR = 'ATU8000 tuning erroneous.';
  TUNINGSUCCESS = 'ATU8000 tuning successful.';
  TXCANTTUNE = 'TX can''t tune to that frequency.';
  UNSUPPORTEDCONFIG = 'WARNING: unsupported config file version %s'+
                       LineEnding+
                       'Triyng to read it anyway.';
  UNTOREADOPTR = 'Unable to read option register.';
  USSTBFO = 'Using stored BFO frequency.';
  VOLATTMAX = 'Volume attenuation at maximum (99).';
  VOLATTMIN = 'Volume attenuation at minimum (0).';
  VOLUMEATT = 'Volume attenuation: ';
  WAKEUPSET = 'Wakeup time set to: ';
  YOUAREINTX = 'Note: you are transmitting.';

implementation

{$R *.lfm}

{ TTRP }

//
// Auxiliary procedures & functions
//

// Make control chars visible in command strings
function TTRP.ShowControlChars(cmd: string): string;

var chp: integer;
    stmp: string = '';
    chtmp: string;
begin
  // Display control chars in command strings (see MSHOWCClick)
  for chp := 1 to length(cmd) do begin
    case cmd[chp] of
      {$IFDEF WIN32}
      // No control chars unicode symbols in Win32 (XP)
      // Show only CR as paragraph symbol
      #$0D: chtmp := '¶';
      otherwise chtmp := cmd[chp];
      {$ELSE}
      #$01: chtmp := '␁';
      #$02: chtmp := '␂';
      #$03: chtmp := '␃';
      #$04: chtmp := '␄';
      #$06: chtmp := '␆';
      #$07: chtmp := '␇';
      #$0A: chtmp := '␊';
      #$0D: chtmp := '␍';
      #$10: chtmp := '␐';
      #$15: chtmp := '␕';
      #$18: chtmp := '␘';
      otherwise chtmp := cmd[chp];
      {$ENDIF}
    end;
    stmp := stmp+chtmp;
  end;
  ShowControlChars := stmp;
end;

// Wait for seconds optionally displaying a progress message in MSG memo
procedure TTRP.WaitTimeSecs(seconds: integer; Mesg: string; DisplayProgress: boolean);

const
  OneSecond = 1000*OneMillisecond;

var StartTime, EndTime, LastTime, NowTime: TTime;
    index: integer = 0;

begin
  MSG.Enabled := TRUE;
  StartTime := Time;
  LastTime := StartTime;
  EndTime := StartTime+Seconds*OneSecond+10*OneMillisecond;
  if DisplayProgress then begin
    if (Mesg <> '') then
      MSG.Lines.Add(Mesg)
    else if DisplayProgress then
      MSG.Lines.Add(PSEWAIT);
    MSG.VertScrollBar.Position := 10000;
    index := MSG.Lines.Count-1;
    if index < 0 then index := 0;
  end;
  repeat
    NowTime := Time;
    if DisplayProgress and ((NowTime-LastTime) > OneSecond) then begin
      MSG.Lines[index] := MSG.Lines[index]+'.';
      LastTime := NowTime;
    end;
    RunGUI;
  until NowTime > EndTime;
end;

// Magnify fonts by the selected ratio
procedure TTRP.FontMagnify(Scaling: boolean);

var c: TControl;
    i, NewHeight, NewHeight1, NewHeight2, tmpTFontMagn, tmpOFontMagn: integer;

begin
  // If we are scaling from a window resize, do the scaling for all fonts,
  // otherwise if the "Magnify all fonts" option is not checked, scale the
  // font used in the RX and TX display without apply the magnification factor
  if MMAGAF.Checked or Scaling then begin
    tmpTFontMagn := FontMagn;
    tmpOFontMagn := FontMagn;
  end else begin
    tmpTFontMagn := 100;
    tmpOFontMagn := FontMagn;
  end;

{$IFDEF DEBUG_FONTMAGN}
  msgtoshow :=
    Format('Scaling = %s, MMAGAF=%s, FontMagn=%d, tmpTfontmagn=%d, tmpOFontMagn=%d',
           [BoolToStr(Scaling, TRUE), BoolToStr(MMAGAF.Checked, TRUE), fontmagn,
           tmpTfontmagn, tmpOFontMagn]);
  {$ENDIF}

  {$IFNDEF DISABLE_FONTSCALE}
  for i := 0 to ComponentCount-1 do begin
    if (Components[i] is TLabel) or
       (Components[i] is TStaticText) or
       (Components[i] is TMemo) or
       (Components[i] is TJButton) then begin
      {$IFDEF DEBUG_AUTOSCALE}
      MsgToShow := 'scaling font';
      {$ENDIF}
      c := TControl(Components[i]);

      if (Components[i] is TstaticText) and (c.Tag <> 0) then begin
        NewHeight1 := Hf[i] * ClientWidth * tmpTFontMagn div (OW * 100);
        NewHeight2 := Hf[i] * ClientHeight * tmpTFontMagn div (OH * 100);
        NewHeight := min(abs(NewHeight1), abs(NewHeight2));
        (Components[i] as TstaticText).Font.Height := NewHeight;
        {$IFDEF DEBUG_FONTSCALE}
        MsgToShow := 'OW='+inttostr(OW)+' OH='+inttostr(OH);
        MsgToShow := 'ClientWidth='+IntToSTr(CLientWidth)+', ClientHeight='+IntToStr(ClientHeight);
        MsgToShow := 'Width='+IntToSTr(Width)+', Height='+IntToStr(Height);
        MsgToShow := 'NewHeight='+IntToStr(NewHeight);
        RunGUI;
        {$ENDIF}
        continue;
      end;

      NewHeight1 := Hf[i] * ClientWidth * tmpOFontMagn div (OW * 100);
      NewHeight2 := Hf[i] * ClientHeight * tmpOFontMagn div (OH * 100);
      NewHeight := min(abs(NewHeight1), abs(NewHeight2));
      {$IFDEF DEBUG_FONTSCALE}
      MsgToShow := 'OW='+inttostr(OW)+' OH='+inttostr(OH);
      MsgToShow := 'ClientWidth='+IntToSTr(CLientWidth)+', ClientHeight='+IntToStr(ClientHeight);
      MsgToShow := 'Width='+IntToSTr(Width)+', Height='+IntToStr(Height);
      MsgToShow := 'NewHeight='+IntToStr(NewHeight);
      RunGUI;
      {$ENDIF}

      if (Components[i] is TJButton) then begin
        (Components[i] as TJButton).LCaption.Font.Height := NewHeight;
      end else begin
        c.Font.Height := NewHeight;
      end;
    end;
  end;
  {$ENDIF}
end;

// Set low power
procedure TTRP.SetLowPower;

begin
  In_Command := TRUE;
  if not SendCommand(TX_LowPwr, '') and not Cmd_Aborted then
    MsgToShow := COMMANDFAILED
  else
    RTXstate.TX_Power := LOW_POWER;
  In_Command := FALSE;
end;

// Enable programmable tune step in the CU by setting option register
// bit 4 to 1 (i.e. 16 decimal). Other bits are zeroed since there is
// no way to read the current option register value
procedure TTRP.EnableProgTuneStep;

begin
  In_Command := TRUE;
  if SendCommand(Set_Option_Reg, '16'+stCR) then begin
    MsgToShow := PROGTUNESTEPEN;
  end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  In_Command := FALSE;
end;

// Set config default values
procedure TTRP.SetDefaultConfig;

begin
 {$IFDEF LINUX}
 SerPortName := '/dev/ttyS0';
 {$IFDEF CPUARM}
 SerPortName := '/dev/ttyAMA0';
 {$ENDIF}
 {$ENDIF}
 {$IFDEF WINDOWS}
 SerPortName := '\\.\COM1';
 {$ENDIF}
 SerPortSpeed := 2400;         // default CU8000 speed
 M300B.Checked := FALSE;       // 300 baud menu item unchecked
 M2400B.Checked := TRUE;       // 2400 baud menu item checked
 MICDNONE.Checked := TRUE;     // No inter-character delay
 // TCP Server default address and port defined in TRPServer object
 MTCHF.Checked := FALSE;       // No TUNE when changing frequency from remote
 MCU920.Checked := FALSE;      // CU 92.0 disabled
 MDISU.Checked := TRUE;        // Transceiver mode off
 MStatus.Checked := FALSE;     // No status reading
 MUSEUTC.Checked := TRUE;      // Use UTC time
 MPROGSTEP.Checked := FALSE;   // Programmable step disabled
 MALWLP.Checked := FALSE;      // Always start in LP mode disabled
 LEDColorOFF := ClOlive;       // LED color OFF (Yellow)
 LEDColorON := clYellow;       // LED color ON  (Yellow)
 sLEDColor := 'yellow';        // Color name
 MYellow.Checked := TRUE;      // Yellow menu item checked
 MRed.Checked := FALSE;        // Red menu item unchecked
 MGreen.Checked := FALSE;      // Green menu item unchecked
 MMAG100.Checked := TRUE;      // 100% magnification (i.e. no magnification)
 MMAGAF.Checked := TRUE;       // Magnify all fonts enabled
 FontMagn := 100;              // 100% font magnification
 MENALL.Checked := FALSE;      // Do not enable all controls
 MSHOWC.Checked := FALSE;      // Do not show CU commands
 MSHOWHAMLIB.Checked := FALSE; // Do not show Hamlib commands
 // Default main window position and size
 StartX := 0;                  // Start window position X
 StartY := 0;                  // Start window position Y
 StartWidth := 732;            // Start window width
 StartHeight := 625;           // Start window heigth
end;

// Load State from CU (firmware V92 only)
procedure TTRP.LoadStateFromCU;

var s, State: string;
    itmp: integer;

begin
 {$IFDEF DEBUG_START}
 MsgToShow := TimeToStr(now)+': start LoadStatefromCU';
 RunGUI;
 {$ELSE}
 MsgToShow := LOADINGSFROMCU;
 RunGUI;
 {$ENDIF}
 In_Command := TRUE;
 FLASH.Enabled := FALSE;

  {$IFDEF TEST_USER_INTERFACE}
  MsgToShow := 'Executing LoadStateFromCU';
  RunGUI;
  In_Command := FALSE;
  {$ELSE}
  if SendCommand(Read_CU_Status, '') then begin
    {$IFDEF TEST_READ_CU}
    State := '*Y123456123456111111111111>';
    {$ELSE}
    State := GetReply(27,CMDTIMEOUT);
    {$ENDIF}
    {$IFDEF DEBUG_READSTATUS}
    MsgToShow := 'State string read: '+State;
    RunGUI;
    {$ENDIF}
    // String returned:
    // 000000000111111111122222222
    // 123456789012345678901234567
    // *YRRRRRRTTTTTTMVVRFCDBAaPQ>
    // Example:
    // *Y1234561234563961F1501000>

    // Proceed only if received string is complete
    if (State[1] = '*') and (State[2] = 'Y') and (State[27]='>') then begin
      with RTXState do begin
        // Frequency
        Freq_RX := StrToInt(copy(State,3,6))*100;
        Freq_TX := StrToInt(copy(State,9,6))*100;

        // Mode
        case State[15] of
          '0': Mode_RX := M_USB;
          '1': Mode_RX := M_R3E;
          '2': Mode_RX := M_AM;
          '3': Mode_RX := M_CW;
          '4': Mode_RX := M_MCW;
          '5': Mode_RX := M_TLX;
          '6': Mode_RX := M_LSB;
          '7': begin
            MsgToShow := 'Mode 7, record it!';
            RunGUI;
          end;
          '8': begin
            Mode_RX := M_AM;
            Mode_TX := M_USB;
          end;
          '9': begin
            Mode_RX := M_LSB;
            Mode_TX := M_USB;
          end;
          'A': begin
            Mode_RX := M_AM;
            Mode_TX := M_R3E;
          end;
          'B': begin
            Mode_RX := M_LSB;
            Mode_TX := M_R3E;
          end;
          'C': begin
            Mode_RX := M_USB;
            Mode_TX := M_AM;
          end;
          'D': begin
            Mode_RX := M_LSB;
            Mode_TX := M_R3E;
          end;
          'E': begin
            Mode_RX := M_USB;
            Mode_TX := M_LSB;
          end;
          'F': begin
            Mode_RX := M_AM;
            Mode_TX := M_LSB;
          end;
          otherwise begin
            MsgToShow := 'Mode = '+State[15];
            RunGUI;
          end;
        end;
        if State[15] in ['0'..'6'] then
          Mode_TX := Mode_RX;

        // Volume (value returned is not 0...99, but $00...$BA,
        // at least on my TRP8255)
        Volume := 99*Hex2Dec(State[16]+State[17]) div $BA;
        {$IFDEF DEBUG_VOLUME}
        MsgToShow := 'Volume read MSB/LSB= '+State[16] + State[17];
        MsgToShow := 'Calculated volume = '+IntToStr(Volume);
        RunGUI;
        {$ENDIF}

        // Tune rate
        case State[18] of
          '0': Freq_Step := 10;
          '1': Freq_Step := 100;
          '2': Freq_Step := 1000;
          '3': Freq_Step := -1;     // programmable step
          otherwise begin
            MsgToShow := 'Rate = '+State[18];
            RunGUI;
          end;
        end;
        if Freq_Step = 10 then Step10HzSelected := TRUE;
        if (Freq_Step = -1) and Programmable_Step_Set then
          Freq_Step := Programmable_Step;

        // Speaker
        case ((ord(State[19]) and $03)) of
          0: begin
            Message ('Found SPKR bits = 00');
          end;
          1: begin
            Flash_Speaker := TRUE;
            Speaker_Enabled := FALSE;
          end;
          2: begin
              Flash_Speaker := TRUE;
              Speaker_Enabled := TRUE;
          end;
          3: begin
              Message ('Found SPKR bits = 11');
          end;
        end;
        {$IFDEF DEBUG_SPEAKER}
        MsgToShow := 'Speaker bits: '+
        IntToStr(((ord(State[19]) and $02) div 2))+' '+
        IntToSTr((ord(State[19]) and $01));
        RunGUI;
        {$ENDIF}

        // TX & Antenna off/on, power and current LEDS
        TX_Enabled   := ((ord(State[20]) and $01) <> 0);
        Antenna_OFF  := ((ord(State[20]) and $02) <> 0);
        if Antenna_OFF then begin
          KeyboardState := ANTENNAOFF;
          SetActiveButtons;
          UpdatePanel;
        end;
        if ((ord(State[20]) and $04) <> 0) then SPLOW.Brush.Color := LEDColorOn;
        if ((ord(State[20]) and $08) <> 0) then SCUR.Brush.Color := LEDColorOn;

        // Dimmer
        Dimmer_Level := StrToInt(State[21]);

        // Filter
        case State[22] of
          '0': Filter := FILTER_INTERMEDIATE;
          '1': Filter := FILTER_WIDE;
          '2': Filter := FILTER_NARROW;
          '3': Filter := FILTER_VERYNARROW;
          '4': Filter := FILTER_DEFAULT;
          otherwise begin
            MsgToShow := 'Filter = '+State[22];
            RunGUI;
          end;
        end;
        // CU does not give Filter LEDS status, so force them to be lit
        if RTXstate.Mode_RX in [M_USB, M_LSB, M_MCW, M_R3E] then
          BINTERClick(Self);
        if RTXstate.Mode_RX = M_AM then
          BWIDEClick(Self);

        // AGC
        case State[23] of
          '0': AGC := AVC_SLOW;
          '1': AGC := AVC_FAST;
          '2': AGC := AVC_OFF;
          '3': AGC := AVC_OFF;
          otherwise begin
            MsgToShow := 'AGC = '+State[23];
            RunGUI;
          end;
        end;

        // RF amplifier & RF Attenuation
        RFAmp_Enabled := (ord(State[24]) and $01) <> 0;
        RFAtt_Enabled := (ord(State[24]) and $02) <> 0;

        // Power
        case State[25] of
          '0': TX_Power := FULL_POWER;
          '1': TX_Power := MEDIUM_POWER;
          '2': TX_Power := LOW_POWER;
          otherwise begin
            MsgToShow := 'Power = '+State[25];
            RunGUI;
          end;
        end;

        // Set low power mode if required
        if MALWLP.Checked then begin
          TX_Power := LOW_POWER;
          SetLowPower;
        end;
        // Duplex
        Duplex := (ord(State[26]) and $01) <> 0;

        // Squelch
        Squelch_Enabled := (ord(State[26]) and $02) <> 0;

        // The BFO frequency is not present in the returned string,
        // so if CW mode read it.
        if Mode_RX = M_CW then begin
          SerFlushInput(SerPort);
          MsgToShow := READINGBFOFREQ;
          RunGUI;
          if SendCommand(Read_BFO_Freq, '') then begin
            s := GetReply(3,CMDTIMEOUT);
            sleep(BFOREADDELAY);
            if not SendCommand(stCR, '') and not Cmd_Aborted then begin
              MsgToShow := COMMANDFAILED;
              RunGUI;
            end;
            if length(s) = 3 then begin
              // May be not more required, but just in case...
              if TryStrToInt(s,itmp) then
                  BFO_freq := 100*itmp
              else begin
                  MsgToShow := BFOFINVALID;
                  MsgToShow := USSTBFO;
                  RunGUI;
              end;
            end;
            // The Read_BFO_Freq command sets the bandwidth to
            // intermediate regardless of the previous value,
            // so set again the correct value.
            RestoreFilterAndAGC;
          end else begin
            if not Cmd_Aborted then begin
              MsgToShow := COMMANDFAILED;
              RunGUI;
            end;
          end;
        end;
      end;
    end else begin
      MsgToShow := ERRRDNGSTATE;
      RunGUI;
    end;
    FLASH.Enabled := TRUE;
    In_Command := FALSE;
    {$IFDEF DEBUG_START}
    MsgToShow := TimeToStr(now)+': end LoadStatefromCU';
    RunGUI;
    {$ELSE}
    MsgToShow := DONE;
    {$ENDIF}
  end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  {$ENDIF}
end;

// Check if firmware is V92
// Works, but modifies the status of the CU (after calling it, the bandwidth
// is set to Intermediate), so it is useful only as check, it is not useable
// to automatically determine if the CU firmware is V92 and then, if yes,
// loading the status with the "Read CU status" command.
function TTRP.Check_V92_Firmware: boolean;

var Version: string;

begin
  SendCommand(Read_CU_Vers, '');
  Version := GetReply(13,CMDTIMEOUT);
  SendCommand(stCR, '');
  if pos('-92', Version) = 0 then
    exit(FALSE)
  else
    exit(TRUE);
end;

// Check if programmable tune step is enabled in config register
// Does not work since:
// * no reply from CU, bits are only shown in the CU display
// Do not use
function TTRP.CheckProgrammableTuneStep: boolean;

var s: string;

var value: integer;

begin
  {$IFDEF TEST_USER_INTERFACE}
  CheckProgrammableTuneStep := TRUE;
  {$ELSE}
  if not SendCommand(Read_Option_Reg,'') then begin
    MsgToShow := UNTOREADOPTR;
    RunGUI;
    CheckProgrammableTuneStep := FALSE;
  end else begin
    s := GetReply(8, CMDTIMEOUT);
    if TryStrToInt(s, value) then begin
      // programmable tune step enabled if b4 = 1
      CheckProgrammableTuneStep := ((value and %00010000 ) <> 0);
    end else
      CheckProgrammableTuneStep := FALSE;
  end;
  {$ENDIF}
end;

// Get RX string - Makes a string from RX frequency display
// (made by 8 single-character TStaticText)
function TTRP.GetRXString: string;

begin
 GetRXString := Trim(RXFreq10M.Caption+RXFreq1M.Caption+RXFreq100k.Caption+
                RXFreq10k.Caption+RXFreq1k.Caption+RXFreqDOT.Caption+
                RXFreq100H.Caption+RXFreq10H.Caption);
end;

// Get TX string - Makes a string from TX frequency display
// made by 7 single-character TStaticText)
function TTRP.GetTXString: string;

begin
 GetTXString := Trim(TXFreq10M.Caption+TXFreq1M.Caption+TXFreq100k.Caption+
                TXFreq10k.Caption+TXFreq1k.Caption+TXFreqDOT.Caption+
                TXFreq100H.Caption);
end;

//Put RX string - Put a string into RX frequency display right-aligned
procedure TTRP.PutRXString(s: string);

var st: string;

begin
 st := s;
 if KeyboardState in [SETGR,SETOR,SETPR] then begin
   if Length(st) < 8 then st := RightStr('        '+s,8);
   RXFreq10M.Caption := st[1];
   RXFreq1M.Caption := st[2];
   RXFreq100k.Caption := st[3];
   RXFreq10k.Caption := st[4];
   RXFreq1k.Caption := st[5];
   RXFreqDOT.Caption := st[6];
   RXFreq100H.Caption := st[7];
   RXFreq10H.Caption := st[8];
 end else begin
   if Length(st) < 7 then st := RightStr('        '+s,7);
   RXFreq10M.Caption := st[1];
   RXFreq1M.Caption := st[2];
   RXFreq100k.Caption := st[3];
   RXFreq10k.Caption := st[4];
   RXFreq1k.Caption := st[5];
   RXFreqDOT.Caption := st[6];
   RXFreq100H.Caption := st[7];
   RXFreq10H.Caption := st[8];
 end;
end;

//Put TX string - Put a string into TX frequency display right-aligned
procedure TTRP.PutTXString(s: string);

var st: string;

begin
  st := s;
  if Length(st) < 7 then st := RightStr('       '+st,7);
  TXFreq10M.Caption := st[1];
  TXFreq1M.Caption := st[2];
  TXFreq100k.Caption := st[3];
  TXFreq10k.Caption := st[4];
  TXFreq1k.Caption := st[5];
  TXFreqDOT.Caption := st[6];
  TXFreq100H.Caption := st[7];
end;

// S-meter visualization. Make visible the signal meter LEDS (=shapes)
// until value, make invisible LEDS from value+1 to 20.
procedure TTRP.SetSMeter(value: integer);

var i: integer;

begin
  for i := 1 to value do SMShapes[i].Show;
  for i := value+1 to 20 do SMShapes[i].Hide;
end;

// Ditto for power meter.
procedure TTRP.SetPMeter(value: integer);

var i: integer;

begin
  for i := 1 to value do PMShapes[i].Show;
  for i := value+1 to 20 do PMShapes[i].Hide;
end;

// Set meters color
procedure TTRP.SetMetersColor;

var i: integer;

begin
  for i:=1 to 20 do begin
    SMShapes[i].Brush.Color := LEDColorON;
    SMShapes[i].Pen.Color := LEDColorON;
    PMShapes[i].Brush.Color := LEDColorON;
    PMShapes[i].Pen.Color := LEDColorON;
  end;
end;

// Adjust decimal separator in real number string
// Latest compilers do not accept anymore both separators (. and ,)
function TTRP.AdjustDecimalSeparator(s: string): string;

var i: integer;

begin
  for i := 1 to Length(s) do
    if s[i]='.' then s[i] := DefaultFormatSettings.DecimalSeparator;
  AdjustDecimalSeparator := s;
end;

// Check for own 500 & 2182 button state and set button caption
procedure TTRP.CheckOwnF;

Var Info : TSearchRec;
    BCapt: string;

begin
  if FindFirst(StateDir+'F500-*',faAnyFile,Info)=0 then begin
    Own500Frequency := TRUE;
    Own500Filename := Info.Name;
    BCapt := copy(copy(Info.Name, 6, Pos('.dat', Info.Name)-6),1,6);
    if BCapt <> '' then
      B500.LCaption.Caption := BCapt
    else
      B500.LCaption.Caption := 'CH2';
    FindClose(Info);
  end else begin
      Own500Frequency := FALSE;
      B500.LCaption.Caption := '500';
  end;

  if FindFirst(StateDir+'F2182-*',faAnyFile,Info)=0 then begin
    Own2182Frequency := TRUE;
    Own2182Filename := Info.Name;
    BCapt := copy(copy(Info.Name, 7, Pos('.dat', Info.Name)-7),1,6);
    if BCapt <> '' then
      B2182.LCaption.Caption := BCapt
    else
      B2182.LCaption.Caption := 'CH1';
    FindClose(Info);
  end else begin
      Own2182Frequency := FALSE;
      B2182.LCaption.Caption := '2182';
  end;
end;

// Load a F500/F2182 file
procedure TTRP.LoadFFile(Sender: TObject);

var FileName: string;
    BandFile: file of TRTXState;

begin
  if Sender = B500 then
    Filename := StateDir+Own500Filename;
  if Sender = B2182 then
    Filename := StateDir+Own2182Filename;

  if FileExists(FileName) then begin
    AssignFile(BandFile,FileName);
    try
      Reset(BandFile);
      Read(BandFile,RTXState);
      CloseFile(BandFile);
    except
      MsgToShow := OLDUPDATING;
      RunGUI;
      Rewrite(BandFile);
      Write(BandFile, RTXstate);
      CloseFile(BandFile);
    end;
 end;
end;

// Open communications
function TTRP.OpenRemote: boolean;

begin
  Result := SendCommand(RemoteEnable, '');
  if Result then begin
    Result := Result and SendCommand(stCAN, '');
    Result := Result and SendCommand(stCR, '');
    Result := Result and SendCommand(stCR, '');
    Result := Result and SendCommand(stCR, '');
  end;
end;

// Close communications
function TTRP.CloseRemote: boolean;
begin
  CloseRemote := SendCommand(RemoteDisable, '');
end;

// Send a command to CU8000
function TTRP.SendCommand(cmd, par: string): boolean;

const
  ACK = #$06;
  NAK = #$15;
  DLE = #$10;

var resp: char = chr(0);
  cmdpar, stmp: string;
  retry: integer = MAXRETRY;
  i,nc: integer;
  ok : boolean = FALSE;

begin
  {$IFDEF TIME_SENDCOMMAND}
  MsgToShow := 'Command to send: '+cmd+par;
  MsgToShow := 'Start: '+FormatDateTime('ss.zzz', Time);
  RunGUI;
  {$ENDIF}
  Enable_Status := FALSE;
  Cmd_Aborted := FALSE;

  cmdpar := cmd+par;

  if MSHOWC.Checked then begin
    MsgToShow := COMMANDSENT+ShowControlChars(CmdPar);
    RunGUI;
  end;

  {$IFDEF TEST_USER_INTERFACE}
  if MStatus.Checked then Enable_status := TRUE;
  exit(TRUE);
  {$ENDIF}

  if SerPort <> 0  then begin
    for i := 1 to length(cmdpar) do begin
      // seems not to be required, but just in case...
      if not MICDNONE.Checked then
        if MICD2.Checked then
          sleep(2)
        else if MICD5.Checked then
          sleep(5)
        else if MICD10.Checked then
          sleep(10);
        repeat
          nc := SerWrite(SerPort, CmdPar[i], 1);
{$IFDEF DEBUG_SENDCOMMAND}
          if nc <> 1 then MsgToShow := 'SerWrite returned ' + IntToStr(nc);
          if retry<MAXRETRY then MsgToShow := 'Resending '+ShowControlChars(CmdPar[i]);
          RunGUI;
{$ENDIF}
          timeout := time + CMDTIMEOUT;
          repeat
            nc := SerRead(SerPort, resp, 1);

{$IFDEF LINUX}
            sleep(1);
{$ENDIF}

{$IFDEF WIN64}
            sleep(1);
{$ENDIF}

{$IFDEF WIN32}
            sleep(0);
{$ENDIF}
          until (nc > 0) or (time > timeout);
{$IFDEF DEBUG_SENDCOMMAND}
          if nc <> 1 then begin
            MsgToShow := 'SerRead returned ' + IntToStr(nc);
            RunGUI;
          end;
{$ENDIF}
          case resp of
            ACK: begin
              ok := TRUE;
              retry := MAXRETRY;
            end;
            NAK: begin
{$IFDEF DEBUG_SENDCOMMAND}
            MsgToShow := 'NAK received, retrying...'+chr(retry+47);
            RunGUI;
{$ENDIF}
              retry := retry - 1;
              ok := FALSE;
              sleep(RETRYDELAY);
            end;
            DLE: begin
              // The CU has reset, acknowledge that...
              SerWrite(SerPort, stACK[1], 1);
              //...notify user...
              MsgToShow := DLEREC;
              WaitTimeSecs(SHORTWAITTIME,PSEWAIT,TRUE);
              // ...and try to restart aborting command
              In_Command := FALSE;
              BONOFFClick(Self,FALSE);
              BONOFFCLick(Self,TRUE);
              Cmd_Aborted := TRUE;
              exit(FALSE);
            end;
            otherwise begin
              if resp = chr(0) then
                stmp := NOTHING
              else
                stmp := resp;
              MsgToShow := Format(SENDCRECVD,[stmp]);
              RunGUI;
              ok := FALSE;
            end;
          end;

          if (time > timeout) then begin
            MsgToShow := TIMEXPCMD;
            retry := retry - 1;
            MsgToShow := RETRYNO+inttostr(MAXRETRY-retry);
            RunGUI;
            ok := FALSE;
          end;
        until ok or (retry = 0);

        if retry = 0 then begin
          {$IFDEF DEBUG_RETRY}
          MsgToShow := Format(RETRYCEXC, [ShowControlChars(cmd+par)]);
          RunGUI;
          {$ENDIF}
          exit(FALSE);
        end;
      end;
    end;
  if MStatus.Checked then Enable_status := TRUE;
  SendCommand := ok;
  {$IFDEF TIME_SENDCOMMAND}
  MsgToShow := 'Stop: '+FormatDateTime('ss.zzz', Time);
  {$ENDIF}
end;

// Get reply to command
function TTRP.GetReply(ReplyLen: integer; ReplyTimeout: extended): string;

var stmp: string;
    buf, resp: char;
    i,nc: integer;

begin
  {$IFDEF TEST_USER_INTERFACE}
  exit(StringOfChar('.', ReplyLen));
  {$ENDIF}
  In_Command := TRUE;
  stmp := '';
  buf := stACK[1];
  for i := 1 to ReplyLen do begin
    while SerWrite(SerPort, buf, 1) <> 1 do;
      timeout := time + ReplyTimeout;
    repeat
      resp := chr(0);
      nc := SerRead(SerPort, resp, 1);
      {$IFDEF LINUX}
      sleep(1);
      {$ENDIF}
      {$IFDEF WINDOWS}
      sleep(0);
      {$ENDIF}
    until ((nc > 0) and (resp < '`')) or (time > timeout);
    {$IFDEF DEBUG_GETREPLY}
    if time > timeout then MsgToShow := 'Timeout in GetReply';
    RunGUI;
    {$ENDIF}
    if resp <> chr(0) then stmp := stmp + resp;
  end;
  SerWrite(SerPort, buf, 1);
  GetReply := stmp;
  In_Command := FALSE;
end;

// Set RX frequency
function TTRP.SetRXFreq: boolean;

var  f: longint;
    sf: string;

begin
  f := RTXstate.Freq_rx div 100;
  str(f, sf);
  while Length(sf) < 6 do sf := '0' + sf;
  In_Command := TRUE;
  SetRXFreq := SendCommand(RX_Prefix, sf + stCR);
  In_Command := FALSE;
end;

// Set RX Mode
function TTRP.SetRXMode: boolean;

begin
  In_Command := TRUE;
  SetRXMode := SendCommand(RX_Prefix, sMode_RX + stCR);
  In_Command := FALSE;
end;

// Set TX frequency
function TTRP.SetTXFreq: boolean;

var  f: longint;
    sf: string;

begin
  f := RTXstate.Freq_tx div 100;
  str(f, sf);
  while Length(sf) < 6 do sf := '0' + sf;
  In_Command := TRUE;
  SetTXFreq := SendCommand(TX_Prefix, sf + stCR);
  In_Command := FALSE;
end;

// Set TX Mode
function TTRP.SetTXMode: boolean;

begin
  In_Command := TRUE;
  SetTXMode := SendCommand(TX_Prefix, sMode_TX + stCR);
  In_Command := FALSE;
end;

// Set RX and TX frequency
function TTRP.SetRXTXFreq: boolean;

var  f: longint;
    sf: string;

begin
  f := RTXstate.Freq_rx div 100;
  str(f, sf);
  while Length(sf) < 6 do sf := '0' + sf;
  In_Command := TRUE;
  SetRXTXFreq := SendCommand(RX_Prefix+TX_Prefix, sf + stCR);
  In_Command := FALSE;
end;

// Display a message in MSG memo
procedure TTRP.Message(ms: string);

var LineEndingLen, LineLen: integer;
    stmp: string;

begin
  stmp := ms;
  LineEndingLen := Length(LineEnding);
  LineLen := Pos(LineEnding,stmp);
  if LineLen <> 0 then begin
    // Multiline string, divide it into component lines
    // and add them to the MSG memo
    repeat
      LineLen := Pos(LineEnding,stmp);
      if LineLen=0 then
          LineLen := Length(stmp)
      else
          LineLen := LineLen-1;
      MSG.Lines.Add(copy(stmp,1,LineLen));
      Delete(stmp,1,LineLen+LineEndingLen);
    until Length(stmp) = 0;
  end else begin
      // Single line string
      MSG.Lines.Add(stmp);
  end;
  // kludge to make TMemo scroll - not always works but better than nothing
  MSG.VertScrollBar.Position := 10000;
end;

// Clear messages in MSG memo
procedure TTRP.MSGDblClick(Sender: TObject);

begin
  MSG.Lines.Clear;
  // kludge to make TMemo scroll
  MSG.VertScrollBar.Position:= 10000;
end;

// Display RX frequency
procedure TTRP.DispRXf(Interactive: boolean);

var tmp: real;
    s: string;

begin
  if RTXstate.uscan then begin
    s := 'SCAN';
  end else begin
    if RTXState.Freq_RX > 0 then begin
      tmp := Float(RTXState.Freq_rx) / 1000.0;
      if (RTXState.Freq_step = 10) or Step10HzSelected then begin
        str(tmp:8:2,s);
      end else begin
        str(tmp:7:1,s);
        s := s + ' ';
      end;
    end else begin
      s := 'CHN.'+IntToStr(-RTXState.Freq_RX);
    end;
  end;
  PutRXString(s);
  if Interactive then RunGUI;
end;

// Display TX frequency (100 Hz steps only)
procedure TTRP.DispTXf(Interactive: boolean);

var tmp: real;
    s: string;

begin
  if RTXState.uscan then begin
    s := 'SCAN';
  end else begin
    if RTXState.Freq_tx > 0 then begin
      tmp := (RTXState.Freq_tx div 100)*100 / 1000.0;
      str(tmp:7:1,s);
    end else begin
      s := 'CHN.'+IntToStr(-RTXState.Freq_tx);
    end;
  end;
  PutTXString(s);
  if Interactive then RunGUI;
end;

// Format BFO frequency
function TTRP.FormatBFOFreq(s: string): string;

begin
  FormatBFOFreq := copy(s,1,2)+'.'+s[3]+' kHz';
end;

// Get auto self test response
function TTRP.GetAutoSelfTestResponse: boolean;

var nc: integer;
    buf: char = #00;
    l: string = '';

begin
  repeat
    timeout := time + TESTTIMEOUT;
    repeat
        nc := SerRead(SerPort, buf, 1);
    until (nc > 0) or (time > timeout);
    if time > timeout then begin
        MsgToShow := TIMEOUTFORSTAR;
        exit(FALSE);
    end;
    SerWrite(SerPort, stACK[1], 1);
    if (buf = '>') and (l = '') then exit(FALSE);
      if (buf in ['*','>']) then begin
        if l <> '' then begin
            if length(l) = 2 then begin
                MsgToShow := Format(TESTNO,[l,'','','']);
                RunGUI;
            end;
            if length(l) >= 4 then begin
              MsgToShow := Format(TESTNO,[copy(l,1,2),': ',SRESULT,copy(l,3,2)]);
              RunGUI;
            end;
        end;
        l := '';
      end else l := l + buf;
  until (buf = '>');

  timeout := time + DLETIMEOUT;
  repeat
    nc := SerRead(SerPort, buf, 1);
  until (nc > 0) or (time > timeout);
  if time > timeout then begin
    MsgToShow := TIMEOUTWDLE;
    exit(FALSE);
  end;

  if buf = stDLE then begin
    WaitTimeSecs(DLEWAITTIME,PSEWAIT,TRUE);
    BONOFFClick(self, FALSE);
    GetAutoSelfTestResponse := TRUE;
  end else GetAutoSelfTestResponse := FALSE;
end;

// Get manual self test response
function TTRP.GetManSelfTestResponse: boolean;

var i,nc: integer;
    buf: char = #00;
    l: string = '';
    StartTime,TimeoutTime: TTime;
    TimeoutExpired: boolean;

begin
  repeat
    // Disable all buttons at the start of a test
    DisableAllButtons;
    MSG.Enabled := TRUE;
    RunGUI;
    TimeoutExpired := FALSE;
    StartTime := Time;
    TimeoutTime := StartTime + TESTTIMEOUT;
    repeat
      nc := SerRead(SerPort, buf, 1);
      TimeoutExpired :=  (Time > TimeoutTime);
    until (nc > 0) or TimeoutExpired;
    if TimeoutExpired then begin
      MsgToShow := TESTTIMEOUTEXPIRED;
      MsgToShow := TESTTIMEOUTEXPIRED1;
      WaitTimeSecs(TESTWAITTIME, PSEWAIT, TRUE);
      BONOFFClick(Self,FALSE);
      exit(FALSE);
    end;
    SerWrite(SerPort, stACK[1], 1);
    if buf = '*' then begin
      l := '';
	for i := 1 to 4 do begin
        StartTime := Time;
        TimeoutTime := StartTime + TESTTIMEOUT;
        repeat
          nc := SerRead(SerPort, buf, 1);
          TimeoutExpired :=  (Time > TimeoutTime);
        until (nc > 0) or TimeoutExpired;
        if TimeoutExpired then begin
          MsgToShow := TESTTIMEOUTEXPIRED;
          MsgToShow := TESTTIMEOUTEXPIRED1;
          WaitTimeSecs(TESTWAITTIME, PSEWAIT, TRUE);
          BONOFFClick(Self, TRUE);
          exit(FALSE);
        end;
          if buf = '>' then break;
          l := l + buf;
          SerWrite(SerPort, stACK[1], 1);
        end;
    end;
    if l <> '' then begin
      MsgToShow := Format(TESTNO,[copy(l,1,2), ': ',SRESULT,copy(l,3,2)]);
      // Test finished, it is now possible to switch to another test, so
      // reenable the [DIMMER] buttons
      SetActiveButtons;
      RunGUI;
    end;
    NextTest := FALSE;
    AbortTest := FALSE;
    repeat
      RunGUI
    until NextTest or AbortTest;
  until (buf = '>') or AbortTest;

  if AbortTest then begin
    StartTime := Time;
    TimeoutTime := StartTime + TESTTIMEOUT;
    repeat
      nc := SerRead(SerPort, buf, 1);
      TimeoutExpired := (Time > TimeoutTime);
    until (nc > 0) or TimeoutExpired;
    if TimeoutExpired then begin
      MsgToShow := TESTTIMEOUTEXPIRED;
      MsgToShow := TESTTIMEOUTEXPIRED1;
      WaitTimeSecs(TESTWAITTIME, PSEWAIT, TRUE);
      BONOFFClick(Self,FALSE);
      exit(FALSE);
    end;
  end;

  if buf = '>' then
    GetManSelfTestResponse := TRUE
  else
    GetManSelfTestResponse := FALSE;
end;

// Keyboard handler
procedure TTRP.KeyboardHandler(keyp: string);

var f: double;
    len, value: integer;
    sRX, sTX, stmp: string;

begin
  len := length(RxFBuf);
  sRX := '';
  sTX := '';

  case KeyboardState of
    SETRXF: begin
        if RxFBuf <> '' then f := StrToFloat(RXFBuf+keyp) else f := 0;
            if f*100 <= MAXRXF then RXFBuf := RXFBuf + keyp;
        sRX:= FloatToStrF(StrToFloat(RXFBuf)/10,ffFixed,5,1);
    end;

    SETTXF: begin
      if TxFBuf <> '' then f := StrToFloat(TXFBuf+keyp) else f := 0;
      if f*100 <= MAXTXF then TXFBuf := TXFBuf + keyp;
      sTX := FloatToStrF(StrToFloat(TXFBuf)/10,ffFixed,5,1);
      TXFreqModified := TRUE;
    end;

    SETRXTXF: begin
      if RxFBuf <> '' then f := StrToFloat(RXFBuf+keyp) else f := 0;
      if f*100 <= MAXRXF then RXFBuf := RXFBuf + keyp;
      sRX := FloatToStrF(StrToFloat(RXFBuf)/10,ffFixed,5,1);
      sTX := sRX;
    end;

    STOCH: begin
      case len of
        0: RXFBuf := RXFBuf + keyp;
        1: begin
          if (RXFBuf[1] in ['0'..'6']) or
             ((RXfbuf[1] = '7') and (keyp[1] in ['0'..'5'])) then
             RXfBuf := RXFBuf + keyp;
        end;
      end;
      SRX := RXFBuf;
    end;

    RCLCH,RCLRXCH: begin
      case len of
          0: RXFBuf := RXFBuf + keyp;
          1: begin
            if (RXFBuf[1] in ['0'..'6']) or
               ((RXfbuf[1] = '7') and (keyp[1] in ['0'..'5'])) or
               (RXfbuf[1] = '8') then
               RXfBuf := RXFBuf + keyp;
          end;
          2: begin
            if RXFBuf[1] = '8' then RXFBuf := RXFBuf + keyp;
          end;
      end;
      SRX := RXFBuf;
    end;

    RCLTXCH: begin
      case len of
        0: TXFBuf := TXFBuf + keyp;
        1: begin
          if (TXFBuf[1] in ['0'..'6']) or
             ((TXfbuf[1] = '7') and (keyp[1] in ['0'..'5'])  or
             (TXfbuf[1] = '8')) then
             TXfBuf := TXFBuf + keyp;
          end;
          2: begin
            if TXFBuf[1] = '8' then TXFBuf := TXFBuf + keyp;
          end;
      end;
      STX := TXFBuf;
    end;

    SETTIME,SETWAKEUP: begin
      len := length(TXFBuf);
      case len of
        0: if (keyp[1] in ['0'..'2']) then TXFBuf := TXFBuf + keyp;
        1: if (TXFbuf[1] in ['0'..'1']) or
              ((TXFBuf[1] = '2') and(keyp[1] in ['0'..'3'])) then
              TXFBuf := TXFBuf + keyp;
        2: if (keyp[1] in ['0'..'5']) then TXFBuf := TXFBuf + keyp;
        3: TXFBuf := TXFBuf + keyp;
      end;
      PutTXstring(TXFBuf);
    end;

    SETBFO: begin
      case len of
        0: begin
          if keyp[1] in ['+','-'] then RXFBuf := RXFBuf + keyp;
          if keyp[1] in ['0'..'3'] then begin
            RXFBuf := '+' + keyp + '.';
            BMINUS.Hide;
            BENTER.Width := ENTERNormalWidth;
          end;
        end;
        1: if keyp[1] in ['0'..'3'] then begin
          RXFBuf := RXFBuf + keyp + '.';
          BMINUS.Hide;
          BENTER.Width := ENTERNormalWidth;
        end;
        3: if (RxFBuf[2] in ['0'..'2']) or
              ((RXFBuf[2] = '3') and (keyp = '0')) then
          RXFBuf := RXFBuf + keyp;
      end;
      PutRXstring(RXFBuf);
    end;

    FILLSCNBUF: begin
      if m = 1 then begin
        if RxFBuf <> '' then f := StrToFloat(RXFBuf+keyp) else f := 0;
        if f*100 <= MAXRXF then RXFBuf := RXFBuf + keyp;
        sRX := FloatToStrF(StrToFloat(RXFBuf)/10,ffFixed,5,1);
      end else begin
        if TxFBuf <> '' then f := StrToFloat(TXFBuf+keyp) else f := 0;
        if f*100 <= MAXTXF then TXFBuf := TXFBuf + keyp;
        sTX := FloatToStrF(StrToFloat(TXFBuf)/10,ffFixed,5,1);
      end;
    end;

    SETOR,SETPR,SETGR: begin
      if (len < 8) and (keyp[1] in ['0'..'1']) then begin
        RXFBuf := RXFBuf + keyp;
        sRX := RXFBuf;
      end;
    end;

    SELFTSTFMA: begin
      case len of
        0: if keyp[1] in ['0'..'3'] then RXFBuf := RXFBuf+keyp;
        1: if (RXFBuf[1] in ['0'..'2']) or
              ((RXFBuf[1] = '3') and (keyp[1] in ['0','1']))
             then RXFBuf := RXFBuf + keyp;
      end;
      sRX := RXFBuf;
    end;

    SELFTSTFMM: begin
      case len of
        0: if keyp[1] in ['0'..'3'] then RXFBuf := RXFBuf+keyp;
        1: if (RXFBuf[1] in ['0'..'3']) or
              ((RXFBuf[1] = '4') and (keyp[1] in ['0','1']))
          then RXFBuf := RXFBuf + keyp;
      end;
      sRX := RXFBuf;
    end;

    READSETSTEP: begin
      stmp := RXFBuf + keyp;
      value := StrToInt(stmp);
      if value <= MAXTUNESTEP then RXFBuf := stmp;
      value := StrToInt(RXFBuf);
      sRX := IntTostr(value div 10)+'.'+IntToStr(value mod 10);
    end;

    SCANSETUP1: begin
      stmp := RXFBuf + keyp;
      value := StrToInt(stmp);
      if value <= 77 then RXFBuf := stmp;
      value := StrToInt(RXFBuf);
      sRX := IntTostr(value);
    end;

    SCANSETUP2: begin
      stmp := RXFBuf + keyp;
      value := StrToInt(stmp);
      if (value <= 75) then begin
        RXFBuf := stmp;
        sRX := IntToStr(value);
      end;
    end;

    SCANDWELL: begin
      stmp := RXFBuf+keyp;
      value := StrToInt(stmp);
      if (value >= 1) and (value <= 99) then begin
        sRX := IntToStr(value div 10)+'.'+IntToStr(value mod 10);
        RXFBuf := stmp;
      end;
    end;
  end;
  if sRX <> '' then PutRXString(sRX);
  if sTX <> '' then PutTXString(sTX);
end;

// Save config to disk - XML version
procedure TTRP.Saveconfig;

var
  Config: TXMLDocument;                       // variable to document
  RootNode, parentNode, tmpNode: TDOMNode;    // variable to nodes
  w,h,l,t: integer;
  s: UnicodeString;

begin
    // Create a document
    Config := TXMLDocument.Create;

    // Create a root node
    RootNode := Config.CreateElement('SkantiControlConfiguration');
    Config.Appendchild(RootNode);

    RootNode := Config.DocumentElement;

    // Create configuration nodes
    parentNode := Config.CreateElement('ConfigVersion');
    tmpNode := Config.CreateTextNode('1');         // insert a value to node
    parentNode.AppendChild(tmpNode);               // save node
    RootNode.AppendChild(parentNode);              // Write element

    parentNode := Config.CreateElement('SerialPortName');
    tmpNode := Config.CreateTextNode(UnicodeString(SerPortName));
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement(UnicodeString('SerialPortSpeed'));
    tmpNode := Config.CreateTextNode(UnicodeString(IntToStr(SerPortSpeed)));
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('EnableAllCmds');
    WriteStr(s,MENALL.Checked);
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('ProgrammableStep');
    WriteStr(s,MPROGSTEP.Checked);
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('ShowCUCmds');
    WriteStr(s,MSHOWC.Checked);
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('ShowHamlibCmds');
    WriteStr(s,MSHOWHAMLIB.Checked);
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('StartInLPMode');
    WriteStr(s,MALWLP.Checked);
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('UseUTC');
    WriteStr(s,MUSEUTC.Checked);
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('CUv92');
    WriteStr(s,MCU920.Checked);
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('LEDColor');
    tmpNode := Config.CreateTextNode(UnicodeString(sLEDColor));
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('StatusReading');
    WriteStr(s,MSTATUS.Checked);
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('TuneAtRemoteFrequencyChange');
    WriteStr(s,MTCHF.Checked);
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    s := '0';
    if MDEFU.Checked then s := '1';
    if MIMMU.Checked then s := '2';

    parentNode := Config.CreateElement('Transceiver');
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    if MICDNONE.Checked then s := '0';
    if MICD2.Checked then s := '2';
    if MICD5.Checked then s := '5';
    if MICD10.Checked then s := '10';

    parentNode := Config.CreateElement('InterCharacterDelay');
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('FontMagnification');
    WriteStr(s,FontMagn);
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('MagnifyAllFonts');
    WriteStr(s,MMAGAF.Checked);
    tmpNode := Config.CreateTextNode(s);
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('TCPServerHost');
    tmpNode := Config.CreateTextNode(UnicodeString(TRPServer.Host));
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('TCPServerPort');
    tmpNode := Config.CreateTextNode(UnicodeString(IntTostr(TRPServer.Port)));
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    w := TRP.Width;
    h := TRP.Height;
    l := TRP.Left;
    t := TRP.Top;

    // Check w, h, l and t values. Under XP and maybe some other system
    // Top and Left may go negative, so check them also
    if w > Screen.Width then w := Screen.Width;
    if h > Screen.Height then h := Screen.Height;
    if t < 0 then t := 0;
    if l < 0 then l := 0;

    parentNode := Config.CreateElement('MainWindowWidth');
    tmpNode := Config.CreateTextNode(UnicodeString(IntTostr(w)));
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('MainWindowHeight');
    tmpNode := Config.CreateTextNode(UnicodeString(IntTostr(h)));
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('MainWindowLeft');
    tmpNode := Config.CreateTextNode(UnicodeString((IntTostr(l))));
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    parentNode := Config.CreateElement('MainWindowTop');
    tmpNode := Config.CreateTextNode(UnicodeString(IntTostr(t)));
    parentNode.Appendchild(tmpNode);
    RootNode.AppendChild(parentNode);

    writeXMLFile(Config, HomeDir+XMLConfigFileName); // write config file

    Config.Free;
end;

procedure TTRP.ReadConfig;

var s,ConfVersion: string;
    Config: TXMLDocument;
    ConfigItem: TDOMNode;
begin
  s := HomeDir+XMLConfigFileName;
  if FileExists(s) then begin
    // We have an XML config file, read it
    ReadXMLFile(Config, s);

    ConfigItem := Config.DocumentElement.FindNode('ConfigVersion');
    ConfVersion := String(ConfigItem.TextContent);
    if ConfVersion <> '1' then
      ShowMessage(Format(UNSUPPORTEDCONFIG,[ConfVersion]));

    ConfigItem := Config.DocumentElement.FindNode('SerialPortName');
    SerPortName := String(ConfigItem.TextContent);

    ConfigItem := Config.DocumentElement.FindNode('SerialPortSpeed');
    SerPortSpeed := StrToInt(String(ConfigItem.TextContent));

    ConfigItem := Config.DocumentElement.FindNode('EnableAllCmds');
    MENALL.Checked := (ConfigItem.TextContent = 'TRUE');

    ConfigItem := Config.DocumentElement.FindNode('ProgrammableStep');
    MPROGSTEP.Checked := (ConfigItem.TextContent = 'TRUE');

    ConfigItem := Config.DocumentElement.FindNode('ShowCUCmds');
    MSHOWC.Checked := (ConfigItem.TextContent = 'TRUE');

    ConfigItem := Config.DocumentElement.FindNode('ShowHamlibCmds');
    MSHOWHAMLIB.Checked := (ConfigItem.TextContent = 'TRUE');

    ConfigItem := Config.DocumentElement.FindNode('StartInLPMode');
    MALWLP.Checked := (ConfigItem.TextContent = 'TRUE');

    ConfigItem := Config.DocumentElement.FindNode('UseUTC');
    MUSEUTC.Checked := (ConfigItem.TextContent = 'TRUE');

    ConfigItem := Config.DocumentElement.FindNode('CUv92');
    MCU920.Checked := (ConfigItem.TextContent = 'TRUE');

    ConfigItem := Config.DocumentElement.FindNode('LEDColor');
    sLEDColor := String(ConfigItem.TextContent);

    // Set images and check/uncheck menu items
    if sLEDColor = 'red' then begin
        LEDColorOFF := clMaroon;
        LEDColorON := clRed;
        MRed.Checked := TRUE;
        MGreen.Checked := FALSE;
        MYellow.Checked := FALSE;
    end else if sLEDColor = 'green' then begin
        LEDColorOFF := clGreen;
        LEDColorON := clLime;
        MRed.Checked := FALSE;
        MGreen.Checked := TRUE;
        MYellow.Checked := FALSE;
    end else if sLEDColor = 'yellow' then begin
        LEDColorOFF := clOlive;
        LEDColorON := clYellow;
        MRed.Checked := FALSE;
        MGreen.Checked := FALSE;
        MYellow.Checked := TRUE;
    end else begin
        // Not a valid color, use Yellow as default and increment ErrorCount
        LEDColorOFF := clOlive;
        LEDColorON := clYellow;
        MRed.Checked := FALSE;
        MGreen.Checked := FALSE;
        MYellow.Checked := TRUE;
    end;

    ConfigItem := Config.DocumentElement.FindNode('StatusReading');
    MSTATUS.Checked := (ConfigItem.TextContent = 'TRUE');

    ConfigItem := Config.DocumentElement.FindNode('StatusReading');
    MTCHF.Checked := (ConfigItem.TextContent = 'TRUE');

    ConfigItem := Config.DocumentElement.FindNode('TuneAtRemoteFrequencyChange');
    MTCHF.Checked := (ConfigItem.TextContent = 'TRUE');
    TuneAtFreqChange := MTCHF.Checked;

    ConfigItem := Config.DocumentElement.FindNode('Transceiver');
    case ConfigItem.TextContent of
      '0': MDISU.Checked := TRUE;
      '1': MDEFU.Checked := TRUE;
      '2': MIMMU.Checked := TRUE;
      otherwise MDISU.Checked := TRUE;
    end;

    ConfigItem := Config.DocumentElement.FindNode('InterCharacterDelay');
    case ConfigItem.TextContent of
      '0': MICDNONE.Checked := TRUE;
      '2': MICD2.Checked := TRUE;
      '5': MICD5.Checked := TRUE;
      '10': MICD10.Checked := TRUE;
      otherwise MICDNONE.Checked := TRUE;
    end;

    ConfigItem := Config.DocumentElement.FindNode('FontMagnification');
    FontMagn := StrToInt(String(ConfigItem.TextContent));
    case FontMagn of
      100: MMAG100.Checked := TRUE;
      110: MMAG110.Checked := TRUE;
      125: MMAG125.Checked := TRUE;
      150: MMAG150.Checked := TRUE;
      175: MMAG175.Checked := TRUE;
      200: MMAG200.Checked := TRUE;
      otherwise begin
        FontMagn := 100;
        MMAG100.Checked := TRUE;
      end;
    end;

    ConfigItem := Config.DocumentElement.FindNode('MagnifyAllFonts');
    MMAGAF.Checked := (ConfigItem.TextContent = 'TRUE');

    ConfigItem := Config.DocumentElement.FindNode('TCPServerHost');
    TRPServer.Host := String(ConfigItem.TextContent);

    ConfigItem := Config.DocumentElement.FindNode('TCPServerPort');
    TRPServer.Port := StrToInt(String(ConfigItem.TextContent));

    ConfigItem := Config.DocumentElement.FindNode('MainWindowWidth');
    StartWidth := StrToInt(String(ConfigItem.TextContent));

    ConfigItem := Config.DocumentElement.FindNode('MainWindowHeight');
    StartHeight := StrToInt(String(ConfigItem.TextContent));

    ConfigItem := Config.DocumentElement.FindNode('MainWindowLeft');
    StartX := StrToInt(String(ConfigItem.TextContent));

    ConfigItem := Config.DocumentElement.FindNode('MainWindowTop');
    StartY := StrToInt(String(ConfigItem.TextContent));

    HaveConfig := TRUE;
  end else begin
    // No XML config file, check for an old config file
    s := HomeDir+ConfigFileName;
    if FileExists(s) then begin
      // found, show a notice and read it
      ShowMessage(OLDUPDATING);
      ReadConfig_old;
      // And save the new XML version
      SaveConfig;
    end else begin
      // No configuration file, set up and save a default one
      ShowMessage(CONFIGNOTFOUND);
      SetDefaultConfig;
      SaveConfig;
    end;
  end;
end;

// Read config from disk - original version
// Keep here for compatibility with old config file format
// try...except does not catch reading-past-eof error on text files,
// so revert to the old good way.
procedure TTRP.ReadConfig_old;

var ConfigFile: TextFile;
    s: string;
    ErrorCount: integer = 0;
    p: integer;

begin
  s := HomeDir+ConfigFileName;
  if FileExists(s) then begin
      AssignFile(ConfigFile, s);
      {$I-}
      Reset(ConfigFile);

      ReadLn(ConfigFile,SerPortName);
      if Eof(ConfigFile) then inc(ErrorCount);
      {$IFDEF WINDOWS}
      // The \\.\ syntax is optional for ports 1..9 but is needed
      // for ports > 9, so insert it anyway if not present
      if pos('\\.\', SerPortName) = 0 then
         SerPortName := '\\.\' + SerPortName;
      {$ENDIF}

      ReadLn(ConfigFile,s);
      if Eof(ConfigFile) then inc(ErrorCount);
      if not TryStrToInt(s,SerPortSpeed) then begin
          inc(ErrorCount);
          SerPortSpeed := 2400;
      end;

      ReadLn(ConfigFile,s);
      if Eof(ConfigFile) then inc(ErrorCount);
      MStatus.Checked := (s = 'TRUE');

      ReadLn(ConfigFile,s);
      if Eof(ConfigFile) then inc(ErrorCount);
      case s of
          '1': MDEFU.Checked := TRUE;
          '2': MIMMU.Checked := TRUE;
          otherwise MDISU.Checked := TRUE;
      end;

      ReadLn(ConfigFile,s);
      if Eof(ConfigFile) then inc(ErrorCount);
      MENALL.Checked := (s = 'TRUE');

      ReadLn(ConfigFile,s);
      if Eof(ConfigFile) then inc(ErrorCount);
      MSHOWC.Checked := (s = 'TRUE');

       ReadLn(ConfigFile,s);
      if Eof(ConfigFile) then inc(ErrorCount);
      MALWLP.Checked := (s = 'TRUE');

      ReadLn(ConfigFile,s);
      if Eof(ConfigFile) then inc(ErrorCount);
      MUSEUTC.Checked := (s = 'TRUE');

      ReadLn(ConfigFile,sLEDColor);
      if Eof(ConfigFile) then inc(ErrorCount);
      if sLEDColor = 'red' then begin
          LEDColorOFF := clMaroon;
          LEDColorON := clRed;
          MRed.Checked := TRUE;
          MGreen.Checked := FALSE;
          MYellow.Checked := FALSE;
      end else if sLEDColor = 'green' then begin
          LEDColorOFF := clGreen;
          LEDColorON := clLime;
          MRed.Checked := FALSE;
          MGreen.Checked := TRUE;
          MYellow.Checked := FALSE;
      end else if sLEDColor = 'yellow' then begin
          LEDColorOFF := clOlive;
          LEDColorON := clYellow;
          MRed.Checked := FALSE;
          MGreen.Checked := FALSE;
          MYellow.Checked := TRUE;
      end else begin
          // Not a valid color, use Yellow as default and increment ErrorCount
          sLEDColor := 'yellow';
          LEDColorOFF := clOlive;
          LEDColorON := clYellow;
          MRed.Checked := FALSE;
          MGreen.Checked := FALSE;
          MYellow.Checked := TRUE;
          inc(ErrorCount);
      end;

      // Read starting dimension and position
      ReadLn(ConfigFile, s);
      if Eof(ConfigFile) then inc(ErrorCount);
      if not TryStrToInt(s, StartWidth) then begin
          StartWidth := 731;
          inc(ErrorCount);
      end;

      ReadLn(ConfigFile, s);
      if Eof(ConfigFile) then inc(ErrorCount);
      if not TryStrToInt(s, StartHeight) then begin
          StartHeight := 628;
          inc(ErrorCount);
      end;

      ReadLn(ConfigFile, s);
      if Eof(ConfigFile) then inc(ErrorCount);
      if not TryStrToInt(s, StartX) then begin
          StartX := 0;
          inc(ErrorCount);
      end;

      ReadLn(ConfigFile, s);
      if Eof(ConfigFile) then inc(ErrorCount);
      if not TryStrToInt(s, StartY) then begin
          StartY := 0;
          inc(ErrorCount);
      end;

      // Read Programmable step enable
      ReadLn(ConfigFile, s);
      if Eof(ConfigFile) then inc(ErrorCount);
      MPROGSTEP.Checked := (s = 'TRUE');

      // Read 92.0 version enable
      ReadLn(ConfigFile, s);
      if Eof(ConfigFile) then inc(ErrorCount);
      MCU920.Checked := (s = 'TRUE');
      if MCU920.Checked then
          MREREADCONF.Enabled := TRUE
      else
          MREREADCONF.Enabled := FALSE;

      // Read inter-character delay
      ReadLn(ConfigFile,s);
      if Eof(ConfigFile) then inc(ErrorCount);
      case s of
          '0': MICDNONE.Checked := TRUE;
          '2': MICD2.Checked := TRUE;
          '5': MICD5.Checked := TRUE;
          '10': MICD10.Checked := TRUE;
          otherwise
              MICDNONE.Checked := TRUE;
      end;

      ReadLn(ConfigFile,s);
      if Eof(ConfigFile) then inc(ErrorCount);
      if s <> '' then
          if not TryStrToInt(s,FontMagn) then begin
              FontMagn := 100;
              inc(ErrorCount);
          end;
      MMAG100.Checked := FALSE;
      MMAG125.Checked := FALSE;
      MMAG150.Checked := FALSE;
      MMAG175.Checked := FALSE;
      MMAG200.Checked := FALSE;
      case FontMagn of
          100: MMAG100.Checked := TRUE;
          110: MMAG110.Checked := TRUE;
          125: MMAG125.Checked := TRUE;
          150: MMAG150.Checked := TRUE;
          175: MMAG175.Checked := TRUE;
          200: MMAG200.Checked := TRUE;
          otherwise
              MMAG100.Checked := TRUE;
      end;

      ReadLn(ConfigFile,s);
      if Eof(ConfigFile) then inc(ErrorCount);
      if s <> '' then begin
          p := Pos(':',s);
          if p=0 then begin
              inc(ErrorCount);
          end else begin
              TRPServer.Host := Copy(s,1,p-1);
              TRPServer.Port := StrToInt(Copy(s,p+1));
          end;
      end;

      // read remote tune configuration
      ReadLn(ConfigFile,s);
      if Eof(ConfigFile) then inc(ErrorCount);
      if s <> '' then begin
          MTCHF.Checked := (s='TRUE');
          TuneAtFreqChange := MTCHF.Checked;
      end;

      // Read magnify all fonts option
      ReadLn(ConfigFile,s);
      if Eof(ConfigFile) then inc(ErrorCount);
      if s <> '' then MMAGAF.Checked := (s='TRUE');

      CloseFile(ConfigFile);
      {$I+}
      // Clear IOResult by calling it
      IOResult;

      // Check for errors. 1 error is ok... (it is the last item EOF)
      if ErrorCount > 1 then begin
          // But more no, so there are some lines missing...
          if ErrorCount < 6 then begin
              // Less than 6 lines missing, may be an old config file...
              ShowMessage(OLDSTYLECONF);
              // ...so set default values for new configuration items...
              MPROGSTEP.Checked := FALSE;
              MCU920.Checked := FALSE;
              MREREADCONF.Enabled := FALSE;
              MICDNONE.Checked := TRUE;
              FontMagn := 100;
              MMAG100.Checked := TRUE;
              MMAGAF.Checked := FALSE;
              // ...and write an updated config file
              SaveConfig;
          end;
          if ErrorCount >= 6 then begin
              // Too many EOF, this configuration file has messed up
              // or is a really old version...
              ShowMessage(CONFIGMESSEDUP);
              // ...so set the defaults and write a new config file.
              SetDefaultConfig;
              SaveConfig;
          end;
      end;
   end else begin
      // No configuration file, set and save a default one
      ShowMessage(CONFIGNOTFOUND);
      SetDefaultConfig;
      SaveConfig;
   end;
  HaveConfig := TRUE;
end;


// Save last state to disk
procedure TTRP.SaveLastState;

var StateFile: FILE of TRTXState;

begin
  AssignFile(StateFile, HomeDir+StateFileName);
  try
    Rewrite(StateFile);
    Write(StateFile, RTXState);
    CloseFile(StateFile);
  except
    ShowMessage(CANTWLASTSTATE);
  end;
end;

// Set default state
procedure TTRP.SetDefaultState;
begin
  with RTXstate do begin
    Freq_rx := 14200000;
    Freq_tx := 14200000;
    Freq_step := 1000;
    BFO_freq := 800;
    Mode_RX := M_USB;
    Mode_TX := M_USB;
    Filter := FILTER_DEFAULT;
    AGC := AVC_SLOW;
    Speaker_Enabled := TRUE;
    RFamp_Enabled := FALSE;
    RFAtt_Enabled := FALSE;
    Squelch_Enabled := FALSE;
    Sensitivity := 0;
    Volume := 80;
    TX_power := FULL_POWER;
    TX_Enabled := TRUE;
    uscan := FALSE;
    Duplex := FALSE;
    PTT_state := FALSE;
    Dimmer_Level := 5;
    Programmable_Step := 10000;
    Antenna_OFF := FALSE;
    Programmable_Step_Set := FALSE;
  end;
  Step10HZSelected := FALSE;
end;

// Read last autosaved state from disk or set default state
procedure TTRP.LoadLastState;

var StateFile: FILE of TRTXState;

begin
  MsgToShow := LOADINGLASTSTA;
  RunGUI;
  AssignFile(StateFile, HomeDir+StateFileName);
  if FileExists(HomeDir+StateFileName) then begin
    try
      Reset(StateFile);
      Read(StateFile, RTXState);
      CloseFile(StateFile);
    except
      MsgToShow := OLDUPDATING;
      RunGUI;
      if RTXState.Freq_rx = 0 then
        // File exists, but maybe empty, so set a valid state
        SetDefaultState
      else begin
        MsgToShow := OLDUPDATING;
        RunGUI;
        RTXstate.Programmable_step := 10000;
        RTXState.Programmable_Step_Set := FALSE;
      end;
    end;
  end else begin
    // No previous state file found
    MsgToShow := NOPREVSTATE;
    RunGUI;
    SetDefaultState;
  end;
  OldRTXState := RTXState;
  Last_AGC := RTXState.AGC;
  Step10HzSelected := (RTXState.Freq_step = 10);
  HaveState := TRUE;
  MsgToShow := DONE;
end;

// Create ordered Channels submenus
procedure TTRP.LoadChannelsDir;

var m: TMenuItem;
    n: integer;
    Info: TSearchRec;
    FileList: TStringList;

begin
  FileList := TStringList.Create;
  FileList.Sorted := TRUE;
  if FindFirst(ChannelsDir+'*.dat', faAnyFile, info) = 0 then begin
    repeat
      delete(Info.Name, Length(Info.Name)-3, 4);
      FileList.Add(Info.Name);
    until FindNext(Info) <> 0;
    FindClose(Info);
    MChannels.Clear;
    for n := 0 to FileList.Count-1 do begin
      m := TmenuItem.Create(MChannels);
      m.Caption := FileList.Strings[n];
      m.OnClick := @LoadChannel;
      MChannels.Add(m);
    end;
    FileList.Clear;
    FileList.Free;
  end else begin
    MChannels.Clear;
    m := TmenuItem.Create(MChannels);
    m.Caption := NOCHANNELS;
    MChannels.Add(m);
  end;
end;

// Load a channel file
procedure TTRP.LoadChannel(Sender: TObject);

var BandFile: FILE of TRTXState;
    FileName: string;

begin
  FileName := ChannelsDir+(Sender as TMenuItem).Caption+'.dat';
  if FileExists(FileName) then begin
    AssignFile(BandFile,FileName);
    try
      Reset(BandFile);
      Read(BandFile,RTXState);
      CloseFile(BandFile);
    except
      MsgToShow := OLDUPDATING;
      RunGUI;
      Rewrite(BandFile);
      Write(BandFile, RTXstate);
      CloseFile(BandFile);
    end;
  end else begin
    MsgToShow := FILENOTFOUND;
    RunGUI;
  end;
  OldRTXstate := RTXState;
  KeyboardState := NORMAL;
  RestoreState;
  UpdatePanel;
  EnableAllControls;
  SetActiveButtons;
end;

// Disable all controls except ON/OFF and some menus
procedure TTRP.DisableAllControls;

var i: integer;

begin
  for i := 0 to ComponentCount - 1 do begin
    if Components[i] is TJButton then begin
      TJButton(Components[i]).Enabled := FALSE;
    end;
    if Components[i] is TShape then begin
      TShape(Components[i]).Brush.Color := clDefault;
      TShape(Components[i]).Enabled := FALSE;
    end;
  end;
  SetAllLEDSOff;
  MLState.Enabled := FALSE;
  MSState.Enabled := FALSE;
  MSChann.Enabled := FALSE;
  MOtFun.Enabled := FALSE;
  MStatus.Enabled := FALSE;
  MTrans.Enabled := FALSE;
  MChannels.Enabled := FALSE;
  BONOFF.Enabled := TRUE;
  FLASH.Enabled := FALSE;

  RXFreq10H.Enabled := TRUE;
  RXFreq100H.Enabled := TRUE;
  RXFreq1k.Enabled := TRUE;
  RXFreq10k.Enabled := TRUE;
  RXFreq100k.Enabled := TRUE;
  RXFreq1M.Enabled := TRUE;
  RXFreq10M.Enabled := TRUE;
  RXFreqDOT.Enabled := TRUE;

  TXFreq100H.Enabled := TRUE;
  TXFreq1k.Enabled := TRUE;
  TXFreq10k.Enabled := TRUE;
  TXFreq100k.Enabled := TRUE;
  TXFreq1M.Enabled := TRUE;
  TXFreq10M.Enabled := TRUE;
  TXFreqDOT.Enabled := TRUE;

  PutRXString('       ');
  PutTXString('       ');
end;

// Disable all buttons
procedure TTRP.DisableAllButtons;

var i: integer;

begin
  for i := 0 to ComponentCount - 1 do begin
    if Components[i] is TJButton then
      TJButton(Components[i]).Enabled := FALSE;
  end;
  BONOFF.Enabled := TRUE;
end;

// Enable all controls
procedure TTRP.EnableAllControls;

var i: integer;

begin
  if KeyboardState = POWEROFF then exit;

  for i := 0 to ComponentCount - 1 do begin
    if Components[i] is TControl then begin
      TControl(Components[i]).Enabled := TRUE;
    end;
    if Components[i] is TMenuItem then begin
      TMenuItem(Components[i]).Enabled := TRUE;
    end;
    if Components[i] is TShape then begin
      TShape(Components[i]).Enabled := TRUE;
    end;
  end;
  FLASH.Enabled := TRUE;
  BMINUS.Hide;
  if MSTATUS.Checked then begin
    SM.Enabled := TRUE;
    TSTATUS.Enabled := TRUE;
  end;
end;

// Set all annunciators off
procedure TTRP.SetAllLEDsOff;

begin
  BUSB.Image.Picture := PImgOFF;
  BLSB.Image.Picture := PImgOFF;
  BAM.Image.Picture  := PImgOFF;
  BTELEX.Image.Picture := PImgOFF;
  BR3E.Image.Picture := PImgOFF;
  BCW.Image.Picture := PImgOFF;
  BMCW.Image.Picture := PImgOFF;
  BLOW.Image.Picture := PImgOFF;
  BMED.Image.Picture := PImgOFF;
  BFULL.Image.Picture := PImgOFF;
  BDUP.Image.Picture := PImgOFF;
  BSCAN.Image.Picture := PImgOFF;
  BWIDE.Image.Picture := PImgOFF;
  BINTER.Image.Picture := PImgOFF;
  BNAR.Image.Picture := PImgOFF;
  BVNAR.Image.Picture := PImgOFF;
  BSPKR.Image.Picture := PImgOFF;
  BRFAMP.Image.Picture := PImgOFF;
  BRFATT.Image.Picture := PImgOFF;
  BSQL.Image.Picture := PImgOFF;
  BAGCON.Image.Picture := PImgOFF;
  BAGCFAST.Image.Picture := PImgOFF;
  BAGCSLOW.Image.Picture := PImgOFF;
  BAGCOFF.Image.Picture := PImgOFF;
  BVOLDN.Image.Picture := PImgArrDnOFF;
  BVOLUP.Image.Picture := PImgArrUpOFF;
end;

// Set active/not active buttons state
procedure TTRP.SetActiveButtons;

begin
  if MENALL.Checked then
    // Do not try to disable inactive buttons, so do nothing and return
    exit
  else begin
    if KeyboardState = POWEROFF then begin
      BONOFF.Enabled := TRUE;
      exit;
    end;
    if RTXstate.uscan then begin
      DisableAllButtons;
      BENTER.Enabled := TRUE;
      RunGUI;
      exit;
    end;
    if RTXState.TX_Enabled then
      BTRANS.Enabled := TRUE
    else
      BTRANS.Enabled := FALSE;

    case RTXState.Mode_RX of
      M_USB,M_LSB,M_R3E: begin
        BWIDE.Enabled := FALSE;
        BINTER.Enabled := TRUE;
        BNAR.Enabled := FALSE;
        BVNAR.Enabled := FALSE;
        BBFODN.Enabled := FALSE;
        BBFOUP.Enabled := FALSE;
        BAGCON.Enabled := TRUE;
        BAGCFAST.Enabled := TRUE;
        BAGCSLOW.Enabled := TRUE;
        BAGCOFF.Enabled := TRUE;
        if RTXState.TX_Enabled then BTRANS.Enabled := TRUE;
      end;
      M_CW: begin
        BWIDE.Enabled := TRUE;
        BINTER.Enabled := TRUE;
        BNAR.Enabled := TRUE;
        BVNAR.Enabled := TRUE;
        BBFODN.Enabled := TRUE;
        BBFOUP.Enabled := TRUE;
        BAGCON.Enabled := TRUE;
        BAGCFAST.Enabled := TRUE;
        BAGCSLOW.Enabled := TRUE;
        BAGCOFF.Enabled := TRUE;
        BTRANS.Enabled := FALSE;
      end;
      M_MCW: begin
        BWIDE.Enabled := TRUE;
        BINTER.Enabled := TRUE;
        BNAR.Enabled := TRUE;
        BVNAR.Enabled := TRUE;
        BBFODN.Enabled := FALSE;
        BBFOUP.Enabled := FALSE;
        BAGCON.Enabled := TRUE;
        BAGCFAST.Enabled := TRUE;
        BAGCSLOW.Enabled := TRUE;
        BAGCOFF.Enabled := TRUE;
        BTRANS.Enabled := FALSE;
      end;
      M_AM: begin
        BWIDE.Enabled := TRUE;
        BINTER.Enabled := FALSE;
        BNAR.Enabled := FALSE;
        BVNAR.Enabled := FALSE;
        BBFODN.Enabled := FALSE;
        BBFOUP.Enabled := FALSE;
        BAGCON.Enabled := TRUE;
        BAGCFAST.Enabled := TRUE;
        BAGCSLOW.Enabled := FALSE;
        BAGCOFF.Enabled := TRUE;
        if RTXState.TX_Enabled then BTRANS.Enabled := TRUE;
      end;
      M_TLX: begin
        BWIDE.Enabled := FALSE;
        BINTER.Enabled := FALSE;
        BNAR.Enabled := FALSE;
        BVNAR.Enabled := FALSE;
        BBFODN.Enabled := FALSE;
        BBFOUP.Enabled := FALSE;
        BAGCON.Enabled := TRUE;
        BAGCFAST.Enabled := TRUE;
        BAGCSLOW.Enabled := FALSE;
        BAGCOFF.Enabled := FALSE;
        if RTXState.TX_Enabled then BTRANS.Enabled := TRUE;
      end;
    end;

    case RTXState.AGC of
      AVC_ON, AVC_FAST, AVC_SLOW: begin
        BSENSDN.Enabled := FALSE;
        BSENSUP.Enabled := FALSE;
      end;
      AVC_OFF: begin
        BSENSDN.Enabled := TRUE;
        BSENSUP.Enabled := TRUE;
      end;
    end;

    case KeyboardState of
      NORMAL: begin
        EnableAllControls;
        B0.Enabled := FALSE;
        B1.Enabled := FALSE;
        B2.Enabled := FALSE;
        B3.Enabled := FALSE;
        B4.Enabled := FALSE;
        B5.Enabled := FALSE;
        B6.Enabled := FALSE;
        B7.Enabled := FALSE;
        B8.Enabled := FALSE;
        B9.Enabled := FALSE;
        BENTER.Enabled := FALSE;
      end;
      SETRXF: begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
        BRCL.Enabled := TRUE;
        BTX.Enabled := TRUE;
      end;
      SETTXF: begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
        BRCL.Enabled := TRUE;
        BRX.Enabled := TRUE;
      end;
      SELFTSTA: begin
        DisableAllButtons;
      end;
      SELFTSTM: begin
        DisableAllButtons;
        BENTER.Enabled := TRUE;
        BDIMUP.Enabled := TRUE;
        BDIMDN.Enabled := TRUE;
      end;
      SELFTSTFMA: begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
      end;
      SELFTSTFMA1: begin
        DisableAllButtons;
      end;
      SELFTSTFMM: begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BDIMUP.Enabled := TRUE;
        BDIMDN.Enabled := TRUE;
        BENTER.Enabled := TRUE;
      end;
      SELFTSTFMM1: begin
        DisableAllButtons;
        BDIMUP.Enabled := TRUE;
        BDIMDN.Enabled := TRUE;
        BENTER.Enabled := TRUE;
      end;
      SCANCH: begin
        DisableAllButtons;
        BENTER.Enabled := TRUE;
      end;
      SETGR,SETOR,SETPR: begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        BENTER.Enabled := TRUE;
      end;
      SETBEEPER: begin
        DisableAllButtons;
        BVOLUP.Enabled := TRUE;
        BVOLDN.Enabled := TRUE;
        BENTER.Enabled := TRUE;
      end;
      READSETSTEP: begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
      end;
      READONHOURS: begin
        DisableAllButtons;
        BENTER.Enabled := TRUE;
      end;
      ANTENNAOFF: begin
        DisableAllButtons;
        BTXONOFF.Enabled := TRUE;
      end;
      SCANSETUP1: begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
        BRX.Enabled := TRUE;
        BTX.Enabled := TRUE;
        BSCAN.Enabled := TRUE;
        BSETT.Enabled := TRUE;
      end;
      SCANSETUP2: begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
      end;
      RCLCH:begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
        BRX.Enabled := TRUE;
        BTX.Enabled := TRUE;
        BRCL.Enabled := TRUE;
      end;
      RCLRXCH:begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
      end;
      RCLTXCH: begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
      end;
      CHANNEL,CHANNELRX,CHANNELTX: begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
        BRX.Enabled := TRUE;
        BTX.Enabled := TRUE;
        BRCL.Enabled := TRUE;
      end;
      SETTIME: begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
        BRX.Enabled := TRUE;
        BRCL.Enabled := TRUE;
        BSETT.Enabled := TRUE;
      end;
      SETWAKEUP: begin
        DisableAllButtons;
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
      end;
      otherwise begin
        B0.Enabled := TRUE;
        B1.Enabled := TRUE;
        B2.Enabled := TRUE;
        B3.Enabled := TRUE;
        B4.Enabled := TRUE;
        B5.Enabled := TRUE;
        B6.Enabled := TRUE;
        B7.Enabled := TRUE;
        B8.Enabled := TRUE;
        B9.Enabled := TRUE;
        BENTER.Enabled := TRUE;
      end;
    end;
  end;
end;

// Update indicators state
procedure TTRP.UpdatePanel;

var tmp_mode: integer;

begin
  // If channel mode or in off state, disable all buttons and exit
  if (RTXState.Freq_RX < 0) or (KeyboardState = POWEROFF) then begin
    SetActiveButtons;
    exit;
  end;

  // Frequency step
  case RTXState.Freq_step of
    10: begin
      STEP10H.Brush.Color := LEDColorON;
      STEP100H.Brush.Color := LEDCOlorOFF;
      STEP1K.Brush.Color := LEDColorOFF;
    end;
    100: begin
      STEP10H.Brush.Color := LEDColorOFF;
      STEP100H.Brush.Color := LEDColorON;
      STEP1K.Brush.Color := LEDColorOFF;
    end;
    1000: begin
      STEP10H.Brush.Color := LEDColorOFF;
      STEP100H.Brush.Color := LEDColorOFF;
      STEP1K.Brush.Color := LEDColorON;
    end;
  end;
  if RTXState.Programmable_Step_Set then begin
    // programmable step - all step LEDS off
    STEP10H.Brush.Color := LEDColorOFF;
    STEP100H.Brush.Color := LEDColorOFF;
    STEP1K.Brush.Color := LEDColorOFF;
  end;

    // Meters
  if Mstatus.Checked then begin
    Enable_Status := TRUE;
    Tstatus.Enabled := TRUE;
    SMGroupBox.Show;
    PMGroupBox.Show;
    LBSIG.Show;
    LBPWR.Show;
  end else begin;
    Tstatus.Enabled := FALSE;
    Enable_Status := FALSE;
    SMGroupBox.Hide;
    PMGroupBox.Hide;
    LBSIG.Hide;
    LBPWR.Hide;
  end;

  // Filter
  case RTXState.Filter of
    FILTER_DEFAULT: begin
      BWIDE.Image.Picture := PImgOFF;
      BINTER.Image.Picture := PImgOFF;
      BNAR.Image.Picture := PImgOFF;
      BVNAR.Image.Picture := PImgOFF;
    end;
    FILTER_WIDE: begin
      BWIDE.Image.Picture := PImgON;
      BINTER.Image.Picture := PImgOFF;
      BNAR.Image.Picture := PImgOFF;
      BVNAR.Image.Picture := PImgOFF;
    end;
    FILTER_INTERMEDIATE: begin
      BWIDE.Image.Picture := PImgOFF;
      BINTER.Image.Picture := PImgON;
      BNAR.Image.Picture := PImgOFF;
      BVNAR.Image.Picture := PImgOFF;
    end;
    FILTER_NARROW: begin
      BWIDE.Image.Picture := PImgOFF;
      BINTER.Image.Picture := PImgOFF;
      BNAR.Image.Picture := PImgON;
      BVNAR.Image.Picture := PImgOFF;;
    end;
    FILTER_VERYNARROW: begin
      BWIDE.Image.Picture := PImgOFF;
      BINTER.Image.Picture := PImgOFF;
      BNAR.Image.Picture := PImgOFF;
      BVNAR.Image.Picture := PImgON;
    end;
  end;

  // SPEAKER
  if RTXState.Speaker_Enabled then
    BSPKR.Image.Picture := PImgON
  else
    BSPKR.Image.Picture := PImgOFF;

  // RF AMPLIFIER
  if RTXState.RFamp_Enabled then
    BRFAMP.Image.Picture := PImgON
  else
    BRFAMP.Image.Picture := PImgOFF;

  // RF ATTENUATOR
  if RTXState.RFatt_Enabled then
    BRFATT.Image.Picture := PImgON
  else
    BRFATT.Image.Picture := PImgOFF;

  // SQUELCH
  if RTXState.Squelch_Enabled then
    BSQL.Image.Picture := PImgON
  else
    BSQL.Image.Picture := PImgOFF;

  // AGC
  case RTXState.AGC of
    AVC_ON: begin
      if not RTXState.PTT_State then begin
        BAGCON.Image.Picture := PImgON;
        BAGCSLOW.Image.Picture := PImgOFF;
        BAGCFAST.Image.Picture := PImgOFF;
        BAGCOFF.Image.Picture := PImgOFF;
      end;
    end;

    AVC_FAST: begin
      if not RTXState.PTT_State then begin
        BAGCON.Image.Picture := PImgON;
        BAGCSLOW.Image.Picture := PImgOFF;
        BAGCFAST.Image.Picture := PImgON;
        BAGCOFF.Image.Picture := PImgOFF;
      end;
    end;

    AVC_SLOW: begin
      if not RTXState.PTT_State then begin
        BAGCON.Image.Picture := PImgON;
        BAGCSLOW.Image.Picture := PImgON;
        BAGCFAST.Image.Picture := PImgOFF;
        BAGCOFF.Image.Picture := PImgOFF;
      end;
    end;

    AVC_OFF: begin
      if not RTXState.PTT_State then begin
        BAGCON.Image.Picture := PImgOFF;
        BAGCSLOW.Image.Picture := PImgOFF;
        BAGCFAST.Image.Picture := PImgOFF;
        BAGCOFF.Image.Picture := PImgON;
      end;
    end;
  end;

  // Update split mode flashing
  if RTXState.Mode_RX <> RTXState.Mode_TX then begin
    case RTXState.Mode_RX of
      M_USB: ButtonToFlashTX := BUSB;
      M_LSB: ButtonToFlashTX := BLSB;
      M_AM:  ButtonToFlashTX := BAM;
      M_R3E: ButtonToFlashTX := BR3E;
    end;
    case RTXState.Mode_TX of
      M_USB: ButtonToFlashRX := BUSB;
      M_LSB: ButtonToFlashRX := BLSB;
      M_AM:  ButtonToFlashRX := BAM;
      M_R3E: ButtonToFlashRX := BR3E;
    end;
      FLASH.Enabled := TRUE;
  end else begin
    FLASH.Enabled := FALSE;
    if RTXState.PTT_State then begin
      if ButtonToFlashTX <> nil then
        ButtonToFlashTX.Image.Picture := PImgON;
    end else begin
      if ButtonToFlashRX <> nil then
        ButtonToFlashRX.Image.Picture := PImgON;
    end;
    ButtonToFlashRX := nil;
    ButtonToFlashTX := nil;
  end;

  // MODE
  if RTXState.PTT_State then begin
    tmp_mode := RTXState.Mode_TX;
  end else begin
    tmp_mode := RTXState.Mode_RX;
  end;
  case tmp_mode of
    M_LSB: begin
      BLSB.Image.Picture := PImgON;
      BUSB.Image.Picture := PImgOFF;
      BAM.Image.Picture := PImgOFF;
      BTELEX.Image.Picture := PImgOFF;
      BCW.Image.Picture := PImgOFF;
      BMCW.Image.Picture := PImgOFF;
      BR3E.Image.Picture := PImgOFF;
      SCUR.Brush.Color := LEDColorOFF;
  end;
    M_USB: begin
      BLSB.Image.Picture := PImgOFF;
      BUSB.Image.Picture := PImgON;
      BAM.Image.Picture := PImgOFF;
      BTELEX.Image.Picture := PImgOFF;
      BCW.Image.Picture := PImgOFF;
      BMCW.Image.Picture := PImgOFF;
      BR3E.Image.Picture := PImgOFF;
      SCUR.Brush.Color := LEDColorOFF;
    end;
    M_AM: begin
      BLSB.Image.Picture := PImgOFF;
      BUSB.Image.Picture := PImgOFF;
      BAM.Image.Picture := PImgON;
      BTELEX.Image.Picture := PImgOFF;
      BCW.Image.Picture := PImgOFF;
      BMCW.Image.Picture := PImgOFF;
      BR3E.Image.Picture := PImgOFF;
      SCUR.Brush.Color := LEDColorOFF;
    end;
    M_TLX: begin
      BLSB.Image.Picture := PImgOFF;
      BUSB.Image.Picture := PImgOFF;
      BAM.Image.Picture := PImgOFF;
      BTELEX.Image.Picture := PImgON;
      BCW.Image.Picture := PImgOFF;
      BMCW.Image.Picture := PImgOFF;
      BR3E.Image.Picture := PImgOFF;
      SCUR.Brush.Color := LEDColorOFF;
    end;
    M_CW: begin
      BLSB.Image.Picture := PImgOFF;
      BUSB.Image.Picture := PImgOFF;
      BAM.Image.Picture := PImgOFF;
      BTELEX.Image.Picture := PImgOFF;
      BCW.Image.Picture := PImgON;
      BMCW.Image.Picture := PImgOFF;
      BR3E.Image.Picture := PImgOFF;
      SCUR.Brush.Color := LEDColorOFF;
    end;
    M_MCW: begin
      BLSB.Image.Picture := PImgOFF;
      BUSB.Image.Picture := PImgOFF;
      BAM.Image.Picture := PImgOFF;
      BTELEX.Image.Picture := PImgOFF;
      BCW.Image.Picture := PImgOFF;
      BMCW.Image.Picture := PImgON;
      BR3E.Image.Picture := PImgOFF;
      SCUR.Brush.Color := LEDColorOFF;
    end;
    M_R3E: begin
      BLSB.Image.Picture := PImgOFF;
      BUSB.Image.Picture := PImgOFF;
      BAM.Image.Picture := PImgOFF;
      BTELEX.Image.Picture := PImgOFF;
      BCW.Image.Picture := PImgOFF;
      BMCW.Image.Picture := PImgOFF;
      BR3E.Image.Picture := PImgON;
      SCUR.Brush.Color := LEDColorOFF;
    end;
    M_500K: begin
      BLSB.Image.Picture := PImgOFF;
      BUSB.Image.Picture := PImgOFF;
      BAM.Image.Picture := PImgOFF;
      BTELEX.Image.Picture := PImgOFF;
      BCW.Image.Picture := PImgOFF;
      BMCW.Image.Picture := PImgON;
      BR3E.Image.Picture := PImgOFF;
      SCUR.Brush.Color := LEDColorON;
    end;
    M_2182K: begin
      BLSB.Image.Picture := PImgOFF;
      BUSB.Image.Picture := PImgOFF;
      BAM.Image.Picture := PImgON;
      BTELEX.Image.Picture  := PImgOFF;
      BCW.Image.Picture := PImgOFF;
      BMCW.Image.Picture := PImgOFF;
      BR3E.Image.Picture := PImgOFF;
      SCUR.Brush.Color := LEDColorON;
    end;
  end;

  // Force CU LEDs to be lit
  if RTXstate.Mode_RX in [M_USB, M_LSB, M_MCW, M_R3E] then
    BINTERClick(Self);
  if RTXstate.Mode_RX = M_AM then
    BWIDEClick(Self);

  // Power
  case RTXState.TX_power of
    LOW_POWER: begin
      BLOW.Image.Picture := PImgON;
      BMED.Image.Picture := PImgOFF;
      BFULL.Image.Picture := PImgOFF;
    end;
    MEDIUM_POWER: begin
      BLOW.Image.Picture := PImgOFF;
      BMED.Image.Picture := PImgON;
      BFULL.Image.Picture := PImgOFF;
    end;
    FULL_POWER: begin
      BLOW.Image.Picture := PImgOFF;
      BMED.Image.Picture := PImgOFF;
      BFULL.Image.Picture := PImgON;
    end;
  end;

  // TX ON/OFF & TX frequency display (clock when TX disabled)
  if RTXState.TX_Enabled then begin
    Clock.Enabled := FALSE;
    BTXONOFF.LCaption.Caption := BaseBTXONOFFCaption+' (ON)';
    BTXONOFF.Color := clLime;
    BTRANS.Enabled := TRUE;
  end else begin
    Clock.Enabled := TRUE;
    BTRANS.Enabled := FALSE;
    BTXONOFF.LCaption.Caption := BaseBTXONOFFCaption+' (OFF)';
    BTXONOFF.Color := $007e7eff;
  end;

  // RX/TX
  if RTXState.TX_Enabled then begin
    BTRANS.Enabled := TRUE;
    if RTXState.PTT_State then begin
      BTRANS.LCaption.Caption := 'RECEIVE';
    end else begin
      BTRANS.LCaption.Caption := 'TRANSMIT';
    end;
  end else BTRANS.Enabled := FALSE;

  // Set volume annunciators
  if RTXState.Volume < 99 then
    BVOLDN.Image.Picture := PImgArrDnOFF
  else
    BVOLDN.Image.Picture := PImgArrDnON;
  if RTXState.Volume > 0 then
    BVOLUP.Image.Picture := PImgArrUpOFF
  else
    BVOLUP.Image.Picture := PImgArrUpON;

  // Scan
  if RTXState.uscan then begin
    BSCAN.Image.Picture := PImgON;
  end else begin
    BSCAN.Image.Picture := PImgOFF;
  end;

  // Duplex (?)
  if RTXState.Duplex then begin
    BDUP.Image.Picture := PImgON;
  end else begin
    BDUP.Image.Picture := PImgOFF;
  end;

  // Disable/enable TX LEDS
  if MStatus.Checked then begin
    SPLOW.Show;
    SCUR.Show;
    SRPWR.Show;
    LRP1.Show;
    LRP2.Show;
  end else begin
    SPLOW.Hide;
    SCUR.Hide;
    SRPWR.Hide;
    LRP1.Hide;
    LRP2.Hide;
  end;

  // LEDs
  if HighSWR then
    // This should flash
    SPLOW.Brush.Color := LEDColorOff
  else
    SPLOW.Brush.Color := LEDColorOn;

  if ReducedPower then
    SRPWR.Brush.Color := LEDColorOn
  else
    SRPWR.Brush.Color := LEDColorOFF;

  // If TCP server connected, light up NET LED
  // (done also in FLASHSPTimer)
  if NumTCPConnections > 0 then
    TSHConnected.Brush.Color := LEDColorON
  else
    TSHConnected.Brush.Color := LEDColorOFF;

  // Set active buttons according to RTXState and KeyboardState
  SetActiveButtons;
end;

// Restore filter and AGC values (needed after some commands that leave the 
// filter in the Intermediate position and the AGC in FAST setting)
procedure TTRP.RestoreFilterAndAGC;

var cmd: string;

begin
  // Set filter
  case RTXState.Filter of
    FILTER_WIDE: cmd := Filt_Wide;
    FILTER_INTERMEDIATE: cmd := Filt_Inter;
    FILTER_NARROW:  cmd := Filt_Narrow;
    FILTER_VERYNARROW: cmd := Filt_VNarrow;
    otherwise begin
      // Invalid value, use default filter
      RTXState.Filter := FILTER_DEFAULT;
      cmd := '';
    end;
  end;
  if cmd <> '' then
    if not (SendCommand(cmd, '') or Cmd_Aborted) then begin
      MsgToShow := COMMANDFAILED;
      RunGUI;
    end;

  // Set AGC
  case RTXState.AGC of
    AVC_ON: cmd := AGC_On;
    AVC_OFF: cmd := AGC_Off;
    AVC_FAST: cmd := AGC_Fast;
    AVC_SLOW: cmd := AGC_Slow;
    otherwise begin
      // Invalid value, default to SLOW
      RTXState.AGC := AVC_SLOW;
      cmd := AGC_SLOW;
    end;
  end;
  if not (SendCommand(cmd, '') or Cmd_Aborted) then
    MsgToShow := COMMANDFAILED;
end;

// Procedure to break loops and keep GUI active
procedure TTRP.RunGUI;
begin
{$IFDEF DEBUG_RUNGUI}
MsgToShow := 'RunGUI called';
{$ENDIF}
Application.ProcessMessages;
end;

// Restore state in RTXState
procedure TTRP.RestoreState;

var cmd: string = '';
    par: string = '';
    i,TmpRXFreq,Fig10Hz: integer;

begin
  {$IFDEF DEBUG_START}
  MsgToShow := TimeToStr(now)+': start RestoreState';
  RunGUI;
  {$ELSE}
  MsgToShow := RESTSTATE;
  RunGUI;
  {$ENDIF}

  while In_Command do begin
    RunGUI;
  end;

  In_Command := TRUE;

  // Save frequency downto 1Hz for later use
  TmpRXfreq := RTXState.Freq_RX;

  // Set and show frequencies
  DispRXf(TRUE);
  DispTXf(TRUE);

  // If channel mode, set and display channel number and exit
  if RTXState.Freq_rx < 0 then begin
    if not (SendCommand(Recall, RX_Prefix+IntToStr(-RTXState.Freq_RX)) or Cmd_Aborted) then begin
      MsgToShow := COMMANDFAILED;
      RunGUI;
    end;
    KeyboardState := CHANNEL;
    SetAllLEDsOff;
    SetActiveButtons;
    In_Command := FALSE;
    exit;
  end else if RTXState.Freq_TX < 0 then begin
    if not (SendCommand(Recall, TX_Prefix+IntToStr(-RTXState.Freq_TX)) or Cmd_Aborted) then begin
      MsgToShow := COMMANDFAILED;
      RunGUI;
    end;
    SetAllLEDsOff;
    In_Command := FALSE;
    KeyboardState := CHANNEL;
    SetActiveButtons;
    exit;
  end else begin
    RTXState.Freq_rx := 100*(RTXState.Freq_RX div 100);
    KeyboardState := NORMAL;
    SetactiveButtons;
  end;
  // Set RX and TX frequency
  if RTXState.Freq_rx = RTXState.Freq_tx then begin
    if not (SetRXTXfreq or Cmd_Aborted) then begin
      MsgToShow := COMMANDFAILED;
      RunGUI;
    end;
  end else begin
    // Set RX frequency
    if not (SetRXFreq or Cmd_Aborted) then begin
      MsgToShow := COMMANDFAILED;
      RunGUI;
    end;

    // Set TX frequency
    if not (SetTXFreq or Cmd_Aborted) then begin
      MsgToShow := COMMANDFAILED;
      RunGUI;
    end;
  end;

  // Set programmable step - must be before step and 10 Hz frequency
  // setting since this command set to 0 the 10 Hz digit
  par := IntToSTr(RTXState.Programmable_step div 100)+stCR;
  if not (SendCommand(Read_Set_Step, par) or Cmd_Aborted) then begin
    MsgToShow := COMMANDFAILED;
    RunGUI;
  end;

  sleep(SLOWCMDDELAY); // wait previous command end - without this the following
                       // command will sometime fail

  // Set frequency step
  if RTXState.Programmable_step_set then begin
    RTXState.Freq_step := RTXState.Programmable_step;
    RTXState.Programmable_Step_Set := TRUE;
    cmd := TuneStepProg;
  end else begin
    case RTXState.Freq_step of
      10: cmd := TuneStep10;
      100: cmd := TuneStep100;
      1000: cmd := TuneStep1k;
    end;
  end;
  if not (SendCommand(cmd, '') or Cmd_Aborted) then begin
    MsgToShow := COMMANDFAILED;
    RunGUI;
  end;

  // If 10 Hz step selected and 10 Hz figure not zero, go to that frequency
  Fig10Hz := (TmpRXFreq mod 100) div 10;
  if (RTXState.Freq_step = 10) and (Fig10Hz > 0) then begin
    for i := 1 to Fig10Hz do begin
      if not (SendCommand(TUNE_UP,'') or Cmd_Aborted) then begin
        MsgToShow := COMMANDFAILED;
        In_Command := FALSE;
        exit;
      end;
    end;
    RTXstate.Freq_rx := RTXstate.Freq_RX + 10*Fig10Hz;
    DispRXf(TRUE);
  end;

  // Set BFO
  if not (SendCommand(Set_BFO, IntToStr(RTXState.BFO_freq div 100)+stCR) or
    Cmd_Aborted) then begin
    MsgToShow := COMMANDFAILED;
    RunGUI;
  end;

  // Set mode
  case RTXState.Mode_RX of
    M_USB: cmd := Mode_USB;
    M_LSB: cmd := Mode_LSB;
    M_AM: cmd := Mode_AM;
    M_R3E: cmd := Mode_R3E;
    M_CW: cmd := Mode_CW;
    M_MCW: cmd := Mode_MCW;
    M_TLX: cmd := Mode_Telex;
    M_2182k: cmd := Mode_AM;
    M_500k: cmd := Mode_MCW;
    otherwise begin
      // Invalid value, default to USB
      RTXState.Mode_RX := M_USB;
      cmd := Mode_USB;
    end;
  end;
  if not (SendCommand(cmd, '') or Cmd_Aborted) then begin
    MsgToShow := COMMANDFAILED;
    RunGUI;
  end;

  if RTXState.Mode_TX <> RTXState.Mode_RX then begin
    case RTXState.Mode_TX of
      M_USB: cmd := Mode_USB;
      M_LSB: cmd := Mode_LSB;
      M_AM:  cmd := Mode_AM;
      M_R3E: cmd := Mode_R3E;
      otherwise begin
        // Invalid value, default to USB
        RTXState.Mode_TX := M_USB;
        cmd := Mode_USB;
      end;
    end;
      if not (SendCommand(TX_Prefix, cmd + stCR) or Cmd_Aborted) then begin
        MsgToShow := COMMANDFAILED;
        RunGUI;
      end;
  end;

  // Set filter
  case RTXState.Filter of
    FILTER_WIDE: cmd := Filt_Wide;
    FILTER_INTERMEDIATE: cmd := Filt_Inter;
    FILTER_NARROW:  cmd := Filt_Narrow;
    FILTER_VERYNARROW: cmd := Filt_VNarrow;
    otherwise begin
        // Invalid value, use default filter
        RTXState.Filter := FILTER_DEFAULT;
        cmd := '';
    end;
  end;
  if cmd <> '' then
    if not (SendCommand(cmd, '') or Cmd_Aborted) then begin
      MsgToShow := COMMANDFAILED;
      RunGUI;
    end;

  // Set AGC
  case RTXState.AGC of
    AVC_ON: cmd := AGC_On;
    AVC_OFF: cmd := AGC_Off;
    AVC_FAST: cmd := AGC_Fast;
    AVC_SLOW: cmd := AGC_Slow;
    otherwise begin
      // Invalid value, default to SLOW
      RTXState.AGC := AVC_SLOW;
      cmd := AGC_SLOW;
    end;
  end;
  if not (SendCommand(cmd, '') or Cmd_Aborted) then begin
    MsgToShow := COMMANDFAILED;
    RunGUI;
  end;

  // Set volume
  if not (SendCommand(AF_GainSet,IntToStr(RTXState.Volume)+stCR) or
    Cmd_Aborted) then begin
      MsgToShow := COMMANDFAILED;
      RunGUI;
    end;

  // Set Antenna attenuator
  if RTXState.RFAtt_Enabled then begin
    if not (SendCommand(Attenuator_On,'') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  end else begin
    if not (SendCommand(Attenuator_Off,'') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  end;

  // Set RF preamp
  if RTXState.RFAmp_Enabled then begin
    if not (SendCommand(Preamp_On,'') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  end else begin
    if not (SendCommand(Preamp_Off,'') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  end;

  // Set speaker
  if RTXState.Speaker_Enabled then begin
    if not (SendCommand(Speaker_On, '') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED
  end else begin
    if not (SendCommand(Speaker_Off, '') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  end;

  // Set squelch
  if RTXState.Squelch_Enabled then begin
    if not (SendCommand(Squelch_On, '') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED
  end else begin
    if not (SendCommand(Squelch_Off, '') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  end;

  // Set TX ON/OFF
  if RTXState.TX_Enabled then begin
    if not (SendCommand(TX_On, '') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  end else begin
    if not (SendCommand(TX_Off, '') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  end;

  // Set power
  if MALWLP.Checked then RTXState.TX_power := LOW_POWER;
  case RTXState.TX_power of
    LOW_POWER: cmd := TX_LowPwr;
    MEDIUM_POWER: cmd := TX_MediumPwr;
    FULL_POWER: cmd := TX_HighPwr;
    otherwise begin
      // Invalid value, default to full power
      RTXState.TX_Power := FULL_POWER;
      cmd := TX_HighPwr;
    end;
  end;
  if not (SendCommand(cmd, '') or Cmd_Aborted) then MsgToShow := COMMANDFAILED;

  // Set dimmer
  if not (RTXState.Dimmer_Level in [0..5]) then RTXState.Dimmer_Level := 5;
  if not (SendCommand(Dimmer_Set, IntToStr(RTXState.Dimmer_Level) + stCR) or
          Cmd_Aborted) then MsgToShow := COMMANDFAILED;

  // Status polling
  if MStatus.Checked then begin
    TStatus.Enabled := TRUE;
    SMGroupBox.Show;
    PMGroupBox.Show;
  end else begin
    TStatus.Enabled := FALSE;
    SMGroupBox.Hide;
    PMGroupBox.Hide;
  end;

  // Scan mode
  if RTXState.uscan then begin
    KeyBoardState := SCANCH;
    SetActiveButtons;
    if not (SendCommand(Scan+Scan, '') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  end;

  // Programmable tune step
  if MPROGSTEP.Checked and RTXState.Programmable_Step_Set then begin
    RTXState.Freq_step := RTXState.Programmable_step;
  end;
  In_Command := FALSE;
  {$IFDEF DEBUG_START}
  MsgToShow := TimeToStr(now)+': stop RestoreState';
  {$ELSE}
  MsgToShow := DONE;
  {$ENDIF}
end;

//
// Object init & functions
//

// Various initializations at startup
procedure TTRP.FormCreate(Sender: TObject);

var i: integer;

begin
  {$IFDEF DEBUG_WIDTH_AND_HEIGHT}
  MsgToShow := 'DesignTimePPI='+IntToStr(DesignTimePPI)+ LineEnding+
  'Screen.PixelsPerInch='+IntTostr(Screen.PixelsPerInch);
  MsgToShow := 'Width='+IntToSTr(Width)+' Heigth='+IntTostr(Height)+LineEnding+
  'ClientWidth='+IntToSTr(CLientWidth)+' CLientHeigth='+IntTostr(ClientHeight);
  {$ENDIF}

  // Check X and Y PPI
  PPI_X := Graphics.ScreenInfo.PixelsPerInchX;
  PPI_Y := Graphics.ScreenInfo.PixelsPerInchY;
  if PPI_X <> PPI_Y then
      ShowMessage(NONSQUAREPIX+
      LineEnding+'PPI_X='+IntTostr(PPI_X)+' PPI_Y='+IntTostr(PPI_Y)+
      LineEnding+EXPECTSTBE);

  TRP.Caption := PROGNAME+ShortVersion;
  {$IFDEF TEST_USER_INTERFACE}
  TRP.Caption := PROGNAME +'- USER INTERFACE TEST MODE!';
  {$ENDIF}
  {$IFDEF SHOW_READONTIME_MENU}
  MREADHOURS.Enabled := TRUE;
  MREADHOURS.Visible := TRUE;
  {$ENDIF}
  {$IFDEF SHOW_SETBEEPER_MENU}
  MSETBEEP.Enabled := TRUE;
  MSETBEEP.Visible := TRUE;
  {$ENDIF}
  {$IFDEF SHOW_PROGSTEP_MENU}
  MSHOWPROGSTEP.Enabled := TRUE;
  MSHOWPROGSTEP.Visible := TRUE;
  {$ENDIF}
  RXFreq10H.Caption := '';
  RXFreq100H.Caption := '';
  RXFreq1k.Caption := '';
  RXFreq10k.Caption := '';
  RXFreq100k.Caption := '';
  RXFreq1M.Caption := '';
  RXFreq10M.Caption := '';

  TXFreq100H.Caption := '';
  TXFreq1k.Caption := '';
  TXFreq10k.Caption := '';
  TXFreq100k.Caption := '';
  TXFreq1M.Caption := '';
  TXFreq10M.Caption := '';

  // Default scan parameters
  with ScanParams do begin
    StartChan := 0;
    StopChan := 75;
    DwellTime := 10;
  end;

  DisableAllControls;
  BONOFF.Enabled := TRUE;
  KeyboardState := POWEROFF;

  // Read config
  ReadConfig;

  // Read button on/off bitmaps
  PImgArrDnOFF := TPicture.Create;
  PImgArrDnON := TPicture.Create;
  PImgArrUpOFF := TPicture.Create;
  PImgArrUpON := TPicture.Create;
  PImgOFF := TPicture.Create;
  PImgON := TPicture.Create;

  PImgArrUpOFF.LoadFromFile(ImgDir+'arrow-up-off-'+sLEDColor+'.bmp');
  PImgArrUpON.LoadFromFile(ImgDir+'arrow-up-on-'+sLEDCOlor+'.bmp');
  PImgArrDnOFF.LoadFromFile(ImgDir+'arrow-down-off-'+sLEDColor+'.bmp');
  PImgArrDnON.LoadFromFile(ImgDir+'arrow-down-on-'+sLEDCOlor+'.bmp');
  PImgOFF.LoadFromFile(ImgDir+'off-'+sLEDColor+'.bmp');
  PImgON.LoadFromFile(ImgDir+'on-'+sLEDCOlor+'.bmp');

  // Initialize Smeter and Pmeter arrays
  SMShapes[1] := SMShape1;
  SMShapes[2] := SMShape2;
  SMShapes[3] := SMShape3;
  SMShapes[4] := SMShape4;
  SMShapes[5] := SMShape5;
  SMShapes[6] := SMShape6;
  SMShapes[7] := SMShape7;
  SMShapes[8] := SMShape8;
  SMShapes[9] := SMShape9;
  SMShapes[10] := SMShape10;
  SMShapes[11] := SMShape11;
  SMShapes[12] := SMShape12;
  SMShapes[13] := SMShape13;
  SMShapes[14] := SMShape14;
  SMShapes[15] := SMShape15;
  SMShapes[16] := SMShape16;
  SMShapes[17] := SMShape17;
  SMShapes[18] := SMShape18;
  SMShapes[19] := SMShape19;
  SMShapes[20] := SMShape20;

  PMShapes[1] := PMShape1;
  PMShapes[2] := PMShape2;
  PMShapes[3] := PMShape3;
  PMShapes[4] := PMShape4;
  PMShapes[5] := PMShape5;
  PMShapes[6] := PMShape6;
  PMShapes[7] := PMShape7;
  PMShapes[8] := PMShape8;
  PMShapes[9] := PMShape9;
  PMShapes[10] := PMShape10;
  PMShapes[11] := PMShape11;
  PMShapes[12] := PMShape12;
  PMShapes[13] := PMShape13;
  PMShapes[14] := PMShape14;
  PMShapes[15] := PMShape15;
  PMShapes[16] := PMShape16;
  PMShapes[17] := PMShape17;
  PMShapes[18] := PMShape18;
  PMShapes[19] := PMShape19;
  PMShapes[20] := PMShape20;

  // Initialize button flash & repeat storage
  ButtonToFlashRX := TJButton.Create(self);
  ButtonToFlashTX := TJButton.Create(self);
  ButtonToRepeat := TJButton.Create(self);

  // Initialize bitmaps
  SetAllLEDsOff;

  // Set colors
  RXFreq10H.Font.Color := LEDColorON;
  RXFreq100H.Font.Color := LEDColorON;
  RXFreq1K.Font.Color := LEDColorON;
  RXFreq10K.Font.Color := LEDColorON;
  RXFreq100K.Font.Color := LEDColorON;
  RXFreq1M.Font.Color := LEDColorON;
  RXFreq10M.Font.Color := LEDColorON;
  RXFreqDOT.Font.Color := LEDColorON;

  TXFreq100H.Font.Color := LEDColorON;
  TXFreq1K.Font.Color := LEDColorON;
  TXFreq10K.Font.Color := LEDColorON;
  TXFreq100K.Font.Color := LEDColorON;
  TXFreq1M.Font.Color := LEDColorON;
  TXFreq10M.Font.Color := LEDColorON;
  TXFreqDOT.Font.Color := LEDColorON;

  SetMetersColor;

  // Read channels directory
  LoadChannelsDir;

  // Windows-specific setup
  {$IFDEF WINDOWS}
  Mttys0.Caption := 'COM1';
  Mttys1.Caption := 'COM2';

  RXfreq10M.Top := 0;
  TXFreq10M.Top := 0;
  RXfreq1M.Top := 0;
  TXFreq1M.Top := 0;
  RXFreqDot.Top := 0;
  TXFreqDot.Top := 0;
  RXfreq100k.Top := 0;
  TXFreq100k.Top := 0;
  RXfreq10k.Top := 0;
  TXFreq10k.Top := 0;
  RXfreq1k.Top := 0;
  TXFreq1k.Top := 0;
  RXfreq100H.Top := 0;
  TXFreq100H.Top := 0;
  RXFreq10H.Top := 0;

  for i := 1 to 20 do begin
    SMShapes[i].Top := -2;
    PMShapes[i].Top := -2;
  end;
  {$ENDIF}

  {$IFDEF CPUARM}
  Mttys0.Caption := 'ttyAMA0';
  Mttys1.Caption := 'ttyUSB0';
  {$ENDIF}

  // Component & window scaling
  {$IFDEF AUTOSCALE}
  for i := 0 to ComponentCount - 1 do begin
    if Components[i] is TControl then begin
      Xs[i] := TControl(Components[i]).Left;
      Ys[i] := TControl(Components[i]).Top;
      Ws[i] := TControl(Components[i]).Width;
      Hs[i] := TControl(Components[i]).Height;
      if (Components[i] is TLabel) or
         (Components[i] is TStaticText) or
         (Components[i] is TMemo) then begin
           Hf[i] := TControl(Components[i]).Font.Height;
      end;
      if (Components[i] is TJButton) then
        Hf[i] := (Components[i] as TJButton).LCaption.Font.Height;
    end;
    {$IFDEF DEBUG_AUTOSCALE}
  //  MsgToShow := 'total of '+IntToStr(i)+' components';
    {$ENDIF} // DEBUG_AUTOSCALE
    {$ENDIF} // AUTOSCALE
  end;

  // Figure out available height
  // Use of ClientHeight gives strange results
  // OH := TRP.ClientHeight;
  OH := BONOFF.Top+BONOFF.Height+1;
  OW := TRP.ClientWidth;
  {$IFDEF DEBUG_AUTOSCALE}
  // Done that way since MSG memo seems not yet created
  ShowMessage('H='+IntTostr(Height)+' W='+IntTostr(Width)+LineEnding+
  'OH='+IntToStr(OH)+' OW='+IntToSTR(OW));
  {$ENDIF}

  // Save default Enter button widths
  EnterNormalWidth := BENTER.Width;
  EnterSETBFOWidth := (BENTER.Width - BMINUS.Width)*96 div 100;

  // Set serial port menu
  {$IFDEF LINUX}
  {$IFDEF CPUARM}
  if (SerPortName = '/dev/ttyAMA0') then begin
  {$ELSE}
  if (SerPortName = '/dev/ttyS0') then begin
  {$ENDIF}
  {$ENDIF}
  {$IFDEF WINDOWS}
  if SerPortName = '\\.\COM1' then begin
  {$ENDIF}
    Mttys0.Checked := TRUE;
    Mttys1.Checked := FALSE;
    MCustom.Checked := FALSE;
  end else begin
    {$IFDEF LINUX}
    {$IFDEF CPUARM}
    if SerPortName = '/dev/ttyUSB0' then begin
    {$ELSE}
    if SerPortName = '/dev/ttyS1' then begin
    {$ENDIF}
    {$ENDIF}
    {$IFDEF WINDOWS}
    if SerPortName = '\\.\COM2' then begin
    {$ENDIF}
      Mttys0.Checked := FALSE;
      Mttys1.Checked := TRUE;
      MCustom.Checked := FALSE;
    end else begin
      Mttys0.Checked := FALSE;
      Mttys1.Checked := FALSE;
      MCustom.Checked := TRUE;
    end;
  end;
  CheckOwnF;
end;

// Adapt main window to the screen size and put it in the saved position
// Must be put here to avoid unpredictable race conditions with the window
// creation.
procedure TTRP.FormShow(Sender: TObject);

begin
 // Adapt main window to the screen height
 if TRP.Height > Screen.Height then TRP.Height := Screen.Height;
 if TRP.Width > Screen.Width then TRP.Width := Screen.Width;

 //Put window in the saved position
 if StartX <= Screen.Width - TRP.Width then
   TRP.Left := StartX
 else
   TRP.Left := Screen.Width - TRP.Width;

 if StartY <= Screen.Height - TRP.Height then
   TRP.Top := StartY
 else
   TRP.Top := Screen.Height - TRP.Height;

 // Resize window if larger than screen size
 if TRP.Width > Screen.Width then
   TRP.Width := Screen.Width
 else
   TRP.Width := StartWidth;

 if TRP.Height > Screen.Height then
   TRP.Height := Screen.Height
 else
   TRP.Height := StartHeight;

 // Apply font magnification factor
 FontMagnify(TRUE);
 StartFontHeight := RxFreq10M.Height;

 {$IFDEF DEBUG_FONTMAGN}
 MsgToShow := 'Screen.Width='+IntToStr(Screen.Width)+LineEnding+
         'Screen.Height='+IntToStr(Screen.Height)+LineEnding+
         'ClientWidth='+IntToStr(TRP.ClientWidth)+LineEnding+
         'ClientHeight='+IntToStr(TRP.ClientHeight)+LineEnding+
         'Font magnification='+IntToStr(FontMagn)+'%';
 {$ENDIF}

 {$IFDEF TEST_MESSAGE}
 MsgToShow := 'Test Message line 1'+LineEnding+
         'Test Message line 2'+LineEnding+
         'Test Message line 3';
 {$ENDIF}
end;

// Close down program
procedure TTRP.CloseProgram;

begin
  if MStatus.Checked then begin
    TStatus.Enabled := FALSE;
    Enable_Status := FALSE;
  end;
  if SerPort > 0 then begin
    CloseRemote;
    SerDrain(SerPort);
    SerClose(SerPort);
  end;

  if ButtonToFlashRX <> nil then ButtonToFlashRX.Destroy;
  if ButtonToFlashTX <> nil then ButtonToFlashTX.Destroy;
  if ButtonToRepeat <> nil then ButtonToRepeat.Destroy;

  if HaveConfig then SaveConfig;
  if HaveState then SaveLastState;

  halt;
end;

// Closing the program with window decoration button
procedure TTRP.FormClose(Sender: TObject; var CloseAction: TCloseAction);

begin
  CloseAction := caNone;
  MExitClick(Sender);
end;

// Allows to enter frequency and some commands using the keyboard
procedure TTRP.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);

var keyp: char;
    sRX, sTX: string;

begin
  {$IFDEF TEST_KEYDOWN}
  MsgToShow := 'key down: '+IntToStr(key)+' ('+chr(key)+')';
  {$ENDIF}
  // If ALT pressed do nothing and exit to allow menu selection with ALT-key
  // To be sure, trap also CTRL and CTRL-ALT combination that we do not use
  if (Shift = [ssCTRL]) or (Shift = [ssALT]) or (Shift = [ssALT,ssCTRL]) then
    exit;
  sRX := '';
  sTX := '';
  case Key of
    VK_NUMPAD0..VK_NUMPAD9: keyp := chr(key-48);
    VK_DECIMAL,VK_OEM_PERIOD: keyp := '.';
    VK_DIVIDE: keyp := '/';
    VK_ADD,VK_UP,VK_OEM_PLUS: keyp := '+';
    VK_SUBTRACT,VK_DOWN,189: keyp := '-';
    VK_MULTIPLY: keyp := '*';
    VK_DELETE: keyp := chr(8);
    VK_PRINT, VK_INSERT,VK_F1..VK_F12:
        // trap these keys, otherwise are read as commands
        keyp := #0;
    otherwise begin
      keyp := UpCase(chr(key));
      if (KeyboardState = NORMAL) and not (keyp in CommandKeys) then exit;
    end;
  end;

  keyp := UpCase(keyp);
  if KeyboardState <> NORMAL then begin
    if (KeyboardState = SETRXF) or (KeyboardState = SETTXF) then begin
      case keyp of
        'R': BRXClick(Sender);
        'T': BTXClick(Sender);
        'U': BUSBClick(Sender);
        'L': BLSBClick(Sender);
        'A': BAMClick(Sender);
        'C': BCWClick(Sender);
        'M': BMCWClick(Sender);
        'E': BR3ECLick(Sender);
        'X': BTELEXClick(Sender);
      end;
    end;

      if KeyboardState = SETBFO then begin
        if keyp in ['+','-'] then KeyboardHandler(Keyp);
      end;
      if (Keyp = chr(13)) and BENTER.Enabled then begin
        {$IFDEF WINDOWS}
        BENTER.SetFocus;
        {$ENDIF}
        BEnterClick(BENTER);
      end;
      if keyp = chr(8) then begin
        case KeyboardState of
          SETRXF,STOCH,RCLCH: begin
            PutRXString(PROMPT);
            RXFBuf := '';
          end;
          SETTXF,SETTIME,SETWAKEUP: begin
            sTX := PROMPT;
            RXFBuf := '';
          end;
          SETRXTXF: begin
            sRX := PROMPT;
            RXFBuf := '';
            sTX := PROMPT;
            RXFBuf := '';
          end;
        end;
      end;
      if keyp in ['0'..'9'] then KeyboardHandler(Keyp);
  end else begin
    case keyp of
      ' ': if BTRANS.Enabled then BTRANSClick(Sender);
      '+': BTUNEUPClick(Sender);
      '-': BTUNEDNClick(Sender);
      '/': BRATEClick(Sender);
      '*': BTUNEClick(Sender);
      'R': BRXCLick(Sender);
      'T': BTXClick(Sender);
      'U': BUSBClick(Sender);
      'L': BLSBClick(Sender);
      'A': BAMClick(Sender);
      'C': BCWClick(Sender);
      'M': BMCWClick(Sender);
      'E': BR3ECLick(Sender);
      'X': BTELEXClick(Sender);
      '.': begin
             case RTXState.TX_power of
               LOW_POWER   : BMEDCLick(Sender);
               MEDIUM_POWER: BFULLClick(Sender);
               FULL_POWER  : BLOWClick(Sender);
             end;
          end;
    end;
  end;
  if sRX <> '' then PutRXString(sRX);
  if sTX <> '' then PutTXString(sTX);
  Key := 0;
end;

// Code to resize the window and all control.
// Taken from an example by Ari Hirviniemi for Delphi.
procedure TTRP.FormResize(Sender: TObject);

var i: integer;
    c: TControl;

begin
    {$IFDEF AUTOSCALE}
    { Here's actual component scaling. }
    for i := 0 to ComponentCount - 1 do begin
        if Components[i] is TControl then begin
               c := TControl(Components[i]);
               c.Left   := (Xs[i] * ClientWidth) div OW;
               c.Top    := (Ys[i] * ClientHeight) div OH;
               c.Width  := (Ws[i] * ClientWidth) div OW;
               c.Height := (Hs[i] * ClientHeight) div OH;
            {$IFDEF DEBUG_AUTOSCALE}
//            MsgToShow := 'h='+IntTostr(c.Height)+' was '+IntTostr(Hs[i]);
            {$ENDIF}
        end;
    end;
    EnterNormalWidth := BENTER.Width;
    EnterSETBFOWidth := (BENTER.Width - BMINUS.Width)*96 div 100;
    if KeyboardState = SETBFO then BENTER.Width := EnterSetBFOWidth;

    if (RTXSTate.FReq_RX > 0) and
       (KeyboardState <> POWEROFF) and
       (KeyboardState <> SETBFO) then begin
        DispRXf(TRUE);
        DispTXf(TRUE);
    end;
    FontMagnify(TRUE);
    {$ELSE}
    TRP.Width := 730;
    TRP.Height := 624;
    {$ENDIF}
end;

//
// Menu handling
//

// "File" menu handling

// "Load state"
procedure TTRP.MLSTATEClick(Sender: TObject);

var StateFile: FILE of TRTXState;

begin
  OpenDialog1.InitialDir := StateDir;
  if OpenDialog1.Execute then begin
    if FileExists(OpenDialog1.FileName) then begin
      AssignFile(StateFile,OpenDialog1.Filename);
      try
        Reset(StateFile);
        Read(StateFile,RTXState);
        CloseFile(StateFile);
      except
        MsgToShow := OLDUPDATING;
        Rewrite(StateFile);
        Write(StateFile, RTXstate);
        CloseFile(StateFile);
      end;
      RestoreState;
      KeyboardState := NORMAL;
      UpdatePanel;
      EnableAllcontrols;
      SetActiveButtons;
    end else MsgToShow := FILENOTFOUND;
  end;
end;

// "Save state" and "Save channel" menu entries
procedure TTRP.MSSTATEClick(Sender: TObject);

var StateFile: file of TRTXState;
    SModeRX: string = '';
    SModeTX: string = '';
    sMtmp,sFtmp: string;

begin
  case RTXState.Mode_RX of
    M_USB: SModeRX := 'USB';
    M_LSB: SModeRX := 'LSB';
    M_AM:  SmodeRX := 'AM';
    M_TLX: SModeRX := 'TELEX';
    M_R3E: SModeRX := 'R3E';
    M_CW:  SModeRX := 'CW';
    M_MCW: SModeRX := 'MCW';
    M_2182K: SModeRX := '2182';
    M_500K: SModeRX := '500';
  end;

  sMtmp := sModeRX;

  if RTXState.Mode_TX <> RTXState.Mode_RX then begin
    case RTXState.Mode_TX of
      M_USB: SModeTX := 'USB';
      M_LSB: SModeTX := 'LSB';
      M_AM:  SmodeTX := 'AM';
      M_TLX: SModeTX := 'TELEX';
      M_R3E: SModeTX := 'R3E';
      M_CW:  SModeTX := 'CW';
      M_MCW: SModeTX := 'MCW';
      M_2182K: SModeTX := '2182';
      M_500K: SModeTX := '500';
    end;
    sMtmp := sModeRX+'-'+sModeTX;
  end;

  if RTXState.TX_Enabled then begin
    if RTXstate.Freq_tx <> RTXstate.Freq_rx then
      sFtmp := GetRXString + '-' + GetTXString
    else
      sFtmp := GetRXString;
  end else begin
    sFtmp := GetRXString+'-TXOFF';
  end;

  if RTXState.Freq_RX < 0 then begin
    sMtmp := '';
    sFtmp := GetRXString;
  end;

  if Sender = MSSTATE then begin
    SaveDialog1.InitialDir := StateDir;
    Savedialog1.Title := SAVESTATE;
    SaveDialog1.Filter := SAVESTATEFILT;
  end;
  if Sender = MSCHANN then begin
    SaveDialog1.InitialDir := ChannelsDir;
    SaveDialog1.Title := SAVECHAN;
    SaveDialog1.Filter := SAVECHANFILT;
  end;

  SaveDialog1.FileName := sFtmp + '-' + sMtmp + '.dat';
  if SaveDialog1.Execute then begin
    AssignFile(StateFile,SaveDialog1.Filename);
    try
      Rewrite(StateFile);
      Write(StateFile,RTXState);
      CloseFile(StateFile);
    except
      ShowMessage(CANTWRITESTATE);
    end;
    if Sender = MSCHANN then MRELCHClick(Sender);
    if Sender = MSSTATE then CheckOwnF;
  end;
end;

// "Manage channels" menu entry
procedure TTRP.MMANCHClick(Sender: TObject);

begin
  UMGR.Caption := CHANMANAGER;
  UMGR.TFLB.Directory := ChannelsDir;
  UMGR.TFLB.UpdateFileList;
  UMGR.Tag := 1;
  UMGR.SPMOVE2CH.Hide;
  UMGR.SPMOVE2ST.Show;
  UMGR.Show;
end;

// "Manage states" menu entry
procedure TTRP.MMANSTClick(Sender: TObject);
begin
  UMGR.Caption := STATEMANAGER;
  UMGR.TFLB.Directory := StateDir;
  UMGR.TFLB.UpdateFileList;
  UMGR.Tag := 0;
  UMGR.SPMOVE2CH.Show;
  UMGR.SPMOVE2ST.Hide;
  UMGR.Show;
end;

// Reload channels direcory" menu entry
procedure TTRP.MRELCHClick(Sender: TObject);
begin
  LoadChannelsDir;
end;

// "Exit" menu entry
procedure TTRP.MExitClick(Sender: TObject);

begin
  if MessageDlg(CLOSEPROGR, REALLYEXIT, mtConfirmation,
     [mbYes, mbNo],0) = mrYes then
     CloseProgram;
end;

//
// "Other functions" menu handling
//

// "Read TU configuration"
procedure TTRP.MRCONFClick(Sender: TObject);

var s: string;

begin
  if In_Command then exit;

  if SendCommand(Read_Config, '') then begin
    s := GetReply(10,CMDTIMEOUT);
    if (Length(s) = 10) and (s[1] = '*') and (s[10]='>') then
      MsgToShow := TUC + Copy(s,2,8)
    else
      MsgToShow := INVALIDREPLY;
    Sleep(SLOWCMDDELAY);
    if not (SendCommand(stCR, '') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
    UpdatePanel;
  end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
end;

// "Read CU version"
procedure TTRP.MRCUClick(Sender: TObject);

var s: string;

begin
  if In_Command then exit;

  if SendCommand(Read_CU_Vers, '') then begin
    s := GetReply(13,CMDTIMEOUT);
    if (Length(s) = 13) and (s[1] = '*') and (s[13]='>') then
      MsgToShow := CUV + Copy(s,2,11)
    else
      MsgToShow := INVALIDREPLY;
    Sleep(SLOWCMDDELAY);
    if not (SendCommand(stCR, '') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
    RestoreFilterAndAGC;
    UpdatePanel;
  end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
end;

// "Read TU version"
procedure TTRP.MRTUClick(Sender: TObject);

var s: string;

begin
  if In_Command then exit;

  if SendCommand(Read_TU_Vers, '') then begin
    s := GetReply(13,CMDTIMEOUT);
    if (Length(s) = 13) and (s[1] = '*') and (s[13]='>') then
      MsgToShow := TUV + Copy(s,2,11)
    else
      MsgToShow := INVALIDREPLY;
    Sleep(SLOWCMDDELAY);
    if not (SendCommand(stCR, '') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
    RestoreFilterAndAGC;
    UpdatePanel;
  end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
end;


// Second function 248: adjust beeper sound level
// It only works partially since the command is accepted, but:
// * the CU does not start the beeping sound, so it is impossible to
//   set the wanted level and to verify that VOLUME UP and VOLUME DOWN
//   commands are accepted and working correctly.
// * after some 7s with no other commands sent (e. g. completing the command
//   or reading the status) the RTX is reset.
// So this menu entry is hidden unless SHOW_SETBEEPERMENU is $defined.
procedure TTRP.MSetBeepClick(Sender: TObject);

begin
  {$IFDEF SHOW_SETBEEPER_MENU}
  if In_Command then exit;

  Enable_Status := FALSE;
  TStatus.Enabled := FALSE;
  if SendCommand(Set_Beeper_Lev, '') then begin
    CmdToRepeat := Set_Beeper_Lev;
    KeyboardState := SETBEEPER;
    SetActiveButtons;
    MsgToShow := 'Use VOLUME DOWN & VOLUME UP to set level, ENTER when done';
    CMDRPT.Enabled := TRUE;
  end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  {$ENDIF}
end;

// Second function 242: Program receiver tune step
procedure TTRP.MSETSTEPClick(Sender: TObject);

begin
  if In_Command then exit;

  MsgToShow := ENTERSTEP;
  MsgToShow := DEFAULTSTEP+
    IntToStr(RTXState.Programmable_step div 1000)+
    '.'+
    IntToSTr((RTXState.Programmable_step div 100) mod 10)+
    ' kHz';
  MsgToShow := PRESSENTER;
  RXFBuf := '';
  KeyboardState := READSETSTEP;
  PutRXString(PROMPT);
  RestoreFilterAndAGC;
  SetActiveButtons;
end;

{$IFDEF SHOW_PROGSTEP_MENU}
// Second function 242: Read receiver tune step
// It only works partially since:
// * step value is shown only in the CU RX-Display and not sent to remote
// * if no more commands are sent after this command (e.g. completing the
//   command or reading the status) for more than about 7s, the RTX is reset.
// This menu entry is enabled, but issues a warning about where the result
// is displayed and it is implemented in a limited way: the command is issued,
// but the value is shown (on the CU only) for about 3s then the command is
// terminated to avoid RTX reset.
procedure TTRP.MSHOWPROGSTEPClick(Sender: TObject);
begin
  if In_Command then exit;

  In_Command := TRUE;
  if SendCommand(Read_Set_Step,'') then begin
    WaitTimeSecs(SHORTWAITTIME, ONLYINTHECU, TRUE);
    if not (SendCommand(stCR,'') or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
  end else MsgToShow := COMMANDFAILED;
  RestoreFilterAndAGC;
  In_Command := FALSE;
end;
{$ENDIF}

// Second function 241: Read accumulated on-time
// It only works partially since:
// * on-time is shown only in the CU RX-Display and not sent to remote
// * if no more commands are sent after this command (e.g. completing the
//   command or reading the status) for more than about 7s, the RTX is reset.
// This menu entry is enabled, but issues a warning about where the result
// is displayed and it is implemented in a limited way: the command is issued,
// but the value is shown (on the CU only) for about 3s then the command is
// terminated to avoid RTX reset.
procedure TTRP.MReadHoursClick(Sender: TObject);

begin
  if In_Command then exit;

  if SendCommand(Read_ON_Time, '') then begin
    WaitTimeSecs(SHORTWAITTIME, ONLYINTHECU, TRUE);
    if not (SendCommand(stCR,'') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  end else MsgToShow := COMMANDFAILED;
  KeyBoardState := NORMAL;
  RestoreFilterAndAGC;
{$IFDEF NOTDEF}
  // The full implementation does not work, don't use
  MsgToShow := ONTIMEONLYINTHECU;
  KeyboardState := READONHOURS;
  DisableAllButtons;
  SetActiveButtons;
  In_Command := TRUE;
  CMDToRepeat := Read_On_Time;
  CMDRPT.Enabled := TRUE;
{$ENDIF}
end;

// Reread configuration (CU status, firmware V92.0 only)
procedure TTRP.MREREADCONFClick(Sender: TObject);

begin
  if MCU920.Checked then begin
    LoadStateFromCU;
    UpdatePanel;
    DispRXf(TRUE);
    DispTXf(TRUE);
   end else MsgToShow := CUV92ONLY;
end;

// "Switch antenna OFF"
procedure TTRP.MSWANTOFFClick(Sender: TObject);

begin
  if In_Command then exit;

  In_Command := TRUE;
  if KeyboardState = NORMAL then begin
    if not (SendCommand(Switch_Ant_OFF, '') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED
    else begin
      KeyBoardState := ANTENNAOFF;
      SetActiveButtons;
      RTXState.TX_Enabled := FALSE;
      MsgToShow := ANTTXOFF;
      UpdatePanel;
    end;
  end;
  In_Command := FALSE;
end;

// "Read BFO frequency"
procedure TTRP.MRBFOClick(Sender: TObject);

var s: string;

begin
  if SendCommand(Read_BFO_Freq, '') then begin
    s := GetReply(3,CMDTIMEOUT);
    MsgToShow := BFO + FormatBFOFreq(s);
    sleep(300);
    if not (SendCommand(stCR, '') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
    RestoreFilterAndAGC;
    UpdatePanel;
  end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
end;

// "Set BFO frequency"
procedure TTRP.MSETBFOClick(Sender: TObject);

begin
  BENTER.Width := EnterSetBFOWidth;
  BMINUS.Show;
  PutRXString(PROMPT);
  RXFBuf := '';
  KeyboardState := SETBFO;
  MsgToShow := ENTERBFOFREQ;
  SetActiveButtons;
end;

// Store BFO frequency
procedure TTRP.MSTOBFOClick(Sender: TObject);

var s: string;

begin
  if not (SendCommand(Store_BFO_Freq, '') or Cmd_Aborted) then
    MsgToShow := COMMANDFAILED
  else begin
    s := GetReply(3,CMDTIMEOUT);
    sleep(150); // required
    SendCommand(stCR, '');
    MsgToShow := BFOSTORED+FormatBFOFreq(s);
    RestoreFilterAndAGC;
  end;
end;

// "Test alarm"
// In the CU this command enables the 7s reset timer,
// so to get around that feature, repeat every 3s the
// command until The "Stop alarm" menu is selected.
// Since the CU in that state is unresponsive to any command,
// disable also all program buttons and reenable them only when
// the test is disabled.
procedure TTRP.MTSTALRMClick(Sender: TObject);

begin
  if not (SendCommand(Test_Alarm, '') or Cmd_Aborted) then
    MsgToShow := COMMANDFAILED
  else begin
    MsgToShow := TESTALSTA;
    // The TEST ALARM command has 7s timeout
    // To override that the TEST ALARM command must be repeated
    CmdToRepeat := Test_Alarm;
    In_Command := TRUE;
    CMDRPT.Enabled := TRUE;
    DisableAllButtons;
  end;
end;

// "Stop alarm"
// Stops alarm and reenables program buttons (see above and under)
procedure TTRP.MSTPALRMClick(Sender: TObject);

begin
  CMDRPT.Enabled := FALSE;
  In_Command := FALSE;
  if not (SendCommand(Stop_Alarm, '') or Cmd_Aborted) then
    MsgToShow := COMMANDFAILED
  else begin
    MsgToShow := STOPALARM;
    EnableAllControls;
    SetActiveButtons;
  end;
end;

// "Send alarm"
// In the CU this command enables the 7s reset timer,
// so to get around that feature, repeat every 3s the
// command until The "Stop alarm" menu is selected.
// Since the CU in that state is unresponsive to any command
// except the "stop alarm" one, disable also all program buttons
// and reenable them only when the test is disabled.
procedure TTRP.MSNDALRMClick(Sender: TObject);

begin
  if MessageDlg(SENDALARM, AREUSURE, mtWarning,
     [mbYes, mbNo],0) = mrYes then begin
    if not (SendCommand(Send_Alarm, '') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED
    else begin
      MsgToShow := SENDINGALARM;
      // The SEND ALARM command has 7s timeout
      // To override that the SEND ALARM command must be repeated
      CmdToRepeat := Send_Alarm;
      In_Command := TRUE;
      CMDRPT.Enabled := TRUE;
      DisableAllButtons;
    end;
  end;
end;

// "Set guard register"
procedure TTRP.MSETGRClick(Sender: TObject);

begin
  OldRXFreq := GetRXString;
  PutRXString('');
  RXFreq10H.Caption := PROMPT;
  RXFBuf := '';
  KeyboardState := SETGR;
  MsgToShow := ENTERREGVALUE;
  SetActiveButtons;
end;

// "Set option register"
procedure TTRP.MSETORClick(Sender: TObject);

begin
  OldRXFreq := GetRXString;
  PutRXString('');
  RXFreq10H.Caption := PROMPT;
  RXFBuf := '';
  KeyboardState := SETOR;
  MsgToShow := ENTERREGVALUE;
  SetActiveButtons;
end;

// "Set preset register"
procedure TTRP.MSETPRClick(Sender: TObject);

begin
  OldRXFreq := GetRXString;
  PutRXString('');
  RXFreq10H.Caption := PROMPT;
  RXFBuf := '';
  KeyboardState := SETPR;
  MsgToShow := ENTERREGVALUE;
  SetActiveButtons;
end;

// "Fill TU8000 scan buffer"
// This command has 7s timeout
procedure TTRP.MFILLBUFClick(Sender: TObject);

begin
  MsgToShow := ENTERFPAIRS;
  OldRXFreq := GetRXString;
  PutRXString(PROMPT);
  RXFBuf := '';
  OldTXFreq := GetTXString;
  PutTXstring('');
  TXFBuf := '';
  n := 1;
  m := 1;
  KeyboardState := FILLSCNBUF;
  SetActiveButtons;
end;

// "Advance scan buffer pointer"
// This command has 7s timeout
procedure TTRP.MADVSCPTRClick(Sender: TObject);

begin
  OldRTXstate := RTXstate;
  if SendCommand(Adv_ScnBuf_Ptr, '') then begin
    PutRXString(SCANTU);
    PutTXString(SCANTU);
    KeyboardState := SCANTUBUF;
    MsgToShow := SCANBUFMODE;
    SetActiveButtons;
  end else
    if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
end;


// "Reset scan buffer pointer"
// This command has 7s timeout
procedure TTRP.MRSTSCBPClick(Sender: TObject);

begin
  if not (SendCommand(RST_ScnBuf_Ptr, '') or Cmd_Aborted) then
    MsgToShow := COMMANDFAILED;
end;

// "Self test (auto)"
procedure TTRP.MTESTAUTOClick(Sender: TObject);

begin
  TStatus.Enabled := FALSE;
  SM.Enabled := FALSE;
  if SendCommand(Exec_AST,'') then begin
    KeyboardState := SELFTSTA;
    SetActiveButtons;
    if GetAutoSelfTestResponse then begin
      BONOFFClick(Sender, FALSE);
    end else begin
      KeyboardState := NORMAL;
      SetActiveButtons;
    end;
  end;
end;

// "Self test (manual)"
procedure TTRP.MTESTMANClick(Sender: TObject);

begin
  TStatus.Enabled := FALSE;
  SM.Enabled := FALSE;
  // Disable all buttons. DIMMER buttons will be reenabled in
  // GetManSelfTestResponse when a test finishes.
  DisableAllButtons;
  RunGUI;
  if SendCommand(Exec_MST,'') then begin
    KeyboardState := SELFTSTM;
    if GetManSelfTestResponse then begin
      BONOFFClick(Sender, FALSE);
      WaitTimeSecs(RESETWAITTIME, PSEWAIT, TRUE);
      BONOFFClick(Sender, FALSE);
    end else KeyboardState := NORMAL;
  end;
end;

// "Self test from... (auto)"
procedure TTRP.MTESTFMAUTOClick(Sender: TObject);

begin
  TStatus.Enabled := FALSE;
  SM.Enabled := FALSE;
  OldRXfreq := GetRXString;
  PutRXstring(PROMPT);
  RXFBuf := '';
  KeyboardState := SELFTSTFMA;
  MsgToShow := ENTERTESTNUM;
  SetActiveButtons;
end;

// "Self test from... (manual)"
procedure TTRP.MTESTFMMANClick(Sender: TObject);

begin
  TStatus.Enabled := FALSE;
  SM.Enabled := FALSE;
  DisableAllButtons;
  OldRXfreq := GetRXString;
  PutRXstring(PROMPT);
  RXFBuf := '';
  KeyboardState := SELFTSTFMM;
  MsgToShow := ENTERTESTNUM;
  SetActiveButtons;
end;

// "Release remote priority"
procedure TTRP.MRELPRIOClick(Sender: TObject);

begin
  if MStatus.Checked then begin
    MsgToShow := CANTRELEASE;
    exit;
  end;
  if SendCommand(stEOT,'') then
    MsgToShow := RELEASED
  else
    if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
end;

// "Reset RTX"
procedure TTRP.MRESETClick(Sender: TObject);

begin
  TStatus.Enabled := FALSE;
  if SendCommand(RTX_RESET, '') then begin
    MsgToShow := RESETDONE;
  end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  SerDrain(SerPort);
  SerClose(SerPort);
  SerPort := 0;
  BONOFF.LCaption.Caption := BaseBONOFFCaption+' (OFF)';
  BONOFF.Color := $007e7eff;
  BTXONOFF.Color := clDefault;
  PutRXString('');
  PutTXString('');
  DisableAllControls;
  RunGUI;
  WaitTimeSecs(RESETWAITTIME, PSEWAIT, TRUE);
  BONOFFClick(Sender, FALSE);
end;

// "Restore current state"
procedure TTRP.MRSTATEClick(Sender: TObject);

begin
  if KeyboardState = NORMAL then begin
    RestoreState;
    EnableAllControls;
    SetActiveButtons;
    UpdatePanel;
  end else
    MsgToShow := TOEXIT;
end;

// "Exit chan/scan modes"
procedure TTRP.MEXITCHANClick(Sender: TObject);

begin
  case KeyboardState of
    CHANNEL,CHANNELRX,CHANNELTX: begin
      RTXState := OldRTXState;
      RestoreState;
      UpdatePanel;
      if ((OldRTXState.Freq_rx < 0) or
       (OldRTXState.Freq_tx < 0)) and not MCU920.Checked then begin
        MsgToShow := Format(PREVIOUSSTATE,['CHAN']);
          if OldRTXState.Freq_rx < 0 then begin
            RTXState.Freq_RX := 10000;
            PutRXString('--------');
          end;
          if OldRTXState.Freq_tx < 0 then begin
            RTXState.Freq_TX := 1600000;
            PutTXString('-------');
          end;
        end;
        if MCU920.Checked then begin
          LoadStatefromCU;
          UpdatePanel;
          DispRXf(TRUE);
          DispTXf(TRUE);
       end;

       KeyboardState := NORMAL;
       SetActiveButtons;
    end;
    SCANCH,SCANTUBUF: begin
      KeyboardState := NORMAL;
      // Send a CR to solicit DLE sending if a timeout occurred
      SendCommand(stCR,'');
      RTXState := OldRTXState;
      RestoreState;
      UpdatePanel;
      SetActiveButtons;
    end;
    otherwise MsgToShow := NOTINTHATSTATE;
  end;
  MsgToShow := NORMALMODE;
  EnableAllControls;
  SetActiveButtons;
  UpdatePanel;
end;

// "Reconnect to CU"
procedure TTRP.MRECCUClick(Sender: TObject);
begin
  BONOFFClick(Sender, FALSE);
  MsgToShow := TRYTOREC;
  sleep(500);
  BONOFFClick(Sender, TRUE);
end;

//
// "Options" menu handling
//

// Save configuration when an option is modified with the program not "ON"
procedure TTRP.MenuOptionClick(Sender: TObject);
begin
  SaveConfig;
end;

// "Enable all controls"
// Autochecked

// "RS232 port"

// "ttyS0" menu entry
procedure TTRP.MttyS0Click(Sender: TObject);

begin
  {$IFDEF LINUX}
    {$IFDEF CPUARM}
      SerPortName := '/dev/ttyAMA0';
    {$ELSE}
      SerPortName := '/dev/ttyS0';
    {$ENDIF}
  {$ENDIF}

  {$IFDEF WINDOWS}
    SerPortName := '\\.\COM1';
  {$ENDIF}

  SaveConfig;
end;

// "ttyS1" menu entry
procedure TTRP.MttyS1Click(Sender: TObject);

begin
    {$IFDEF LINUX}
      {$IFDEF CPUARM}
        SerPortName := '/dev/ttyUSB0';
      {$ELSE}
        SerPortName := '/dev/ttyS1';
      {$ENDIF}
    {$ENDIF}

    {$IFDEF WINDOWS}
      SerPortName := '\\.\COM2';
    {$ENDIF}

    SaveConfig;
end;

// "Custom..." menu entry
procedure TTRP.MCustomClick(Sender: TObject);

begin
  FSerport.Show;
  FSerport.Eser.Text := SerPortName;
end;

// "RS232 speed"

// "300 baud" menu entry
procedure TTRP.M300BClick(Sender: TObject);

begin
  SerPortSpeed := 300;
  SaveConfig;
end;

// "2400 baud"  menu entry
procedure TTRP.M2400BClick(Sender: TObject);

begin
  SerPortSpeed := 2400;
  SaveConfig;
end;

// "Status reading" menu entry
procedure TTRP.MStatusClick(Sender: TObject);

begin
  MStatus. Checked := not MStatus.Checked;
  if Mstatus.Checked then begin
    if SerPortSpeed < 2400 then begin
      MsgToShow := SPEEDTOOSLOW;
      MStatus.Checked := FALSE;
    end else begin
      SM.Enabled := TRUE;
      TSTATUS.Enabled := TRUE;
      Enable_Status := TRUE;
    end;
  end else begin
    SM.Enabled := FALSE;
    TSTATUS.Enabled := FALSE;
    Enable_Status := FALSE;
  end;
  SaveConfig;
  UpdatePanel;
end;

// "TUNE when remote changes frequency" menu entry
procedure TTRP.MTCHFClick(Sender: TObject);
begin
  TuneAtFreqChange := MTCHF.Checked;
  SaveConfig;
end;

// "TCP Server address:port"  menu entry
procedure TTRP.MTCPSERVERClick(Sender: TObject);
begin
  FTCP.EDADDPORT.Text := TRPServer.Host+':'+IntToStr(TRPServer.Port);
  FTCP.Show;
end;

// "Use UTC time" is autochecked and does not need a OnClick event

// "Enable programmable tune step" menu entry
procedure TTRP.MPROGSTEPClick(Sender: TObject);

begin
  if not MPROGSTEP.Checked then begin
   // disable programmable tune step and set 1kHz steps
    if SendCommand(TUNESTEP1K,'') then begin
      if RTXState.Programmable_Step_Set then begin
        RTXState.Freq_step := 1000;
        RTXState.Programmable_Step_Set := FALSE;
      end;
      MsgToShow := PROGTUNESTEPDI;
      UpdatePanel;
    end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  end else begin
    EnableProgTuneStep;
  end;
end;

// "Transceiver" menu entry

// "Disabled" submenu entry
// Autochecked, does not need a OnClick event

// "Deferred update" submenu entry
procedure TTRP.MDEFUClick(Sender: TObject);

begin
  if (RTXstate.Freq_rx <= MAXTXF) and (RTXstate.Freq_rx >= MINTXF) then begin
    RTXState.Freq_tx := RTXState.Freq_rx;
    if not (SetTXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
    DispTXf(TRUE);
  end;
  SaveConfig;
end;

// "Immediate update" submenu entry
procedure TTRP.MIMMUClick(Sender: TObject);

begin
  if (RTXstate.Freq_rx < MAXTXF) and (RTXstate.Freq_rx > MINTXF) then begin
    RTXState.Freq_tx := RTXState.Freq_rx;
    if not (SetTXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
    DispTXf(TRUE);
  end;
  SaveConfig;
end;

// "CU firmware 92.0" menu entry
procedure TTRP.MCU920Click(Sender: TObject);

begin
  if MCU920.Checked then begin
    if Check_V92_Firmware then begin
      MsgToShow := OKCUV92FOUND;
      MREREADCONF.Enabled := TRUE;
    end else begin
      MsgToShow := CUV92NOTFOUND;
      MCU920.Checked := FALSE;
      MREREADCONF.Enabled := FALSE;
    end;
  end else MREREADCONF.Enabled := FALSE;
  // The Read_CU_Version command alters filter and AGC settings,
  // so restore previous values
  RestoreFilterAndAGC;
end;

// "LED Color" menu entry

// "Green"
procedure TTRP.MGreenClick(Sender: TObject);

begin
  LEDCOlorOFF := clGreen;
  LEDColorON := clLime;
  sLEDColor := 'green';

  RXFreq10H.Font.Color := LEDColorON;
  RXFreq100H.Font.Color := LEDColorON;
  RXFreq1k.Font.Color := LEDColorON;
  RXFreq10k.Font.Color := LEDColorON;
  RXFreq100k.Font.Color := LEDColorON;
  RXFreq1M.Font.Color := LEDColorON;
  RXFreq10M.Font.Color := LEDColorON;
  RXFreqDOT.Font.Color := LEDColorON;

  TXFreq100H.Font.Color := LEDColorON;
  TXFreq1k.Font.Color := LEDColorON;
  TXFreq10k.Font.Color := LEDColorON;
  TXFreq100k.Font.Color := LEDColorON;
  TXFreq1M.Font.Color := LEDColorON;
  TXFreq10M.Font.Color := LEDColorON;
  TXFreqDOT.Font.Color := LEDColorON;

  PImgOFF.LoadFromFile(ImgDir+'off-'+sLEDColor+'.bmp');
  PImgON.LoadFromFile(ImgDir+'on-'+sLEDColor+'.bmp');
  PImgArrDnOFF.LoadFromFile(ImgDir+'arrow-down-off-'+sLEDColor+'.bmp');
  PImgArrDnON.LoadFromFile(ImgDir+'arrow-down-on-'+sLEDColor+'.bmp');
  PImgArrUpOFF.LoadFromFile(ImgDir+'arrow-up-off-'+sLEDColor+'.bmp');
  PImgArrUpON.LoadFromFile(ImgDir+'arrow-down-on-'+sLEDColor+'.bmp');

  if RTXState.Volume < 99 then
    BVOLDN.Image.Picture := PImgArrDnOFF
  else
    BVOLDN.Image.Picture := pImgArrDnON;
  if RTXState.Volume > 0 then
    BVOLUP.Image.Picture := PImgArrUpOFF
  else
    BVOLUP.Image.Picture := PImgArrUpON;
  SetMetersColor;
  SaveConfig;
  UpdatePanel;
end;

// "Yellow"
procedure TTRP.MYellowClick(Sender: TObject);

begin
  LEDCOlorOFF := clOlive;
  LEDColorON := clYellow;
  sLEDColor := 'yellow';
  RXFreq10H.Font.Color := LEDColorON;
  RXFreq100H.Font.Color := LEDColorON;
  RXFreqDOT.Font.COlor := LEDColorON;
  RXFreq1k.Font.Color := LEDColorON;
  RXFreq10k.Font.Color := LEDColorON;
  RXFreq100k.Font.Color := LEDColorON;
  RXFreq1M.Font.Color := LEDColorON;
  RXFreq10M.Font.Color := LEDColorON;

  TXFreq100H.Font.Color := LEDColorON;
  TXFreqDOT.Font.Color := LEDColorON;
  TXFreq1k.Font.Color := LEDColorON;
  TXFreq10k.Font.Color := LEDColorON;
  TXFreq100k.Font.Color := LEDColorON;
  TXFreq1M.Font.Color := LEDColorON;
  TXFreq10M.Font.Color := LEDColorON;

  PImgOFF.LoadFromFile(ImgDir+'off-'+sLEDColor+'.bmp');
  PImgON.LoadFromFile(ImgDir+'on-'+sLEDColor+'.bmp');
  PImgArrDnOFF.LoadFromFile(ImgDir+'arrow-down-off-'+sLEDColor+'.bmp');
  PImgArrDnON.LoadFromFile(ImgDir+'arrow-down-on-'+sLEDColor+'.bmp');
  PImgArrUpOFF.LoadFromFile(ImgDir+'arrow-up-off-'+sLEDColor+'.bmp');
  PImgArrUpON.LoadFromFile(ImgDir+'arrow-down-on-'+sLEDColor+'.bmp');

  if RTXState.Volume < 99 then
    BVOLDN.Image.Picture := PImgArrDnOFF
  else
    BVOLDN.Image.Picture := PImgArrDnON;
  if RTXState.Volume > 0 then
    BVOLUP.Image.Picture := PImgArrUpOFF
  else
    BVOLUP.Image.Picture := PImgArrUpON;
  SetMetersColor;
  SaveConfig;
  UpdatePanel;
end;

// "Red"
procedure TTRP.MRedClick(Sender: TObject);

begin
  LEDCOlorOFF := clMaroon;
  LEDColorON := clRed;
  sLEDColor := 'red';
  RXFreq10H.Font.Color := LEDColorON;
  RXFreq100H.Font.Color := LEDColorON;
  RXFreq1k.Font.Color := LEDColorON;
  RXFreq10k.Font.Color := LEDColorON;
  RXFreq100k.Font.Color := LEDColorON;
  RXFreq1M.Font.Color := LEDColorON;
  RXFreq10M.Font.Color := LEDColorON;
  RXFreqDOT.Font.Color := LEDColorON;

  TXFreq100H.Font.Color := LEDColorON;
  TXFreq1k.Font.Color := LEDColorON;
  TXFreq10k.Font.Color := LEDColorON;
  TXFreq100k.Font.Color := LEDColorON;
  TXFreq1M.Font.Color := LEDColorON;
  TXFreq10M.Font.Color := LEDColorON;
  TXFreqDOT.Font.Color := LEDColorON;

  PImgOFF.LoadFromFile(ImgDir+'off-'+sLEDColor+'.bmp');
  PImgON.LoadFromFile(ImgDir+'on-'+sLEDColor+'.bmp');
  PImgArrDnOFF.LoadFromFile(ImgDir+'arrow-down-off-'+sLEDColor+'.bmp');
  PImgArrDnON.LoadFromFile(ImgDir+'arrow-down-on-'+sLEDColor+'.bmp');
  PImgArrUpOFF.LoadFromFile(ImgDir+'arrow-up-off-'+sLEDColor+'.bmp');
  PImgArrUpON.LoadFromFile(ImgDir+'arrow-down-on-'+sLEDColor+'.bmp');

  if RTXState.Volume < 99 then
    BVOLDN.Image.Picture := PImgArrDnOFF
  else
    BVOLDN.Image.Picture := PImgArrDnON;
  if RTXState.Volume > 0 then
    BVOLUP.Image.Picture := PImgArrUpOFF
  else
    BVOLUP.Image.Picture := PImgArrUpON;
  SetMetersColor;
  SaveConfig;
  UpdatePanel;
end;

// "Font magnification"
procedure TTRP.MFONTMAGNClick(Sender: TObject);
begin
  FontMagn := (Sender as TMenuItem).Tag;
  SaveConfig;
  FontMagnify;
end;

// "Magnify all fonts" option
procedure TTRP.MMAGAFClick(Sender: TObject);

begin
  SaveConfig;
  if MMAGAF.Checked then begin
    FontMagnify(TRUE);
  end else begin
    FontMagnify;
  end;
end;

procedure TTRP.MENALLClick(Sender: TObject);

begin
  if MENALL.Checked then EnableAllControls;
  SaveConfig;
  UpdatePanel;
end;

// "Band scan window" menu item
procedure TTRP.MSCANWClick(Sender: TObject);
begin
  FSCAN.Show;
end;

// Start dormant state
procedure TTRP.MSTDORMSTClick(Sender: TObject);
begin
  if not (SendCommand(Start_Dorm_State,'') or Cmd_Aborted) then
    MsgToShow := COMMANDFAILED
  else begin
    WaitTimeSecs(LONGWAITTIME,DORMSTSTA,TRUE);
    BONOFFClick(Self, TRUE);
  end;
end;


//
// "Help" menu handling
//

// "About"
procedure TTRP.MABOUTClick(Sender: TObject);

begin
  FAbout.Show;
end;

// "Manual"
procedure TTRP.MMANClick(Sender: TObject);

begin
  FMAN.Show;
end;

// "GNU License"
procedure TTRP.MGNULICClick(Sender: TObject);

begin
  FGNU.Show;
end;

//
// Buttons handling (left block row 1: main tuning)
//

// TUNE down
procedure TTRP.BTUNEDNClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  if RTXState.Freq_rx-RTXState.Freq_step >= MINRXF then begin
    if SendCommand(Tune_Dn,'') then begin
      RTXState.Freq_rx := RTXState.Freq_rx - RTXState.Freq_step;
      DispRXf(TRUE);
      if MIMMU.Checked then begin
        if RTXState.Freq_rx-RTXState.Freq_step >= MINTXF then begin
          RTXState.Freq_tx := RTXState.Freq_rx;
          if not (SetTXFreq or Cmd_Aborted) then
            MsgToShow := COMMANDFAILED;
          DispTXf(TRUE);
        end else begin
          MsgToShow := TXCANTTUNE;
        end;
      end;
    end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  end;
  UpdateFTXTimeout := time+TXUPTIMEOUT;
  TXFReqModified := FALSE;
  In_Command := FALSE;
end;

// Enable button autorepeat
procedure TTRP.BTUNEDNMouseDown(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
  ButtonToRepeat := BTUNEDN;
  RPT.Interval := 300;
  enough := FALSE;
  RPT.Enabled := TRUE;
  BTUNEDN.BevelOuter := bvLowered;
end;

// Disable button autorepeat
procedure TTRP.BTUNEDNMouseUp(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
  enough := TRUE;
  RPT.Enabled := FALSE;
  ButtonToRepeat := nil;
  BTUNEDN.BevelOuter := bvRaised;
  In_Command := FALSE;
end;

// Step select
procedure TTRP.BRATEClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  if RTXState.Programmable_Step_Set then begin
    if not (SendCommand(TuneStep10,'') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED
    else begin
      RTXstate.Freq_step := 10;
      Step10HzSelected := TRUE;
      RTXstate.Programmable_Step_Set := FALSE;
    end;
  end else begin
    case RTXState.Freq_step of
      10: begin
        if SendCommand(TuneStep100,'') then
          RTXState.Freq_step := 100
        else
          if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
       end;

       100: begin
         if SendCommand(TuneStep1k,'') then
           RTXState.Freq_step := 1000
         else
           if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
       end;
       1000: begin
         if not MPROGSTEP.Checked then begin
          if SendCommand(TuneStep10,'') then begin
            RTXState.Freq_step := 10;
            RTXState.Programmable_Step_Set := FALSE;
            Step10HzSelected := TRUE;
          end else
            if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
         end else begin
           if SendCommand(TuneStepProg,'') then begin
             RTXState.Freq_step := RTXState.Programmable_step;
             RTXState.Programmable_Step_Set := TRUE;
             Step10HZSelected := FALSE;
             RTXState.Freq_rx := 100*(RTXState.Freq_rx div 100);
             SetRXfreq;
           end else
             if not Cmd_Aborted then MsgToShow := COMMANDFAILED
         end;
      end;
    end;
  end;
  UpdatePanel;
  DispRXf(TRUE);
  In_Command := FALSE;
end;

// TUNE up
procedure TTRP.BTUNEUPClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  if RTXState.Freq_rx+RTXState.Freq_step <= MAXRXF then begin
    if SendCommand(Tune_Up,'') then begin
      RTXState.Freq_rx := RTXState.Freq_rx + RTXState.Freq_step;
      DispRXf(TRUE);
      if MIMMU.Checked then begin
        if RTXState.Freq_rx+RTXState.Freq_step <= MAXTXF then begin
          RTXState.Freq_tx := RTXState.Freq_rx;
          if not (SetTXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
          DispTXf(TRUE);
        end else begin
          MsgToShow := TXCANTTUNE;
        end;
      end;
    end else
      if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  end;
  UpdateFTXTimeout := time+TXUPTIMEOUT;
  TXFReqModified := FALSE;
  In_Command := FALSE;
end;

// Enable button autorepeat
procedure TTRP.BTUNEUPMouseDown(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);
begin
  ButtonToRepeat := BTUNEUP;
  RPT.Interval := 300;
  enough := FALSE;
  RPT.Enabled := TRUE;
  BTUNEUP.BevelOuter := bvLowered;
end;

// Disable button repeat
procedure TTRP.BTUNEUPMouseUp(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);
begin
  enough := TRUE;
  RPT.Enabled := FALSE;
  ButtonToRepeat := nil;
  BTUNEUP.BevelOuter := bvRaised;
  In_Command := FALSE;
end;

//
// Buttons handling (block 1 row 2: BFO tuning)
//

// BFO down
procedure TTRP.BBFODNClick(Sender: TObject);

var s: string = '';

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  if SendCommand(BFO_Tune_Dn,'') then begin
    {$IFDEF TEST_USER_INTERFACE}
    s := '+06';
    {$ELSE}
    s := GetReply(3,CMDTIMEOUT);
    {$ENDIF}
    In_Command := TRUE;
    if (s <> '') and (s[1] in ['+','-']) then begin
      MsgToShow := BFO + FormatBFOFreq(s);
      PutRXString(LeftStr(FormatBFOfreq(s),4));
      RunGUI;
      sleep(300);
      DispRXF(TRUE);
      RTXState.BFO_Freq := 100*StrToInt(s);
    end;
  end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  In_Command := FALSE;
end;

// Enable button autorepeat
procedure TTRP.BBFODNMouseDown(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
{$IFDEF BFOREPEAT}
   ButtonToRepeat := BBFODN;
   RPT.Interval := 700;
   enough := FALSE;
   RPT.Enabled := TRUE;
   BBFODN.BevelOuter := bvLowered;
{$ENDIF}
end;

// Disable button repeat
procedure TTRP.BBFODNMouseUp(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
{$IFDEF BFOREPEAT}
  enough := TRUE;
  RPT.Enabled := FALSE;
  ButtonToRepeat := nil;
  BBFODN.BevelOuter := bvRaised;
  In_Command := FALSE;
{$ENDIF}
end;

// BFO up
procedure TTRP.BBFOUPClick(Sender: TObject);

var s: string = '';

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  if SendCommand(BFO_Tune_Up,'') then begin
    {$IFDEF TEST_USER_INTERFACE}
    s := '+06';
    {$ELSE}
    s := GetReply(3,CMDTIMEOUT);
    {$ENDIF}
    In_Command := TRUE;
    if (s <> '') and (s[1] in ['+','-']) then begin
      MsgToShow := BFO + FormatBFOfreq(s);
      PutRXString(LeftStr(FormatBFOfreq(s),4));
      RunGUI;;
      sleep(300);
      DispRXF(TRUE);
      RTXState.BFO_Freq := 100*StrToInt(s);
    end;
  end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  In_Command := FALSE;
end;

// Enable button autorepeat
procedure TTRP.BBFOUPMouseDown(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
{$IFDEF BFOREPEAT}
  ButtonToRepeat := BBFOUP;
  RPT.Interval := 700;
  enough := FALSE;
  RPT.Enabled := TRUE;
  BBFOUP.BevelOuter := bvLowered;
{$ENDIF}
end;

// Disable button autorepeat
procedure TTRP.BBFOUPMouseUp(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
{$IFDEF BFOREPEAT}
  enough := TRUE;
  RPT.Enabled := FALSE;
  ButtonToRepeat := nil;
  BBFOUP.BevelOuter := bvRaised;
  In_Command := FALSE;
{$ENDIF}
end;

//
// Buttons handling (block 1 row 3: IF filter selection)
//

// Wide
procedure TTRP.BWIDEClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if not (RTXState.Mode_RX in [M_CW, M_AM, M_MCW]) then exit;

  In_Command := TRUE;
  if SendCommand(Filt_Wide,'') then begin
    RTXState.Filter := FILTER_WIDE;
    UpdatePanel;
  end;
  In_Command := FALSE;
end;

// Intermediate
procedure TTRP.BINTERClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if not (RTXState.Mode_RX in [M_CW, M_USB, M_LSB, M_R3E, M_MCW]) then exit;

  In_Command := TRUE;
  if SendCommand(Filt_Inter,'') then begin
    RTXState.Filter := FILTER_INTERMEDIATE;
    UpdatePanel;
   end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
   In_Command := FALSE;
end;

// Narrow
procedure TTRP.BNARClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if not (RTXState.Mode_RX in [M_CW, M_MCW]) then exit;

  In_Command := TRUE;
  if SendCommand(Filt_Narrow,'') then begin
    RTXState.Filter := FILTER_NARROW;
    UpdatePanel;
  end else if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  In_Command := FALSE;
end;

// Very narrow
procedure TTRP.BVNARClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if not ((RTXState.Mode_RX = M_CW) or (RTXState.Mode_RX = M_MCW)) then exit;

  In_Command := TRUE;
  if SendCommand(Filt_Vnarrow,'') then begin
    RTXState.Filter := FILTER_VERYNARROW;
    UpdatePanel;
  end else
    if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  In_Command := FALSE;
end;

//
// Buttons handling (block 1 row 4: misc controls)
//

// Speaker
procedure TTRP.BSPKRClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  RTXState.Speaker_Enabled := not RTXState.Speaker_Enabled;
  if RTXState.Speaker_Enabled then begin
    if not (SendCommand(Speaker_On,'') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED;
  end else begin
    if not (SendCommand(Speaker_Off,'') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED;
  end;
  UpdatePanel;
  In_Command := FALSE;
end;

// RF preamp
procedure TTRP.BRFAMPClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  RTXState.RFamp_Enabled := not RTXState.RFAmp_Enabled;
  if RTXState.RFamp_Enabled then begin
    if not (SendCommand(PreAmp_On,'') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED;
  end else begin
    if not (SendCommand(PreAmp_Off,'') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  end;
  UpdatePanel;
  In_Command := FALSE;
end;

// RF attenuator
procedure TTRP.BRFATTClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  RTXState.RFAtt_Enabled := not RTXState.RFAtt_Enabled;
  if RTXState.RFatt_Enabled then begin
    if not (SendCommand(Attenuator_On,'') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED;
  end else begin
    if not (SendCommand(Attenuator_Off,'') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED;
  end;
  UpdatePanel;
  In_Command := FALSE;
end;

// Squelch
procedure TTRP.BSQLClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  RTXState.Squelch_Enabled := not RTXState.Squelch_Enabled;
  if RTXState.Squelch_Enabled then begin
    if not (SendCommand(Squelch_On,'') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED;
  end else begin
    if not (SendCommand(Squelch_Off,'') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED;
  end;
  UpdatePanel;
  In_Command := FALSE;
end;

//
// Buttons handling (block 1 row 5: AGC)
//

// AGC ON
procedure TTRP.BAGCONClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  if SendCommand(AGC_ON,'') then begin
    if RTXState.AGC = AVC_OFF then RTXState.AGC := Last_AGC;
    UpdatePanel;
  end else
    if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  In_Command := FALSE;
end;

// AGC FAST
procedure TTRP.BAGCFASTClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  if SendCommand(AGC_Fast,'') then begin
    RTXstate.AGC := AVC_FAST;
    Last_AGC := AVC_FAST;
    UpdatePanel;
   end else
    if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  In_Command := FALSE;
end;

// AGC SLOW
procedure TTRP.BAGCSLOWClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  if SendCommand(AGC_Slow,'') then begin
    RTXstate.AGC := AVC_SLOW;
    Last_AGC := AVC_SLOW;
    UpdatePanel;
  end else
    if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  In_Command := FALSE;
end;

// AGC OFF
procedure TTRP.BAGCOFFClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  if SendCommand(AGC_Off,'') then begin
    RTXstate.AGC := AVC_OFF;
    UpdatePanel;
  end else
    if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  In_Command := FALSE;
end;

//
// Buttons handling (block 1 row 6: sensitivity)
//

// Sensitivity down
procedure TTRP.BSENSDNClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  if not (SendCommand(Sensitivity_Dn,'') or Cmd_Aborted) then
    MsgToShow := COMMANDFAILED;
  if RTXState.Sensitivity  > 0 then
    RTXState.Sensitivity := RTXState.Sensitivity-1;
  In_Command := FALSE;
end;

// Enable button autorepeat
procedure  TTRP.BSENSDNMouseDown(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
  ButtonToRepeat := BSENSDN;
  RPT.Interval := 300;
  enough := FALSE;
  RPT.Enabled := TRUE;
  BSENSDN.BevelOuter := bvLowered;
end;

// Disable button autorepeat
procedure TTRP.BSENSDNMouseUp(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
  enough := TRUE;
  RPT.Enabled := FALSE;
  ButtonToRepeat := nil;
  BSENSDN.BevelOuter := bvRaised;
end;

// Sensitivity up
procedure TTRP.BSENSUPClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  if not (SendCommand(Sensitivity_Up,'') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  if RTXState.Sensitivity < 99 then
      RTXState.Sensitivity := RTXState.Sensitivity+1;;
  In_Command := FALSE;
end;

// Enable button autorepeat
procedure TTRP.BSENSUPMouseDown(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
  ButtonToRepeat := BSENSUP;
  RPT.Interval := 300;
  enough := FALSE;
  RPT.Enabled := TRUE;
  BSENSUP.BevelOuter := bvLowered;
end;

// Disable button autorepeat
procedure TTRP.BSENSUPMouseUp(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
  enough := TRUE;
  RPT.Enabled := FALSE;
  ButtonToRepeat := nil;
  BSENSUP.BevelOuter := bvRaised;
end;

//
// Buttons handling (block 1 row 7: volume)
//

// Volume down

procedure TTRP.BVOLDNClick(Sender: TObject);

var s: string;

begin
  if not (KeyboardState in [NORMAL,SETBEEPER]) or In_Command then exit;

  case KeyboardState of
    NORMAL: begin
      if RTXstate.Volume < 99 then begin
        inc(RTXState.Volume);
        s := IntToStr(RTXState.Volume);
        if length(s) < 2 then s := '0'+s;
        if not (SendCommand(AF_GainSet, s+stCR) or Cmd_Aborted) then
          MsgToShow := COMMANDFAILED
        else
          MsgToShow := VOLUMEATT+IntToStr(RTXState.Volume);
      end else
        MsgToShow := VOLATTMAX;
      UpdatePanel;
    end;
    SETBEEPER: begin
      if not (SendCommand (AF_GainDec, '') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED;
    end;
  end;
end;

// Enable button autorepeat
procedure TTRP.BVOLDNMouseDown(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
  BVOLDN.Image.Picture := PImgArrDnON;
  ButtonToRepeat := BVOLDN;
  RPT.Interval := 300;
  enough := FALSE;
  RPT.Enabled := TRUE;
  BVOLDN.BevelOuter := bvLowered;
end;

// Disable button autorepeat
procedure TTRP.BVOLDNMouseUp(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
  enough := TRUE;
  RPT.Enabled := FALSE;
  ButtonToRepeat := nil;
  if RTXState.Volume < 98 then BVOLDN.Image.Picture := PImgArrDnOFF;
  if RTXState.Volume >= 0 then BVOLUP.Image.Picture := PImgArrUpOFF;
  BVOLDN.BevelOuter := bvRaised;
end;


// Volume up
procedure TTRP.BVOLUPClick(Sender: TObject);

var s: string;

begin
  if not (KeyboardState in [NORMAL, SETBEEPER]) or In_Command then exit;

  case KeyboardState of
    NORMAL: begin
      if RTXState.Volume > 0 then begin
        dec(RTXState.Volume);
        s := IntToStr(RTXState.Volume);
        if length(s) < 2 then s := '0'+s;
        if not (SendCommand(AF_GainSet, s+stCR) or Cmd_Aborted) then
            MsgToShow := COMMANDFAILED
        else
            MsgToShow := VOLUMEATT+IntToStr(RTXState.Volume);
      end else
        MsgToShow := VOLATTMIN;
      UpdatePanel;
    end;
    SETBEEPER: begin
      if not (SendCommand (AF_GainInc,'') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED
    end;
end;
In_Command := FALSE;
end;

// Enable button autorepeat
procedure TTRP.BVOLUPMouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);

begin
  BVOLUP.Image.Picture := PImgArrUpON;
  ButtonToRepeat := BVOLUP;
  RPT.Interval := 300;
  enough := FALSE;
  RPT.Enabled := TRUE;
  BVOLUP.BevelOuter := bvLowered;
end;

// Disable button autorepeat
procedure TTRP.BVOLUPMouseUp(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
  enough := TRUE;
  RPT.Enabled := FALSE;
  ButtonToRepeat := nil;
  if RTXState.Volume > 1 then BVOLUP.Image.Picture := PImgArrUpOFF;
  if RTXState.Volume <= 99 then BVOLDN.Image.Picture := PImgArrDnOFF;
  BVOLUP.BevelOuter := bvRaised;
end;

//
// Buttons handling (block 2: Mode)
//

// USB
procedure TTRP.BUSBClick(Sender: TObject);

begin
  if ((KeyboardState <> NORMAL) and
     (KeyboardState <> SETRXF) and
     (KeyboardState <> SETTXF) and
     (KeyboardState <> SETRXTXF)) or In_Command then exit;

  if RTXState.PTT_State then MsgToShow := YOUAREINTX;
  case KeyboardState of
    NORMAL: begin
      In_Command := TRUE;
      if SendCommand(Mode_USB,'') then begin
        RTXState.Mode_RX := M_USB;
        RTXState.Mode_TX := M_USB;
        sMode_RX := '';
        sMode_TX := '';
        RTXState.Filter:=FILTER_DEFAULT;
        RTXstate.AGC := AVC_SLOW;
        In_Command := FALSE;
        UpdatePanel;
      end else
        if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
    end;
    SETRXF: begin
      if (RTXState.Mode_TX in [M_USB,M_LSB,M_AM,M_R3E]) then begin
        sMode_RX := Mode_USB;
        RTXState.Mode_RX := M_USB;
        KeyboardState := SETRXM;
        BENTERClick(Sender);
      end else MsgToShow := INVALIDSPLIT;
    end;
    SETTXF: begin
      if (RTXState.Mode_RX in [M_USB,M_LSB,M_AM]) then begin
        sMode_TX := Mode_USB;
        RTXState.Mode_TX := M_USB;
        KeyboardState := SETTXM;
        BENTERClick(Sender);
      end else MsgToShow := INVALIDSPLIT;
    end;
    SETRXTXF: begin
      MsgToShow := NOTALLOWED;
    end;
  end;
  SetActiveButtons;
  In_Command := FALSE;
end;

// LSB
procedure TTRP.BLSBClick(Sender: TObject);

begin
  if ((KeyboardState <> NORMAL) and
      (KeyboardState <> SETRXF) and
      (KeyboardState <> SETTXF) and
      (KeyboardState <> SETRXTXF)) or In_Command then exit;

  if RTXState.PTT_State then MsgToShow := YOUAREINTX;
  case KeyboardState of
      NORMAL: begin
        In_Command := TRUE;
        if SendCommand(Mode_LSB,'') then begin
          RTXState.Mode_RX := M_LSB;
          RTXState.Mode_TX := M_LSB;
          sMode_RX := '';
          sMode_TX := '';
          RTXState.Filter:=FILTER_DEFAULT;
          RTXstate.AGC := AVC_SLOW;
          In_Command := FALSE;
          UpdatePanel;
        end else
          if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
      end;
      SETRXF: begin
        if (RTXState.Mode_TX in [M_USB,M_LSB,M_AM,M_R3E]) then begin
          sMode_RX := Mode_LSB;
          RTXState.Mode_RX := M_LSB;
          KeyboardState := SETRXM;
          BENTERClick(Sender);
        end else MsgToShow := INVALIDSPLIT;
      end;
      SETTXF: begin
        if (RTXState.Mode_RX in [M_USB,M_LSB,M_AM]) then begin
          sMode_TX := Mode_LSB;
          RTXState.Mode_TX := M_LSB;
          KeyboardState := SETTXM;
          BENTERClick(Sender);
        end else MsgToShow := INVALIDSPLIT;
      end;
      SETRXTXF: begin
        MsgToShow := NOTALLOWED;
      end;
  end;
  In_Command := FALSE;
  SetActiveButtons;
end;

// AM
procedure TTRP.BAMClick(Sender: TObject);

begin
  if ((KeyboardState <> NORMAL) and
      (KeyboardState <> SETRXF) and
      (KeyboardState <> SETTXF) and
      (KeyboardState <> SETRXTXF)) or In_Command then exit;

  if RTXState.PTT_State then MsgToShow := YOUAREINTX;
  case KeyboardState of
    NORMAL: begin
      In_Command := TRUE;
      if SendCommand(Mode_AM,'') then begin
        RTXState.Mode_RX := M_AM;
        RTXState.Mode_TX := M_AM;
        sMode_RX := '';
        sMode_TX := '';
        RTXState.Filter:=FILTER_DEFAULT;
        RTXstate.AGC := AVC_FAST;
        In_Command := FALSE;
        UpdatePanel;
      end else
        if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
    end;
    SETRXF: begin
      if (RTXState.Mode_TX in [M_USB,M_LSB,M_AM,M_R3E]) then begin
        sMode_RX := Mode_AM;
        RTXState.Mode_RX := M_AM;
        KeyboardState := SETRXM;
        BENTERClick(Sender);
      end else MsgToShow := INVALIDSPLIT;
    end;
    SETTXF: begin
      if (RTXState.Mode_RX in [M_USB,M_LSB,M_AM]) then begin
        sMode_TX := Mode_AM;
        RTXState.Mode_TX := M_AM;
        KeyboardState := SETTXM;
        BENTERClick(Sender);
      end else MsgToShow := INVALIDSPLIT;
    end;
    SETRXTXF: begin
      MsgToShow := NOTALLOWED;
    end;
  end;
  In_Command := FALSE;
  SetActiveButtons;
end;

// Jump to 500 kHz or to the custom channel #1 if defined
procedure TTRP.B500Click(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if RTXState.PTT_State then MsgToShow := YOUAREINTX;

  if Own500Frequency then begin
    MsgToShow := OWN500FREQ;
    LoadFfile(Sender);
    RestoreState;
    UpdatePanel;
  end else begin
    In_Command := TRUE;
    if SendCommand(F500k,'') then begin
      with RTXState do begin
        Freq_RX := 500000;
        Mode_RX := M_500k;
        Mode_TX := M_500k;
        sMode_RX := '';
        sMode_TX := '';
        Squelch_Enabled := FALSE;
        Speaker_Enabled := TRUE;
        RFAMP_Enabled := FALSE;
        RFATT_Enabled := FALSE;
        AGC := AVC_SLOW;
        Filter := FILTER_INTERMEDIATE;
      end;
      In_Command := FALSE;
      UpdatePanel;
      DispRXf(TRUE);
    end else
      if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  end;
  In_Command := FALSE;
end;

// TELEX
procedure TTRP.BTELEXClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if RTXState.PTT_State then MsgToShow := YOUAREINTX;
  case KeyboardState of
    NORMAL: begin
      In_Command := TRUE;
      if SendCommand(Mode_TELEX,'') then begin
        RTXState.Mode_RX := M_TLX;
        RTXState.Mode_TX := M_TLX;
        sMode_RX := '';
        sMode_TX := '';
        RTXState.Filter:=FILTER_DEFAULT;
        RTXstate.AGC := AVC_FAST;
        In_Command := FALSE;
        UpdatePanel;
      end else
        if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
    end;
    SETRXF,SETTXF: MsgToShow := INVALIDSPLIT;
    SETRXTXF: begin
      MsgToShow := NOTALLOWED;
    end;
  end;
  In_Command := FALSE;
end;

// R3E
procedure TTRP.BR3EClick(Sender: TObject);

begin
  if ((KeyboardState <> NORMAL) and
      (KeyboardState <> SETRXF) and
      (KeyboardState <> SETTXF) and
      (KeyboardState <> SETRXTXF)) or In_Command then exit;

  if RTXState.PTT_State then MsgToShow := YOUAREINTX;
  case KeyboardState of
    NORMAL: begin
      In_Command := TRUE;
      if SendCommand(Mode_R3E,'') then begin
        RTXState.Mode_RX := M_R3E;
        RTXState.Mode_TX := M_R3E;
        sMode_RX := '';
        sMode_TX := '';
        RTXState.Filter:=FILTER_DEFAULT;
        RTXstate.AGC := AVC_SLOW;
        In_Command := FALSE;
        UpdatePanel;
      end else
        if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
      end;
      SETRXF: begin
        MsgToShow := INVALIDSPLIT;
      end;
      SETTXF: begin
        if (RTXState.Mode_RX in [M_LSB,M_AM]) then begin
          sMode_TX := Mode_R3E;
          RTXState.Mode_TX := M_R3E;
          KeyboardState := SETTXM;
          BENTERClick(Sender);
        end else MsgToShow := INVALIDSPLIT;
      end;
      SETRXTXF: begin
        MsgToShow := NOTALLOWED;
      end;
  end;
  In_Command := FALSE;
  SetActiveButtons;
end;

// Jump to 2182 kHz or to the custom channel #2 if defined
procedure TTRP.B2182Click(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;
  if RTXState.PTT_State then MsgToShow := YOUAREINTX;

  if Own2182Frequency then begin
    MsgToShow := OWN2182FREQ;
    LoadFFile(Sender);
    RestoreState;
    UpdatePanel;
  end else begin
    In_Command := TRUE;
    if SendCommand(F2182k,'') then begin
      with RTXState do begin
        Freq_RX := 2182000;
        Freq_tx := 2182000;
        Mode_RX := M_2182k;
        Mode_TX := M_2182k;
        sMode_RX := '';
        sMode_TX := '';
        TX_Enabled := TRUE;
        Squelch_Enabled := FALSE;
        Speaker_Enabled := TRUE;
        RFAMP_Enabled := FALSE;
        RFATT_Enabled := FALSE;
        AGC := AVC_FAST;
        Filter := FILTER_DEFAULT;
        TX_Power := FULL_POWER;
      end;
      In_Command := FALSE;
      UpdatePanel;
      DispRXf(TRUE);
      DispTXf(TRUE);
    end else
      if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  end;
  In_Command := FALSE;
end;

// CW
procedure TTRP.BCWClick(Sender: TObject);

begin
    if (KeyboardState <> NORMAL) or In_Command then exit;
    if RTXState.PTT_State then MsgToShow := YOUAREINTX;
    case KeyboardState of
        NORMAL: begin
          In_Command := TRUE;
          if SendCommand(Mode_CW,'') then begin
            RTXState.Mode_RX := M_CW;
            RTXState.Mode_TX := M_CW;
            sMode_RX := '';
            sMode_TX := '';
            RTXState.Filter:=FILTER_INTERMEDIATE;
            RTXstate.AGC := AVC_FAST;
            BWIDE.Enabled := TRUE;
            BINTER.Enabled := TRUE;
            BNAR.Enabled := TRUE;
            BVNAR.Enabled := TRUE;
            SetActiveButtons;
            In_Command := FALSE;
            UpdatePanel;
          end else
              if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
        end;
        SETRXF,SETTXF: MsgToShow := INVALIDSPLIT;
    end;
    In_Command := FALSE;
end;

// MCW
procedure TTRP.BMCWClick(Sender: TObject);

begin
    if (KeyboardState <> NORMAL) or In_Command then exit;
    In_Command := TRUE;
    if RTXState.PTT_State then MsgToShow := YOUAREINTX;
    case KeyboardState of
        NORMAL: begin
          if SendCommand(Mode_MCW,'') then begin
            RTXState.Mode_RX := M_MCW;
            RTXState.Mode_TX := M_MCW;
            sMode_RX := '';
            sMode_TX := '';
            RTXState.Filter:=FILTER_INTERMEDIATE;
            RTXstate.AGC := AVC_SLOW;
            SetActiveButtons;
            UpdatePanel;
          end else
              if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
        end;
        SETRXF,SETTXF: MsgToShow := INVALIDSPLIT;
    end;
    In_Command := FALSE;
end;

//
// Buttons handling (block 3: numeric keypad & frequency entry)
//

// RX
procedure TTRP.BRXClick(Sender: TObject);

begin
  case KeyboardState of
    NORMAL,CHANNEL,CHANNELRX,CHANNELTX: begin
      KeyboardState := SETRXF;
      OldRXFreq := GetRXString;
      PutRXString(PROMPT);
      RXFBuf := '';
      sMode_RX := '';
    end;
    SETTXF: begin
      KeyboardState := SETTXRXF;
      OldRXFreq := GetTXString;
      PutRXString(PROMPT);
      RXFBuf := '';
      sMode_RX := '';
    end;
    SETRXTXF: begin
      PutRXString(PROMPT);
      RXFBuf := '';
      PutTXString(PROMPT);
      TXFBuf := '';
    end;
    SETTIME:begin
      OldTXFreq := GetTXString;
      PutTXString(PROMPT);
      TXFBuf := '';
      sMode_TX := '';
      MsgToShow := ENTERWAKEUP;
      KeyboardState := SETWAKEUP;
    end;
    SETRXF: begin
      PutRXString(PROMPT);
      RXFBuf := '';
      sMode_RX := '';
    end;
    SETWAKEUP: begin
      PutTXString('');
      TXFBuf := '';
      sMode_TX := '';
    end;
    RCLCH: begin
      KeyboardState := RCLRXCH;
      DispTXf(TRUE);
    end;
    SCANSETUP1: begin
      ScanParams.OpenControlPort := TRUE;
      MsgToShow := OPENCONTROLPORT;
    end;
  end;
  SetActiveButtons;
end;

// Recall channel
procedure TTRP.BRCLClick(Sender: TObject);

begin
    case KeyboardState of
      NORMAL,RCLCH,CHANNEL,CHANNELRX,CHANNELTX: begin
        OldRXFreq := GetRXString;
        if KeyboardState = NORMAL then OLDRTXState := RTXState;
        RXFreq10H.Caption := '';
        PutRXString(PROMPT);
        PutTXString('');
        RXFBuf := '';
        KeyboardState := RCLCH;
        MsgToShow := ENTERCHANNEL;
        SetActiveButtons;
      end;
      SETTIME: begin
        // Works partially, no reply from the CU, the wakeup time is only shown
        // in the TX display
        if not (SendCommand(Read_Wakeup_Time,'') or Cmd_Aborted) then
          MsgToShow := COMMANDFAILED
        else begin
          WaitTimeSecs(SHORTWAITTIME, ONLYINTHECU, TRUE);
          if not (SendCommand(stCR,'') or Cmd_Aborted) then
            MsgToShow := COMMANDFAILED;
        end;
        KeyBoardState := NORMAL;
        DispTXF(TRUE);
      end;
      SETRXF: begin
        KeyboardState := RCLRXCH;
        SetActiveButtons;
      end;
      SETTXF: begin
        KeyboardState := RCLTXCH;
        SetActiveButtons;
      end;
  end;
  SetActiveButtons;
end;

// TX
procedure TTRP.BTXClick(Sender: TObject);

begin
  if not RTXstate.TX_Enabled then exit;

  case KeyboardState of
    NORMAL,CHANNEL,CHANNELRX,CHANNELTX: begin
      OldTXFreq := GetTXString;
      PutTXString(PROMPT);
      TXFBuf := '';
      sMode_TX := '';
      KeyboardState := SETTXF;
    end;
    SETRXF: begin
      OldTXFreq := GetTXString;
      PutTXString(PROMPT);
      TXFBuf := '';
      sMode_TX := '';
      KeyboardState := SETRXTXF;
    end;
    SETTXF: begin
      PutTXString(PROMPT);
      TXFBuf := '';
      sMode_TX := '';
    end;
    SETRXTXF: begin
      PutRXString(PROMPT);
      RXFBuf := '';
      PutTXString(PROMPT);
      TXFBuf := '';
    end;
    RCLCH: begin
      KeyboardState := RCLPROMTXCH;
      DispRXf(TRUE);
      SetActiveButtons;
      // if not SendCommand(Recall,TX_Prefix) then MsgToShow := COMMANDFAILED);
      // read reply, if any, and display the recalled frequency or display
      // something in the tx display
    end;
    SCANSETUP1: begin
      ScanParams.OpenControlPort := FALSE;
      MsgToShow := CLOSECONTROLPORT;
    end;
    RCLPROMTXCH: begin
      if not SendCommand(TX_prefix, '') then MsgToShow := COMMANDFAILED;
      // read reply if any, and display the recalled frequency or display
      // something in the tx display
    end;
  end;
  SetActiveButtons;
end;

// Number key button
procedure TTRP.BNUMClick(Sender: TObject);

begin
  KeyboardHandler(chr((Sender as TJbutton).tag));
end;

// ENTER
procedure TTRP.BENTERClick(Sender: TObject);

var f: longint;
    cmd,param,regname,sv: string;
    chn,hhmm,regval,len,i,value: integer;
    ok: boolean;

begin
  cmd := '';
  case KeyboardState of
  SETRXF: begin
    if RXFBuf <> '' then begin
      f := trunc(StrToFloat(AdjustDecimalSeparator(RXFBuf)) * 100);
      if (f >= MINRXF) and (f < MAXRXF) then begin
        RTXstate.Freq_rx := f;
        if not (SetRXFreq or Cmd_Aborted) then
          MsgToShow := COMMANDFAILED;
      end else MsgToShow := RXCANTTUNE;
      if MIMMU.Checked and
         (f >= MINTXF) and
         (f <= MAXTXF) then begin
        RTXState.Freq_tx := f;
        SetTXFreq;
        DispTXf(TRUE);
      end;
    end;
    RXFBuf := '';
    DispRXf(TRUE);
  end;
  SETRXM: begin
    if not (SetRXMode or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
    DispRXf(TRUE);
  end;
  SETTXF: begin
    if TXFBuf <> '' then begin
      f := trunc(StrToFloat(AdjustDecimalSeparator(TXFBuf)) * 100);
      if (f >= MINTXF) and (f <= MAXTXF) then begin
        RTXState.Freq_tx := f;
        if not (SetTXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
        if MIMMU.Checked then begin
          RTXState.Freq_RX := f;
          SetRXFreq;
          DispRXf(TRUE);
        end;
      end else MsgToShow := TXCANTTUNE;
    end;
    TXFBuf := '';
    DispTXf(TRUE);
  end;
  SETTXM: begin
    if not (SetTXMode or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
    DispTXf(TRUE);
  end;
  SETRXTXF, SETTXRXF: begin
    if RXFBuf <> '' then begin
      f := trunc(StrToFloat(AdjustDecimalSeparator(RXFBuf)) * 100);
      if (f >= MINRXF) and (f <= MAXRXF) then begin
          if (f >= MINTXF) and (f <= MAXTXF) then begin
            RTXState.Freq_RX := f;
            RTXState.Freq_tx := f;
            if not (SetRXTXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
          end else MsgToShow := TXCANTTUNE;
        end else MsgToShow := RXCANTTUNE;
     end else begin
       if KeyboardState = SETRXTXF then
         RTXState.Freq_TX := RTXState.Freq_rx
       else
         RTXState.Freq_RX := RTXState.Freq_TX
     end;
      if not (SetRXTXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
      KeyboardState := NORMAL;
      TXFBuf := '';
      DispRXf(TRUE);
      DispTXf(TRUE);
  end;
  STOCH: begin
    if (RXFBuf <> '') then begin
      if not (SendCommand(Store, RXFBuf+stCR) or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED;
    end;
    RXFBuf := '';
    DispRXf(TRUE);
    RestoreFilterAndAGC;
  end;
  RCLCH,RCLRXCH,RCLTXCH: begin
    if KeyboardState = RCLTXCH then
      ok := TryStrToInt(TXFBuf,chn)
    else
      ok := TryStrToInt(RXFBuf,chn);
    if ok and ((chn in [0..75]) or (chn >= 800)) then begin
       if KeyboardState = RCLRXCH then
          param := RX_Prefix+RXFBuf+stCR
        else if keyboardState = RCLTXCH then
          param := TX_Prefix+TXFBuf+stCR
        else
          param := RXFBuf+stCR;
        if SendCommand(Recall, param) then begin
          if MCU920.Checked then begin
            OldRTXState := RTXState;
            LoadStateFromCU;
            if (RTXState.Freq_RX=0) and (RTXState.Freq_TX=0) then begin
              MsgToShow := Format(CHANNOTDEF,[chn]);
              RTXState := OldRTXState;
              RestoreState;
            end;
        end else begin
          if KeyboardState in [RCLCH,RCLRXCH] then
            RTXState.Freq_RX := -chn;
          if KeyboardState in [RCLCH,RCLTXCH] then
            RTXState.Freq_TX := -chn;
          case KeyboardState of
            RCLCH: KeyboardState := CHANNEL;
            RCLRXCH: keyboardState := CHANNELRX;
            RCLTXCH: keyboardState := CHANNELTX;
          end;
          SetAllLEDsOff;
          SetActiveButtons;
          DispRXf(TRUE);
          DispTXf(TRUE);
          MsgToShow := CHANNELMODE;
        end;
      end else
        if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
      RXFBuf := '';
    end else begin
      if KeyboardState = RCLTXCH then begin
        PutTXString(PROMPT);
        TXFBuf := '';
      end else begin
        PutRXString(PROMPT);
        RXFBuf := '';
      end;
      if ok then begin
        MsgToShow := INVALIDCHANNEL;
        exit;
      end;
    end;
    DispRXf(TRUE);
    DispTXf(TRUE);
  end;
  SETTIME: begin
    if TXFBuf <> '' then begin
      if length(TXFBuf) = 4 then begin
        hhmm := StrToInt(TXFBuf);
        if (hhmm div 100 < 24) and (hhmm mod 100 < 60) then begin
          if not (SendCommand(Set_Time, TXFBuf+stCR) or Cmd_Aborted) then
            MsgToShow := COMMANDFAILED;
        end;
      end else MsgToShow := INVALIDTIME;
      DispTXf(TRUE);
    end else begin
      if not (SendCommand(Start_Dorm_State,'') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED
      else begin
        MsgToShow := DORMSTSTA;
        WaitTimeSecs(LONGWAITTIME,PSEWAIT,TRUE);
        BONOFFClick(Self, TRUE);
        KeyboardState := POWEROFF;
      end;
    end;
    if not RTXState.TX_Enabled then Clock.Enabled := TRUE;
  end;
  SETBFO: begin
    if RXFBuf <> '' then begin
      i := pos('.', RXFbuf);
      if i > 0 then Delete(RXfBuf,i,1);
      if length(RXfBuf) = 2 then
        RXFBuf := RXFBuf[1]+'0'+RXFBuf[2];
      if length(RXFBuf) = 3 then begin
        if not (SendCommand(Set_BFO, RXFbuf+stCR) or Cmd_Aborted) then
          MsgToShow := COMMANDFAILED
        else
          RTXState.BFO_freq := 100*trunc(StrToFloat(RXFBuf));
      end;
    end;
    DispRXF(TRUE);
    BMINUS.Hide;
    BENTER.Width := EnterNormalWidth;
  end;
  SETWAKEUP: begin
    if TXFBuf <> '' then begin
      if length(TXFBuf) = 4 then begin
        if not (SendCommand(Set_Wakeup_Time,TXFBuf+stCR) or
                Cmd_Aborted) then MsgToShow := COMMANDFAILED;
        MsgToShow := WAKEUPSET+TXFBuf;
        DispTXf(TRUE);
      end else MsgToShow := INVALIDTIME;
    end;
  end;
  SETOR,SETPR,SETGR: begin
    if RXFBuf <> '' then begin
      len := length(RXFBuf);
      regval := 0;
      for i := len downto 1 do
        if RXFBuf[i] = '1' then regval := regval + (1 shl (len - i));
        case KeyboardState of
          SETOR: begin
              cmd := Set_Option_Reg;
              regname := 'Option';
          end;
          SETPR: begin
            cmd := Set_Preset_Reg;
            regname := 'Preset';
          end;
          SETGR: begin
            cmd := Set_Guard_Reg;
            regname:= 'Guard';
          end;
          otherwise regname:='?';
        end;
        if not (Sendcommand(cmd,IntToStr(regval)+stCR) or
                Cmd_Aborted) then MsgToShow := COMMANDFAILED
        else begin
          sv := GetRXString;
          while Length(sv) < 8 do sv := '0' + sv;
          MsgToShow := Format(REGISTERSET,[RegName,sv,regval]);
        end;
      end;
      KeyBoardState := NORMAL;
      DispRXf(TRUE);
      EnableAllControls;
      SetActiveButtons;
    end;
    SELFTSTM,SELFTSTFMM1: begin
        if SendCommand(stCR,'') then
          AbortTest := TRUE
        else
          if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
    end;
    SELFTSTFMA: begin
      KeyboardState := SELFTSTFMA1;
      SetActiveButtons;
      if SendCommand(Exec_AST_Num, RXFBuf+stCR) then begin
        if GetAutoSelfTestResponse then BONOFFClick(Sender, FALSE);
      end else MsgToShow := INVALIDTESTNUM;
      DispRXf(TRUE);
      KeyboardState := NORMAL;
    end;
    SELFTSTFMM: begin
      if SendCommand(Exec_MST_Num, RXFBuf+stCR) then begin
          KeyBoardState := SELFTSTFMM1;
          SetActiveButtons;
          if GetManSelfTestResponse then begin
            BONOFFClick(self, TRUE);
            WaitTimeSecs(RESETWAITTIME, PSEWAIT, TRUE);
            BONOFFClick(Sender, FALSE);
          end else begin
            if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
            DispRXf(TRUE);
            KeyboardState := NORMAL;
          end;
        end;
      end;
      FILLSCNBUF: begin
        if (GetRXString = PROMPT) and (m = 1) then begin
          if not (SendCommand(Fill_Scan_Buf, '') or Cmd_Aborted) then
            MsgToShow := COMMANDFAILED;
          for i := 1 to n-1 do begin
            RXFBuf := ScanBuf[i,1];
            while length(RXFbuf) < 6 do RXFBuf := '0'+RXFBuf;
            TXFBuf := ScanBuf[i,2];
            while length(TXFbuf) < 6 do TXFBuf := '0'+TXFBuf;
            if not (SendCommand(RX_Prefix+RXFBuf,TX_Prefix+TXFBuf+stCR) or
                    Cmd_Aborted) then MsgToShow := COMMANDFAILED;
          end;
          if not (SendCommand(stCR,'') or Cmd_Aborted) then
            MsgToShow := COMMANDFAILED;
          DispRXf(TRUE);
          DispTXf(TRUE);
      end else begin
        if m = 1 then begin
          ScanBuf[n,m] := RXFBuf;
          m := 2;
          TXFBuf := '';
          PutTXString(PROMPT);
          exit;
        end else begin
          ScanBuf[n,m] := TXFBuf;
          PutTXString('');
          RXFBuf := '';
          TXFBuf := '';
          PutRXString(PROMPT);
          m := 1;
          n := n + 1;
          exit;
        end;
      end;
    end;
    SCANSETUP1: begin
      if RXFBuf = '' then begin
        Keyboardstate := NORMAL;
        EnableAllControls;
        SetActiveButtons;
        DispRXf(TRUE);
        DispTXf(TRUE);
      end else begin
        ScanParams.StartChan := StrToInt(RXFbuf);
        if ScanParams.StartChan in [0..74] then begin
          PutRXString(PROMPT);
          MsgToShow := Format(ENTERSCANSTOP,[ScanParams.StartChan+1]);
          RXFBuf := '';
          KeyboardState := SCANSETUP2;
        end else begin
          PutRXString(PROMPT);
          RXFBuf := '';
          EnableAllControls;
          SetActiveButtons;
          MsgToShow := INVALIDCHANNEL;
        end;
      end;
    end;
    SCANSETUP2: begin
      if RXFBuf = '' then begin
        Keyboardstate := NORMAL;
        EnableAllControls;
        SetActiveButtons;
        DispRXf(TRUE);
        DispTXf(TRUE);
      end else begin
        ScanParams.StopChan := StrToInt(RXFbuf);
        if (ScanParams.StopChan in [0..75]) and
           (ScanParams.StopChan > ScanParams.StartChan) then begin
          with ScanParams do
            MsgToShow := Format(SCANPARAMS1,[StartChan, StopChan]);
          KeyboardState := NORMAL;
          EnableAllControls;
          SetActiveButtons;
          DispRXF(TRUE);
          DispTXF(TRUE);
        end else begin
          KeyboardState := NORMAL;
          EnableAllControls;
          SetActiveButtons;
          MsgToShow := STOPGREATERSTART;
          DispRXf(TRUE);
          DispTXf(TRUE);
        end;
      end;
    end;
    SCANDWELL: begin
      if RXFBuf = '' then begin
        Keyboardstate := NORMAL;
        EnableAllControls;
        SetActiveButtons;
        DispRXf(TRUE);
        DispTXf(TRUE);
      end else begin
        ScanParams.DwellTime := StrToInt(RXFBUF);
        MsgToShow := Format(DWELLTIMESET,[Float(ScanParams.DwellTime)/10]);
        EnableAllControls;
        SetActiveButtons;
        DispRXF(TRUE);
        DispTXf(TRUE);
      end;
    end;
      SCANCH: begin
        if not (SendCommand(stCR, '') or Cmd_Aborted) then
          MsgToShow := COMMANDFAILED
        else begin
          CMDRPT.Enabled := FALSE;
          CMDToRepeat := '';
          RTXState.uscan := FALSE;
          if MCU920.Checked then begin
            LoadStateFromCU;
            UpdatePanel;
            DispRXf(TRUE);
            DispTXf(TRUE);
          end else begin
            PutRXString('STOPPED');
            PutTXString('STOPPED');
            MsgToShow := Format(PREVIOUSSTATE,['SCAN']);
          end;
          KeyboardState := NORMAL;
          EnableAllControls;
          SetActiveButtons;
        end;
      end;
      READSETSTEP: begin
        if RXFBuf = '' then
          value := RTXState.Programmable_step div 100
        else
          value := StrToInt(RXFBuf);
        if (value >= MINTUNESTEP) and (value <= MAXTUNESTEP) then begin
          if not (SendCommand(Read_Set_Step, IntToStr(value)+stCR) or
            Cmd_Aborted) then MsgToShow := COMMANDFAILED
          else
            RTXstate.Programmable_step := 100 * value;
        end else begin
          MsgToShow := INVALIDSTEP;
          exit;
        end;
        DispRXf(TRUE);
        RXFBuf := '';
        KeyboardState := NORMAL;
        RestoreState;
        EnableAllControls;
        SetActiveButtons;
      end;
      SETBEEPER: begin
        if not (SendCommand(stCR,'') or Cmd_Aborted) then
          MsgToShow := COMMANDFAILED;
        KeyboardState := NORMAL;
        EnableAllControls;
        SetActiveButtons;
        Enable_Status := MSTATUS.Checked;
        TStatus.Enabled := MSTATUS.Checked;
        RestoreState;
        UpdatePanel;
      end;
    READONHOURS: begin
      CMDRPT.Enabled := FALSE;
      KeyboardState := NORMAL;
      RestoreState;
      EnableAllControls;
      SetActiveButtons;
      DispRXf(TRUE);
      DispTXf(TRUE);
      end;
      NORMAL: begin
        if not (SendCommand(stCR, '') or Cmd_Aborted) then
          MsgToShow := COMMANDFAILED;
        KeyboardState := NORMAL;
        EnableAllControls;
        SetActiveButtons;
        DispRXf(TRUE);
        DispTXf(TRUE);
      end;
  end;
  if not (KeyboardState in [CHANNEL,SCANTUBUF,SCANCH,POWEROFF,SCANSETUP2,
                            CHANNELRX,CHANNELTX]) then
    KeyboardState := NORMAL;
  UpdatePanel;
  SetActiveButtons;
end;

//
// Buttons handling (block 4: TUNE & power level)
//

// TUNE
procedure TTRP.BTUNEClick(Sender: TObject);

var s: string;

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;
  Enable_Status := FALSE;
  if SendCommand(TX_TUNE,'') then begin
    s := GetReply(1,TUNETIMEOUT);
    if s = '>' then begin
      MsgToShow := TUNCOMPLETED;
    end else
      if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  end;
  UpdatePanel;
  Enable_Status := TRUE;
end;

// Low power
procedure TTRP.BLOWClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;
  SetLowPower;
  UpdatePanel;
end;

// Medium power
procedure TTRP.BMEDClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if SendCommand(TX_MediumPwr,'') then begin
    RTXState.TX_power := MEDIUM_POWER;
    UpdatePanel;
  end else
    if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
end;

// Full power
procedure TTRP.BFULLClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if SendCommand(TX_HighPwr,'') then begin
    RTXState.TX_power := FULL_POWER;
    UpdatePanel;
  end else
    if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
end;

//
// Buttons handling (block 5 row 1: misc functions 2)
//

// Store channel
procedure TTRP.BSTOClick(Sender: TObject);

begin
  case KeyboardState of
    NORMAL: begin
      PutRXString(PROMPT);
      RXFBuf := '';
      KeyboardState := STOCH;
      MsgToShow := ENTERCHANNEL;
    end;
    STOCH: begin
      PutRXString(PROMPT);
      RXFbuf := '';
    end;
  end;
  RXFreq10H.Caption := '';
  SetActiveButtons;
end;

// Scan
procedure TTRP.BSCANClick(Sender: TObject);

var sPort: string;

begin
  {$IFNDEF SCANSYNTAX}
  MsgToShow := SCANNOTIMPLEM);
  {$ELSE}
  if not (KeyboardState in [NORMAL,SCANSETUP1, SCANCH]) then exit;

  case KeyboardState of
    NORMAL: begin
    PutRxString(PROMPT);
    PutTXstring('');
    RXFBuf := '';
    MsgToShow := ENTERSCANSTART;
    with ScanParams do
    MsgToShow := Format(CURRENTSCANPARAMS,[StartChan,
                                     StopChan,
                                     DwellTime div 10,
                                     DwellTime mod 10,
                                     BoolToStr(OpenControlPort, TRUE)]);
    KeyBoardState := SCANSETUP1;
    end;
    SCANSETUP1: begin
      with ScanParams do begin
          if not SendCommand(Scan,
             IntToStr(StartChan)+#13+
             IntToStr(StopChan)+#13) then
            MsgToShow := COMMANDFAILED;
          if not SendCommand(Scan+Set_Time,IntToStr(DwellTime)+#13) then
            MsgToShow := COMMANDFAILED;
      end;
      if Scanparams.OpenControlPort then
       sPort := RX_Cmd
      else
       sPort := TX_Cmd;
      if not (SendCommand(Scan+sPort+Scan,'') or Cmd_Aborted) then
       MsgToShow := COMMANDFAILED
      else begin
        KeyboardState := SCANCH;
        SetActiveButtons;
        PutRXString('SCAN');
        PutTXString('SCAN');
        ButtonToFlashRX := BSCAN;
        FLASH.Enabled := TRUE;

        // The SCAN command has 7s timeout
        // To override that the SCAN command must be repeated
        CmdToRepeat := Scan;
        In_Command := TRUE;
        CMDRPT.Enabled := TRUE;
      end;
    end;
  end;
  {$ENDIF}
  SetActiveButtons;
end;

// Set clock
procedure TTRP.BSETTClick(Sender: TObject);

var t: TDateTime;

begin
  case KeyboardState of
    NORMAL: begin
      Clock.Enabled := FALSE;
      KeyboardState := SETTIME;
      PutTXString(PROMPT);
      TXFBuf := '';
      MsgToShow := ENTERTIME;
    end;
    SETTIME: begin
      PutTXString('');
      if MUSEUTC.Checked then
          t := LocalTimeToUniversal(now)
      else
          t := now;
      TXFBuf := FormatDateTime('hhnn', t);
      PutTXString(TXFBuf);
    end;
    SETWAKEUP: begin
      PutTXString('');
      TXFBuf := '';
    end;
    SCANSETUP1: begin
      KeyboardState := SCANDWELL;
      MsgToShow := ENTERDWELL;
    end;
  end;
  SetActiveButtons;
end;

// "+-" key (shown only when setting BFO)
procedure TTRP.BMINUSClick(Sender: TObject);

begin
  if KeyboardState = SETBFO then begin
    if length(RXFBuf) = 1 then begin
      if RXfBuf = '-' then RXFBuf := '+' else RXFBuf := '-';
    end;
    if length(RXfBuf) = 0 then RXFBuf := '-';
    PutRXstring(RXFBuf);
  end;
end;

// Duplex
procedure TTRP.BDUPClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if SendCommand(Duplex, '') then begin
    RTXState.Duplex := not RTXState.Duplex;
    UpdatePanel;
    // we must do something else?
  end else
    if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
end;

//
// Buttons handling (block 5 row 2: Dimmer)
//

// Dimmer down
procedure TTRP.BDIMDNClick(Sender: TObject);

begin
  if In_Command then exit;

  case KeyboardState of
    NORMAL: begin
      if RTXState.Dimmer_Level > 0 then begin
       Dec(RTXState.Dimmer_Level);
       if SendCommand(Dimmer_Set,IntToStr(RTXState.Dimmer_Level) + stCR) then
          MsgToShow := DIMLEVEL+IntToStr(RTXState.Dimmer_Level)
       else
        if not Cmd_Aborted then MsgToShow := COMMANDFAILED
      end else begin
        MsgToShow := DIMLEVMIN;
      end;
    end;
    SELFTSTM,SELFTSTFMM1: begin
      if SendCommand(Dimmer_Dn,'') then
        NextTest := TRUE
      else
        if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
    end;
  end;
end;

// Enable button autorepeat
procedure TTRP.BDIMDNMouseDown(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
  ButtonToRepeat := BDIMDN;
  RPT.Interval := 300;
  enough := FALSE;
  RPT.Enabled := TRUE;
  BDIMDN.BevelOuter := bvLowered;
end;

// Disable button autorepeat
procedure TTRP.BDIMDNMouseUp(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Integer);
begin
  enough := TRUE;
  RPT.Enabled := FALSE;
  ButtonToRepeat := nil;
  BDIMDN.BevelOuter := bvRaised;
end;

// Dimmer up
procedure TTRP.BDIMUPClick(Sender: TObject);

begin
  while In_Command do RunGUI;

  case KeyboardState of
    NORMAL: begin
      if RTXState.Dimmer_Level < 5 then begin
        Inc(RTXState.Dimmer_Level);
        if SendCommand(Dimmer_Set,IntToStr(RTXState.Dimmer_Level) + stCR) then
          MsgToShow := DIMLEVEL+IntToStr(RTXState.Dimmer_Level)
        else
          if not Cmd_Aborted then MsgToShow := COMMANDFAILED
      end else begin
        MsgToShow := DIMLEVELMAX;
      end;
    end;
    SELFTSTM,SELFTSTFMM1: begin
      if SendCommand(Dimmer_Up,'') then
        NextTest := TRUE
      else
        if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
    end;
    SETBEEPER: begin
      if not (SendCommand (Dimmer_Up,'') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED;
    end;
  end;
end;

// Enable button autorepeat
procedure TTRP.BDIMUPMouseDown(Sender: TObject; Button: TMouseButton;
        Shift: TShiftState; X, Y: Integer);

begin
  ButtonToRepeat := BDIMUP;
  RPT.Interval := 300;
  enough := FALSE;
  RPT.Enabled := TRUE;
  BDIMUP.BevelOuter := bvLowered;
end;

// Disable button autorepeat
procedure TTRP.BDIMUPMouseUp(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Integer);

begin
  enough := TRUE;
  RPT.Enabled := FALSE;
  ButtonToRepeat := nil;
  BDIMUP.BevelOuter := bvRaised;
end;

//
// Buttons handling (block 5 row 3: TX & power on/off )
//

// TX enabled/disabled
procedure TTRP.BTXONOFFClick(Sender: TObject);

begin
  if RTXState.PTT_State or
   In_Command or
   not(KeyboardState in [NORMAL,ANTENNAOFF]) then exit;

  In_Command := TRUE;
  RTXState.TX_Enabled := not RTXState.TX_Enabled;
  if RTXState.TX_Enabled then begin
    if not (SendCommand(TX_On,'') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
    SetTXfreq;
    DispTXF(TRUE);
    if KeyboardState = ANTENNAOFF then RestoreState;
    KeyboardState := NORMAL;
    EnableAllControls;
    SetActiveButtons;
  end else begin
    if not (SendCommand(TX_Off,'') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  end;
  UpdatePanel;
  In_Command := FALSE;
end;

// Power on/off (NB: Starts & stops program, does not switch on&off radio!)
procedure TTRP.BONOFFClick(Sender: TObject; CloseComm: boolean);

begin
  if In_Command then exit;

  if SerPort > 0 then begin
    BONOFF.LCaption.Caption := BaseBONOFFCaption+'(OFF)';
    BONOFF.Color := $007e7eff;
    if MStatus.Checked then begin
      TSTatus.Enabled := FALSE;
      Enable_Status := FALSE;
    end;
    If CloseComm then begin
      CloseRemote;
      MSG.Lines.Clear;
    end;
    SerDrain(SerPort);
    SerClose(SerPort);
    SerPort := 0;
    SaveConfig;
    SaveLastState;
    sMode_RX := '';
    PutRXString('');
    PutTXString('');
    RXFreq10H.Caption := '';
    sMode_TX := '';
    SM.Enabled := FALSE;
    TSTATUS.Enabled := FALSE;
    SetSmeter(0);
    SetPmeter(0);
    if TRPServer.Connected then TRPServer.Disconnect(TRUE);
    DisableAllControls;
    BTXONOFF.Color := clDefault;
    Step10HzSelected := FALSE;
    MSG.Enabled := FALSE;
    MCU920.Enabled := FALSE;
    FLSP_RCMD.Enabled := FALSE;
    BONOFF.Enabled := TRUE;
    MSG.Enabled := FALSE;
    KeyboardState := POWEROFF;
    In_Command := FALSE;
  end else begin
    MSG.Enabled := TRUE;
    MsgToShow := INITPROGRAM;
    RunGUI;
    MSG.VertScrollBar.Position := 10000;
    MCU920.Enabled := TRUE;
    BONOFF.LCaption.Caption := BaseBONOFFCaption+' (ON)';
    In_Command := FALSE;
    Enough := TRUE;
    SetMetersColor;
    {$IFDEF TEST_USER_INTERFACE}
    SerPort := 1;
    {$ELSE}
    SerPort := SerOpen(SerPortName);
    {$ENDIF}
    if SerPort > 0 then begin
      BONOFF.Color := clLime;
      if M300B.Checked then begin
        SerSetParams(SerPort,300,7,OddParity,1,[]);
      end;
      if M2400B.Checked then begin
        SerSetParams(SerPort,2400,7,OddParity,1,[]);
      end;
      SerSetDTR(SerPort,True);
      SerSetRTS(SerPort,True);

      // Get rid of any extra character laying around
      SerFlushInput(SerPort);
      SerFlushOutput(SerPort);

      // Try to connect to the Skanti CU
      if OpenRemote then begin
        KeyboardState := NORMAL;
        EnableAllControls;

        // Works, but command used to read firmware version modify
        // the state, so it is useless.
        // Have_V92_Firmware := Check_V92_Firmware;

        // The programmable step value is read from the last state file
        // since it is not possible to read it from the CU, so load it
        // anyway.
        LoadLastState;

        // Check for custom 2182 and 500 buttons
        CheckOwnF;

        if not MCU920.Checked then begin
          // CU V92 not enabled, so restore the state read above
          RestoreState;
          MREREADCONF.Enabled := FALSE;
        end else begin
          // CU V92 enabled, read state from CU itself
          LoadStateFromCU;
          DispRXf(TRUE);
          DispTXf(TRUE);
          // Get rid of any 10Hz digit rewriting the RX frequency.
          // In the CU returned status the 10 Hz figure is not available
          SetRXFreq;
          UpdatePanel;
          MREREADCONF.Enabled := TRUE;
        end;
          ReducedPower := FALSE;
          HighSWR := FALSE;
          FLSP_RCMD.Enabled := TRUE;
          UpdatePanel;

          // Start net rigctl server
          if not TRPServer.Listen(TRPServer.Port,TRPServer.Host) then
            MsgToShow := 'TRPserver socket: listen() failed';
            MsgToShow := READY;
        end else begin
          SerDrain(SerPort);
          SerClose(SerPort);
          SerPort := 0;
          if MessageDlg(ERROR, FAILEDTOINIT, mtError,
            [mbYes, mbNo],0) = mrNo then
            CloseProgram
          else
            BONOFFClick(Sender, TRUE);
        end;
      end else begin
        ShowMessage(CANTOPENSERIAL+SerPortName);
      end;
  end;
end;

//
// Extra program functions (not present in CU8000)
//

// Alternative tuning step selection way (click on indicators)

// 1 kHz step
procedure TTRP.STEP1KMouseUp(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Integer);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if Button = mbLeft then begin
    if SendCommand(TuneStep1k, '') then begin
      RTXState.Freq_step := 1000;
      RTXState.Freq_RX := (RTXState.Freq_RX div 1000)*1000;
      Step10HzSelected := FALSE;
      RTXstate.Programmable_Step_Set := FALSE;
      DispRXf(TRUE);
      SetRXFreq;
      UpdatePanel;
      end else
        if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  end;
end;

// 100 Hz step
procedure TTRP.STEP100HMouseUp(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Integer);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if Button = mbLeft then begin
    if SendCommand(TuneStep100, '') then begin
      RTXState.Freq_step := 100;
      RTXState.Freq_RX := (RTXState.Freq_RX div 100)*100;
      Step10HzSelected := FALSE;
      RTXState.Programmable_Step_Set := FALSE;
      SetRXFreq;
      DispRXf(TRUE);
      UpdatePanel;
    end else
      if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  end;
end;

// 10 Hz step
procedure TTRP.STEP10HMouseUp(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Integer);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if Button = mbLeft then begin
    if SendCommand(TuneStep10, '') then begin
      RTXState.Freq_step := 10;
      DispRXf(TRUE);
      Step10HzSelected := TRUE;
      RTXstate.Programmable_Step_Set := FALSE;
      UpdatePanel;
    end else
      if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  end;
end;

// Alternative frequency setting way: left/right click on the frequency figures
// Clicking on the decimal point rewrites displayed frequency
procedure TTRP.RXfreqMouseUp(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Integer);

var step: integer;

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  In_Command := TRUE;
  TXFreqModified := FALSE;

  step := (Sender as TStaticText).Tag;

  if Button = mbLeft then begin
    // Click on decimal point
    if step = -1 then step := 0;
      if step = -10 then begin
        // click on 10 Hz figure
        RTXState.Freq_rx := RTXState.Freq_rx - 10;
          if not (SendCommand(Tune_Dn,'') or Cmd_Aborted) then
            MsgToShow := COMMANDFAILED;
          DispRXf(TRUE);
      end else
        if RTXState.Freq_rx - step >= MINRXF then
           RTXState.Freq_rx := RTXState.Freq_rx-step;
  end;
  if Button = mbRight then begin
    // Click on decimal point
    if step = -1 then step := 0;
    if step = -10 then begin
      // click on 10 Hz figure
      RTXState.Freq_rx := RTXState.Freq_rx + 10;
      if not (SendCommand(Tune_Dn,'') or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED;
      DispRXf(TRUE);
    end else
      if RTXState.Freq_rx + step <= MAXRXF then
        RTXState.Freq_rx := RTXState.Freq_rx+step;
  end;
  if step >= 0 then if not (SetRXFreq or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
  DispRXf(TRUE);

  if RTXState.TX_Enabled and MIMMU.checked then begin
      RTXState.Freq_tx := 100*((RTXState.Freq_rx+50) div 100);
      if not (SetTXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
      DispTXf(TRUE);
  end;
  In_Command := FALSE;
  UpdateFTXTimeout := time+TXUPTIMEOUT;
end;

// Same for TX
procedure TTRP.TXfreqMouseUp(Sender: TObject; Button: TMouseButton;
          Shift: TShiftState; X, Y: Integer);

var step: integer;

begin
  if (KeyboardState <> NORMAL) or In_COmmand then exit;

  In_Command := TRUE;
  TXFreqModified := TRUE;
  if RTXState.TX_Enabled then begin
    step := (Sender as TStaticText).tag;
    // Click on TX decimal point
    if step = -1 then step := 0;
    if Button = mbLeft then begin
      if RTXState.Freq_tx - step >= MINTXF then
        RTXState.Freq_tx := RTXState.Freq_tx-step;
    end;
    if Button = mbRight then begin
      if RTXState.Freq_tx + step <= MAXTXF then
        RTXState.Freq_tx := RTXState.Freq_tx+step;
    end;
    if not (SetTXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
    DispTXf(TRUE);

    if MIMMU.Checked then begin
      RTXState.Freq_rx := RTXState.Freq_tx;
      if not (SetRXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
      DispRXf(TRUE);
    end;
  end;
  In_Command := FALSE;
  UpdateFTXTimeout := time+TXUPTIMEOUT;
end;

// Switch receive/transmit. In transmit sends " command every 2s
// (" has 3s internal timeout)
// This command works in all modes except CW and MCW, but TX audio must come
// from the TELEX input and CU microphone and AUX inputs are ignored since
// priority is never released.
procedure TTRP.BTRANSClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) and (KeyboardState <> CHANNEL) then exit;

  RTXState.PTT_state := not RTXState.PTT_State;

  if RTXState.PTT_State then begin
    if not (SendCommand(TX_Cmd,'') or Cmd_Aborted) then begin
      MsgToShow := COMMANDFAILED;
      RTXState.PTT_state := not RTXState.PTT_State;
    end;
    TX.Enabled := TRUE;
  end else begin
    if not (SendCommand(RX_Cmd,'') or Cmd_Aborted) then begin
      MsgToShow := COMMANDFAILED;
      RTXState.PTT_state := not RTXState.PTT_State;
    end;
    TX.Enabled := FALSE;
  end;
  UpdatePanel;
end;

// Copy RX frequency to TX frequency
procedure TTRP.BTXEQRXClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  if (RTXState.Freq_rx >= MINTXF) and (RTXState.Freq_rx <= MAXTXF) then begin
    RTXState.Freq_tx := 100*((RTXState.Freq_rx + 50) div 100);
    SetTXFreq;
    DispTXf(TRUE);
  end else MsgToShow := TXCANTTUNE;
end;

// Copy TX frequency to RX frequency
procedure TTRP.BRXEQTXClick(Sender: TObject);

begin
  if (KeyboardState <> NORMAL) or In_Command then exit;

  // no need to check for bounds since TX coverage < RX coverage
  RTXState.Freq_rx := RTXState.Freq_tx;
  SetRXFreq;
  DispRXf(TRUE);
  DispTXf(TRUE);
end;

// Allows to use mouse wheel for tuning

// Mouse wheel over RX frequency display
procedure TTRP.RXFreqMouseWheel(Sender: TObject; Shift: TShiftState;
    WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);

var step: integer = -1;

begin
  if In_Command or (KeyboardState <> NORMAL) then exit;
  In_Command := TRUE;
  {$IFDEF DEBUG_RXMOUSEWHEEL}
      MsgToShow := 'X='+IntToStr(Mousepos.X)+' y='+IntToStr(MousePos.Y);
  {$ENDIF}
  step := (Sender as TstaticText).tag;
  TXFreqModified := FALSE;
  if WheelDelta > 0 then begin
    // Wheel on decimal point
    if step = -1 then step := 0;
      if step >= 0 then begin
        if RTXState.Freq_rx + step <= MAXRXF then begin
          RTXState.Freq_rx := RTXState.Freq_rx + step;
          if not (SetRXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
          DispRXf(TRUE);
        end else MsgToShow := RXCANTTUNE;
     end;
     if step = -10 then begin
       // Wheel on 10 HZ figure
       if RTXState.Freq_Step = 10 then begin
         if RTXState.Freq_rx + RTXState.Freq_step <= MAXRXF then begin
           RTXState.Freq_rx := RTXState.Freq_rx + RTXState.Freq_step;
           if not (SendCommand(Tune_Up,'') or Cmd_Aborted) then
             MsgToShow := COMMANDFAILED;
           DispRXf(TRUE);
         end;
       end;
     end;
  end;
  if WheelDelta < 0 then begin
    // Wheel on decimal point
    if step = -1 then step := 0;
    if step >= 0 then begin
      if RTXState.Freq_rx - step >= MINRXF then begin
        RTXState.Freq_rx := RTXState.Freq_rx-step;
        if not (SetRXFreq or Cmd_Aborted) then
          MsgToShow := COMMANDFAILED;
        DispRXf(TRUE);
      end else MsgToShow := RXCANTTUNE;
    end;
    if step = -10 then begin
      // Wheel on 10 HZ figure
      if RTXState.Freq_Step = 10 then begin
        if RTXState.Freq_rx - RTXState.Freq_step >= MINRXF then begin
          if not (SendCommand(Tune_Dn,'') or Cmd_Aborted) then
            MsgToShow := COMMANDFAILED;
          RTXState.Freq_rx := RTXState.Freq_rx - RTXState.Freq_step;
          DispRXf(TRUE);
        end;
      end;
    end;
  end;
  if MIMMU.Checked then begin
    if RTXState.freq_rx >= MINTXF then begin
      RTXState.Freq_tx := 100*((RTXState.Freq_rx+50) div 100);
      if not (SetTXFreq or Cmd_Aborted) then
        MsgToShow := COMMANDFAILED;
      DispTXf(TRUE);
    end else MsgToShow := TXCANTTUNE;
  end;
  Handled := TRUE;
  In_Command := FALSE;
  UpdateFTXTimeout := time+TXUPTIMEOUT;
end;

// Mouse wheel over TX frequency display
procedure TTRP.TXFreqMouseWheel(Sender: TObject; Shift: TShiftState;
    WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);

var step: integer = -1;

begin
  if In_Command or (KeyboardState <> NORMAL) then exit;
  In_Command := TRUE;
  {$IFDEF DEBUG_TXMOUSEWHEEL}
      MsgToShow := 'X='+IntToStr(Mousepos.X)+' y='+IntToStr(MousePos.Y);
  {$ENDIF}

  step := (Sender as TStaticText).Tag;

  TXFreqModified := TRUE;
  if WheelDelta > 0 then begin
    if step >= 0 then begin
      if RTXState.Freq_tx + step <= MAXTXF then begin
        RTXState.Freq_tx := RTXState.Freq_tx + step;
        if not (SetTXFreq or Cmd_Aborted) then
          MsgToShow := COMMANDFAILED;
        DispTXf(TRUE);
      end else MsgToShow := RXCANTTUNE;
    end;
  end;
  if WheelDelta < 0 then begin
    if step >= 0 then begin
       if RTXState.Freq_tx - step >= MINTXF then begin
         RTXState.Freq_tx := RTXState.Freq_tx - step;
         if not (SetTXFreq or Cmd_Aborted) then
           MsgToShow := COMMANDFAILED;
        DispTXf(TRUE);
       end else MsgToShow := TXCANTTUNE;
    end;
  end;
  if MIMMU.Checked then begin
    RTXState.Freq_rx := RTXState.Freq_tx;
    if not (SetRXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
    DispRXf(TRUE);
  end;
  Handled := TRUE;
  In_Command := FALSE;
  UpdateFTXTimeout := time+TXUPTIMEOUT;
end;

// Mouse wheel anywhere else
procedure TTRP.FormMouseWheel(Sender: TObject; Shift: TShiftState;
          WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);

begin
  if In_Command or (KeyboardState <> NORMAL) then exit;
  In_Command := TRUE;
{$IFDEF DEBUG_FORMMOUSEWHEEL}
  MsgToShow := 'X='+IntToStr(Mousepos.X)+' y='+IntToStr(MousePos.Y);
{$ENDIF}
  TXFreqModified := FALSE;
  if WheelDelta > 0 then begin
    if SendCommand(Tune_Up, '') then begin
      if RTXState.Freq_rx+RTXState.Freq_step <= MAXRXF then begin
        RTXState.Freq_rx := RTXState.Freq_rx + RTXState.Freq_step;
        DispRXf(TRUE);
      end else MsgToShow := RXCANTTUNE;
    end else
      if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  end;
  if WheelDelta < 0 then begin
    if SendCommand(Tune_Dn, '') then begin
      if RTXState.Freq_rx-RTXState.Freq_step >= MINRXF then begin
        RTXState.Freq_rx := RTXState.Freq_rx - RTXState.Freq_step;
        DispRXf(TRUE);
      end else MsgToShow := RXCANTTUNE;
    end else
      if not Cmd_Aborted then MsgToShow := COMMANDFAILED;
  end;
  if MIMMU.Checked then begin
    if RTXState.freq_rx >= MINTXF then begin
      RTXState.Freq_tx := 100*((RTXState.Freq_rx+50) div 100);
      if not (SetTXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
      DispTXf(TRUE);
    end else MsgToShow := TXCANTTUNE;
  end;
  Handled := TRUE;
  In_Command := FALSE;
  UpdateFTXTimeout := time+TXUPTIMEOUT;
end;

//
// Timers
//

// Flash speaker timer (only a program-running indicator)
// and execute some remote commands.
procedure TTRP.FLSP_RCMDTimer(Sender: TObject);

begin
  // Flash speaker "LED"
  if (RTXSTate.Speaker_Enabled) and Flash_Speaker then begin
    SPKRLed := not SPKRLed;
    if SPKRLed then
      BSPKR.Image.Picture := PImgON
    else
      BSPKR.Image.Picture := PImgOFF;
  end;
  // If remote connection(s) present, light NET "LED" and execute commands
  if NumTCPConnections = 0 then
    TSHConnected.Brush.Color := LEDColorOff
  else
    TSHConnected.Brush.Color := LEDColorOn;

  // Execute required remote commands

  // Remote has changed frequency, update it
  if RemFreqChange then begin
    DispRXf(FALSE);
    DispTXf(FALSE);
    RemFreqChange := FALSE;
  end;

  // Tune at frequency change required, do it
  if TuneRequired then begin
    TuneRequired := FALSE;
    BTuneClick(Sender);
    SendCommand(stEOT,''); // Release priority
  end;

  // Main window update needed, do it
  if PanelUpdateRequired then begin
    PanelUpdateRequired := FALSE;
    UpdatePanel;
  end;

{$IFDEF DEBUG_KBSTATE}
   if KeyboardState <> OldKeyboardState then begin
     OldKeyboardState := KeyboardState;
     MsgToShow := 'KeyboardState now '+KBStatenames[int64(KeyboardState)];
   end;
{$ENDIF}
end;

// Flash annunciator if split mode or scanning
procedure TTRP.FLASHTimer(Sender: TObject);

begin
  if (RTXState.Mode_RX = RTXState.Mode_TX) and
     (KeyboardState <> SCANCH) then exit;

  Mode_Flash := not Mode_Flash;
  if RTXState.PTT_State then begin
    if Mode_Flash then
      ButtonToFlashTX.Image.Picture := PImgON
    else
      ButtonToFlashTX.Image.Picture := PImgOFF;
  end else begin
    if Mode_Flash then
      ButtonToFlashRX.Image.Picture := PImgON
    else
      ButtonToFlashRX.Image.Picture := PImgOFF;
  end;
  RunGUI;
end;

// Timer to update S-meter, status LEDS and TX frequency every 250 ms
// Must be less than Status timer to be sure to display all measures.
procedure TTRP.SMTimer(Sender: TObject);

var i: integer;

begin
  SetSmeter(s);
  SetPmeter(t);
  SMLabel.Caption := IntTostr(s)+OF20+LEDSToSunits[s]+')';
  PMLabel.Caption := inttostr(t)+OF20+IntToStr(5*t)+'%)';

  if length(status)>0 then begin
    {$IFDEF DEBUG_SMTIMER}
    MsgToShow := 'Status='+Status;
    {$ENDIF}
    for i := 1 to length(status) do begin
      case Status[i] of
        'u': begin
            MsgToShow := TUNINGSUCCESS;
        end;
        'v': begin
            MsgToShow := TUNINGERROR;
        end;
        'w': begin
            MsgToShow := SWRLESS4;
            HighSWR := FALSE;
            ReducedPower := FALSE;
        end;
        'x': begin
               MsgToShow := SWRGREAT4;
            HighSWR := TRUE;
           end;
        'y': begin
              ReducedPower := FALSE;
            MsgToShow := POWNORMAL;
          end;
        'z': begin
            ReducedPower := TRUE;
            MsgToShow := POWREDUCED;
        end;
        '>': begin
            MsgToShow := GOTGREATER;
        end;
      end;
    end;
    status := '';
    UpdatePanel;
  end;
end;

// Timer to update TX frequency in transceiver mode
procedure TTRP.TXFUpTimerTimer(Sender: TObject);

begin
  // if in deferred transceiver mode, update TX frequency if timeout expired
  if MDEFU.Checked and
   RTXState.TX_Enabled and
   (time > UpdateFTXTimeout) and
   not In_Command and
   ((((RTXState.Freq_rx + 50) div 100) <> (RTXState.Freq_tx div 100)) and
      (RTXState.Freq_rx < MAXTXF)) then begin
    In_Command := TRUE;
    if TXFreqModified then begin
      RTXState.Freq_rx := RTXState.Freq_tx;
      if not (SetRXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
      DispRXf(TRUE);
      TXFreqModified := FALSE;
    end else begin
      if (RTXState.freq_rx >= MINTXF) and (RTXState.freq_rx <= MAXTXF) then begin
        RTXState.Freq_tx := 100*((RTXState.Freq_rx+50) div 100);
        if RTXState.Freq_tx > MAXTXF then RTXState.Freq_tx := MAXTXF;
        if RTXState.Freq_tx < MINTXF then RTXState.Freq_tx := MINTXF;
        if not (SetTXFreq or Cmd_Aborted) then MsgToShow := COMMANDFAILED;
        DispTXf(TRUE);
      end else MsgToShow := TXCANTTUNE;
    end;
    In_Command := FALSE;
  end;
end;

// Timer for command autorepeat (to get around the 7s timeout)
procedure TTRP.CMDRPTTimer(Sender: TObject);
begin
  In_Command := TRUE;
  if In_CMDRPT then exit;
  In_CMDRPT := TRUE;
  if not (SendCommand(CmdToRepeat, '') or Cmd_Aborted) then
    MsgToShow := COMMANDFAILED;
  In_CMDRPT := FALSE;
end;

// TX timer. If PTT active sends " command every 2s
procedure TTRP.TXTimer(Sender: TObject);

begin
  if RTXState.PTT_State then
    if not (SendCommand(TX_cmd,'') or Cmd_Aborted) then
      MsgToShow := COMMANDFAILED;
end;

// Clock timer. If TX off, shows time in the TX frequency display
procedure TTRP.ClockTimer(Sender: TObject);

var t: TDateTime;

begin
  if (not RTXState.TX_Enabled) AND (KeyboardState <> POWEROFF) then begin
    if MUSEUTC.Checked then
      t := LocalTimeToUniversal(now)
    else
      t := now;
    PutTXString(FormatDateTime('hh nn', t));
    if Time_Flash then TXFreq1K.Caption := '⊔';
    Time_Flash := not Time_Flash;
  end;
end;

// Button repeat timer: repeat button in ButtonToRepeat
procedure TTRP.RPTTimer(Sender: TObject);

begin
  if In_Repeat then exit;
  In_Repeat := TRUE;
  if not enough and (ButtonToRepeat <> nil) then
    ButtonToRepeat.OnClick(Sender);
  In_Repeat := FALSE;
end;

// Status timer. Get status (S & RF meters, etc.) every 500 ms.
procedure TTRP.TStatusTimer(Sender: TObject);

VAR buf: char = chr(0);
    sc: char = '`';
    tc: char = '`';
    nc: integer;

begin
  {$IFDEF TEST_USER_INTERFACE}
  s := 20-random(10);
  t := 20-random(10);
  status := 'uvwxyz';
  exit;
  {$ENDIF}
  if In_Command then exit;

  In_Command := TRUE;

  if Enable_Status then begin
    status := '';
    SerWrite(SerPort, Get_Status[1], 1);

    timeout := time + CMDTIMEOUT;
    repeat
      nc := SerRead(SerPort, buf, 1);
      {$IFDEF LINUX}
          sleep(1);
      {$ENDIF}
      {$IFDEF WINDOWS}
          sleep(0);
      {$ENDIF}
    until (nc > 0) or (time > timeout);

    if time > timeout then begin
      Enable_Status := FALSE;
      MStatus.Checked := FALSE;
      MsgToShow := NOREPLY;
      In_Command := FALSE;
      exit;
    end;

    repeat
      timeout := time + CMDTIMEOUT;
      repeat
        nc := SerRead(SerPort, buf, 1);
        {$IFDEF LINUX}
            sleep(1);
        {$ENDIF}
        {$IFDEF WINDOWS}
            sleep(0);
        {$ENDIF}
       until (nc > 0) or (time > timeout);

       SerWrite(SerPort, stACK[1], 1);

       if (buf in StatusRange) then status := status+buf;
    until (buf in MetersRange) or (time > timeout);

     if time > timeout then begin
       Enable_Status := FALSE;
       MStatus.Checked := FALSE;
       MsgToShow := NOREPLY;
       In_Command := FALSE;
       exit;
     end;

     if (buf in MetersRange) then
       sc := buf
     else
       status:=status+buf;

     {$IFDEF DEBUG_STATUSTIMER}
     MsgToShow := 'Got S='+sc;
     {$ENDIF}

     timeout := time + CMDTIMEOUT;
     repeat
       nc := SerRead(SerPort, buf, 1);
       {$IFDEF LINUX}
       sleep(1);
       {$ENDIF}
       {$IFDEF WINDOWS}
       sleep(0);
       {$ENDIF}
     until (nc > 0) or (time > timeout);

     if time > timeout then begin
       Enable_Status := FALSE;
       MStatus.Checked := FALSE;
       MsgToShow := NOREPLY;
       In_Command := FALSE;
       exit;
     end;

     if (buf in MetersRange) then
       tc := buf
     else
       status:=status+buf;

     // Disable status reading
     SerWrite(SerPort, stCAN[1], 1);

     timeout := time + CMDTIMEOUT;
     repeat
       nc := SerRead(SerPort, buf, 1);
       {$IFDEF LINUX}
       sleep(1);
       {$ENDIF}
       {$IFDEF WINDOWS}
       sleep(0);
       {$ENDIF}
     until (nc > 0) or (time > timeout);

     if time > timeout then begin
       Enable_Status := FALSE;
       MStatus.Checked := FALSE;
       MsgToShow := NOREPLY;
       In_Command := FALSE;
       exit;
     end;

     {$IFDEF DEBUG_STATUSTIMER}
     MsgToShow := 'status: '+status+' S:'+sc+' T:'+tc;
     {$ENDIF}

     s := ord(sc) - ord('`');
     t := ord(tc) - ord('`');

     {$IFDEF DEBUG_STATUSTIMER}
     if (s<5) then
       Status := 'uwz'
     else
       if (s<10) then Status := 'vxy' else Status:='';
     {$ENDIF}
  end;
  In_Command := FALSE;
end;

// hamlib net rigctl-more-or-less-compatible server networking code (lNET)
// Remote commands handling code is in UParse,pas, UCapabilities.pas and UState.pas
// The server code is so far incomplete, it's a little more than the bare minimum
// needed to work with latest versions of WSJTX, FLDIGI, GRIG, XDX and XLOG.
// See also the comments in UParse.pas

// Accept a connection
procedure TTRP.TRPServerAccept(aSocket: TLSocket);

begin
  NumTCPConnections := NumTCPConnections+1;
  // Check if Transceiver and/or Status reading are enabled
  // If yes, issue a warning
  if MIMMU.Checked or MDEFU.Checked or MStatus.Checked then
    MsgToShow := TRANSSTATUSENA;
end;

// Handle disconnect received from client
procedure TTRP.TRPServerDisconnect(aSocket: TLSocket);
begin
  if NumTCPConnections > 0 then NumTCPConnections := NumTCPConnections - 1;
end;

// Error handling
procedure TTRP.TRPServerError(const amsg: string; aSocket: TLSocket);
begin
  MsgToShow := Format(ERRORNET,[amsg]);
  UpdatePanel;
end;

// Receive data from client
procedure TTRP.TRPServerReceive(aSocket: TLSocket);

var len: integer;

begin
  len := aSocket.GetMessage(RXLine);
  {$ifdef DEBUG_NET}
  MsgToShow := 'Received '+IntToStr(len)+' bytes';
  {$endif}
  if len > 0 then begin
    {$ifdef DEBUG_NET}
    MsgToShow := 'Net RX:'+ShowControlChars(RXLine);
    {$endif}
    ParseNetMsg(RXLine,aSocket);
  end;
end;


// Things to do at startup
initialization

begin
  // Use internal Lazarus versioning
  if GetProgramVersion(ProgVersion) then begin
    Major := ProgVersion[1];
    Minor := ProgVersion[2];
    Revision := ProgVersion[3];
    Build := ProgVersion[4];
    // Make version strings
    ShortVersion := 'v'+IntTostr(Major)+'.'+IntTostr(Minor)+IntToStr(revision)+
                 ' build '+IntToStr(Build);
    LongVersion := ShortVersion+LineEnding+COPYRIGHTST;
  end else begin
    ShowMessage(GETPROGVFAIL);
    ShortVersion := NOVERSINFO;
    LongVersion := ShortVersion+LineEnding+COPYRIGHTST;
  end;
  {$IFDEF LINUX}
  HomeDir := GetUserDir+'.SkantiControl/';
  StateDir := HomeDir+'States/';
  ChannelsDir := HomeDir+'Channels/';
  ImgDir := '/usr/share/SkantiControl/Img/';
  DocDir := '/usr/share/doc/skanticontrol/';
  BandScansDir := HomeDir+'BandScans';
  {$ENDIF}
  {$IFDEF WINDOWS}
  // Use the executable path to discover the location of the
  // SkantiControl directory.
  HomeDir := ExtractFilePath(Application.ExeName);

  StateDir := HomeDir+'States\';
  ChannelsDir := HomeDir+'Channels\';
  ImgDir := HomeDir+'Img\';
  DocDir := HomeDir;
  Application.Initialize;
  BandScansDir := HomeDir+'BandScans\';
  {$ENDIF}

  // Check if directories, image, manual and license files exists

  // Working and configuration directories, if not exist create them
  if not DirectoryExists(HomeDir) then CreateDir(HomeDir);
  if not DirectoryExists(StateDir) then CreateDir(StateDir);
  if not DirectoryExists(ChannelsDir) then CreateDir(ChannelsDir);
  if not DirectoryExists(BandScansDir) then CreateDir(BandScansDir);

  ErrMsg := '';

  // Check if image and doc directories exists...
  if not DirectoryExists(ImgDir) then
    ErrMsg := Dir+ImgDir+NOTF+LineEnding+CHECKINST;
  if not DirectoryExists(DocDir) then
    // ...if not, the installation is severely broken.
    ErrMsg := DIR+DocDir+NOTF+LineEnding+CHECKINST;

  // Manual and License
  if not (FileExists(DocDir+'Manual-en.txt') and
          FileExists(DocDir+'gpl-3.0.txt')) then
    // Ditto
    ErrMsg := DOCFILESNOTF+LineEnding+CHECKINST;

  // Image files
  ImgFilesOK := FileExists(ImgDir+'off-red.bmp') and
                FileExists(ImgDir+'on-red.bmp') and
                FileExists(ImgDir+'off-green.bmp') and
                FileExists(ImgDir+'on-green.bmp') and
                FileExists(ImgDir+'off-yellow.bmp') and
                FileExists(ImgDir+'arrow-down-off-red.bmp') and
                FileExists(ImgDir+'arrow-down-on-red.bmp') and
                FileExists(ImgDir+'arrow-down-off-green.bmp') and
                FileExists(ImgDir+'arrow-down-on-green.bmp') and
                FileExists(ImgDir+'arrow-down-off-yellow.bmp') and
                FileExists(ImgDir+'arrow-down-on-yellow.bmp') and
                FileExists(ImgDir+'arrow-up-off-red.bmp') and
                FileExists(ImgDir+'arrow-up-on-red.bmp') and
                FileExists(ImgDir+'arrow-up-off-green.bmp') and
                FileExists(ImgDir+'arrow-up-on-green.bmp') and
                FileExists(ImgDir+'arrow-up-off-yellow.bmp') and
                FileExists(ImgDir+'arrow-up-on-yellow.bmp');
  if not ImgFilesOK then
    // Ditto
    ErrMsg := COLFILESNOTF+LineEnding+CHECKINST;

  // If installation is severely broken, notify user and close program now.
  if ErrMsg <> '' then begin
    MessageDlg(ERROR, ErrMsg, mtError,[mbOK],0);
    halt;
  end;
end;

end.

