//public void Park()
        //{
        //    tl.LogMessage("Park", "Not implemented");
        //    throw new ASCOM.MethodNotImplementedException("Park");
        //}

        public void PulseGuide(GuideDirections Direction, int Duration)
        {
            //Command: “:MnXXXXX#” “:MsXXXXX#” “:MeXXXXX#” “:MwXXXXX#”
            //Response: (none)
            //Command motion for XXXXX milliseconds in the direction specified at the currently selected guide
            //rate. If XXXXX has a value of zero, motion is continuous and requires a “:Mx00000#” command
            //to terminate.x is the same direction of the previous command.
            // XXXXX is in the range of 0 to 32767.
            pulseGuiding = true;
            String MoveDurationCommand;
            String XXXXX = Duration.ToString().PadLeft(5, '0');

            switch (Direction)
            {
            case ASCOM.DeviceInterface.GuideDirections.guideEast:
                MoveDurationCommand = ":Me" + XXXXX + "#";
                break;

            case ASCOM.DeviceInterface.GuideDirections.guideNorth:
                MoveDurationCommand = ":Mn" + XXXXX + "#";
                break;

            case ASCOM.DeviceInterface.GuideDirections.guideSouth:
                MoveDurationCommand = ":Ms" + XXXXX + "#";
                break;

            case ASCOM.DeviceInterface.GuideDirections.guideWest:
                MoveDurationCommand = ":Mw" + XXXXX + "#";
                break;

            default:
                MoveDurationCommand = "";
                break;
            }
            if (MoveDurationCommand != "")
            {
                var MoveDurationTransaction = new ZEQ25NoReplyTransaction(MoveDurationCommand);
                Task.Run(() => transactionProcessor.CommitTransaction(MoveDurationTransaction));
                log.Info("Waiting for move command completion");
                MoveDurationTransaction.WaitForCompletionOrTimeout();
                Thread.Sleep(Duration);
            }
            pulseGuiding = false;
        }
        public void MoveAxis(TelescopeAxes Axis, double Rate)
        {
            // When the motion is stopped by setting the rate to zero the scope will be set to the previous TrackingRate
            // or to no movement, depending on the state of the Tracking property.
            // iOptron ZEQ25 mount takes care of this.

            // StopMovingTransaction should execute before setting isMoving to false
            isMoving = (Rate != 0) || isMoving;
            //Command: “:SRn#”
            //Response: “1”
            //Sets the moving rate used for the N-S-E-W buttons.
            // For n, specify an integer from 1 to 9.
            // 1 stands for 1x sidereal tracking rate, 2 stands for 2x,
            // 3 stands for 8x, 4 stands for 16x, 5 stands for 64x,
            // 6 stands for 128x, 7 stands for 256x, 8 stands for 512x,
            // 9 stands for maximum speed(larger than 512x).
            //Command: “:mn#” “:me#” “:ms#” “:mw#”
            //Response: (none)
            log.Info("MoveAxis Axis: {0}", Axis);
            log.Info("MoveAxis Rate: {0}", Rate);
            String SetRateCommand;
            String MoveCommand;
            bool   response  = false;
            int    direction = Math.Sign(Rate);

            int speed     = (int)(Math.Abs(Rate) / SiderealRateDPS);
            int speed_int = speed;

            // Round up to nearest power of 2 using bitwise method
            speed--;
            speed |= speed >> 1;  // divided by 2
            speed |= speed >> 2;  // divided by 4
            speed |= speed >> 4;  // divided by 16
            speed |= speed >> 8;  // divided by 256
            speed |= speed >> 16; // divided by 65536
            speed++;              // next power of 2
                                  // Note: 4x and 32x are not supported so will need to be ignored or substituted

            int x = speed >> 1;   // previous power of 2

            // next power of 2 - requested speed        (proximity to next power of 2)
            // requested speed - previous power of 2    (proximity to previous power of 2)
            // set speed_power to nearest power of 2
            int speed_power = (speed - speed_int) > (speed_int - x) ? x : speed;

            log.Info("MoveAxis, Speed: {0} x SiderealRateDPS", speed);
            switch (speed_power)
            {
            case (0):
                var StopMovingTransaction = new ZEQ25NoReplyTransaction(":q#");
                Task.Run(() => transactionProcessor.CommitTransaction(StopMovingTransaction));
                StopMovingTransaction.WaitForCompletionOrTimeout();
                isMoving = false;
                return;

            case (1):
                SetRateCommand = ":SR1#";
                break;

            case (2):
                SetRateCommand = ":SR2#";
                break;

            case (4):
                SetRateCommand = ":SR2#";     // 4x not supported set to 2x instead
                break;

            case (8):
                SetRateCommand = ":SR3#";
                break;

            case (16):
                SetRateCommand = ":SR4#";
                break;

            case (32):
                SetRateCommand = ":SR4#";     // 32x not supported set to 16x instead
                break;

            case (64):
                SetRateCommand = ":SR5#";
                break;

            case (128):
                SetRateCommand = ":SR6#";
                break;

            case (256):
                SetRateCommand = ":SR7#";
                break;

            case (512):
                SetRateCommand = ":SR8#";
                break;

            case (1024):
                SetRateCommand = ":SR9#";
                break;

            default:
                SetRateCommand = ":SR1#";
                break;
            }

            var SetRateTransaction = new ZEQ25BooleanTransaction(SetRateCommand)
            {
                Timeout = TimeSpan.FromSeconds(2)
            };

            Task.Run(() => transactionProcessor.CommitTransaction(SetRateTransaction));
            log.Info("Waiting for set rate command completion");
            SetRateTransaction.WaitForCompletionOrTimeout();
            response = SetRateTransaction.Value;

            if (response == false)
            {
                log.Info("MoveAxis", "Error setting speed!");
                return;
            }
            switch (Axis)
            {
            case TelescopeAxes.axisPrimary:
                // East is positive
                MoveCommand = (direction == 1) ? ":me#" : ":mw#";
                break;

            case TelescopeAxes.axisSecondary:
                // North is positive if pierSide is pierEast
                MoveCommand = (direction == 1) ? ":mn#" : ":ms#";
                // Not inverting axis intentionally to avoid problems near pole
                break;

            default:
                // Not expected!
                return;
            }
            var MoveTransaction = new ZEQ25NoReplyTransaction(MoveCommand);

            Task.Run(() => transactionProcessor.CommitTransaction(MoveTransaction));
            log.Info("Waiting for move command completion");
            MoveTransaction.WaitForCompletionOrTimeout();
            if (MoveTransaction.Failed)
            {
                log.Info("MoveTransaction Failed!");
            }
        }