From c24b4c116129779d22c5d93ee4efe94eaf41f43e Mon Sep 17 00:00:00 2001 From: Olivier Balitch Date: Sat, 27 Sep 2025 11:46:00 +0200 Subject: [PATCH] Initial support of LXD600 mount: no StarPatch, no long format, no park support for this mount. --- .../TelescopeUnitTests.cs | 1 + Meade.net.Telescope/Telescope.cs | 52 +++++---- .../SharedResourcesUnitTests.cs | 103 ++++++++++++++++++ Meade.net/SharedResources.cs | 13 ++- Meade.net/TelescopeList.cs | 7 ++ 5 files changed, 152 insertions(+), 24 deletions(-) diff --git a/Meade.net.Telescope.UnitTests/TelescopeUnitTests.cs b/Meade.net.Telescope.UnitTests/TelescopeUnitTests.cs index 5da6cd2..c1bbbf3 100644 --- a/Meade.net.Telescope.UnitTests/TelescopeUnitTests.cs +++ b/Meade.net.Telescope.UnitTests/TelescopeUnitTests.cs @@ -917,6 +917,7 @@ namespace Meade.net.Telescope.UnitTests [TestCase("Autostar", "43Eg", true)] [TestCase("LX200 Classic", "", false)] + [TestCase("LXD600", TelescopeList.LXD600_6_12S, false)] public void CanPark_Get_ReturnsTrue(string productName, string firmware, bool canPark) { ConnectTelescope(productName, firmware, $"{_testProperties.AlignmentMode}N0"); diff --git a/Meade.net.Telescope/Telescope.cs b/Meade.net.Telescope/Telescope.cs index 6ab350c..19bc187 100644 --- a/Meade.net.Telescope/Telescope.cs +++ b/Meade.net.Telescope/Telescope.cs @@ -632,22 +632,26 @@ namespace ASCOM.Meade.net { var isStarPatch = false; - var firmwareVersionArray = SharedResourcesWrapper.FirmwareVersion.ToCharArray(); - if (firmwareVersionArray.Length > 1) + //LXD600 is based on LX-200 classic Meade and does not support StarPatch + if (SharedResourcesWrapper.ProductName != TelescopeList.LXD600) { - //If last character is a number - var lastChr = firmwareVersionArray[firmwareVersionArray.Length - 1]; - if (char.IsNumber(lastChr)) + var firmwareVersionArray = SharedResourcesWrapper.FirmwareVersion.ToCharArray(); + if (firmwareVersionArray.Length > 1) { - // Get case of second to last character - var secondLastChar = firmwareVersionArray[firmwareVersionArray.Length - 2]; - // lower case = StarPatch, upper case = Meade - isStarPatch = char.IsLower(secondLastChar); - } - else - { - // lower case = Meade, upper case = StarPatch - isStarPatch = char.IsUpper(lastChr); + //If last character is a number + var lastChr = firmwareVersionArray[firmwareVersionArray.Length - 1]; + if (char.IsNumber(lastChr)) + { + // Get case of second to last character + var secondLastChar = firmwareVersionArray[firmwareVersionArray.Length - 2]; + // lower case = StarPatch, upper case = Meade + isStarPatch = char.IsLower(secondLastChar); + } + else + { + // lower case = Meade, upper case = StarPatch + isStarPatch = char.IsUpper(lastChr); + } } } @@ -765,7 +769,8 @@ namespace ASCOM.Meade.net private bool IsLongFormatSupported() { - if (SharedResourcesWrapper.ProductName == TelescopeList.LX200CLASSIC) + if ((SharedResourcesWrapper.ProductName == TelescopeList.LX200CLASSIC) || + (SharedResourcesWrapper.ProductName == TelescopeList.LXD600)) { return false; } @@ -1532,7 +1537,8 @@ namespace ASCOM.Meade.net { try { - var canPark = SharedResourcesWrapper.ProductName != TelescopeList.LX200CLASSIC; + var canPark = ((SharedResourcesWrapper.ProductName != TelescopeList.LX200CLASSIC) && + (SharedResourcesWrapper.ProductName != TelescopeList.LXD600)); LogMessage("CanPark", "Get - " + canPark); return canPark; } @@ -1810,7 +1816,8 @@ namespace ASCOM.Meade.net TelescopeList.LX200GPS, TelescopeList.RCX400, TelescopeList.LX200CLASSIC, - TelescopeList.LX800 + TelescopeList.LX800, + TelescopeList.LXD600 }; return unParkableScopes.Contains(SharedResourcesWrapper.ProductName); @@ -2294,7 +2301,8 @@ namespace ASCOM.Meade.net var isTracking = Tracking; - if (SharedResourcesWrapper.ProductName != TelescopeList.LX200CLASSIC) + if ((SharedResourcesWrapper.ProductName != TelescopeList.LX200CLASSIC) && + (SharedResourcesWrapper.ProductName != TelescopeList.LXD600)) { SharedResourcesWrapper.SendBlind(Tl, "hP"); //:hP# Autostar, Autostar II and LX 16" Slew to Park Position @@ -2302,7 +2310,7 @@ namespace ASCOM.Meade.net } else { - LogMessage("Park", $"LX200 Classic does not support parking"); + LogMessage("Park", $"LX200 Classic or LXD600 do not support parking"); throw new NotImplementedException("Park"); } @@ -2953,7 +2961,7 @@ namespace ASCOM.Meade.net LogMessage("TargetAltitude Set", $"{command}"); var response = SharedResourcesWrapper.SendBool(Tl, command); //:SasDD*MM# - // Set target object altitude to sDD*MM# or sDD*MM’SS# [LX 16”, Autostar, Autostar II] + // Set target object altitude to sDD*MM# or sDD*MM�SS# [LX 16�, Autostar, Autostar II] // Returns: // 1 Object within slew range // 0 Object out of slew range @@ -2990,9 +2998,9 @@ namespace ASCOM.Meade.net LogMessage("TargetAzimuth Set", $"{command}"); var response = SharedResourcesWrapper.SendBool(Tl, command); //: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: - // 0 – Invalid + // 0 � Invalid // 1 - Valid if (!response) throw new InvalidOperationException("Target Azimuth out of slew range."); diff --git a/Meade.net.UnitTests/SharedResourcesUnitTests.cs b/Meade.net.UnitTests/SharedResourcesUnitTests.cs index 740f652..5649700 100644 --- a/Meade.net.UnitTests/SharedResourcesUnitTests.cs +++ b/Meade.net.UnitTests/SharedResourcesUnitTests.cs @@ -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 profileWrapperMock = new Mock(); + 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 profileFactoryMock = new Mock(); + 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] public void Connect_WhenDeviceIdIsSerialButGVPIsAutostar_ThenConnectsAndSetsProductToAutostarAndFirmware() { diff --git a/Meade.net/SharedResources.cs b/Meade.net/SharedResources.cs index 3544a53..2651c52 100644 --- a/Meade.net/SharedResources.cs +++ b/Meade.net/SharedResources.cs @@ -211,7 +211,7 @@ namespace ASCOM.Meade.net private const string ParkedAltName = "Parked Altitude"; private const string ParkedAzimuthName = "Parked Azimuth"; 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)"; public static void WriteProfile(ProfileProperties profileProperties) @@ -470,11 +470,20 @@ namespace ASCOM.Meade.net } } SharedSerial.Connected = true; - + try { ProductName = SendString(traceLogger, "GVP"); 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) { diff --git a/Meade.net/TelescopeList.cs b/Meade.net/TelescopeList.cs index 15c8307..2a410fb 100644 --- a/Meade.net/TelescopeList.cs +++ b/Meade.net/TelescopeList.cs @@ -45,6 +45,13 @@ public const string LX200CLASSIC = "LX200 Classic"; //GVP command is not supported! #endregion + #region LXD600 + public const string LXD600 = "LXD600"; + + public const string LXD600_6_12S = "6.12S"; + + #endregion + #region RCX400 // ReSharper disable once InconsistentNaming public const string RCX400 = "RCX400";