Merged in feature/SupportMeadeLXD600 (pull request #54)

Initial support of LXD600 mount: no StarPatch, no long format, no park support for this mount.

Approved-by: Colin Dawson
This commit is contained in:
Olivier Balitch
2025-12-30 22:21:40 +00:00
committed by Colin Dawson
5 changed files with 152 additions and 24 deletions
@@ -917,6 +917,7 @@ namespace Meade.net.Telescope.UnitTests
[TestCase("Autostar", "43Eg", true)] [TestCase("Autostar", "43Eg", true)]
[TestCase("LX200 Classic", "", false)] [TestCase("LX200 Classic", "", false)]
[TestCase("LXD600", TelescopeList.LXD600_6_12S, false)]
public void CanPark_Get_ReturnsTrue(string productName, string firmware, bool canPark) public void CanPark_Get_ReturnsTrue(string productName, string firmware, bool canPark)
{ {
ConnectTelescope(productName, firmware, $"{_testProperties.AlignmentMode}N0"); ConnectTelescope(productName, firmware, $"{_testProperties.AlignmentMode}N0");
+30 -22
View File
@@ -632,22 +632,26 @@ namespace ASCOM.Meade.net
{ {
var isStarPatch = false; var isStarPatch = false;
var firmwareVersionArray = SharedResourcesWrapper.FirmwareVersion.ToCharArray(); //LXD600 is based on LX-200 classic Meade and does not support StarPatch
if (firmwareVersionArray.Length > 1) if (SharedResourcesWrapper.ProductName != TelescopeList.LXD600)
{ {
//If last character is a number var firmwareVersionArray = SharedResourcesWrapper.FirmwareVersion.ToCharArray();
var lastChr = firmwareVersionArray[firmwareVersionArray.Length - 1]; if (firmwareVersionArray.Length > 1)
if (char.IsNumber(lastChr))
{ {
// Get case of second to last character //If last character is a number
var secondLastChar = firmwareVersionArray[firmwareVersionArray.Length - 2]; var lastChr = firmwareVersionArray[firmwareVersionArray.Length - 1];
// lower case = StarPatch, upper case = Meade if (char.IsNumber(lastChr))
isStarPatch = char.IsLower(secondLastChar); {
} // Get case of second to last character
else var secondLastChar = firmwareVersionArray[firmwareVersionArray.Length - 2];
{ // lower case = StarPatch, upper case = Meade
// lower case = Meade, upper case = StarPatch isStarPatch = char.IsLower(secondLastChar);
isStarPatch = char.IsUpper(lastChr); }
else
{
// lower case = Meade, upper case = StarPatch
isStarPatch = char.IsUpper(lastChr);
}
} }
} }
@@ -765,7 +769,8 @@ namespace ASCOM.Meade.net
private bool IsLongFormatSupported() private bool IsLongFormatSupported()
{ {
if (SharedResourcesWrapper.ProductName == TelescopeList.LX200CLASSIC) if ((SharedResourcesWrapper.ProductName == TelescopeList.LX200CLASSIC) ||
(SharedResourcesWrapper.ProductName == TelescopeList.LXD600))
{ {
return false; return false;
} }
@@ -1532,7 +1537,8 @@ namespace ASCOM.Meade.net
{ {
try try
{ {
var canPark = SharedResourcesWrapper.ProductName != TelescopeList.LX200CLASSIC; var canPark = ((SharedResourcesWrapper.ProductName != TelescopeList.LX200CLASSIC) &&
(SharedResourcesWrapper.ProductName != TelescopeList.LXD600));
LogMessage("CanPark", "Get - " + canPark); LogMessage("CanPark", "Get - " + canPark);
return canPark; return canPark;
} }
@@ -1810,7 +1816,8 @@ namespace ASCOM.Meade.net
TelescopeList.LX200GPS, TelescopeList.LX200GPS,
TelescopeList.RCX400, TelescopeList.RCX400,
TelescopeList.LX200CLASSIC, TelescopeList.LX200CLASSIC,
TelescopeList.LX800 TelescopeList.LX800,
TelescopeList.LXD600
}; };
return unParkableScopes.Contains(SharedResourcesWrapper.ProductName); return unParkableScopes.Contains(SharedResourcesWrapper.ProductName);
@@ -2294,7 +2301,8 @@ namespace ASCOM.Meade.net
var isTracking = Tracking; var isTracking = Tracking;
if (SharedResourcesWrapper.ProductName != TelescopeList.LX200CLASSIC) if ((SharedResourcesWrapper.ProductName != TelescopeList.LX200CLASSIC) &&
(SharedResourcesWrapper.ProductName != TelescopeList.LXD600))
{ {
SharedResourcesWrapper.SendBlind(Tl, "hP"); SharedResourcesWrapper.SendBlind(Tl, "hP");
//:hP# Autostar, Autostar II and LX 16" Slew to Park Position //:hP# Autostar, Autostar II and LX 16" Slew to Park Position
@@ -2302,7 +2310,7 @@ namespace ASCOM.Meade.net
} }
else else
{ {
LogMessage("Park", $"LX200 Classic does not support parking"); LogMessage("Park", $"LX200 Classic or LXD600 do not support parking");
throw new NotImplementedException("Park"); throw new NotImplementedException("Park");
} }
@@ -2953,7 +2961,7 @@ namespace ASCOM.Meade.net
LogMessage("TargetAltitude Set", $"{command}"); LogMessage("TargetAltitude Set", $"{command}");
var response = SharedResourcesWrapper.SendBool(Tl, command); var response = SharedResourcesWrapper.SendBool(Tl, command);
//:SasDD*MM# //:SasDD*MM#
// Set target object altitude to sDD*MM# or sDD*MMSS# [LX 16, Autostar, Autostar II] // Set target object altitude to sDD*MM# or sDD*MMSS# [LX 16, Autostar, Autostar II]
// Returns: // Returns:
// 1 Object within slew range // 1 Object within slew range
// 0 Object out of slew range // 0 Object out of slew range
@@ -2990,9 +2998,9 @@ namespace ASCOM.Meade.net
LogMessage("TargetAzimuth Set", $"{command}"); LogMessage("TargetAzimuth Set", $"{command}");
var response = SharedResourcesWrapper.SendBool(Tl, command); var response = SharedResourcesWrapper.SendBool(Tl, command);
//:SzDDD*MM# //:SzDDD*MM#
// Sets the target Object Azimuth[LX 16 and Autostar II only] // Sets the target Object Azimuth[LX 16 and Autostar II only]
// Returns: // Returns:
// 0 Invalid // 0 Invalid
// 1 - Valid // 1 - Valid
if (!response) if (!response)
throw new InvalidOperationException("Target Azimuth out of slew range."); throw new InvalidOperationException("Target Azimuth out of slew range.");
@@ -488,6 +488,109 @@ namespace Meade.net.UnitTests
} }
} }
[Test]
public void Connect_WhenDeviceIdIsSerialGVPGVTPartiallySupported_ThenConnectsAndSetsProductToLXD600()
{
string deviceId = "Serial";
string DriverId = "ASCOM.MeadeGeneric.Telescope";
string TraceStateDefault = "false";
string ComPortDefault = "COM1";
string SpeedDefault = "9600";
string DataBitsDefault = "8";
string StopBitsDefault = "One";
string HandshakeDefault = "None";
string ParityDefault = "None";
string RtsDtrEnabledDefault = "false";
string GuideRateProfileNameDefault = "10.077939"; //67% of sidereal rate
string PrecisionDefault = "Unchanged";
string ParkedBehaviourDefault = "No Coordinates";
string ParkedAltDefault = "0";
string ParkedAzimuthDefault = "180";
string FocalLengthDefault = "2000";
string ApertureAreaDefault = "32685";
string ApertureDiameterDefault = "203";
Mock<IProfileWrapper> profileWrapperMock = new Mock<IProfileWrapper>();
profileWrapperMock.SetupAllProperties();
profileWrapperMock.Setup(x => x.GetValue(DriverId, "Trace Level", string.Empty, TraceStateDefault))
.Returns(TraceStateDefault);
profileWrapperMock.Setup(x => x.GetValue(DriverId, "COM Port", string.Empty, ComPortDefault))
.Returns(ComPortDefault);
profileWrapperMock
.Setup(x => x.GetValue(DriverId, "Guide Rate Arc Seconds Per Second", string.Empty,
GuideRateProfileNameDefault)).Returns(GuideRateProfileNameDefault);
profileWrapperMock.Setup(x => x.GetValue(DriverId, "Precision", string.Empty, PrecisionDefault))
.Returns(PrecisionDefault);
profileWrapperMock.Setup(x =>
x.GetValue(DriverId, "Speed", string.Empty, SpeedDefault))
.Returns(() => SpeedDefault);
profileWrapperMock.Setup(x =>
x.GetValue(DriverId, "Data Bits", string.Empty, DataBitsDefault))
.Returns(() => DataBitsDefault);
profileWrapperMock.Setup(x =>
x.GetValue(DriverId, "Stop Bits", string.Empty, StopBitsDefault))
.Returns(() => StopBitsDefault);
profileWrapperMock.Setup(x =>
x.GetValue(DriverId, "Hand Shake", string.Empty, HandshakeDefault))
.Returns(() => HandshakeDefault);
profileWrapperMock.Setup(x =>
x.GetValue(DriverId, "Parity", string.Empty, ParityDefault))
.Returns(() => ParityDefault);
profileWrapperMock.Setup(x =>
x.GetValue(DriverId, "Rts / Dtr", string.Empty, RtsDtrEnabledDefault))
.Returns(() => RtsDtrEnabledDefault);
profileWrapperMock.Setup(x =>
x.GetValue(DriverId, "Parked Behaviour", string.Empty, ParkedBehaviourDefault))
.Returns(() => ParityDefault);
profileWrapperMock.Setup(x =>
x.GetValue(DriverId, "Parked Altitude", string.Empty, ParkedAltDefault))
.Returns(() => ParkedAltDefault);
profileWrapperMock.Setup(x =>
x.GetValue(DriverId, "Parked Azimuth", string.Empty, ParkedAzimuthDefault))
.Returns(() => ParkedAzimuthDefault);
profileWrapperMock.Setup(x =>
x.GetValue(DriverId, "Focal Length (mm)", string.Empty, FocalLengthDefault))
.Returns(() => FocalLengthDefault);
profileWrapperMock.Setup(x =>
x.GetValue(DriverId, "Aperture Area (mm²)", string.Empty, ApertureAreaDefault))
.Returns(() => ApertureAreaDefault);
profileWrapperMock.Setup(x =>
x.GetValue(DriverId, "Aperture Diameter (mm)", string.Empty, ApertureDiameterDefault))
.Returns(() => ApertureDiameterDefault);
Mock<IProfileFactory> profileFactoryMock = new Mock<IProfileFactory>();
profileFactoryMock.Setup(x => x.Create()).Returns(profileWrapperMock.Object);
SharedResources.ProfileFactory = profileFactoryMock.Object;
string serialPortReturn = string.Empty;
string productNameAndFirmwareVersion = TelescopeList.LXD600_6_12S;
_serialMock.Setup(x => x.Transmit("#:GVP#")).Callback(() => { serialPortReturn = productNameAndFirmwareVersion; });
_serialMock.Setup(x => x.Transmit("#:GVN#")).Callback(() => { serialPortReturn = productNameAndFirmwareVersion; });
_serialMock.Setup(x => x.Transmit("#:GG#")).Callback(() => { serialPortReturn = "0"; });
_serialMock.Setup(x => x.ReceiveTerminated("#")).Returns(() => serialPortReturn);
var connectionResult = SharedResources.Connect(deviceId, string.Empty, _traceLoggerMock.Object);
try
{
Assert.That(connectionResult.SameDevice, Is.EqualTo(1));
Assert.That(SharedResources.ProductName, Is.EqualTo(TelescopeList.LXD600));
Assert.That(SharedResources.FirmwareVersion, Is.EqualTo(productNameAndFirmwareVersion));
}
finally
{
SharedResources.Disconnect(deviceId, String.Empty);
}
}
[Test] [Test]
public void Connect_WhenDeviceIdIsSerialButGVPIsAutostar_ThenConnectsAndSetsProductToAutostarAndFirmware() public void Connect_WhenDeviceIdIsSerialButGVPIsAutostar_ThenConnectsAndSetsProductToAutostarAndFirmware()
{ {
+11 -2
View File
@@ -211,7 +211,7 @@ namespace ASCOM.Meade.net
private const string ParkedAltName = "Parked Altitude"; private const string ParkedAltName = "Parked Altitude";
private const string ParkedAzimuthName = "Parked Azimuth"; private const string ParkedAzimuthName = "Parked Azimuth";
private const string FocalLengthName = "Focal Length (mm)"; private const string FocalLengthName = "Focal Length (mm)";
private const string ApertureAreaName = "Aperture Area (mm²)"; private const string ApertureAreaName = "Aperture Area (mm)";
private const string ApertureDiameterName = "Aperture Diameter (mm)"; private const string ApertureDiameterName = "Aperture Diameter (mm)";
public static void WriteProfile(ProfileProperties profileProperties) public static void WriteProfile(ProfileProperties profileProperties)
@@ -470,11 +470,20 @@ namespace ASCOM.Meade.net
} }
} }
SharedSerial.Connected = true; SharedSerial.Connected = true;
try try
{ {
ProductName = SendString(traceLogger, "GVP"); ProductName = SendString(traceLogger, "GVP");
FirmwareVersion = SendString(traceLogger, "GVN"); FirmwareVersion = SendString(traceLogger, "GVN");
//LXD600 mount uses LX-200 based firmware. When both above commands are sent to the scope,
//they both return the "6.12S" string.
//There may be other values of the string applicable to LXD600 mount or other values of the string
//may allow identify LXD650/LXD700 or other scopes. May require further adjustments.
if (String.Equals(ProductName, FirmwareVersion) && String.Equals(ProductName, TelescopeList.LXD600_6_12S))
{
ProductName = TelescopeList.LXD600;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
+7
View File
@@ -45,6 +45,13 @@
public const string LX200CLASSIC = "LX200 Classic"; //GVP command is not supported! public const string LX200CLASSIC = "LX200 Classic"; //GVP command is not supported!
#endregion #endregion
#region LXD600
public const string LXD600 = "LXD600";
public const string LXD600_6_12S = "6.12S";
#endregion
#region RCX400 #region RCX400
// ReSharper disable once InconsistentNaming // ReSharper disable once InconsistentNaming
public const string RCX400 = "RCX400"; public const string RCX400 = "RCX400";