From 96aa175ca5bb2f6bfadc5bfacdab43e30398fb4a Mon Sep 17 00:00:00 2001 From: olivier Date: Tue, 14 Jul 2026 23:29:41 +0200 Subject: [PATCH 1/2] Add OpenAstroTech (OAT) polar alignment system support - New OATAlignmentDevice/OATAlignmentVM/OATAlignment classes, integrating the LX200 OpenAstroTech INDI driver (POLAR_AZ/POLAR_ALT) as a third selectable system alongside UPAS/OAPA. - AutomatedAdjustmentController: raise the per-move safety ceiling from a fixed 5 arcmin to a 4 deg emergency backstop, letting the existing 0.5/0.25/0.125-damped least-squares correction stay proportional to the measured error instead of being artificially truncated. - Stall detection: abort automated adjustments with a clear notification if several consecutive commanded moves produce no measurable change in the polar error, instead of looping indefinitely. - Tested end-to-end on real sky (Raspberry Pi / PINS + Touch-N-Stars). --- .../AutomatedAdjustmentController.cs | 86 ++++++- PolarAlignment/IPolarAlignmentSystem.cs | 3 +- PolarAlignment/Instructions/PolarAlignment.cs | 8 + .../NINA.Plugins.PolarAlignment.csproj | 21 +- PolarAlignment/OAT/OATAlignment.cs | 131 ++++++++++ PolarAlignment/OAT/OATAlignmentDevice.cs | 224 ++++++++++++++++++ PolarAlignment/OAT/OATAlignmentVM.cs | 133 +++++++++++ PolarAlignment/PolarAlignmentPlugin.cs | 125 +++++----- .../Properties/Settings.Designer.cs | 166 ++++++++++--- PolarAlignment/Properties/Settings.settings | 26 ++ PolarAlignment/TPAPAVM.cs | 20 ++ .../UniversalPolarAlignmentBaseVM.cs | 7 + 12 files changed, 834 insertions(+), 116 deletions(-) create mode 100644 PolarAlignment/OAT/OATAlignment.cs create mode 100644 PolarAlignment/OAT/OATAlignmentDevice.cs create mode 100644 PolarAlignment/OAT/OATAlignmentVM.cs diff --git a/PolarAlignment/AutomatedAdjustmentController.cs b/PolarAlignment/AutomatedAdjustmentController.cs index b6766ce..01895ee 100644 --- a/PolarAlignment/AutomatedAdjustmentController.cs +++ b/PolarAlignment/AutomatedAdjustmentController.cs @@ -23,10 +23,23 @@ internal sealed class AutomatedAdjustmentController { /// private const double MinimumMoveMagnitude = 0.05; /// - /// Maximum correction magnitude issued in a single solve or move cycle. - /// Large residuals are intentionally corrected over multiple iterations. + /// Absolute emergency ceiling on any single move, regardless of what the learned + /// response model predicts. This exists purely as a backstop against a badly wrong or + /// still-unconverged model (e.g. a near-singular fit early on) proposing an absurd + /// command — it is intentionally far above any move that should occur in normal + /// operation. + /// + /// The REAL, proportional limiting factor is the 0.5/0.25/0.125 damping already applied + /// to the raw least-squares solution in : since that + /// raw solution already predicts (given an accurate model) a command that fully zeroes + /// the current residual, a candidate at 50% of it is inherently proportional to the + /// current error — large errors produce large (but damped) corrections, small errors + /// produce small ones. A low fixed ceiling here (previously 5.0, ≈5 arcmin at gear ratio + /// 1) defeated that proportionality and forced many more solve/move cycles than + /// necessary for large initial residuals, even though hardware like OAT can safely move + /// several degrees in one command. /// - private const double MaximumMoveMagnitude = 5.0; + private const double MaximumMoveMagnitude = 240.0; // ≈4° at gear ratio 1.0 — emergency backstop only /// /// Small damping term used as a numerical floor and as regularization when /// inverting the local response model. @@ -47,13 +60,42 @@ internal sealed class AutomatedAdjustmentController { /// private const int MaxSamples = 12; + /// + /// Below this measured total-error change (in degrees), a commanded move that was + /// reported as electrically/protocol-successful is treated as having produced no + /// detectable physical motion. This is deliberately small — well under any sane + /// single-axis move at any reasonable gear ratio — so it only fires on genuine + /// non-movement, not on a merely small correction. + /// + private const double StallMovementThresholdDegrees = 0.0015; // ≈0.1 arcmin + + /// + /// Number of consecutive "commanded a real move but measured essentially zero change" + /// events required before declaring a stall. Requiring several in a row avoids false + /// positives from a single noisy/failed plate solve. + /// + private const int MaxConsecutiveStalls = 3; + private readonly Queue samples = new Queue(); private AutomatedAdjustmentObservation currentObservation; private PendingPlan pendingPlan; private bool hasObservation; + private int consecutiveStalls; public int SampleCount => samples.Count; + /// + /// True once several consecutive commanded moves have produced no measurable change in + /// the observed polar error — the physical signature of a motor stuck at an end-stop (or + /// otherwise mechanically unable to move), even though the driver/INDI layer reported the + /// command as having completed successfully. Callers should stop issuing further moves + /// when this is set. + /// + public bool StallDetected { get; private set; } + + /// Human-readable explanation of the last detected stall, for logging/notification. + public string StallReason { get; private set; } + /// /// Gets whether the current sample set is rich enough and well-conditioned enough /// to estimate a two-axis local response model. @@ -70,6 +112,9 @@ public void Reset() { currentObservation = null; pendingPlan = null; hasObservation = false; + consecutiveStalls = 0; + StallDetected = false; + StallReason = null; } /// @@ -89,6 +134,8 @@ public void UpdateObservation(double azimuthErrorDegrees, double altitudeErrorDe deltaAzimuth, deltaAltitude)); + CheckForStall(pendingPlan.Plan, Math.Sqrt(deltaAzimuth * deltaAzimuth + deltaAltitude * deltaAltitude)); + if (!pendingPlan.Plan.IsProbe && latestObservation.TotalErrorDegrees > pendingPlan.BeforeMoveObservation.TotalErrorDegrees * ModelResetWorseningFactor) { samples.Clear(); } @@ -108,6 +155,10 @@ public void UpdateObservation(double azimuthErrorDegrees, double altitudeErrorDe /// move that is predicted to reduce the residual error norm. /// public AutomatedAdjustmentPlan CreatePlan() { + if (StallDetected) { + return AutomatedAdjustmentPlan.Skip(StallReason ?? "Mount stall detected — automated adjustments paused."); + } + if (!hasObservation) { return AutomatedAdjustmentPlan.Skip("No continuous error measurement is available yet."); } @@ -142,6 +193,35 @@ public void NoteFailedExecution() { pendingPlan = null; } + /// + /// Compares a commanded move against the measured error change it produced. A real move + /// (above the deadband) that yields essentially zero measured change, repeated several + /// times in a row, is the observable signature of a motor that physically cannot move + /// (end-stop reached, mechanical jam, disconnected coupler, etc.) — the driver can report + /// the command as "done" without the mount having actually moved. + /// + private void CheckForStall(AutomatedAdjustmentPlan commandedPlan, double measuredChangeDegrees) { + var commandedMagnitude = Math.Sqrt(commandedPlan.XMagnitude * commandedPlan.XMagnitude + + commandedPlan.YMagnitude * commandedPlan.YMagnitude); + + if (commandedMagnitude < MinimumMoveMagnitude) { + // Command was itself negligible — not a meaningful test of whether the mount moves. + return; + } + + if (measuredChangeDegrees < StallMovementThresholdDegrees) { + consecutiveStalls++; + if (consecutiveStalls >= MaxConsecutiveStalls) { + StallDetected = true; + StallReason = $"Commanded {commandedMagnitude:F2} units but measured error changed by only " + + $"{measuredChangeDegrees * 60.0:F3}' over {consecutiveStalls} consecutive moves — " + + "the mount does not appear to be responding (possible end-stop or mechanical stall)."; + } + } else { + consecutiveStalls = 0; + } + } + private void AddSample(ResponseSample sample) { samples.Enqueue(sample); while (samples.Count > MaxSamples) { diff --git a/PolarAlignment/IPolarAlignmentSystem.cs b/PolarAlignment/IPolarAlignmentSystem.cs index fd030c1..e12499b 100644 --- a/PolarAlignment/IPolarAlignmentSystem.cs +++ b/PolarAlignment/IPolarAlignmentSystem.cs @@ -6,7 +6,8 @@ namespace NINA.Plugins.PolarAlignment { public enum PolarAlignmentSystemType { None, UPAS, - OAPA + OAPA, + OAT // OpenAstroExplorer via INDI LX200 OpenAstroTech driver } public enum Axis { diff --git a/PolarAlignment/Instructions/PolarAlignment.cs b/PolarAlignment/Instructions/PolarAlignment.cs index 552f428..cba3fc5 100644 --- a/PolarAlignment/Instructions/PolarAlignment.cs +++ b/PolarAlignment/Instructions/PolarAlignment.cs @@ -629,6 +629,14 @@ await messageBroker.Publish( } localCTS.Token.ThrowIfCancellationRequested(); await TPAPAVM.MoveCloser(progress, localCTS.Token); + + if (TPAPAVM.AutomatedAdjustmentStalled) { + Logger.Error($"Automated polar alignment adjustment aborted: {TPAPAVM.AutomatedAdjustmentStallReason}"); + Notification.ShowError( + $"Polar alignment automated adjustments stopped: the mount does not appear to be responding to move commands.{Environment.NewLine}" + + "Check for a mechanical end-stop, disconnected motor, or driver fault, then restart the process."); + localCTS.Cancel(); + } } else { Logger.Warning("Skipping error publication and automated correction because the continuous estimate was unstable."); } diff --git a/PolarAlignment/NINA.Plugins.PolarAlignment.csproj b/PolarAlignment/NINA.Plugins.PolarAlignment.csproj index ff37caf..ee1e561 100644 --- a/PolarAlignment/NINA.Plugins.PolarAlignment.csproj +++ b/PolarAlignment/NINA.Plugins.PolarAlignment.csproj @@ -1,30 +1,31 @@ - + net10.0 Library true - true - true - true + true + true + true Three Point Polar Alignment Three Point Polar Alignment Stefan Berg @isbeorn NINA.Plugins Three Point Polar Alignment almost anywhere in the sky Copyright © 2021-2025 - $(DefaultItemExcludes);**/*.xaml.cs;**/*.xaml + $(DefaultItemExcludes);**/*.xaml.cs;**/*.xaml 2.2.6.2 - - + + - + + @@ -41,10 +42,10 @@ Settings.Designer.cs - + - + diff --git a/PolarAlignment/OAT/OATAlignment.cs b/PolarAlignment/OAT/OATAlignment.cs new file mode 100644 index 0000000..c095d45 --- /dev/null +++ b/PolarAlignment/OAT/OATAlignment.cs @@ -0,0 +1,131 @@ +using NINA.Core.Utility; +using NINA.INDI; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace NINA.Plugins.PolarAlignment.OAT { + + /// + /// implementation for the OpenAstroExplorer + /// mount controlled by the INDI LX200 OpenAstroTech driver. + /// + /// Unlike the serial-port-based OAPA / Avalon systems, the OAT's polar alignment + /// motors are embedded in the telescope INDI driver. This class routes movement + /// commands through the already-connected INDIClient rather than opening a new + /// serial port. + /// + /// Axis mapping: + /// XAxis → Azimuth (INDI: POLAR_AZ / element OAT_POLAR_AZ, arcmin) + /// YAxis → Altitude (INDI: POLAR_ALT / element OAT_POLAR_ALT, arcmin) + /// + /// Gear ratio: + /// Default is 1.0 for both axes (1 logical unit = 1 arcminute). + /// The AutomatedAdjustmentController learns the actual response empirically, + /// so this only affects the initial probe step size. + /// + public sealed class OATAlignment : IPolarAlignmentSystem { + + // Maximum time to wait for a single motor move before declaring a timeout. + private static readonly TimeSpan DefaultMoveTimeout = TimeSpan.FromSeconds(120); + + private readonly OATAlignmentDevice _device; + + // Accumulated logical-unit positions (arcmin when gearRatio = 1). + private float _xPosition = 0f; // AZ + private float _yPosition = 0f; // ALT + + // ── Construction ───────────────────────────────────────────────────── + + /// + /// INDI device name as reported by indiserver, e.g. "LX200 OpenAstroTech". + /// + /// + /// Thrown when INDI is not connected or the device is unknown (telescope not connected). + /// + public OATAlignment(string deviceName) { + if (!INDIClient.Instance.IsConnected) + throw new InvalidOperationException( + "INDI server is not connected. Connect the OAT telescope in NINA first."); + + if (!INDIClient.Instance.IsDeviceKnown(deviceName)) + throw new InvalidOperationException( + $"INDI device '{deviceName}' is not connected. " + + "Make sure the OAT telescope is connected in NINA."); + + _device = new OATAlignmentDevice(deviceName); + Logger.Info($"[OATAlignment] Connected to INDI device '{deviceName}'."); + } + + // ── IPolarAlignmentSystem ──────────────────────────────────────────── + + public bool Connected => + INDIClient.Instance.IsConnected && + INDIClient.Instance.IsDeviceKnown(_device.Id); + + public string Status => _device.AlignmentStatus; + + // Accumulated positions in logical units (arcmin for default gearRatio=1) + public float XPosition1 => _xPosition / XGearRatio; // AZ + public float YPosition1 => _yPosition / YGearRatio; // ALT + public float ZPosition1 => 0f; + + public float XGearRatio { get; set; } = 1f; + public float YGearRatio { get; set; } = 1f; + public float ZGearRatio { get; set; } = 1f; + + public LastDirection XLastDirection { get; private set; } = LastDirection.Positive; + public LastDirection YLastDirection { get; private set; } = LastDirection.Positive; + public LastDirection ZLastDirection { get; private set; } = LastDirection.Positive; + + /// + /// Moves the specified axis by logical units + /// (arcmin when gearRatio = 1) and waits until the INDI driver reports + /// the move is complete. + /// + public async Task MoveRelative(Axis axis, int speed, float position, CancellationToken token) { + // 'speed' is intentionally ignored — the OAT manages its own motor speed. + switch (axis) { + case Axis.XAxis: { + XLastDirection = position >= 0 ? LastDirection.Positive : LastDirection.Negative; + double arcmin = position * XGearRatio; + _xPosition += (float)arcmin; + using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(token); + timeoutCts.CancelAfter(DefaultMoveTimeout); + await _device.SendMoveAz(arcmin, timeoutCts.Token); + break; + } + case Axis.YAxis: { + YLastDirection = position >= 0 ? LastDirection.Positive : LastDirection.Negative; + double arcmin = position * YGearRatio; + _yPosition += (float)arcmin; + using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(token); + timeoutCts.CancelAfter(DefaultMoveTimeout); + await _device.SendMoveAlt(arcmin, timeoutCts.Token); + break; + } + default: + throw new ArgumentException($"Axis {axis} is not supported by OAT polar alignment."); + } + } + + /// + /// Moves the axis to an absolute logical-unit position by computing the delta + /// from the internally tracked current position. + /// + public Task MoveAbsolute(Axis axis, int speed, float position, CancellationToken token) { + float currentPos = axis == Axis.XAxis ? XPosition1 : YPosition1; + float delta = position - currentPos; + return MoveRelative(axis, speed, delta, token); + } + + /// + /// No-op: property state is refreshed automatically via INDI event callbacks. + /// + public Task RefreshStatus(CancellationToken token) => Task.CompletedTask; + + public void Dispose() { + _device?.Dispose(); + } + } +} diff --git a/PolarAlignment/OAT/OATAlignmentDevice.cs b/PolarAlignment/OAT/OATAlignmentDevice.cs new file mode 100644 index 0000000..5760ea5 --- /dev/null +++ b/PolarAlignment/OAT/OATAlignmentDevice.cs @@ -0,0 +1,224 @@ +using NINA.Core.Utility; +using NINA.INDI; +using NINA.INDI.Devices; +using NINA.INDI.Enums; +using NINA.INDI.Protocol; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace NINA.Plugins.PolarAlignment.OAT { + + /// + /// Lightweight proxy that registers with the already-running LX200 OAT INDI driver + /// and forwards polar alignment motor commands via the POLAR_ALT / POLAR_AZ INDI + /// number properties, without touching the telescope CONNECTION switch. + /// + /// INDI property mapping (from lx200_OpenAstroTech.cpp): + /// POLAR_ALT / element OAT_POLAR_ALT — relative altitude move in arcminutes + /// underlying command: :MAL{value}# range: -140..140 arcmin + /// POLAR_AZ / element OAT_POLAR_AZ — relative azimuth move in arcminutes + /// underlying command: :MAZ{value}# range: -320..320 arcmin + /// + /// Completion is signalled by the driver broadcasting state=Ok after state=Busy on + /// the corresponding property (some OAT firmware versions skip Busy and go directly + /// to Ok). We use polling rather than a TCS callback to avoid a race between + /// stale GetProperties responses and the actual command response. + /// + internal sealed class OATAlignmentDevice : INDIDevice { + + public const string DefaultDeviceName = "LX200 OpenAstroTech"; + + // Maximum time to wait for a move to complete (Arduino may be slow) + private static readonly TimeSpan DefaultMoveTimeout = TimeSpan.FromSeconds(90); + + // Set to the UTC time just before we call SendProperty; used in callbacks + // to distinguish stale GetProperties echoes from command responses. + // Note: 'volatile' is not permitted on 'long' fields in C# (CS0677) — atomicity + // for writes is already provided by Interlocked.Exchange at every write site. + private long _azCommandSentAtTicks = 0; + private long _altCommandSentAtTicks = 0; + + // Completion sources for in-flight moves; null when no move is pending. + private volatile TaskCompletionSource _altMoveCompletion; + private volatile TaskCompletionSource _azMoveCompletion; + + public OATAlignmentDevice(string deviceName) + : base(new INDIDeviceInfo { + Id = deviceName, + Name = deviceName, + Driver = "indi_lx200_OpenAstroTech" + }) { + } + + // ── Public state ──────────────────────────────────────────────────── + + public bool IsAltMoving => GetNumberProperty("POLAR_ALT")?.State == PropertyState.Busy; + public bool IsAzMoving => GetNumberProperty("POLAR_AZ")?.State == PropertyState.Busy; + public bool IsMoving => IsAltMoving || IsAzMoving; + + public string AlignmentStatus { + get { + if (IsMoving) return "Moving"; + var altProp = GetNumberProperty("POLAR_ALT"); + if (altProp == null) return "Properties not yet received"; + return altProp.State == PropertyState.Alert ? "Alert" : "Idle"; + } + } + + // ── Move commands ──────────────────────────────────────────────────── + + /// + /// Sends a relative altitude move of arcminutes and + /// returns a Task that completes when the driver reports the move is finished. + /// + public async Task SendMoveAlt(double arcmin, CancellationToken token) { + Logger.Info($"[OATAlignment] SendMoveAlt {arcmin:F3} arcmin"); + + // Note state BEFORE sending so we can detect when state changes + var stateBefore = GetNumberProperty("POLAR_ALT")?.State; + Logger.Debug($"[OATAlignment] POLAR_ALT state before command: {stateBefore}"); + + // Create TCS and stamp the send time BEFORE registering the callback window + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var reg = token.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: false); + tcs.Task.ContinueWith(_ => reg.Dispose(), TaskContinuationOptions.ExecuteSynchronously); + + Interlocked.Exchange(ref _altCommandSentAtTicks, DateTime.UtcNow.Ticks); + _altMoveCompletion = tcs; + + INDIClient.Instance.SendProperty(BuildNumberProperty("POLAR_ALT", "OAT_POLAR_ALT", arcmin)); + Logger.Debug("[OATAlignment] POLAR_ALT setNumber sent, awaiting completion callback..."); + + try { + using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(token); + timeoutCts.CancelAfter(DefaultMoveTimeout); + var timeoutReg = timeoutCts.Token.Register( + () => tcs.TrySetException(new TimeoutException("POLAR_ALT move timed out")), + useSynchronizationContext: false); + try { await tcs.Task; } + finally { timeoutReg.Dispose(); } + } catch { + _altMoveCompletion = null; + throw; + } + + Logger.Debug($"[OATAlignment] POLAR_ALT move complete. New state: {GetNumberProperty("POLAR_ALT")?.State}"); + } + + /// + /// Sends a relative azimuth move of arcminutes and + /// returns a Task that completes when the driver reports the move is finished. + /// + public async Task SendMoveAz(double arcmin, CancellationToken token) { + Logger.Info($"[OATAlignment] SendMoveAz {arcmin:F3} arcmin"); + + // Note state BEFORE sending so we can detect when state changes + var stateBefore = GetNumberProperty("POLAR_AZ")?.State; + Logger.Debug($"[OATAlignment] POLAR_AZ state before command: {stateBefore}"); + + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var reg = token.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: false); + tcs.Task.ContinueWith(_ => reg.Dispose(), TaskContinuationOptions.ExecuteSynchronously); + + Interlocked.Exchange(ref _azCommandSentAtTicks, DateTime.UtcNow.Ticks); + _azMoveCompletion = tcs; + + INDIClient.Instance.SendProperty(BuildNumberProperty("POLAR_AZ", "OAT_POLAR_AZ", arcmin)); + Logger.Debug("[OATAlignment] POLAR_AZ setNumber sent, awaiting completion callback..."); + + try { + using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(token); + timeoutCts.CancelAfter(DefaultMoveTimeout); + var timeoutReg = timeoutCts.Token.Register( + () => tcs.TrySetException(new TimeoutException("POLAR_AZ move timed out")), + useSynchronizationContext: false); + try { await tcs.Task; } + finally { timeoutReg.Dispose(); } + } catch { + _azMoveCompletion = null; + throw; + } + + Logger.Debug($"[OATAlignment] POLAR_AZ move complete. New state: {GetNumberProperty("POLAR_AZ")?.State}"); + } + + // ── INDI property callbacks ────────────────────────────────────────── + + public override void OnNumberPropertyUpdated(INDINumberProperty p) { + // Log every POLAR_ALT / POLAR_AZ callback regardless of whether a move is pending + if (p.Name == "POLAR_ALT" || p.Name == "POLAR_AZ") { + var hasTcs = p.Name == "POLAR_ALT" ? _altMoveCompletion != null : _azMoveCompletion != null; + var sentAt = p.Name == "POLAR_ALT" ? _altCommandSentAtTicks : _azCommandSentAtTicks; + var lagMs = sentAt == 0 ? -1.0 + : (DateTime.UtcNow.Ticks - sentAt) / TimeSpan.TicksPerMillisecond; + Logger.Debug( + $"[OATAlignment] OnNumberPropertyUpdated {p.Name} state={p.State} " + + $"hasPendingTCS={hasTcs} lagSinceCommand={lagMs:F0}ms"); + } + + // The driver sets state=Busy when it starts the motor, then Ok (or Alert) + // when the motor stops. We only resolve the TCS on terminal states. + // + // Guard: ignore callbacks that arrive before we ever sent a command + // (lagSinceCommand < 0) — these are stale GetProperties echoes. + if (p.Name == "POLAR_ALT" && p.State != PropertyState.Busy) { + // Only accept if we already sent a command (commandSentAt != 0) + if (_altCommandSentAtTicks == 0) { + Logger.Debug("[OATAlignment] Ignoring POLAR_ALT callback — no command sent yet (stale GetProperties)."); + return; + } + var tcs = _altMoveCompletion; + _altMoveCompletion = null; + if (p.State == PropertyState.Alert) + tcs?.TrySetException(new InvalidOperationException("POLAR_ALT driver returned Alert state.")); + else + tcs?.TrySetResult(true); + } + else if (p.Name == "POLAR_AZ" && p.State != PropertyState.Busy) { + if (_azCommandSentAtTicks == 0) { + Logger.Debug("[OATAlignment] Ignoring POLAR_AZ callback — no command sent yet (stale GetProperties)."); + return; + } + var tcs = _azMoveCompletion; + _azMoveCompletion = null; + if (p.State == PropertyState.Alert) + tcs?.TrySetException(new InvalidOperationException("POLAR_AZ driver returned Alert state.")); + else + tcs?.TrySetResult(true); + } + } + + public override void OnTextPropertyUpdated(INDITextProperty p) { } + public override void OnSwitchPropertyUpdated(INDISwitchProperty p) { } + + // ── Dispose ────────────────────────────────────────────────────────── + + /// + /// Unregisters from INDIClient WITHOUT sending CONNECTION=Off so the telescope + /// session is not affected. + /// + public new void Dispose() { + try { + _altMoveCompletion?.TrySetCanceled(); + _azMoveCompletion?.TrySetCanceled(); + INDIClient.Instance.UnregisterDevice(this); + } catch (Exception ex) { + Logger.Error($"[OATAlignment] Dispose error: {ex.Message}"); + } + } + + // ── Private helpers ────────────────────────────────────────────────── + + private INDINumberProperty BuildNumberProperty(string propName, string elementName, double value) { + return new INDINumberProperty { + DeviceName = Id, + Name = propName, + Numbers = new List { + new INDINumber { Name = elementName, Value = value } + } + }; + } + } +} diff --git a/PolarAlignment/OAT/OATAlignmentVM.cs b/PolarAlignment/OAT/OATAlignmentVM.cs new file mode 100644 index 0000000..e155281 --- /dev/null +++ b/PolarAlignment/OAT/OATAlignmentVM.cs @@ -0,0 +1,133 @@ +using NINA.Core.Utility; +using NINA.Profile.Interfaces; + +namespace NINA.Plugins.PolarAlignment.OAT { + + /// + /// ViewModel for the OpenAstroExplorer / LX200 OAT polar alignment system. + /// + /// Inherits the full connect/disconnect, nudge, and automated-adjustment loop + /// from . The only OAT-specific + /// additions are: + /// • — configurable INDI device name + /// • / are no-ops (OAT ignores speed) + /// • No backlash on Y axis (altitude motor has no backlash compensation in the driver) + /// + public class OATAlignmentVM : UniversalPolarAlignmentBaseVM { + + public OATAlignmentVM(IProfileService profileService) : base(profileService) { } + + protected override string SystemName => "OAT Polar Alignment"; + + protected override IPolarAlignmentSystem CreateSystem() { + var name = string.IsNullOrWhiteSpace(Properties.Settings.Default.OATDeviceName) + ? OATAlignmentDevice.DefaultDeviceName + : Properties.Settings.Default.OATDeviceName; + return new OATAlignment(name); + } + + // ── OAT-specific settings ──────────────────────────────────────────── + + public string OATDeviceName { + get => string.IsNullOrWhiteSpace(Properties.Settings.Default.OATDeviceName) + ? OATAlignmentDevice.DefaultDeviceName + : Properties.Settings.Default.OATDeviceName; + set { + Properties.Settings.Default.OATDeviceName = value; + CoreUtil.SaveSettings(Properties.Settings.Default); + RaisePropertyChanged(); + } + } + + // ── UniversalPolarAlignmentBaseVM abstract members ─────────────────── + + public override bool DoAutomatedAdjustments { + get => Properties.Settings.Default.OATDoAutomatedAdjustments; + set { + Properties.Settings.Default.OATDoAutomatedAdjustments = value; + CoreUtil.SaveSettings(Properties.Settings.Default); + RaisePropertyChanged(); + } + } + + public override double AutomatedAdjustmentSettleTime { + get => Properties.Settings.Default.OATSettleTime; + set { + Properties.Settings.Default.OATSettleTime = value; + CoreUtil.SaveSettings(Properties.Settings.Default); + RaisePropertyChanged(); + } + } + + /// + /// AZ gear ratio: 1 logical unit = XGearRatio arcminutes sent to POLAR_AZ. + /// Default 1.0 is appropriate for direct arcminute control. + /// + public override float XGearRatio { + get => Properties.Settings.Default.OATXGearRatio; + set { + if (value < 0.01f) value = 0.01f; + Properties.Settings.Default.OATXGearRatio = value; + if (upa != null) upa.XGearRatio = value; + CoreUtil.SaveSettings(Properties.Settings.Default); + RaisePropertyChanged(); + RaisePropertyChanged(nameof(PositionX)); + } + } + + /// Speed is ignored by the OAT driver; exposed for interface compatibility. + public override int XSpeed { + get => 0; + set { /* OAT manages its own speed */ } + } + + /// + /// ALT gear ratio: 1 logical unit = YGearRatio arcminutes sent to POLAR_ALT. + /// Default 1.0. + /// + public override float YGearRatio { + get => Properties.Settings.Default.OATYGearRatio; + set { + if (value < 0.01f) value = 0.01f; + Properties.Settings.Default.OATYGearRatio = value; + if (upa != null) upa.YGearRatio = value; + CoreUtil.SaveSettings(Properties.Settings.Default); + RaisePropertyChanged(); + RaisePropertyChanged(nameof(PositionY)); + } + } + + public override int YSpeed { + get => 0; + set { /* OAT manages its own speed */ } + } + + public override bool ReverseAzimuth { + get => Properties.Settings.Default.OATReverseAzimuth; + set { + Properties.Settings.Default.OATReverseAzimuth = value; + CoreUtil.SaveSettings(Properties.Settings.Default); + RaisePropertyChanged(); + } + } + + public override bool ReverseAltitude { + get => Properties.Settings.Default.OATReverseAltitude; + set { + Properties.Settings.Default.OATReverseAltitude = value; + CoreUtil.SaveSettings(Properties.Settings.Default); + RaisePropertyChanged(); + } + } + + /// Backlash compensation on the AZ axis (arcmin). + public override float XBacklashCompensation { + get => Properties.Settings.Default.OATXBacklashCompensation; + set { + Properties.Settings.Default.OATXBacklashCompensation = value; + CoreUtil.SaveSettings(Properties.Settings.Default); + RaisePropertyChanged(); + } + } + } +} diff --git a/PolarAlignment/PolarAlignmentPlugin.cs b/PolarAlignment/PolarAlignmentPlugin.cs index 86b35ad..1a42339 100644 --- a/PolarAlignment/PolarAlignmentPlugin.cs +++ b/PolarAlignment/PolarAlignmentPlugin.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Composition; @@ -15,25 +15,40 @@ using NINA.Plugin.Interfaces; using NINA.Plugins.PolarAlignment.Avalon; using NINA.Plugins.PolarAlignment.OAPA; +using NINA.Plugins.PolarAlignment.OAT; using NINA.Profile; using NINA.Profile.Interfaces; namespace NINA.Plugins.PolarAlignment { [Export(typeof(IPluginManifest))] public class PolarAlignmentPlugin : PluginBase, INotifyPropertyChanged { - public static UniversalPolarAlignmentVM UniversalPolarAlignmentVM { get; private set; } + /// + /// Live singleton instance, cached at construction time so that other components + /// (e.g. the Touch-N-Stars REST controller) can write settings THROUGH this VM's own + /// property setters instead of poking Properties.Settings.Default directly via reflection. + /// Writing through the wrapper properties keeps NINA's own Options WPF page (and any + /// other live binding to this instance) in sync via RaisePropertyChanged — bypassing it + /// leaves stale cached values in any open WPF binding, which can later get pushed back + /// and silently clobber the REST-driven change. + /// + public static PolarAlignmentPlugin Instance { get; private set; } + + public static UniversalPolarAlignmentVM UniversalPolarAlignmentVM { get; private set; } public static UniversalPolarAlignmentOAPAVM UniversalPolarAlignmentOAPAVM { get; private set; } + public static OATAlignmentVM OATAlignmentVM { get; private set; } public static IPolarAlignmentSystemVM ActiveAlignmentSystemVM => Properties.Settings.Default.SelectedPolarAlignmentSystem switch { "UPAS" => UniversalPolarAlignmentVM, "OAPA" => UniversalPolarAlignmentOAPAVM, - _ => null + "OAT" => OATAlignmentVM, + _ => null }; public PolarAlignmentSystemType SelectedPolarAlignmentSystem { get { - return Enum.TryParse(Properties.Settings.Default.SelectedPolarAlignmentSystem, out var result) + return Enum.TryParse( + Properties.Settings.Default.SelectedPolarAlignmentSystem, out var result) ? result : PolarAlignmentSystemType.None; } set { @@ -43,13 +58,15 @@ public PolarAlignmentSystemType SelectedPolarAlignmentSystem { RaisePropertyChanged(nameof(IsSystemSelected)); RaisePropertyChanged(nameof(IsUPASSelected)); RaisePropertyChanged(nameof(IsOAPASelected)); + RaisePropertyChanged(nameof(IsOATSelected)); RaisePropertyChanged(nameof(ActiveSystem)); } } public bool IsSystemSelected => SelectedPolarAlignmentSystem != PolarAlignmentSystemType.None; - public bool IsUPASSelected => SelectedPolarAlignmentSystem == PolarAlignmentSystemType.UPAS; - public bool IsOAPASelected => SelectedPolarAlignmentSystem == PolarAlignmentSystemType.OAPA; + public bool IsUPASSelected => SelectedPolarAlignmentSystem == PolarAlignmentSystemType.UPAS; + public bool IsOAPASelected => SelectedPolarAlignmentSystem == PolarAlignmentSystemType.OAPA; + public bool IsOATSelected => SelectedPolarAlignmentSystem == PolarAlignmentSystemType.OAT; /// Instance wrapper for XAML binding with PropertyChanged support. public IPolarAlignmentSystemVM ActiveSystem => ActiveAlignmentSystemVM; @@ -58,14 +75,16 @@ public PolarAlignmentSystemType SelectedPolarAlignmentSystem { [ImportingConstructor] public PolarAlignmentPlugin(IProfileService profileService) { + Instance = this; if (Properties.Settings.Default.UpdateSettings) { Properties.Settings.Default.Upgrade(); Properties.Settings.Default.UpdateSettings = false; CoreUtil.SaveSettings(Properties.Settings.Default); } ResetSettingsCommand = new GalaSoft.MvvmLight.Command.RelayCommand(ResetSettings); - UniversalPolarAlignmentVM = new UniversalPolarAlignmentVM(profileService); + UniversalPolarAlignmentVM = new UniversalPolarAlignmentVM(profileService); UniversalPolarAlignmentOAPAVM = new UniversalPolarAlignmentOAPAVM(profileService); + OATAlignmentVM = new OATAlignmentVM(profileService); PluginId = this.Identifier; } @@ -73,23 +92,25 @@ public PolarAlignmentPlugin(IProfileService profileService) { private void ResetSettings() { try { - if(MyMessageBox.Show($"This will reset all TPPA settings to their defaults. {Environment.NewLine}Are you sure?", "Reset All Settings", System.Windows.MessageBoxButton.YesNo, System.Windows.MessageBoxResult.No) == System.Windows.MessageBoxResult.Yes) { + if (MyMessageBox.Show( + $"This will reset all TPPA settings to their defaults. {Environment.NewLine}Are you sure?", + "Reset All Settings", + System.Windows.MessageBoxButton.YesNo, + System.Windows.MessageBoxResult.No) == System.Windows.MessageBoxResult.Yes) { Properties.Settings.Default.Reset(); CoreUtil.SaveSettings(Properties.Settings.Default); RaisePropertyChanged(null); UniversalPolarAlignmentVM.RaiseAllPropertiesChanged(); UniversalPolarAlignmentOAPAVM.RaiseAllPropertiesChanged(); + OATAlignmentVM.RaiseAllPropertiesChanged(); } - } catch(Exception ex) { + } catch (Exception ex) { Logger.Error(ex); } - } public bool DefaultEastDirection { - get { - return Properties.Settings.Default.DefaultEastDirection; - } + get => Properties.Settings.Default.DefaultEastDirection; set { Properties.Settings.Default.DefaultEastDirection = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -98,9 +119,7 @@ public bool DefaultEastDirection { } public bool RefractionAdjustment { - get { - return Properties.Settings.Default.RefractionAdjustment; - } + get => Properties.Settings.Default.RefractionAdjustment; set { Properties.Settings.Default.RefractionAdjustment = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -109,9 +128,7 @@ public bool RefractionAdjustment { } public bool UseContinuousErrorEstimator { - get { - return Properties.Settings.Default.UseContinuousErrorEstimator; - } + get => Properties.Settings.Default.UseContinuousErrorEstimator; set { Properties.Settings.Default.UseContinuousErrorEstimator = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -120,9 +137,7 @@ public bool UseContinuousErrorEstimator { } public double DefaultMoveRate { - get { - return Properties.Settings.Default.DefaultMoveRate; - } + get => Properties.Settings.Default.DefaultMoveRate; set { Properties.Settings.Default.DefaultMoveRate = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -131,9 +146,7 @@ public double DefaultMoveRate { } public int DefaultTargetDistance { - get { - return Properties.Settings.Default.DefaultTargetDistance; - } + get => Properties.Settings.Default.DefaultTargetDistance; set { Properties.Settings.Default.DefaultTargetDistance = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -142,20 +155,16 @@ public int DefaultTargetDistance { } public double DefaultSearchRadius { - get { - return Properties.Settings.Default.DefaultSearchRadius; - } + get => Properties.Settings.Default.DefaultSearchRadius; set { - Properties.Settings.Default.DefaultSearchRadius = Math.Max(30, Math.Min(180, value)); ; + Properties.Settings.Default.DefaultSearchRadius = Math.Max(30, Math.Min(180, value)); CoreUtil.SaveSettings(Properties.Settings.Default); RaisePropertyChanged(); } } public double DefaultAltitudeOffset { - get { - return Properties.Settings.Default.DefaultAltitudeOffset; - } + get => Properties.Settings.Default.DefaultAltitudeOffset; set { Properties.Settings.Default.DefaultAltitudeOffset = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -164,9 +173,7 @@ public double DefaultAltitudeOffset { } public double DefaultAzimuthOffset { - get { - return Properties.Settings.Default.DefaultAzimuthOffset; - } + get => Properties.Settings.Default.DefaultAzimuthOffset; set { Properties.Settings.Default.DefaultAzimuthOffset = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -175,20 +182,16 @@ public double DefaultAzimuthOffset { } public double MoveTimeoutFactor { - get { - return Properties.Settings.Default.MoveTimeoutFactor; - } + get => Properties.Settings.Default.MoveTimeoutFactor; set { Properties.Settings.Default.MoveTimeoutFactor = value; CoreUtil.SaveSettings(Properties.Settings.Default); RaisePropertyChanged(); } } - + public Color AltitudeErrorColor { - get { - return Properties.Settings.Default.AltitudeErrorColor; - } + get => Properties.Settings.Default.AltitudeErrorColor; set { Properties.Settings.Default.AltitudeErrorColor = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -197,9 +200,7 @@ public Color AltitudeErrorColor { } public Color AzimuthErrorColor { - get { - return Properties.Settings.Default.AzimuthErrorColor; - } + get => Properties.Settings.Default.AzimuthErrorColor; set { Properties.Settings.Default.AzimuthErrorColor = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -208,30 +209,25 @@ public Color AzimuthErrorColor { } public Color TotalErrorColor { - get { - return Properties.Settings.Default.TotalErrorColor; - } + get => Properties.Settings.Default.TotalErrorColor; set { Properties.Settings.Default.TotalErrorColor = value; CoreUtil.SaveSettings(Properties.Settings.Default); RaisePropertyChanged(); } } - + public Color TargetCircleColor { - get { - return Properties.Settings.Default.TargetCircleColor; - } + get => Properties.Settings.Default.TargetCircleColor; set { Properties.Settings.Default.TargetCircleColor = value; CoreUtil.SaveSettings(Properties.Settings.Default); RaisePropertyChanged(); } } + public Color SuccessColor { - get { - return Properties.Settings.Default.SuccessColor; - } + get => Properties.Settings.Default.SuccessColor; set { Properties.Settings.Default.SuccessColor = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -240,11 +236,9 @@ public Color SuccessColor { } public double AlignmentTolerance { - get { - return Properties.Settings.Default.AlignmentTolerance; - } + get => Properties.Settings.Default.AlignmentTolerance; set { - if(value < 0) { value = 0; } + if (value < 0) value = 0; Properties.Settings.Default.AlignmentTolerance = value; CoreUtil.SaveSettings(Properties.Settings.Default); RaisePropertyChanged(); @@ -252,9 +246,7 @@ public double AlignmentTolerance { } public bool LogError { - get { - return Properties.Settings.Default.LogError; - } + get => Properties.Settings.Default.LogError; set { Properties.Settings.Default.LogError = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -263,9 +255,7 @@ public bool LogError { } public bool StopTrackingWhenDone { - get { - return Properties.Settings.Default.StopTrackingWhenDone; - } + get => Properties.Settings.Default.StopTrackingWhenDone; set { Properties.Settings.Default.StopTrackingWhenDone = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -274,9 +264,7 @@ public bool StopTrackingWhenDone { } public bool AutoPause { - get { - return Properties.Settings.Default.AutoPause; - } + get => Properties.Settings.Default.AutoPause; set { Properties.Settings.Default.AutoPause = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -288,6 +276,5 @@ public bool AutoPause { protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } - } } diff --git a/PolarAlignment/Properties/Settings.Designer.cs b/PolarAlignment/Properties/Settings.Designer.cs index 3e6eb2c..6443b42 100644 --- a/PolarAlignment/Properties/Settings.Designer.cs +++ b/PolarAlignment/Properties/Settings.Designer.cs @@ -9,20 +9,20 @@ //------------------------------------------------------------------------------ namespace NINA.Plugins.PolarAlignment.Properties { - - + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.13.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - + public static Settings Default { get { return defaultInstance; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("3")] @@ -34,7 +34,7 @@ public double DefaultMoveRate { this["DefaultMoveRate"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -46,7 +46,7 @@ public bool DefaultEastDirection { this["DefaultEastDirection"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("10")] @@ -58,7 +58,7 @@ public int DefaultTargetDistance { this["DefaultTargetDistance"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("10")] @@ -70,7 +70,7 @@ public double DefaultSearchRadius { this["DefaultSearchRadius"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("2")] @@ -82,7 +82,7 @@ public double DefaultAltitudeOffset { this["DefaultAltitudeOffset"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("1")] @@ -94,7 +94,7 @@ public double DefaultAzimuthOffset { this["DefaultAzimuthOffset"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("#FFFFFF00")] @@ -106,7 +106,7 @@ public double DefaultAzimuthOffset { this["AltitudeErrorColor"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("#FF00FFFF")] @@ -118,7 +118,7 @@ public double DefaultAzimuthOffset { this["AzimuthErrorColor"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("#FFFF0000")] @@ -130,7 +130,7 @@ public double DefaultAzimuthOffset { this["TotalErrorColor"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("#FFFF0099")] @@ -142,7 +142,7 @@ public double DefaultAzimuthOffset { this["TargetCircleColor"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -154,7 +154,7 @@ public bool UpdateSettings { this["UpdateSettings"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("2")] @@ -166,7 +166,7 @@ public double MoveTimeoutFactor { this["MoveTimeoutFactor"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("#FF32CD32")] @@ -178,7 +178,7 @@ public double MoveTimeoutFactor { this["SuccessColor"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -190,7 +190,7 @@ public bool LogError { this["LogError"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -202,7 +202,7 @@ public bool RefractionAdjustment { this["RefractionAdjustment"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -226,7 +226,7 @@ public double AlignmentTolerance { this["AlignmentTolerance"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -238,7 +238,7 @@ public bool StopTrackingWhenDone { this["StopTrackingWhenDone"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -250,7 +250,7 @@ public bool UseAvalonPolarAlignmentSystem { this["UseAvalonPolarAlignmentSystem"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("2")] @@ -262,7 +262,7 @@ public float AvalonXGearRatio { this["AvalonXGearRatio"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("700")] @@ -274,7 +274,7 @@ public int AvalonXSpeed { this["AvalonXSpeed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("22")] @@ -286,7 +286,7 @@ public float AvalonYGearRatio { this["AvalonYGearRatio"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("700")] @@ -298,7 +298,7 @@ public int AvalonYSpeed { this["AvalonYSpeed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -310,7 +310,7 @@ public bool DoAutomatedAdjustments { this["DoAutomatedAdjustments"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("2")] @@ -322,7 +322,7 @@ public double AutomatedAdjustmentSettleTime { this["AutomatedAdjustmentSettleTime"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("0")] @@ -334,7 +334,7 @@ public float AvalonXBacklashCompensation { this["AvalonXBacklashCompensation"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -346,7 +346,7 @@ public bool AvalonReverseAzimuth { this["AvalonReverseAzimuth"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -358,7 +358,7 @@ public bool AvalonReverseAltitude { this["AvalonReverseAltitude"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -371,6 +371,8 @@ public bool AutoPause { } } + // ── OAPA ───────────────────────────────────────────────────────────── + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -382,7 +384,7 @@ public bool UseOAPAPolarAlignmentSystem { this["UseOAPAPolarAlignmentSystem"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("None")] @@ -526,5 +528,103 @@ public int OAPAYHoldPercent { this["OAPAYHoldPercent"] = value; } } + + // ── OAT (OpenAstroExplorer / LX200 OpenAstroTech INDI driver) ───────── + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("LX200 OpenAstroTech")] + public string OATDeviceName { + get { + return ((string)(this["OATDeviceName"])); + } + set { + this["OATDeviceName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool OATDoAutomatedAdjustments { + get { + return ((bool)(this["OATDoAutomatedAdjustments"])); + } + set { + this["OATDoAutomatedAdjustments"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("2")] + public double OATSettleTime { + get { + return ((double)(this["OATSettleTime"])); + } + set { + this["OATSettleTime"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public float OATXGearRatio { + get { + return ((float)(this["OATXGearRatio"])); + } + set { + this["OATXGearRatio"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public float OATYGearRatio { + get { + return ((float)(this["OATYGearRatio"])); + } + set { + this["OATYGearRatio"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public float OATXBacklashCompensation { + get { + return ((float)(this["OATXBacklashCompensation"])); + } + set { + this["OATXBacklashCompensation"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool OATReverseAzimuth { + get { + return ((bool)(this["OATReverseAzimuth"])); + } + set { + this["OATReverseAzimuth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool OATReverseAltitude { + get { + return ((bool)(this["OATReverseAltitude"])); + } + set { + this["OATReverseAltitude"] = value; + } + } } } diff --git a/PolarAlignment/Properties/Settings.settings b/PolarAlignment/Properties/Settings.settings index 59d47db..74b2515 100644 --- a/PolarAlignment/Properties/Settings.settings +++ b/PolarAlignment/Properties/Settings.settings @@ -128,5 +128,31 @@ 50 + + + + LX200 OpenAstroTech + + + False + + + 2 + + + 1 + + + 1 + + + 0 + + + False + + + False + diff --git a/PolarAlignment/TPAPAVM.cs b/PolarAlignment/TPAPAVM.cs index 4c1ee3e..785bfaa 100644 --- a/PolarAlignment/TPAPAVM.cs +++ b/PolarAlignment/TPAPAVM.cs @@ -58,6 +58,12 @@ public TPAPAVM(IProfileService profileService, IWeatherDataMediator weatherDataM private readonly AutomatedAdjustmentController automatedAdjustmentController = new AutomatedAdjustmentController(); private bool lastContinuousEstimateStable = true; + /// True once the automated-adjustment controller has detected that commanded + /// moves are not producing any measurable change in the polar error (mount stalled / + /// at an end-stop). The correction loop should stop issuing further moves and abort. + public bool AutomatedAdjustmentStalled => automatedAdjustmentController.StallDetected; + public string AutomatedAdjustmentStallReason => automatedAdjustmentController.StallReason; + public void ActivateFirstStep() { automatedAdjustmentController.Reset(); lastContinuousEstimateStable = true; @@ -194,6 +200,20 @@ public async Task MoveCloser(IProgress progress, Cancellation var activeSystem = ActiveAlignmentSystemVM; if (activeSystem == null || !activeSystem.DoAutomatedAdjustments) { return; } + // Auto-connect the alignment system if it has never been connected. + // The NINA desktop UI has a Connect button, but Touch'N'Stars does not, + // so we connect lazily here on first use. + if (!activeSystem.Connected) { + progress?.Report(new ApplicationStatus() { Status = "Connecting to alignment system..." }); + await activeSystem.Connect(); + if (!activeSystem.Connected) { + progress?.Report(new ApplicationStatus() { Status = "Failed to connect to alignment system — check NINA log." }); + Logger.Error("[TPPA] Auto-connect to alignment system failed."); + return; + } + Logger.Info("[TPPA] Alignment system auto-connected successfully."); + } + var useContinuousErrorEstimator = UseContinuousErrorEstimator; if (useContinuousErrorEstimator && !lastContinuousEstimateStable) { diff --git a/PolarAlignment/UniversalPolarAlignmentBaseVM.cs b/PolarAlignment/UniversalPolarAlignmentBaseVM.cs index 07773e1..393d431 100644 --- a/PolarAlignment/UniversalPolarAlignmentBaseVM.cs +++ b/PolarAlignment/UniversalPolarAlignmentBaseVM.cs @@ -81,6 +81,13 @@ public void Disconnect() { upa.Dispose(); } catch (Exception ex) { Logger.Error(ex); + } finally { + // upa.Connected reflects global INDI connectivity, not this object's own + // disposed state — without clearing the reference, the next Connect() call's + // "if (upa?.Connected == true) return" guard would short-circuit on a stale, + // already-disposed instance and never rebuild a fresh system, silently leaving + // the VM's own Connected flag stuck at false. + upa = null; } Notification.ShowInformation($"Disconnected from {SystemName}"); } From 4848ceab3601f5e2fa4ea4e372ebc95a7ea4569e Mon Sep 17 00:00:00 2001 From: olivier Date: Mon, 27 Jul 2026 12:43:03 +0200 Subject: [PATCH 2/2] Revert non-functional formatting changes to minimize diff Per review feedback from nitr57: PolarAlignmentPlugin.cs and Settings.Designer.cs carried a lot of incidental reformatting (BOM removal, get{}->expression-body conversions, blank-line/ alignment changes) that had nothing to do with OAT support and only made the diff diverge from upstream unnecessarily. Reverted all of it, keeping only the actual OAT-related additions: - PolarAlignmentPlugin.cs: OAT wiring (Instance singleton, VM property, switch arm, IsOATSelected, constructor/reset wiring) - Settings.Designer.cs: the 8 new OAT setting properties - csproj: restored the original BOM --- .../NINA.Plugins.PolarAlignment.csproj | 2 +- PolarAlignment/PolarAlignmentPlugin.cs | 114 +++++++++++------- .../Properties/Settings.Designer.cs | 68 +++++------ 3 files changed, 107 insertions(+), 77 deletions(-) diff --git a/PolarAlignment/NINA.Plugins.PolarAlignment.csproj b/PolarAlignment/NINA.Plugins.PolarAlignment.csproj index 7fc885b..24799c0 100644 --- a/PolarAlignment/NINA.Plugins.PolarAlignment.csproj +++ b/PolarAlignment/NINA.Plugins.PolarAlignment.csproj @@ -1,4 +1,4 @@ - + net10.0 Library diff --git a/PolarAlignment/PolarAlignmentPlugin.cs b/PolarAlignment/PolarAlignmentPlugin.cs index 1a42339..3f98d44 100644 --- a/PolarAlignment/PolarAlignmentPlugin.cs +++ b/PolarAlignment/PolarAlignmentPlugin.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Composition; @@ -33,22 +33,21 @@ public class PolarAlignmentPlugin : PluginBase, INotifyPropertyChanged { /// public static PolarAlignmentPlugin Instance { get; private set; } - public static UniversalPolarAlignmentVM UniversalPolarAlignmentVM { get; private set; } + public static UniversalPolarAlignmentVM UniversalPolarAlignmentVM { get; private set; } public static UniversalPolarAlignmentOAPAVM UniversalPolarAlignmentOAPAVM { get; private set; } - public static OATAlignmentVM OATAlignmentVM { get; private set; } + public static OATAlignmentVM OATAlignmentVM { get; private set; } public static IPolarAlignmentSystemVM ActiveAlignmentSystemVM => Properties.Settings.Default.SelectedPolarAlignmentSystem switch { "UPAS" => UniversalPolarAlignmentVM, "OAPA" => UniversalPolarAlignmentOAPAVM, - "OAT" => OATAlignmentVM, - _ => null + "OAT" => OATAlignmentVM, + _ => null }; public PolarAlignmentSystemType SelectedPolarAlignmentSystem { get { - return Enum.TryParse( - Properties.Settings.Default.SelectedPolarAlignmentSystem, out var result) + return Enum.TryParse(Properties.Settings.Default.SelectedPolarAlignmentSystem, out var result) ? result : PolarAlignmentSystemType.None; } set { @@ -64,9 +63,9 @@ public PolarAlignmentSystemType SelectedPolarAlignmentSystem { } public bool IsSystemSelected => SelectedPolarAlignmentSystem != PolarAlignmentSystemType.None; - public bool IsUPASSelected => SelectedPolarAlignmentSystem == PolarAlignmentSystemType.UPAS; - public bool IsOAPASelected => SelectedPolarAlignmentSystem == PolarAlignmentSystemType.OAPA; - public bool IsOATSelected => SelectedPolarAlignmentSystem == PolarAlignmentSystemType.OAT; + public bool IsUPASSelected => SelectedPolarAlignmentSystem == PolarAlignmentSystemType.UPAS; + public bool IsOAPASelected => SelectedPolarAlignmentSystem == PolarAlignmentSystemType.OAPA; + public bool IsOATSelected => SelectedPolarAlignmentSystem == PolarAlignmentSystemType.OAT; /// Instance wrapper for XAML binding with PropertyChanged support. public IPolarAlignmentSystemVM ActiveSystem => ActiveAlignmentSystemVM; @@ -82,9 +81,9 @@ public PolarAlignmentPlugin(IProfileService profileService) { CoreUtil.SaveSettings(Properties.Settings.Default); } ResetSettingsCommand = new GalaSoft.MvvmLight.Command.RelayCommand(ResetSettings); - UniversalPolarAlignmentVM = new UniversalPolarAlignmentVM(profileService); + UniversalPolarAlignmentVM = new UniversalPolarAlignmentVM(profileService); UniversalPolarAlignmentOAPAVM = new UniversalPolarAlignmentOAPAVM(profileService); - OATAlignmentVM = new OATAlignmentVM(profileService); + OATAlignmentVM = new OATAlignmentVM(profileService); PluginId = this.Identifier; } @@ -92,11 +91,7 @@ public PolarAlignmentPlugin(IProfileService profileService) { private void ResetSettings() { try { - if (MyMessageBox.Show( - $"This will reset all TPPA settings to their defaults. {Environment.NewLine}Are you sure?", - "Reset All Settings", - System.Windows.MessageBoxButton.YesNo, - System.Windows.MessageBoxResult.No) == System.Windows.MessageBoxResult.Yes) { + if(MyMessageBox.Show($"This will reset all TPPA settings to their defaults. {Environment.NewLine}Are you sure?", "Reset All Settings", System.Windows.MessageBoxButton.YesNo, System.Windows.MessageBoxResult.No) == System.Windows.MessageBoxResult.Yes) { Properties.Settings.Default.Reset(); CoreUtil.SaveSettings(Properties.Settings.Default); RaisePropertyChanged(null); @@ -104,13 +99,16 @@ private void ResetSettings() { UniversalPolarAlignmentOAPAVM.RaiseAllPropertiesChanged(); OATAlignmentVM.RaiseAllPropertiesChanged(); } - } catch (Exception ex) { + } catch(Exception ex) { Logger.Error(ex); } + } public bool DefaultEastDirection { - get => Properties.Settings.Default.DefaultEastDirection; + get { + return Properties.Settings.Default.DefaultEastDirection; + } set { Properties.Settings.Default.DefaultEastDirection = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -119,7 +117,9 @@ public bool DefaultEastDirection { } public bool RefractionAdjustment { - get => Properties.Settings.Default.RefractionAdjustment; + get { + return Properties.Settings.Default.RefractionAdjustment; + } set { Properties.Settings.Default.RefractionAdjustment = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -128,7 +128,9 @@ public bool RefractionAdjustment { } public bool UseContinuousErrorEstimator { - get => Properties.Settings.Default.UseContinuousErrorEstimator; + get { + return Properties.Settings.Default.UseContinuousErrorEstimator; + } set { Properties.Settings.Default.UseContinuousErrorEstimator = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -137,7 +139,9 @@ public bool UseContinuousErrorEstimator { } public double DefaultMoveRate { - get => Properties.Settings.Default.DefaultMoveRate; + get { + return Properties.Settings.Default.DefaultMoveRate; + } set { Properties.Settings.Default.DefaultMoveRate = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -146,7 +150,9 @@ public double DefaultMoveRate { } public int DefaultTargetDistance { - get => Properties.Settings.Default.DefaultTargetDistance; + get { + return Properties.Settings.Default.DefaultTargetDistance; + } set { Properties.Settings.Default.DefaultTargetDistance = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -155,16 +161,20 @@ public int DefaultTargetDistance { } public double DefaultSearchRadius { - get => Properties.Settings.Default.DefaultSearchRadius; + get { + return Properties.Settings.Default.DefaultSearchRadius; + } set { - Properties.Settings.Default.DefaultSearchRadius = Math.Max(30, Math.Min(180, value)); + Properties.Settings.Default.DefaultSearchRadius = Math.Max(30, Math.Min(180, value)); ; CoreUtil.SaveSettings(Properties.Settings.Default); RaisePropertyChanged(); } } public double DefaultAltitudeOffset { - get => Properties.Settings.Default.DefaultAltitudeOffset; + get { + return Properties.Settings.Default.DefaultAltitudeOffset; + } set { Properties.Settings.Default.DefaultAltitudeOffset = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -173,7 +183,9 @@ public double DefaultAltitudeOffset { } public double DefaultAzimuthOffset { - get => Properties.Settings.Default.DefaultAzimuthOffset; + get { + return Properties.Settings.Default.DefaultAzimuthOffset; + } set { Properties.Settings.Default.DefaultAzimuthOffset = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -182,16 +194,20 @@ public double DefaultAzimuthOffset { } public double MoveTimeoutFactor { - get => Properties.Settings.Default.MoveTimeoutFactor; + get { + return Properties.Settings.Default.MoveTimeoutFactor; + } set { Properties.Settings.Default.MoveTimeoutFactor = value; CoreUtil.SaveSettings(Properties.Settings.Default); RaisePropertyChanged(); } } - + public Color AltitudeErrorColor { - get => Properties.Settings.Default.AltitudeErrorColor; + get { + return Properties.Settings.Default.AltitudeErrorColor; + } set { Properties.Settings.Default.AltitudeErrorColor = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -200,7 +216,9 @@ public Color AltitudeErrorColor { } public Color AzimuthErrorColor { - get => Properties.Settings.Default.AzimuthErrorColor; + get { + return Properties.Settings.Default.AzimuthErrorColor; + } set { Properties.Settings.Default.AzimuthErrorColor = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -209,25 +227,30 @@ public Color AzimuthErrorColor { } public Color TotalErrorColor { - get => Properties.Settings.Default.TotalErrorColor; + get { + return Properties.Settings.Default.TotalErrorColor; + } set { Properties.Settings.Default.TotalErrorColor = value; CoreUtil.SaveSettings(Properties.Settings.Default); RaisePropertyChanged(); } } - + public Color TargetCircleColor { - get => Properties.Settings.Default.TargetCircleColor; + get { + return Properties.Settings.Default.TargetCircleColor; + } set { Properties.Settings.Default.TargetCircleColor = value; CoreUtil.SaveSettings(Properties.Settings.Default); RaisePropertyChanged(); } } - public Color SuccessColor { - get => Properties.Settings.Default.SuccessColor; + get { + return Properties.Settings.Default.SuccessColor; + } set { Properties.Settings.Default.SuccessColor = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -236,9 +259,11 @@ public Color SuccessColor { } public double AlignmentTolerance { - get => Properties.Settings.Default.AlignmentTolerance; + get { + return Properties.Settings.Default.AlignmentTolerance; + } set { - if (value < 0) value = 0; + if(value < 0) { value = 0; } Properties.Settings.Default.AlignmentTolerance = value; CoreUtil.SaveSettings(Properties.Settings.Default); RaisePropertyChanged(); @@ -246,7 +271,9 @@ public double AlignmentTolerance { } public bool LogError { - get => Properties.Settings.Default.LogError; + get { + return Properties.Settings.Default.LogError; + } set { Properties.Settings.Default.LogError = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -255,7 +282,9 @@ public bool LogError { } public bool StopTrackingWhenDone { - get => Properties.Settings.Default.StopTrackingWhenDone; + get { + return Properties.Settings.Default.StopTrackingWhenDone; + } set { Properties.Settings.Default.StopTrackingWhenDone = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -264,7 +293,9 @@ public bool StopTrackingWhenDone { } public bool AutoPause { - get => Properties.Settings.Default.AutoPause; + get { + return Properties.Settings.Default.AutoPause; + } set { Properties.Settings.Default.AutoPause = value; CoreUtil.SaveSettings(Properties.Settings.Default); @@ -276,5 +307,6 @@ public bool AutoPause { protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } + } } diff --git a/PolarAlignment/Properties/Settings.Designer.cs b/PolarAlignment/Properties/Settings.Designer.cs index 6443b42..eca659d 100644 --- a/PolarAlignment/Properties/Settings.Designer.cs +++ b/PolarAlignment/Properties/Settings.Designer.cs @@ -9,20 +9,20 @@ //------------------------------------------------------------------------------ namespace NINA.Plugins.PolarAlignment.Properties { - - + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.13.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - + public static Settings Default { get { return defaultInstance; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("3")] @@ -34,7 +34,7 @@ public double DefaultMoveRate { this["DefaultMoveRate"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -46,7 +46,7 @@ public bool DefaultEastDirection { this["DefaultEastDirection"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("10")] @@ -58,7 +58,7 @@ public int DefaultTargetDistance { this["DefaultTargetDistance"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("10")] @@ -70,7 +70,7 @@ public double DefaultSearchRadius { this["DefaultSearchRadius"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("2")] @@ -82,7 +82,7 @@ public double DefaultAltitudeOffset { this["DefaultAltitudeOffset"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("1")] @@ -94,7 +94,7 @@ public double DefaultAzimuthOffset { this["DefaultAzimuthOffset"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("#FFFFFF00")] @@ -106,7 +106,7 @@ public double DefaultAzimuthOffset { this["AltitudeErrorColor"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("#FF00FFFF")] @@ -118,7 +118,7 @@ public double DefaultAzimuthOffset { this["AzimuthErrorColor"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("#FFFF0000")] @@ -130,7 +130,7 @@ public double DefaultAzimuthOffset { this["TotalErrorColor"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("#FFFF0099")] @@ -142,7 +142,7 @@ public double DefaultAzimuthOffset { this["TargetCircleColor"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -154,7 +154,7 @@ public bool UpdateSettings { this["UpdateSettings"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("2")] @@ -166,7 +166,7 @@ public double MoveTimeoutFactor { this["MoveTimeoutFactor"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("#FF32CD32")] @@ -178,7 +178,7 @@ public double MoveTimeoutFactor { this["SuccessColor"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -190,7 +190,7 @@ public bool LogError { this["LogError"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -202,7 +202,7 @@ public bool RefractionAdjustment { this["RefractionAdjustment"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -226,7 +226,7 @@ public double AlignmentTolerance { this["AlignmentTolerance"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("True")] @@ -238,7 +238,7 @@ public bool StopTrackingWhenDone { this["StopTrackingWhenDone"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -250,7 +250,7 @@ public bool UseAvalonPolarAlignmentSystem { this["UseAvalonPolarAlignmentSystem"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("2")] @@ -262,7 +262,7 @@ public float AvalonXGearRatio { this["AvalonXGearRatio"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("700")] @@ -274,7 +274,7 @@ public int AvalonXSpeed { this["AvalonXSpeed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("22")] @@ -286,7 +286,7 @@ public float AvalonYGearRatio { this["AvalonYGearRatio"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("700")] @@ -298,7 +298,7 @@ public int AvalonYSpeed { this["AvalonYSpeed"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -310,7 +310,7 @@ public bool DoAutomatedAdjustments { this["DoAutomatedAdjustments"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("2")] @@ -322,7 +322,7 @@ public double AutomatedAdjustmentSettleTime { this["AutomatedAdjustmentSettleTime"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("0")] @@ -334,7 +334,7 @@ public float AvalonXBacklashCompensation { this["AvalonXBacklashCompensation"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -346,7 +346,7 @@ public bool AvalonReverseAzimuth { this["AvalonReverseAzimuth"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -358,7 +358,7 @@ public bool AvalonReverseAltitude { this["AvalonReverseAltitude"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -371,8 +371,6 @@ public bool AutoPause { } } - // ── OAPA ───────────────────────────────────────────────────────────── - [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("False")] @@ -384,7 +382,7 @@ public bool UseOAPAPolarAlignmentSystem { this["UseOAPAPolarAlignmentSystem"] = value; } } - + [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("None")]