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;

			tmrStatus.Enabled := True;
		end
		else
		begin // Turn the Home time BACK ON
			ledHome.Caption := 'Homing';
			ledHome.ColorIndex := 2;
			FAtHome := False tmrHoming.Enabled := True;
		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.
