unit uAntenna;

interface

{$REGION ' H E A D E R '}

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, ComCtrls, StdCtrls, AxCtrls, OleCtrls, DBOleCtl, MMSystem,
  Registry, Menus, Buttons, Math,
  AbNumEdit, AbLED, AbGradient, AbSwitch, _GClass,
  VCL.LPControl, SLControlCollection, LPControlDrawLayers, LPTransparentControl,

  SLComponentCollection, ILLinearGauge, VrControls, VrRotarySwitch, VrBlinkLed,
  ILLed, TypInfo,
  SDL_StringL, nrclasses, nrcomm,
  uAbout,  uSETICommon, uTime, uMount, uSkyMap,
  ILBasicInstrument, ILSegmentIndicator,

  ULBasicControl, ILAnalogInstrument, ILAngularGauge, Mitov.Types,
  Mitov.VCLTypes, JvExControls, JvArrayButton, nrcommbox, JvSpin, NMCLibI04;
{$ENDREGION}
//
{$REGION ' T Y P E S  '}

const

  CsbAzStatusID = 0;
  CsbAzStatus = CsbAzStatusID + 1;
  CsbElStatusID = CsbAzStatus + 1;
  CsbElStatus = CsbElStatusID + 1;
  CsbNoteID = CsbElStatus + 1;
  CsbNote = CsbNoteID + 1;

  Yellow = 1;
  Blue = 0;
  CMoveError = 0.2;

  // NumAntModules = 3; // The current number of Kerr modules on the antenna
  // Three module addreses  In---[Az=3]---[El=2]---[I/O=1]
  // Two   module addreses  In---[Az=2]---[El=1]
  AddrEl = 2;
  AddrAz = 3;
  AddrIO = 1;
  FilterRelay = 2; // Channel on the Head End electronics Kerr module
  Laser = 0; // Channel for the Laser

  AntennaKey = '\SOFTWARE\SETI Net\Antenna';
  // Version must match the build version

  FilterIn = True;
  FilterOut = not FilterIn;

type
  TfrmAntenna = class(TForm)
    tmrStatus: TTimer;
    menuMain: TMainMenu;
    mnuHelp: TMenuItem;
    mnuAbout: TMenuItem;
    tmrHoming: TTimer;
    sbAntenna: TStatusBar;
    gElevation: TILLinearGauge;
    ndlElYellow: TILExternalLinearGaugeControlHandElement;
    ndlElGreen: TILExternalLinearGaugeControlHandElement;
    gAzMotor: TILAngularGauge;
    ndlAzMotorRed: TILExternalGaugeControlHandElement;
    gElMotor: TILAngularGauge;
    ndlElMotorRed: TILExternalGaugeControlHandElement;
    btnTest: TButton;
    mnuMount: TMenuItem;
    mnuSkyMap: TMenuItem;
    nrComm1: TnrComm;
    gbPosition: TGroupBox;
    lblElMotor: TLabel;
    neElMotorPosition: TAbNumEdit;
    btnQuickStart: TButton;
    lblAzMotor: TLabel;
    neAzMotorPosition: TAbNumEdit;
    gAzimuth: TILAngularGauge;
    ndlAzGreen: TILExternalGaugeControlHandElement;
    ndlAzYellow: TILExternalGaugeControlHandElement;
    ledTest: TAbLED;
    pnlAntennaControl: TPanel;
    gbAntennaControl: TGroupBox;
    ledHome: TVrBlinkLed;
    gbStatus: TGroupBox;
    ledStatusTick: TAbLED;
    ledReady: TAbLED;
    swOnOff: TVrRotarySwitch;
    btnSerialConfig: TButton;
    nrDeviceBox1: TnrDeviceBox;
    CheckBox1: TCheckBox;
    gbLNAControl: TGroupBox;
    swFilter: TAbRockerSwitch;
    lblFilter: TLabel;
    imgBPF: TImage;
    ledHoming: TAbLED;
    neAz: TAbNumEdit;
    sbAz: TJvSpinButton;
    neEl: TAbNumEdit;
    nsEl: TJvSpinButton;
    lblBandpass: TLabel;
    lblInOut: TLabel;
    lvProperties: TListView;
    btnSearchAgain: TButton;
    btnSimpleMsgBox: TButton;
    procedure GoHome;
    procedure FormCreate(Sender: TObject);

    procedure tmrStatusTimer(Sender: TObject);
    procedure swFilterClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);

    procedure mnuAboutClick(Sender: TObject);
    procedure btnSetHomeClick(Sender: TObject);
    procedure tmrHomingTimer(Sender: TObject);

    procedure btnQuickStartClick(Sender: TObject);
    procedure btnTestClick(Sender: TObject);
    procedure ndlAzYellowChange(Sender: TObject; AValue, AMin, AMax: Real);
    procedure ndlElYellowChange(Sender: TObject; AValue, AMin, AMax: Real);
    procedure ledHomeClick(Sender: TObject);
    procedure ndlElGreenChange(Sender: TObject; AValue, AMin, AMax: Real);
    procedure mnuMountClick(Sender: TObject);
    procedure mnuSkyMapClick(Sender: TObject);
    procedure cmboPortClick(Sender: TObject);
    procedure btnSerialConfigClick(Sender: TObject);
    procedure btnSearchAgainClick(Sender: TObject);

    procedure nrDeviceBox1Change(Sender: TObject);

    procedure sbAzBottomClick(Sender: TObject);
    procedure sbAzTopClick(Sender: TObject);
    procedure nsElBottomClick(Sender: TObject);
    procedure nsElTopClick(Sender: TObject);
    procedure neElValueChanged(Sender: TObject);
    procedure neAzValueChanged(Sender: TObject);
    procedure ndlAzMotorRedChange(Sender: TObject; AValue, AMin, AMax: Real);
    procedure ndlAzGreenChange(Sender: TObject; AValue, AMin, AMax: Real);
    procedure ndlElMotorRedChange(Sender: TObject; AValue, AMin, AMax: Real);
    procedure btnSimpleMsgBoxClick(Sender: TObject);
    procedure swOnOffClick(Sender: TObject);

    // property Compass: TCompass read getCompass write setCompass;
  private
    { Private declarations }

    MotorsFound: boolean;
    FInUse: boolean;
    FSimulation: boolean;
    FMode: TAntMode; // Servo system on
    hFind, hRes: THandle;
  //  Song: pChar;
    FAtHome: boolean;
    FElHome: boolean;
    FAzHome: boolean;

    FAzMoveDone: boolean;
    FElMoveDone: boolean;

    FAzMotorPositionSimulated: integer;
    FQuickStartAzMotorPosition: integer; // quick start motor variables
    FQuickStartElMotorPosition: integer;
    AzAddr: byte;
    ElAddr: byte;
    IOAddr: byte;
    FReady: boolean;

    SETINetReg: TRegistry;
    AzServoPosSimulation, ElServoPosSimulation: Single;
    FElMotorPositionSimulated: integer;


    // FCompass: TCompass;
    // FKerr: boolean;

    procedure AddInfo(sCapt, sVal: string);
    procedure ChangeMode(NewMode: TAntMode);
    procedure SetSimulation(value: boolean);
    procedure setMode(value: TAntMode);

    procedure SetReady(value: boolean);
    function GetReady: boolean;
    procedure SetAtHome(value: boolean);
    function getAtHome: boolean;

    function getElMotorPosition: integer;
    procedure setElMotorPosition(value: integer);
    function getAzMotorPosition: integer;
    procedure setAzMotorPosition(value: integer);

    procedure setFilter(value: boolean);
    function getFilter: boolean;
    function getAzMoveDone: boolean;
    function getElMoveDone: boolean;
    procedure setElMotorRed(value: Double);
    function getElMotorRed: Double;
    procedure setAzMotorRed(value: Double);
    function getAzMotorRed: Double;

    function getAzYellow: Double;
    procedure SetAzYellow(value: Double);

    function getElYellow: Double;
    procedure SetElYellow(value: Double);
    // procedure PlayResourceWAV(SoundResourceID: String; SyncOrAsync: integer);
    function DLLTest: boolean;

    property AzMotorPosition: integer read getAzMotorPosition write setAzMotorPosition;
    property AzMotorRed: Double read getAzMotorRed write setAzMotorRed;
    property ElMotorPosition: integer read getElMotorPosition write setElMotorPosition;
    property ElMotorRed: Double read getElMotorRed write setElMotorRed;

  public
    { Public declarations }
    // RotorDLL: string;
    procedure WMDeviceChange(var Msg: TMessage); message WM_DEVICECHANGE;
    procedure AddDeviceInfo(Dev: TnrDevice);
    procedure QuickStart;
    property InUse: boolean read FInUse write FInUse;
    property AzMoveDone: boolean read getAzMoveDone;
    property ElMoveDone: boolean read getElMoveDone;
    property AtHome: boolean read getAtHome write SetAtHome;
    property Ready: boolean read GetReady write SetReady;
    property Filter: boolean read getFilter write setFilter default True; //

    property Mode: TAntMode read FMode write setMode;
    property Simulation: boolean read FSimulation write SetSimulation;
    property ElYellow: Double read getElYellow write SetElYellow;
    property AzYellow: Double read getAzYellow write SetAzYellow;

  end;

var
  frmAntenna: TfrmAntenna;
{$ENDREGION}

implementation

var

  LastElMotorPosition: integer;
  LastAzMotorPosition: integer;
  Port: PAnsiChar = 'COM3';
{$R *.dfm}
{$REGION ' C R E A T E '}

procedure TfrmAntenna.FormCreate(Sender: TObject);
(* C r e a t e   F o r m *)
var
  res: byte;
  test: integer;

  buf1: array [0 .. 255] of AnsiChar;
begin

  (* REGISTRY ENTRIES *)
  SETINetReg := TRegistry.create;
  SETINetReg.LazyWrite := False;
  try
    SETINetReg.rootkey := HKEY_CURRENT_USER;
    if SETINetReg.keyexists(AntennaKey) then
    begin { Key Exists }
      if SETINetReg.openkey(AntennaKey, False) then
      // OpenKey will create the key if the second parameter is TRUE and the key does not already exist
      // This one will NOT create the key
      begin
        try // open key
          with SETINetReg do
          begin // Antenna Parameters entries
            FQuickStartAzMotorPosition := ReadInteger('AzMotorPosition');
            FQuickStartElMotorPosition := ReadInteger('ElMotorPosition');
            // not to AzmotorPosition (Property) - why??
            nrComm1.ComPortNo := ReadInteger('Port');
            nrComm1.BaudRate := ReadInteger('Baud');
            Left := ReadInteger('Left');
            If (Screen.MonitorCount = 1) AND (Left > 640) then
              Left := 200; // Set for Single monitor
            Top := ReadInteger('Top');
          end; // with SETINetReg
        except // an exception is raised if there isn't one of the values
          SETINetReg.DeleteKey(AntennaKey);
          // If the version value didn't exist delete the whole key
        end; // Open key
      end; // Open Key
    end; // SETINetAntennaSRTKey exists

    if not SETINetReg.keyexists(AntennaKey) then
    // D E F A U L T   R E G   E N T R I E S
    begin { New User }
      with SETINetReg do
      begin
        openkey(AntennaKey, True);
        // OpenKey will create the key if the second parameter is true and the key does not already exist
        nrComm1.BaudRate := 19200;
        nrComm1.ComPortNo := 1;
        Left := 100;
        Top := 100;
      end;
    end; // Key Exists

  finally // Antenna
    SETINetReg.free;
  end; // Finished with Reg

  gbLNAControl.Enabled := False;

  ElAddr := AddrEl; // Azimuth is second Kerr module in the string.
  AzAddr := AddrAz;
  IOAddr := AddrIO;

  sbAntenna.Panels[CsbNote].Text := 'Ready';

  FAzMoveDone := True;
  FElMoveDone := True;
  // DLLTest;
{$IF Defined(EUREKALOG)}
  Caption := frmAntenna.Caption + (' + EurekaLog ');
{$IFEND}
{$IF Defined(NoFilterKerr) }
  Caption := frmAntenna.Caption + '  -- NO FILTER KERR Controller --';

{$IFEND}
  Caption := Caption + ' (' + IntToStr(SizeOf(Pointer) * 8) + ' Bit)';
  Version(buf1);
  Caption := Caption + ' - Rotor DLL Version: ' + buf1;

{$IF NOT Defined(Antenna) }
  mnuMount.Visible := False;
  mnuSkyMap.Visible := False;
{$IFEND}
end;

function TfrmAntenna.DLLTest: boolean;
var
  res: boolean;
begin
  sbAntenna.Panels[CsbNote].Text := 'Searching for Rotor Motors';
  res := KerrInit(nrComm1.ComName, nrComm1.BaudRate);
  // NmcShutdown;// Don't do this shit.
  If res then
  begin
    MotorsFound := True;
    sbAntenna.Panels[CsbNote].Text := 'Found Rotor Motors';
    Mode := AntRun;
  end
  else
  begin
    MotorsFound := False;
    Caption := Caption + ' - Rotors NOT connected';
    sbAntenna.Panels[CsbNote].Text := 'Rotor Motors NOT found';
  //  PlayResourceWAV('Error', SND_SYNC); // Error, FoundETWAV, Ready
  end;
  result := MotorsFound;
end;
{$ENDREGION}
//
{$REGION ' S T A T U S '}
//

function TfrmAntenna.getAzMotorRed: Double;
begin
  result := ndlAzMotorRed.value
end;

procedure TfrmAntenna.setAzMotorRed(value: Double);
begin
  if frmMount.Mount = mtAzEl then
  begin
    if (ndlAzYellow.value < 180) then

      ndlAzMotorRed.value := frmMount.AzStop - value
    else
      ndlAzMotorRed.value := 360 - (value);
  end;

  if frmMount.Mount = mtDrift then
  begin
    if (ndlAzYellow.value < 180) then

      ndlAzMotorRed.value := value // (frmMount.AzStop )
    else
      ndlAzMotorRed.value := 360 - (value);
  end;

end;

function TfrmAntenna.getElMotorRed: Double;
begin
  result := ndlElMotorRed.value
end;

procedure TfrmAntenna.setElMotorRed(value: Double);
begin
  // if InRange(Value, frmMount.ElStart, frmMount.ElStop) then
  ndlElMotorRed.value := value - frmMount.ElStart
  // else

  // beep();
end;

function TfrmAntenna.getAzYellow: Double;
begin
  result := ndlAzYellow.value;
end;

procedure TfrmAntenna.SetAzYellow(value: Double);
begin
  ndlAzYellow.value := value;
  neAz.value := value;
end;

function TfrmAntenna.getElYellow: Double;
begin
  result := ndlElYellow.value;
end;

procedure TfrmAntenna.SetElYellow(value: Double);
begin
  // if InRange(value, frmMount.ElStart, frmMount.ElStop) then
  // ndlElYellow.value := value
  // else
  // begin
  // beep();
  // if value > frmMount.ElStop then
  // ndlElYellow.value := frmMount.ElStop
  // else
  // ndlElYellow.value := frmMount.ElStart
  // end;
  ndlElYellow.value := value;
  neEl.value := ndlElYellow.value;
end;

procedure TfrmAntenna.swOnOffClick(Sender: TObject);
begin
  case swOnOff.SwitchPosition of
    integer(AntOff):
      begin
        ChangeMode(AntOff);
        Ready := False;
      end;
    integer(AntRun):
      begin
        if DLLTest then
        begin
          ChangeMode(AntRun);
         // tmrStatus.Interval := 250;
            tmrStatus.Interval := 150;
          GoHome;
        end
        else
        begin
          swOnOff.SwitchPosition := integer(AntOff);
        end;
      end;

    integer(AntSim):
      begin
        ChangeMode(AntSim);
        tmrStatus.Interval := 5;
        Ready := True;
      end;
  end;
end;

procedure TfrmAntenna.ChangeMode(NewMode: TAntMode);
begin
  // trmStatus.Enabled := False;
  Mode := NewMode;
  case NewMode of
    AntSim:
      begin
       // mnuSound.Checked := (False);
        //
        FSimulation := True;
        ledHome.BlinkSpeed := 0;

        AtHome := True;

        sbAntenna.Panels[CsbNote].Text := 'Simulating.';
        AzServoPosSimulation := 0;
        ElServoPosSimulation := 0;
        tmrStatus.Enabled := True;
        ledHome.Caption := 'Ready';
        ledHome.ColorIndex := 4;
      end;
    AntOff:
      begin
        tmrStatus.Enabled := False;

        setOn(False);
        ledHome.Caption := 'Off';
        ledHome.ColorIndex := 1;
        // tmrStatus.Interval := 250;
        gElMotor.Enabled := False;
        gAzMotor.Enabled := False;
        gElevation.Enabled := False;
        gAzimuth.Enabled := False;
        sbAntenna.Panels[CsbNote].Text := 'Comm Port DIS Connected';
        NmcShutdown; // Don't put this shit anywhere else.
      end;
    AntRun:
      begin

      //  PlayResourceWAV('Connecting', SND_SYNC);
        FSimulation := False;
        sbAntenna.Panels[CsbNote].Text := 'Searching for Motors';

        ledHome.ColorIndex := 2;
        ledHome.Caption := 'Home Req''d';
        ledHome.BlinkSpeed := 500;
        setOn(True);
        tmrStatus.Enabled := True;

      end;
  end;
end;
{$ENDREGION}
//
{$REGION ' N E E D L E S    '}

// A Z   Y E L L O W   C H A N G E
procedure TfrmAntenna.ndlAzGreenChange(Sender: TObject; AValue, AMin, AMax: Real);
begin
  frmMount.Azimuth := AValue;
end;

procedure TfrmAntenna.ndlAzMotorRedChange(Sender: TObject; AValue, AMin, AMax: Real);
begin
  // ShowMessage('ndlAzMotorRedChange ');
end;

procedure TfrmAntenna.ndlAzYellowChange(Sender: TObject; AValue, AMin, AMax: Real);
begin
  neAz.value := AValue;
end;

procedure TfrmAntenna.neAzValueChanged(Sender: TObject);
var
  Dif: integer;

begin
  if (frmMount.Mount = mtAzEl) then
  begin // Bounce out of no fly zone
    if (frmMount.InZone(neAz.ValueAsInt, Dif) = True) then
    begin
      if (Dif < 0) then
        ndlAzYellow.value := frmMount.AzStop - 1
      else
        ndlAzYellow.value := frmMount.AzStart + 1;
    end;
    AzMotorRed := neAz.value;

    if (ndlAzYellow.value < 180) then
    begin

      // ndlAzYellow.value := neAz.value;
      AzMotorRed := ndlAzYellow.value;
      ndlElYellow.value := ndlElYellow.value;
    end
    else
    begin
      ndlAzYellow.value := neAz.value;
      AzMotorRed := ndlAzYellow.value;
      ndlElYellowChange(Self, ndlElYellow.value, 0, 0);
      // Jim - Required to 'Kick' the El needles after moving above 180 deg Az
    end;
  end;

  // if (frmMount.Mount = mtDrift) then
  // begin // Keep in fly zone
  // if (frmMount.InZone(neAz.ValueAsInt, Dif)) then
  // begin
  // AzMotorRed := neAz.value;
  // end
  // else
  // begin
  // // if (Dif < 0) then
  // // ndlAzYellow.Value := frmMount.AzStop - 1
  // // else
  // // ndlAzYellow.Value := frmMount.AzStart + 1;
  // end;
  //
  // end;

end;

procedure TfrmAntenna.sbAzBottomClick(Sender: TObject);
begin
  neAz.value := neAz.value - 1.0;

end;

procedure TfrmAntenna.sbAzTopClick(Sender: TObject);
begin
  neAz.value := neAz.value + 1.0;

end;

// E L   Y E L L O W   C H A N G E
procedure TfrmAntenna.ndlElYellowChange(Sender: TObject; AValue, AMin, AMax: Real);

begin
  if InRange(AValue, 0, frmMount.ElStart) then
  begin
    SetElYellow(frmMount.ElStart + 0.1);
    beep();
    exit
  end;
  if InRange(AValue, frmMount.ElStop, 90) then
  begin
    SetElYellow(frmMount.ElStop - 0.1);
    beep();
    exit
  end;
  if (ndlAzYellow.value < 180) then
  begin
    // neEl.value := AValue;
    ElMotorRed := AValue
  end
  else
  begin
    // neEl.value := AValue;
    ElMotorRed := 180 - AValue;
  end;
end;

procedure TfrmAntenna.neElValueChanged(Sender: TObject);
begin
  ElMotorRed := neEl.value;
  SetElYellow(neEl.value);
end;

procedure TfrmAntenna.ndlElGreenChange(Sender: TObject; AValue, AMin, AMax: Real);
begin
  frmMount.Elevation := AValue;
end;

procedure TfrmAntenna.ndlElMotorRedChange(Sender: TObject; AValue, AMin, AMax: Real);
begin
  // ShowMessage('ndlElMotorRedChange ' + floatToStr(AValue));
end;

{$ENDREGION}
//
{$REGION ' S E R I A L   P O R T'}

procedure TfrmAntenna.btnSerialConfigClick(Sender: TObject);
begin
  nrComm1.ConfigDialog;
end;

procedure TfrmAntenna.nrDeviceBox1Change(Sender: TObject);
begin
  lvProperties.Items.Clear;
  AddDeviceInfo(nrComm1.Device[nrComm1.DeviceIndex]);

end;

procedure TfrmAntenna.nsElBottomClick(Sender: TObject);
begin
  neEl.value := neEl.value - 1.0;
  // ElYellow := neEl.Value;
end;

procedure TfrmAntenna.nsElTopClick(Sender: TObject);
begin
  neEl.value := neEl.value + 1.0;
  // ElYellow := neEl.Value;
end;

procedure TfrmAntenna.WMDeviceChange(var Msg: TMessage);
var
  i: integer;
begin
  i := nrComm1.DeviceIndex;
  nrComm1.WMDeviceChange(Msg);
  nrComm1.DeviceIndex := i;
  nrDeviceBox1Change(Self);
end;

procedure TfrmAntenna.AddInfo(sCapt, sVal: string);
var
  Itm: TListItem;
begin
  if Trim(sVal) = '' then
    exit;
  with lvProperties do
  begin
    Itm := Items.Add;
    Itm.Caption := sCapt;
    Itm.SubItems.Add(sVal);
  end;
end;

procedure TfrmAntenna.AddDeviceInfo(Dev: TnrDevice);
begin
  if Dev = nil then
    exit;
  AddInfo('DOS', Dev.NameDeviceDos);
  AddInfo('Friendly Name', Dev.NameFriendly);
  AddInfo('Description', Dev.Description);
  // AddInfo('PDO', dev.NamePDO);
  AddInfo('Service', Dev.Service);
  AddInfo('Manufacturer', Dev.Manufacturer);
end;
{$ENDREGION}
//
{$REGION ' G e t t e r s    a n d    S e t t e r s  }

procedure TfrmAntenna.GoHome;
begin
  // sbAntenna.Panels[CsbNote].text := ' HOMING';
  ledHoming.Checked := False;
  tmrStatus.Enabled := False; // Shut down the normal timer
  FAzHome := False;
  FElHome := False;

  if Mode = AntRun then
    SetHome;

  gAzMotor.value := 0;
  gAzMotor.value := 0; // Set Yellow  needle positon
  ElMotorPosition := 0;

  gElMotor.value := 0; // Yellow (Proposed) needle
  LastElMotorPosition := -1;
  LastAzMotorPosition := -1;
  ElMotorPosition := 0;
  AzMotorPosition := 0;
  // Delay(1000); // Delay one second for motors to start
  ledHome.Caption := 'Homing';
  ledHome.ColorIndex := 2;
  tmrHoming.Enabled := True; // Then start the time
end;

function TfrmAntenna.getAzMoveDone: boolean;
begin
  if (Mode = AntRun) then
  begin
    // if uRotorIFDynamic.getAzMoveDone then
    if NMCLibI04.getAzMoveDone then
    begin
      result := True
    end
    else
    begin
      result := False;
    end;
  end; // Run mode
  if (Mode = AntSim) then
  begin

  end;
end;

function TfrmAntenna.getElMoveDone: boolean;
begin
  // If uRotorIFDynamic.getElMoveDone then
  If NMCLibI04.getElMoveDone then
  begin
    result := True;
  end
  else
  begin
    result := False;
  end;
end;

procedure TfrmAntenna.btnSearchAgainClick(Sender: TObject);

begin
  // DLLTest;
end;
//
//(* P L A Y   R E S O U R C E   W A V *)
//procedure TfrmAntenna.PlayResourceWAV(SoundResourceID: String; SyncOrAsync: integer);
//var
//  hFind, hRes: THandle;
//  Song: pChar;
//begin
//  // SND_SYNC   = $0000;  { play synchronously (default) SND_ASYNC= $0001;  play asynchronously
//  if NOT mnuSound.Checked then
//    exit;
//  hFind := FindResource(HInstance, PWideChar(SoundResourceID), 'WAVE');
//  // Play the song
//
//  if hFind <> 0 then
//  begin
//    hRes := LoadResource(HInstance, hFind);
//    if hRes <> 0 then
//    begin
//      Song := LockResource(hRes);
//      if Assigned(Song) then
//      begin
//        if (SyncOrAsync = SND_ASYNC) then
//          SndPlaySound(Song, SND_ASYNC or snd_Memory) // Start play and go on
//        else
//          SndPlaySound(Song, SND_SYNC or snd_Memory);
//        // Start play and wait for finish
//      end;
//      UnlockResource(hRes);
//    end;
//    FreeResource(hFind);
//  end { of song };
//end;

procedure TfrmAntenna.setMode(value: TAntMode);
begin

  if FMode <> value then
  begin
    case value of
      AntSim:
        swOnOff.SwitchPosition := integer(AntSim);
      AntOff:
        swOnOff.SwitchPosition := integer(AntOff);
      AntRun:
        swOnOff.SwitchPosition := integer(AntRun);
    end;
  end;

  // sbAntenna.Panels[CsbMode].text := GetEnumName(TypeInfo(TAntMode), Ord(value));
  FMode := value;
end;

procedure TfrmAntenna.QuickStart;
begin
  if (btnQuickStart.Enabled) then
  begin
    btnQuickStartClick(Self);
    Delay(100);
    btnQuickStartClick(Self);
  end;
end;

procedure TfrmAntenna.SetSimulation(value: boolean);
begin
  FSimulation := value;
 // Ready := value;
  if FSimulation then
    swOnOff.SwitchPosition := integer(AntSim)

  else
    swOnOff.SwitchPosition := integer(AntOff);
end;

procedure TfrmAntenna.setFilter(value: boolean);
begin
  if ((Mode = AntRun) AND (Ready)) then
  begin
    swFilter.Checked := value;

    NMCLibI04.setFilter(value);
  end;
end;

function TfrmAntenna.getFilter: boolean;

begin
  result := NMCLibI04.getFilter;
end;

function TfrmAntenna.getAzMotorPosition: integer; // GET AZ MOTOR
var
  pos: integer;
begin
  if Mode = AntRun then
  begin

    pos := NMCLibI04.getAzMotorPosition;
    neAzMotorPosition.ValueAsInt := pos;
    result := pos;
  end
  else
    result := FAzMotorPositionSimulated;
end;

procedure TfrmAntenna.setAzMotorPosition(value: integer); // SET AZ MOTOR
begin
  if Mode = AntRun then
  begin
    // uRotorIFDynamic.setAzMotorPosition(value);
    NMCLibI04.setAzMotorPosition(value);
    neAzMotorPosition.ValueAsInt := getAzMotorPosition;
  end
  else
    FAzMotorPositionSimulated := value;
end;

function TfrmAntenna.getElMotorPosition: integer; // GET EL Motor
var
  pos: integer;
begin
  if Mode = AntRun then
  begin
    // pos := uRotorIFDynamic.getElMotorPosition;
    pos := NMCLibI04.getElMotorPosition;
    neElMotorPosition.ValueAsInt := pos;
    result := pos;
  end
  else
    result := FElMotorPositionSimulated;
end;

procedure TfrmAntenna.setElMotorPosition(value: integer); // SET EL MOTOR
begin
  if Mode = AntRun then
  begin
    // uRotorIFDynamic.setElMotorPosition(value);
    NMCLibI04.setElMotorPosition(value);
    neElMotorPosition.ValueAsInt := getElMotorPosition;
  end
  else
    FElMotorPositionSimulated := value;
end;

procedure TfrmAntenna.SetAtHome(value: boolean); // H O M E     H O M E
begin
  gElMotor.Enabled := False;
  gAzMotor.Enabled := False;
  gElevation.Enabled := False;
  gAzimuth.Enabled := False;

  // E L   N e e d l e s

  ElMotorPosition := 0;
  ElMotorRed := frmMount.ElStart;;
  gElMotor.value := 0;
  SetElYellow(frmMount.ElStart);
  ndlElGreen.value := frmMount.ElStart;
  neEl.value := frmMount.ElStart;

  // A Z   N e e d l e s
  neAz.value := 0;
  AzMotorPosition := 0;

  if (frmMount.Mount = mtAzEl) then
  begin
    ndlAzYellow.value := frmMount.AzStop - 1;
    ndlAzMotorRed.value := 0;
  end;
  if (frmMount.Mount = mtDrift) then
  begin
    ndlAzYellow.value := frmMount.AzStart;
    AzMotorRed := frmMount.AzStart;
  end;
  if (frmMount.Mount = mtBirdBath) then
    ndlAzYellow.value := frmMount.AzStart;

  gAzMotor.value := 0;
  // AzGreen := frmMount.AzStart; // Jim Don't do this
  ndlAzGreen.value := frmMount.AzStop;
  // AzYellow := frmMount.AzStart; // Jim or this
  ndlAzYellow.value := frmMount.AzStop;

  Filter := True;

  gElMotor.Enabled := True;
  gAzMotor.Enabled := True;
  gElevation.Enabled := True;
  gAzimuth.Enabled := True;

  frmSkyMap.Searching := False;
  // ledHome.Caption := 'Moving';
  ledHome.ColorIndex := 3;
 // PlayResourceWAV('Ready', SND_SYNC); // Error, FoundETWAV, Ready
  swFilter.Checked := True;
end;

function TfrmAntenna.GetReady: boolean;
begin
  result := FReady; // or FSimulation;

end;

procedure TfrmAntenna.ledHomeClick(Sender: TObject);
begin
  GoHome;

end;

procedure TfrmAntenna.SetReady(value: boolean);
(* S e t   R e a d y *)
begin
  ledReady.Checked := value;
  if not(value) then
  begin
    Filter := False;
  end;
  gbLNAControl.Enabled := value;
  FReady := value;
end;

procedure TfrmAntenna.cmboPortClick(Sender: TObject);
var
  ComPortStr: AnsiString;
  numFound: integer;
begin
  // LoadComPort;
  ComPortStr := (nrDeviceBox1.Items[nrDeviceBox1.ItemIndex]);
  if ComPortStr = '' then
  begin
    Mode := AntOff;
    ShowMessage('No Com Port Selected');
    swOnOff.SwitchPosition := 1;
    exit;
  end;

  try
    // numFound := SetNmcInit(PAnsiChar(ComPortStr), 19200); // CPort := 'Com5';
    numFound := NmcInit(PAnsiChar(ComPortStr), 19200); // CPort := 'Com5';
  except
    if numFound <> NMCLibI04.CNumKerrModules then
    begin
      ShowMessage('Modules Found: ' + IntToStr(numFound) + ' Error');
      swOnOff.SwitchPosition := 1;
      exit;
    end;
  end;

  if (numFound = NMCLibI04.CNumKerrModules) then
  begin
    sbAntenna.Panels[CsbNote].Text := ' Homing Required  ';

  end
  // else
  // begin // Set Antenna OFF on error
  // if NOT mnuSound.Checked then
  // exit;
  // hFind := FindResource(HInstance, 'Error', 'WAVE');
  // if hFind <> 0 then
  // begin
  // hRes := LoadResource(HInstance, hFind);
  // if hRes <> 0 then
  // begin
  // Song := LockResource(hRes);
  // if Assigned(Song) then
  // SndPlaySound(Song, SND_ASYNC or snd_Memory);
  // UnlockResource(hRes);
  // end;
  // FreeResource(hFind);
  // end;

  // sbAntenna.Panels[CsbNote].Text := 'Modules Found: ' + IntToStr(numFound) + ', Error';
  // end;

end;

procedure TfrmAntenna.swFilterClick(Sender: TObject);
begin
  Filter := swFilter.Checked;
end;

procedure TfrmAntenna.mnuAboutClick(Sender: TObject);
begin
  frmAbout.ShowVersion;
end;

procedure TfrmAntenna.mnuMountClick(Sender: TObject);
begin
  frmMount.Visible := Not frmMount.Visible;

end;

function TfrmAntenna.getAtHome: boolean;
begin
  result := FAtHome;
end;

procedure TfrmAntenna.btnSetHomeClick(Sender: TObject);
begin
  //
end;

procedure TfrmAntenna.btnSimpleMsgBoxClick(Sender: TObject);
var
  buf1: array [0 .. 255] of AnsiChar;
begin
  SimpleMsgBox('Testing testing 123');
  Version(buf1);
  Caption := Caption + ' - DLL Version date: ' + buf1;
end;

procedure TfrmAntenna.mnuSkyMapClick(Sender: TObject);
begin
  frmSkyMap.Visible := Not frmSkyMap.Visible;
  frmAntenna.BringToFront
end;

procedure TfrmAntenna.btnQuickStartClick(Sender: TObject);
(* ==================================  Quick Start ======================= *)
begin

  ServoSetGain2(ElAddr, ElKp, ElKd, ElKi, ElIl, ElOl, ElCl, ElEl, ElSr, ElDc, ElSm);
  ServoSetGain2(AzAddr, AzKp, AzKd, AzKi, AzIl, AzOl, AzCl, AzEl, AzSr, AzDc, AzSm);

  // Set both motors and gauges to saved position
  ServoSetPos(AzAddr, FQuickStartAzMotorPosition);
  AzMotorPosition := FQuickStartAzMotorPosition;
  gAzMotor.value := AzMotorPosition / AzPulsePerDeg;
  gAzMotor.value := gAzMotor.value;

  ServoSetPos(ElAddr, FQuickStartElMotorPosition);
  ElMotorPosition := FQuickStartElMotorPosition;
  gElMotor.value := ElMotorPosition / ElPulsePerDeg;

  gElMotor.Enabled := True;
  gAzMotor.Enabled := True;
  gElevation.Enabled := True;
  gAzimuth.Enabled := True;

  sbAntenna.Panels[CsbNote].Text := 'Quick Start Complete. Ready to use.';

  // trmStatus.Enabled := True; // Start the normal status timer
  Filter := True;
  Ready := True;

  // *************************************
  // hFind := FindResource(HInstance, 'Ready', 'WAVE');
  // if hFind <> 0 then
  // begin
  // if NOT mnuSound.Checked then
  // exit;
  // hRes := LoadResource(HInstance, hFind);
  // if hRes <> 0 then
  // begin
  // Song := LockResource(hRes);
  // if Assigned(Song) then
  // SndPlaySound(Song, SND_ASYNC or snd_Memory);
  // UnlockResource(hRes);
  // end;
  // FreeResource(hFind);
  // PlaySound('Connecting.wav', 0, snd_Async); // Play the 'Connecting' sound
  // end;
  // PlaySound('Ready.Wav', 0, snd_ASync);

  // ***********************************
end;

{$ENDREGION}
//
{$REGION ' T i m e r s   }

procedure TfrmAntenna.tmrHomingTimer(Sender: TObject);
(* =============   Homing Timer Tick ======================================== *)
const
  stat_items = SEND_POS or SEND_VEL;
begin
  ledHoming.Checked := NOT ledHoming.Checked;
  if Mode = AntRun then
  begin
    tmrHoming.Enabled := False;
    // Turn the Home timer Off
    if getHome then

    begin // ========  W E   A R E   H O M E   ==================
      FAtHome := True;

      FElHome := True;
      sbAntenna.Panels[CsbNote].Text := 'Found Rotor Motors';
      FAzHome := True;
      Ready := FAtHome;

      ledHoming.Checked := False;
      AtHome := True;
      // sbAntenna.Panels[CsbNote].text := 'At Home';
      ledHome.Caption := 'Ready';
      ledHome.ColorIndex := 4;
      tmrHoming.Enabled := False;
      tmrStatus.Enabled := True;
    end
    else
    begin
      tmrHoming.Enabled := True;
      // Turn the Home time BACK ON
      // sbAntenna.Panels[CsbNote].text := 'Hunting for home';

      ledHome.Caption := 'Homing';
      ledHome.ColorIndex := 2;
      FAtHome := False
    end
  end { Live }
  else // SimulationMode
  begin
    FAtHome := True;
    tmrHoming.Enabled := False;
    tmrStatus.Enabled := True;
    ledHome.Caption := 'Ready';
    ledHome.ColorIndex := 4;
  end;

end;

procedure TfrmAntenna.tmrStatusTimer(Sender: TObject);
(* ==================================      S T A T U S   T I M E R   T I C K  =============== *)
var
  AzError, ElError: Single;
  AzMoving, ElMoving: boolean;
begin
  ledStatusTick.Checked := not ledStatusTick.Checked;
  if (Mode = AntRun) then
  begin

    setElevationtrajectory(ElMotorRed);
    setAzimuthTrajectory(AzMotorRed);

    // tmpNdl := (ElMotorPosition / ElPulsePerDeg) + frmMount.ElStart;
    // // Jim - ElGreenNeedle calculate over 100 degrees
    // if tmpNdl < frmMount.ElStop then
    // ndlElGreen.Value := tmpNdl;

    gElMotor.value := (ElMotorPosition / ElPulsePerDeg); // Test Jim - WTF
    gAzMotor.value := (AzMotorPosition / AzPulsePerDeg);

    frmMount.CheckNewAntAvailable;
  end; // Run Only

  if (Mode = AntSim) then
  begin // Simulation
    ElError := abs(gElMotor.value - abs(ElMotorRed));
    if ((gElMotor.value > ElMotorRed) and (ElError > CMoveError)) then
    begin
      ElServoPosSimulation := ElServoPosSimulation - 0.1;
      gElMotor.value := abs(ElServoPosSimulation);
      ElMoving := True;
    end
    else
      if ((gElMotor.value < ElMotorRed) and (ElError > CMoveError)) then
      begin
        ElServoPosSimulation := ElServoPosSimulation + 0.1;
        gElMotor.value := abs(ElServoPosSimulation);
        ElMoving := True;
      end
      else
        ElMoving := False;

    AzError := AzMotorRed - gAzMotor.value;
    if (AzError - CMoveError) > 0 then
    begin
      AzServoPosSimulation := AzServoPosSimulation + 0.3;
      gAzMotor.value := AzServoPosSimulation;
      AzMoving := True;
    end
    else
      if (AzError + CMoveError) < 0 then
      begin
        AzServoPosSimulation := AzServoPosSimulation - 0.3;
        gAzMotor.value := AzServoPosSimulation;
        AzMoving := True;
      end
      else
        AzMoving := False;

  end; { simulation }

  if Mode = AntRun then
  begin
    if AzMoveDone then
      AzMoving := False
    else
      AzMoving := True;

    if ElMoveDone then
      ElMoving := False
    else
      AzMoving := True;
  end;
  // frmMount.getNewAntennaAzElAvailable;
  if (frmMount.Mount = mtAzEl) then
  begin
    if (ndlAzYellow.value < 180) then
      ndlAzGreen.value := frmMount.AzStop - gAzMotor.value
    else
      ndlAzGreen.value := frmMount.AzStop - gAzMotor.value + 180;
  end;

  if (frmMount.Mount = mtDrift) then
  begin
    if (ndlAzYellow.value < 180) then

      ndlAzGreen.value := gAzMotor.value
    else
      // ndlAzGreen.Value := frmMount.AzStart - gAzMotor.Value + 180;
      ndlAzGreen.value := gAzMotor.value + 180;
  end;

  if gElMotor.value < 90 then
  begin
    ndlElGreen.value := gElMotor.value + frmMount.ElStart;
  end
  else
  begin
    ndlElGreen.value := 180 - gElMotor.value - frmMount.ElStart
  end;

  neAzMotorPosition.value := gAzMotor.value;
  neElMotorPosition.value := gElMotor.value;
  if (Filter = True) then
    lblInOut.Caption := 'IN'
  else
    lblInOut.Caption := 'OUT';

  if AzMoving then
  begin
    sbAntenna.Panels[CsbAzStatus].Text := 'Moving';
  end
  else
  begin
    sbAntenna.Panels[CsbAzStatus].Text := 'Move Done';
  end;
  if ElMoving then
  begin
    sbAntenna.Panels[CsbElStatus].Text := 'Moving';
  end
  else
  begin
    sbAntenna.Panels[CsbElStatus].Text := 'Move Done';
  end;

  if (AzMoving OR ElMoving) then
  begin
    ledHome.Caption := 'Moving';
    ledHome.ColorIndex := 3;
  end
  else
  begin
    ledHome.Caption := 'Go Home';
    ledHome.ColorIndex := 4;
  end;
{$IF Defined(Antenna) }
  frmSkyMap.DrawSearchArea;
{$IFEND}
end;

procedure TfrmAntenna.btnTestClick(Sender: TObject);
var
  ret: Single;
begin
  ret := PowerOf2(6);
  ShowMessage('6^2 = ' + FloatToStr(ret));
end;

{$ENDREGION}
//
{$REGION ' C L O S E '}

procedure TfrmAntenna.FormClose(Sender: TObject; var Action: TCloseAction);
(* C l o s e *)
begin
  SETINetReg := TRegistry.create; // S A V E  R E G   E N T R I E S
  try
    with SETINetReg do
    begin
      rootkey := HKEY_CURRENT_USER;
      DeleteKey(AntennaKey); // Kill the whole key every time.
      openkey(AntennaKey, True); // Then write it back

      // OpenKey will create the key if the second parameter is true and the key does not already exist
      begin
        // WriteInteger('AzMotorPosition', FQuickStartAzMotorPosition); //
        WriteInteger('AzMotorPosition', neAzMotorPosition.ValueAsInt); //
        // WriteInteger('ElMotorPosition', FQuickStartElMotorPosition); //
        WriteInteger('ElMotorPosition', neElMotorPosition.ValueAsInt); //
        WriteInteger('Port', nrComm1.ComPortNo);
        WriteInteger('Baud', nrComm1.BaudRate);
        WriteInteger('Left', Left);
        WriteInteger('Top', Top);
      end;
      CloseKey;
    end;
    // SETINetReg
  finally
    SETINetReg.free;
  end; // try..finally

  // setNmcShutdown();
end;
{$ENDREGION}

end.
