Files
MeadeGeneric/MeadeAutostar497/Controller/TelescopeController.cs
T
ColinD 2a97d151a4 Merged in develop (pull request #4)
Develop

* Removed unwanted files

* Adding git ignore file

* Started working on getting the basic communications with the scope working.
    Working on routines to get and set the date and time in the handbox.

* Switched the serial port over to using the .net frameworks serial port.
    Extracted the serial port into it's own class and created a simpler command processing mechanism.

* Implemented AbortSlew and did some code tidy up.

* Forced all code to 64-bit only, will make this 32/64 bit before release.
    Fixed issue in the SerialProcessor when setting the time, makes sure that there's no chance of thread stealing when pulling out the junk messages
    Added ConformanceResult.txt to show the progress of the driver development

* Added code for the site latitude
    and started work on the longitude.

* Added unit tests for reading and writing the utcDate.
    Fixed a couple of defects in the code that was setting the utcDate.

* Corrected Longitude value range

* Added support for UTC offset.

* Pulse guiding support added

* Added SiteLatitude unit tests

* Added unit tests for SiteLongitude

* Added unit tests for the new Pulse guide implementation.

* Added support for AlignmentMode

* Added support for AtPark and Park

* Added support for parking the scope
    Added support for reading the scope Azimuth

* Added support for reading Declination

* Added 5 second timeout for the serial port.
    Fixed problem with the GW command not working on the Autostar 497.

* Fixed broken unit test

* Added support for altitude

* Tidying up resharper warinings

* Implemented RightAscension
    TargetRightAscension
    TargetDec
    SlewToCoord
    and SlewToCoordAsync

* Sorted out the target RA and Dec exceptions to be compliant with ascom.

* Implemented SlewToAltAz and SlewToAltAzAsync

* Implemented SyncToTarget functionality

* Implemented slew to target

* Added support for MoveAxis

* Added support for tracking rate

* Added IFocuserV3 to the driver and made sure that it's registered for ASCOM as a focuser as well.

* Fixed issue with Target RA and Dec loosing precision

* Telescope driver now passes the Ascom conformance.

* Upgraded driver version to 0.1

* Added explicit locks around all sequences of commands.

* Basic implementation of the IFocusserV3

* Focuser passes validation, this makes the code V0.2

Approved-by: Colin Dawson <me@cjdawson.com>
2019-05-13 23:46:50 +00:00

977 lines
32 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.IO.Ports;
using System.Linq;
using System.Threading;
using ASCOM.DeviceInterface;
using ASCOM.Utilities;
using ASCOM.Utilities.Interfaces;
namespace ASCOM.MeadeAutostar497.Controller
{
//todo stop this being a singleton, and instead use a server to make only a single instance.
public sealed class TelescopeController : ITelescopeController
{
private const double INVALID_PARAMETER = -1000;
private static readonly Lazy<TelescopeController> Lazy = new Lazy<TelescopeController>();
public static TelescopeController Instance => Lazy.Value;
//todo remove this as it can cause problems in production
private ISerialProcessor _serialPort;
public ISerialProcessor SerialPort
{
get => _serialPort ?? (_serialPort = new SerialProcessor());
set
{
if (_serialPort == value)
return;
if (_serialPort != null)
{
if (_serialPort.IsOpen)
throw new InvalidOperationException("Please disconnect before changing the serial engine.");
}
_serialPort = value;
}
}
private IUtil _util;
public IUtil Util
{
get => _util ?? (_util = new Util());
set
{
if (Equals(_util, value))
return;
_util = value;
}
}
private string _port = "COM1";
public string Port
{
get => _port;
set
{
if (_port == value) return;
if (Connected)
throw new InvalidOperationException("Please disconnect from the scope before changing port.");
if (!ValidPort(value))
throw new InvalidOperationException($"Unable to select port {value} as it does not exist.");
_port = value;
}
}
private bool ValidPort(string value)
{
return SerialPort.GetPortNames().Contains(value);
}
public bool Connected
{
get => SerialPort.IsOpen;
set
{
if (value == Connected)
return;
if (value)
{
//Connecting
try
{
AtPark = false;
SerialPort.DtrEnable = false;
SerialPort.RtsEnable = false;
SerialPort.BaudRate = 9600;
SerialPort.DataBits = 8;
SerialPort.StopBits = StopBits.One;
SerialPort.Parity = Parity.None;
SerialPort.PortName = Port;
SerialPort.Open();
TestConnectionActive();
SetFocuserSpeedFastest();
}
catch (Exception)
{
if (SerialPort.IsOpen)
SerialPort.Close();
throw;
}
}
else
{
//Disconnecting
SerialPort.Close();
AtPark = false;
}
}
}
private void TestConnectionActive()
{
var firmwareVersionNumber = SerialPort.CommandTerminated(":GVN#", "#");
if (string.IsNullOrEmpty(firmwareVersionNumber))
{
throw new InvalidOperationException("Failed to communicate with telescope.");
}
}
public bool Slewing
{
get
{
if (!Connected) return false;
if (movingAxis())
return true;
var result = SerialPort.CommandTerminated(":D#", "#");
return result != string.Empty;
}
}
public DateTime utcDate
{
get
{
string telescopeDate = SerialPort.CommandTerminated(":GC#", "#");
string telescopeTime = SerialPort.CommandTerminated(":GL#", "#");
int month = telescopeDate.Substring(0, 2).ToInteger();
int day = telescopeDate.Substring(3, 2).ToInteger();
int year = telescopeDate.Substring(6, 2).ToInteger();
if (year < 2000) //todo fix this hack that will create a Y2K100 bug
{
year = year + 2000;
}
int hour = telescopeTime.Substring(0, 2).ToInteger();
int minute = telescopeTime.Substring(3, 2).ToInteger();
int second = telescopeTime.Substring(6, 2).ToInteger();
var utcCorrection = GetUtcCorrection();
//Todo is this telescope local time, or real utc?
var newDate = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc) + utcCorrection;
return newDate;
}
set
{
var utcCorrection = GetUtcCorrection();
var localDateTime = value - utcCorrection;
//Todo is this telescope local time, or real utc?
var timeResult = SerialPort.CommandChar($":SL{localDateTime:HH:mm:ss}#");
if (timeResult != '1')
{
throw new InvalidOperationException("Failed to set local time");
}
SerialPort.Lock();
try
{
var dateResult = SerialPort.CommandChar($":SC{localDateTime:MM/dd/yy}#");
if (dateResult != '1')
{
throw new InvalidOperationException("Failed to set local date");
}
//throwing away these two strings which represent
SerialPort.ReadTerminated("#"); //Updating Planetary Data#
SerialPort.ReadTerminated("#"); // #
}
finally
{
SerialPort.Unlock();
}
}
}
private TimeSpan GetUtcCorrection()
{
string utcOffSet = SerialPort.CommandTerminated(":GG#", "#");
double utcOffsetHours = double.Parse(utcOffSet);
TimeSpan utcCorrection = TimeSpan.FromHours(utcOffsetHours);
return utcCorrection;
}
public double SiteLatitude
{
get
{
var latitude = SerialPort.CommandTerminated( ":Gt#", "#");
return DmsToDouble(latitude);
}
set
{
if (value > 90)
throw new InvalidValueException("Latitude cannot be greater than 90 degrees.");
if (value < -90)
throw new InvalidValueException("Latitude cannot be less than -90 degrees.");
int d = Convert.ToInt32(Math.Floor(value));
int m = Convert.ToInt32(60 * (value - d));
var result = SerialPort.CommandChar($":Sts{d:00}*{m:00}#");
if (result != '1')
throw new InvalidOperationException("Failed to set site latitude.");
}
}
private double DmsToDouble(string dms)
{
if (IsNumeric(dms[0]))
{
double l = int.Parse(dms.Substring(0, 3));
l = l + double.Parse(dms.Substring(4, 2)) / 60;
if (dms.Length == 9)
l = l + double.Parse(dms.Substring(7, 2)) / 60 / 60;
return l;
}
double lat = int.Parse(dms.Substring(1, 2));
lat = lat + double.Parse(dms.Substring(4, 2)) / 60;
if (dms.Length == 9)
lat = lat + double.Parse(dms.Substring(7, 2)) / 60 / 60;
if (dms[0] == '-')
lat = -lat;
return lat;
}
private bool IsNumeric(char c)
{
char[] nums = new[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
return nums.Contains(c);
}
public double SiteLongitude
{
get
{
var longitude = SerialPort.CommandTerminated(":Gg#", "#");
double l = DmsToDouble(longitude);
if (l > 180)
l = l - 360;
return l;
}
set
{
if (value > 180)
throw new InvalidValueException("Longitude cannot be greater than 180 degrees.");
if (value < -180)
throw new InvalidValueException("Longitude cannot be lower than -180 degrees.");
int d = Convert.ToInt32(Math.Floor(value));
int m = Convert.ToInt32(60 * (value - d));
var result = SerialPort.CommandChar($":Sg{d:000}*{m:00}#");
if (result != '1')
throw new InvalidOperationException("Failed to set site longitude.");
}
}
public AlignmentModes AlignmentMode
{
get
{
const char ack = (char)6;
//var alignmentString = SerialPort.CommandTerminated(":GW#", "#");
var alignmentString = SerialPort.CommandChar(ack.ToString());
//:GW# Get Scope Alignment Status
//Returns: <mount><tracking><alignment>#
// where:
//mount: A - AzEl mounted, P - Equatorially mounted, G - german mounted equatorial
//tracking: T - tracking, N - not tracking
//alignment: 0 - needs alignment, 1 - one star aligned, 2 - two star aligned, 3 - three star aligned.
switch (alignmentString)
{
case 'A': return AlignmentModes.algAltAz;
case 'P': return AlignmentModes.algPolar;
case 'G': return AlignmentModes.algGermanPolar;
default:
throw new InvalidValueException($"unknown alignment returned from telescope: {alignmentString}");
}
}
set
{
switch (value)
{
case AlignmentModes.algAltAz:
SerialPort.Command(":AA#");
//:AA# Sets telescope the AltAz alignment mode
//Returns: nothing
break;
case AlignmentModes.algPolar:
case AlignmentModes.algGermanPolar:
SerialPort.Command(":AP#");
//:AP# Sets telescope to Polar alignment mode
//Returns: nothing
break;
default:
throw new ArgumentOutOfRangeException(nameof(value), value, null);
}
//:AL# Sets telescope to Land alignment mode
//Returns: nothing
}
}
public bool AtPark { get; private set; }
public double Azimuth
{
get
{
var result = SerialPort.CommandTerminated(":GZ#", "#");
//:GZ# Get telescope azimuth
//Returns: DDD*MM#T or DDD*MMSS#
//The current telescope Azimuth depending on the selected precision.
double az = DmsToDouble(result);
return az;
}
}
public double RightAscension {
get
{
var result = SerialPort.CommandTerminated(":GR#", "#");
//:GR# Get Telescope RA
//Returns: HH: MM.T# or HH:MM:SS#
//Depending which precision is set for the telescope
double ra = HmsToDouble(result);
return ra;
}
}
private double HmsToDouble(string hms)
{
return Util.HMSToHours(hms);
}
public double Declination
{
get
{
var result = SerialPort.CommandTerminated(":GD#", "#");
//:GD# Get Telescope Declination.
//Returns: sDD* MM# or sDD*MMSS#
//Depending upon the current precision setting for the telescope.
double az = DmsToDouble(result);
return az;
}
}
private double _targetRightAscension = INVALID_PARAMETER;
public double TargetRightAscension {
get
{
if (_targetRightAscension == INVALID_PARAMETER)
throw new ASCOM.InvalidOperationException("Target not set");
//var result = SerialPort.CommandTerminated(":Gr#", "#");
////:Gr# Get current/target object RA
////Returns: HH: MM.T# or HH:MM:SS
////Depending upon which precision is set for the telescope
//double targetRa = HmsToDouble(result);
//return targetRa;
return _targetRightAscension;
}
set
{
if (value < 0)
throw new InvalidValueException("Right ascension value cannot be below 0");
if (value >= 24)
throw new InvalidValueException("Right ascension value cannot be greater than 23:59:59");
//todo implement the low precision version
var hms = Util.HoursToHMS(value, ":", ":", ":", 2);
var response = SerialPort.CommandChar($":Sr{hms}#");
//:SrHH:MM.T#
//:SrHH:MM:SS#
//Set target object RA to HH:MM.T or HH: MM: SS depending on the current precision setting.
// Returns:
//0 Invalid
//1 - Valid
if (response == '0')
throw new InvalidOperationException("Failed to set TargetRightAscension.");
_targetRightAscension = value;
}
}
private double _targetDeclination = INVALID_PARAMETER;
public double TargetDeclination {
get
{
if (_targetDeclination == INVALID_PARAMETER)
throw new ASCOM.InvalidOperationException("Target not set");
//var result = SerialPort.CommandTerminated(":Gd#", "#");
////:Gd# Get Currently Selected Object/Target Declination
////Returns: sDD* MM# or sDD*MMSS#
////Depending upon the current precision setting for the telescope.
//double targetDec = DmsToDouble(result);
//return targetDec;
return _targetDeclination;
}
set
{
//todo implement low precision version of this.
if (value > 90)
throw new ASCOM.InvalidValueException("Declination cannot be greater than 90.");
if (value < -90)
throw new ASCOM.InvalidValueException("Declination cannot be less than -90.");
var dms = Util.DegreesToDMS(value, "*", ":", ":", 2);
var s = value < 0 ? '-' : '+';
var result = SerialPort.CommandChar($":Sd{s}{dms}#");
//:SdsDD*MM#
//Set target object declination to sDD*MM or sDD*MM:SS depending on the current precision setting
//Returns:
//1 - Dec Accepted
//0 Dec invalid
if (result == '0')
{
throw new ASCOM.InvalidOperationException("Target declination invalid");
}
_targetDeclination = value;
}
}
public DriveRates TrackingRate
{
get
{
var result = SerialPort.CommandTerminated(":GT#", "#");
double rate = double.Parse(result);
if (rate == 60.1)
return DriveRates.driveLunar;
else if (rate == 60.1)
return DriveRates.driveSidereal;
return DriveRates.driveKing;
}
set
{
switch (value)
{
case DriveRates.driveSidereal:
SerialPort.Command(":TQ#");
//:TQ# Selects sidereal tracking rate
//Returns: Nothing
break;
case DriveRates.driveLunar:
SerialPort.Command(":TL#");
//:TL# Set Lunar Tracking Rage
//Returns: Nothing
break;
case DriveRates.driveSolar:
SerialPort.Command(":TS#");
//:TS# Select Solar tracking rate. [LS Only]
//Returns: Nothing
break;
case DriveRates.driveKing:
break;
default:
throw new ArgumentOutOfRangeException(nameof(value), value, null);
}
}
}
public int FocuserMaxIncrement { get; set; } = 7000;
public int FocuserMaxStep { get; set; } = 7000;
public double Altitude {
get
{
var result = SerialPort.CommandTerminated(":GA#", "#");
//:GA# Get Telescope Altitude
//Returns: sDD* MM# or sDD*MMSS#
//The current scope altitude. The returned format depending on the current precision setting.
return DmsToDouble(result);
}
}
public void AbortSlew()
{
SerialPort.Command("#:Q#");
}
public void PulseGuide(GuideDirections direction, int duration)
{
string d = string.Empty;
switch (direction)
{
case GuideDirections.guideEast:
d = "e";
break;
case GuideDirections.guideNorth:
d = "n";
break;
case GuideDirections.guideSouth:
d = "s";
break;
case GuideDirections.guideWest:
d = "w";
break;
}
if (UserNewerPulseGuiding)
{
_serialPort.Command($":Mg{d}{duration:0000}#");
Thread.Sleep(duration); //todo figure out if this is really needed
}
else
{
SerialPort.Lock();
try
{
_serialPort.Command(":RG#"); //Make sure we are at guide rate
_serialPort.Command($":M{d}#");
Thread.Sleep(duration);
_serialPort.Command($":Q{d}#");
//classic only !!!, this is needed since once in a while one is not enough
Thread.Sleep(200);
_serialPort.Command($":Q{d}#");
}
finally
{
SerialPort.Unlock();
}
}
}
public void Park()
{
if (AtPark)
return;
AtPark = true;
_serialPort.Command(":hP#");
}
public void SlewToCoordinates(double rightAscension, double declination)
{
SlewToCoordinatesAsync(rightAscension, declination);
while (Slewing) //wait for slew to complete
{
Util.WaitForMilliseconds(200); //be responsive to AbortSlew();
}
}
public void SlewToCoordinatesAsync(double rightAscension, double declination)
{
SerialPort.Lock();
try
{
TargetRightAscension = rightAscension;
TargetDeclination = declination;
DoSlewAsync(true);
}
finally
{
SerialPort.Unlock();
}
}
public void SlewToAltAz(double azimuth, double altitude)
{
SlewToAltAzAsync(azimuth, altitude);
while (Slewing) //wait for slew to complete
{
Util.WaitForMilliseconds(200); //be responsive to AbortSlew();
}
}
private double TargetAltitude
{
set
{
if (value > 90)
throw new ASCOM.InvalidValueException("Altitude cannot be greater than 90.");
if (value < 0)
throw new ASCOM.InvalidValueException("Altitide cannot be less than 0.");
var dms = Util.DegreesToDMS(value, "*", "'", ".", 2);
var s = value < 0 ? '-' : '+';
var result = SerialPort.CommandChar($":Sa{s}{dms}#");
//:SasDD*MM#
//Set target object altitude to sDD*MM# or sDD*MMSS# [LX 16”, Autostar, Autostar II]
//Returns:
//1 Object within slew range
//0 Object out of slew range
if (result == '0')
throw new ASCOM.InvalidOperationException("Target altitude out of slew range");
}
}
private double TargetAzimuth
{
set
{
if (value >= 360)
throw new ASCOM.InvalidValueException("Azimuth cannot be 360 or higher.");
if (value < 0)
throw new ASCOM.InvalidValueException("Azimuth cannot be less than 0.");
var dms = Util.DegreesToDM(value, "*", ":", 2);
var result = SerialPort.CommandChar($":Sd{dms}#");
//:SzDDD*MM#
//Sets the target Object Azimuth[LX 16” and Autostar II only]
//Returns:
//0 Invalid
//1 - Valid
if (result == '0')
throw new ASCOM.InvalidOperationException("Target Azimuth out of slew range");
}
}
public void SlewToAltAzAsync(double azimuth, double altitude)
{
SerialPort.Lock();
try
{
TargetAltitude = altitude;
TargetAzimuth = azimuth;
DoSlewAsync(false);
}
finally
{
SerialPort.Unlock();
}
}
public void SyncToTarget()
{
var result = SerialPort.CommandTerminated(":CM#", "#");
//:CM# Synchronizes the telescope's position with the currently selected database object's coordinates.
//Returns:
//LX200's - a "#" terminated string with the name of the object that was synced.
// Autostars & Autostar II - At static string: " M31 EX GAL MAG 3.5 SZ178.0'#"
if (result == string.Empty)
throw new ASCOM.InvalidOperationException("Unable to perform sync");
}
public void SlewToTarget()
{
SlewToTargetAsync();
while (Slewing)
{
Util.WaitForMilliseconds(200);
}
}
public void SlewToTargetAsync()
{
if (TargetDeclination == INVALID_PARAMETER || TargetRightAscension == INVALID_PARAMETER )
throw new ASCOM.InvalidOperationException("No target selected to slew to.");
DoSlewAsync(true);
}
private bool movingAxis()
{
return _movingPrimary || _movingSecondary;
}
private bool _movingPrimary;
private bool _movingSecondary;
public void MoveAxis(TelescopeAxes axis, double rate)
{
SerialPort.Lock();
try
{
var absrate = Math.Abs(rate);
switch (absrate)
{
case 0:
//do nothing, it's ok this time as we're halting the slew.
break;
case 1:
SerialPort.Command(":RG#");
//:RG# Set Slew rate to Guiding Rate (slowest)
//Returns: Nothing
break;
case 2:
SerialPort.Command(":RC#");
//:RC# Set Slew rate to Centering rate (2nd slowest)
//Returns: Nothing
break;
case 3:
SerialPort.Command(":RM#");
//:RM# Set Slew rate to Find Rate (2nd Fastest)
//Returns: Nothing
break;
case 4:
SerialPort.Command(":RS#");
//:RS# Set Slew rate to max (fastest)
//Returns: Nothing
break;
default:
throw new ASCOM.InvalidValueException($"Rate {rate} not supported");
}
switch (axis)
{
case TelescopeAxes.axisPrimary:
if (rate == 0)
{
_movingPrimary = false;
SerialPort.Command(":Qe#");
//:Qe# Halt eastward Slews
//Returns: Nothing
SerialPort.Command(":Qw#");
//:Qw# Halt westward Slews
//Returns: Nothing
}
else if (rate > 0)
{
SerialPort.Command(":Me#");
//:Me# Move Telescope East at current slew rate
//Returns: Nothing
_movingPrimary = true;
}
else
{
SerialPort.Command(":Mw#");
//:Mw# Move Telescope West at current slew rate
//Returns: Nothing
_movingPrimary = true;
}
break;
case TelescopeAxes.axisSecondary:
if (rate == 0)
{
_movingSecondary = false;
SerialPort.Command(":Qn#");
//:Qn# Halt northward Slews
//Returns: Nothing
SerialPort.Command(":Qs#");
//:Qs# Halt southward Slews
//Returns: Nothing
}
else if (rate > 0)
{
SerialPort.Command(":Mn#");
//:Mn# Move Telescope North at current slew rate
//Returns: Nothing
_movingSecondary = true;
}
else
{
SerialPort.Command(":Ms#");
//:Ms# Move Telescope South at current slew rate
//Returns: Nothing
_movingSecondary = true;
}
break;
default:
throw new ASCOM.MethodNotImplementedException("Can not move this axis.");
}
}
finally
{
SerialPort.Unlock();
}
}
public void FocuserHalt()
{
SerialPort.Command(":FQ#");
//:FQ# Halt Focuser Motion
//Returns: Nothing
}
public void FocuserMove(int newPosition)
{
//todo implement backlash compensation
//todo implement direction reverse
//todo implement dynamic braking
if (newPosition < -FocuserMaxIncrement || newPosition > FocuserMaxIncrement)
{
throw new ASCOM.InvalidValueException($"position out of range {-FocuserMaxIncrement} < {newPosition} < {FocuserMaxIncrement}");
}
if (newPosition == 0)
return;
if (newPosition > 0)
{
//desired move direction is out
MoveFocuser(true, Math.Abs(newPosition));
}
else
{
//desired move direction is in
MoveFocuser(false, Math.Abs(newPosition));
}
}
private void MoveFocuser(bool directionOut, int steps)
{
SerialPort.Command(directionOut ? ":F+#" : ":F-#");
//:F+# Start Focuser moving inward (toward objective)
//Returns: None
//:F-# Start Focuser moving outward (away from objective)
//Returns: None
Util.WaitForMilliseconds(steps);
FocuserHalt();
}
private void SetFocuserSpeedFastest()
{
SerialPort.Command(":FF#");
//:FF# Set Focus speed to fastest setting
//Returns: Nothing
}
private void SetFocuserSpeedSlowest()
{
SerialPort.Command(":FS#");
//:FS# Set Focus speed to slowest setting
//Returns: Nothing
}
private void SetFocuserSpeed( int speed)
{
if (speed < 1)
throw new ArgumentOutOfRangeException("speed is too low");
if (speed > 4)
throw new ArgumentOutOfRangeException("speed is too high");
SerialPort.Command($":F{speed}#");
//:F<n># Autostar, Autostar II set focuser speed to <n> where <n> is an ASCII digit 1..4
//Returns: Nothing
//All others Not Supported
}
//todo remove the polar parameter and split method into two.
private void DoSlewAsync( bool polar)
{
SerialPort.Lock();
try
{
switch (polar)
{
case true:
var response = SerialPort.CommandChar(":MS#");
//:MS# Slew to Target Object
//Returns:
//0 Slew is Possible
//1<string># Object Below Horizon w/string message
//2<string># Object Below Higher w/string message
switch (response)
{
case '0':
//We're slewing everything should be working just fine.
break;
case '1':
//Below Horizon
string belowHorizonMessage = SerialPort.ReadTerminated("#");
throw new ASCOM.InvalidOperationException(belowHorizonMessage);
case '2':
//Below Horizon
string belowMinimumElevationMessage = SerialPort.ReadTerminated("#");
throw new ASCOM.InvalidOperationException(belowMinimumElevationMessage);
default:
throw new ASCOM.DriverException("This error should not happen");
}
break;
case false:
var maResponse = SerialPort.CommandChar(":MA#");
//:MA# Autostar, LX 16”, Autostar II Slew to target Alt and Az
//Returns:
//0 - No fault
//1 Fault
// LX200 Not supported
if (maResponse == '1')
{
throw new ASCOM.InvalidOperationException("fault");
}
break;
}
}
finally
{
SerialPort.Unlock();
}
}
public bool UserNewerPulseGuiding { get; set; } = true; //todo make this a device setting
}
}